Thomas Klausner | 1334a6f | 2016-12-02 14:56:16 +0100 | [diff] [blame] | 1 | AES Coding Tips for Developers |
| 2 | |
| 3 | NOTE: WinZip^(R) users do not need to read or understand the information |
| 4 | contained on this page. It is intended for developers of Zip file utilities. |
| 5 | |
| 6 | This document contains information that may be helpful to developers and other |
| 7 | interested parties who wish to support the AE-1 and AE-2 AES encryption formats |
| 8 | in their own Zip file utilities. WinZip Computing makes no warranties regarding |
| 9 | the information provided in this document. In particular, WinZip Computing does |
| 10 | not represent or warrant that the information provided here is free from errors |
| 11 | or is suitable for any particular use, or that the file formats described here |
| 12 | will be supported in future versions of WinZip. You should test and validate |
| 13 | all code and techniques in accordance with good programming practice. |
| 14 | |
| 15 | This information supplements the basic encryption specification document found |
| 16 | here. |
| 17 | |
| 18 | This document assumes that you are using Dr. Brian Gladman's AES encryption |
| 19 | package. Dr. Gladman has generously made public a sample application that |
| 20 | demonstrates the use of his encryption/decryption and other routines, and the |
| 21 | code samples shown below are derived from this sample application. Dr. |
| 22 | Gladman's AES library and the sample application are available from the AES |
| 23 | project page on Dr. Gladman's web site. |
| 24 | |
| 25 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 26 | |
| 27 | Generating a salt value |
| 28 | |
| 29 | Please read the discussion of salt values in the encryption specification. |
| 30 | |
| 31 | Dr. Gladman has provided a pseudo-random number generator in the files PRNG.C |
| 32 | and PRNG.H. You may find this suitable for generating salt values. These files |
| 33 | are included in the sample package available through the AES project page on |
| 34 | Dr. Gladman's web site. |
| 35 | |
| 36 | Here are guidelines for using Dr. Gladman's generator. Note that the generator |
| 37 | is used rather like an I/O stream: it is opened (initialized), used, and |
| 38 | finally closed. To obtain the best results, it is recommended that you |
| 39 | initialize the generator when your application starts and close it when your |
| 40 | application closes. (If you are coding in C++, you may wish to wrap these |
| 41 | actions in a C++ class that initializes the generator on construction and |
| 42 | closes it on destruction.) |
| 43 | |
| 44 | 1. You will need to provide an entropy function in your code for |
| 45 | initialization of the generator. The entropy function need not be |
| 46 | particularly sophisticated for this use. Here is one possibility for such a |
| 47 | function, based primarily upon the Windows performance counter: |
| 48 | |
| 49 | int entropy_fun( |
| 50 | unsigned char buf[], |
| 51 | unsigned int len) |
| 52 | { |
| 53 | unsigned __int64 pentium_tsc[1]; |
| 54 | unsigned int i; |
| 55 | static unsigned int num = 0; |
| 56 | // this sample code returns the following sequence of entropy information |
| 57 | // - the current 8-byte Windows performance counter value |
| 58 | // - an 8-byte representation of the current date/time |
| 59 | // - an 8-byte value built from the current process ID and thread ID |
| 60 | // - all subsequent calls return the then-current 8-byte performance |
| 61 | // counter value |
| 62 | switch (num) |
| 63 | { |
| 64 | case 1: |
| 65 | ++num; |
| 66 | // use a value that is unlikely to repeat across system reboots |
| 67 | GetSystemTimeAsFileTime((FILETIME *)pentium_tsc); |
| 68 | break; |
| 69 | case 2: |
| 70 | ++num; |
| 71 | { |
| 72 | // use a value that distinguishes between different instances of this |
| 73 | // code that might be running on different processors at the same time |
| 74 | unsigned __int32 processtest = GetCurrentProcessId(); |
| 75 | unsigned __int32 threadtest = GetCurrentThreadId(); |
| 76 | pentium_tsc[0] = processtest; |
| 77 | pentium_tsc[0] = (pentium_tsc[0] << 32) + threadtest; |
| 78 | } |
| 79 | break; |
| 80 | case 0: |
| 81 | ++num; |
| 82 | // fall through to default case |
| 83 | default: |
| 84 | // use a rapidly-changing value |
| 85 | // Note: check QueryPerformanceFrequency() first to |
| 86 | // ensure that QueryPerformanceCounter() will work. |
| 87 | QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc); |
| 88 | break; |
| 89 | } |
| 90 | for(i = 0; i < 8 && i < len; ++i) |
| 91 | buf[i] = ((unsigned char*)pentium_tsc)[i]; |
| 92 | return i; |
| 93 | } |
| 94 | |
| 95 | Note: the required prototype for the entropy function is defined in PRNG.H. |
| 96 | |
| 97 | 2. Initialize the generator by calling prng_init(), providing the addresses of |
| 98 | your entropy function and of an instance of a prng_ctx structure (defined |
| 99 | in PRNG.H). The prng_ctx variable maintains a context for the generator and |
| 100 | is used as a parameter for the other generator functions. Therefore, the |
| 101 | variable's state must be maintained until the generator is closed. |
| 102 | |
| 103 | prng_ctx ctx; |
| 104 | prng_init(entropy_fun, &ctx); |
| 105 | |
| 106 | You only need to do this once per application session (as long as you keep |
| 107 | the "stream" open). |
| 108 | |
| 109 | 3. To obtain a sequence of random bytes of arbitrary size, use prng_rand(). |
| 110 | This code obtains 16 random bytes, suitable for use as a salt value for |
| 111 | 256-bit AES encryption: |
| 112 | |
| 113 | unsigned char buffer[16]; |
| 114 | prng_rand(buffer, sizeof(buffer), &ctx); |
| 115 | |
| 116 | Note that the ctx parameter is the same prng_ctx variable that was used in |
| 117 | the initialization call. |
| 118 | |
| 119 | 4. When you are done with the generator (this would normally be when your |
| 120 | application closes), close it by calling prng_end: |
| 121 | |
| 122 | prng_end(&ctx); |
| 123 | |
| 124 | Again, the ctx parameter is the same prng_ctx variable that was used in the |
| 125 | initialization call. |
| 126 | |
| 127 | Encryption and decryption |
| 128 | |
| 129 | The actual encryption and decryption of data are handled quite similarly, and |
| 130 | again are rather stream-like: a stream is "opened", data is passed to it for |
| 131 | encryption or decryption, and then it is closed. The password verifier is |
| 132 | returned when the stream is opened, and the authentication code is returned |
| 133 | when the stream is closed. |
| 134 | |
| 135 | Here is the basic technique: |
| 136 | |
| 137 | 1. Initialize the "stream" for encryption or decryption and obtain the |
| 138 | password verification value. |
| 139 | |
| 140 | There is no difference in the initialization, regardless of whether you are |
| 141 | encrypting or decrypting: |
| 142 | |
| 143 | fcrypt_ctx zctx; // the encryption context |
| 144 | int rc = fcrypt_init( |
| 145 | KeySize, // extra data value indicating key size |
| 146 | pszPassword, // the password |
| 147 | strlen(pszPassword), // number of bytes in password |
| 148 | achSALT, // the salt |
| 149 | achPswdVerifier, // on return contains password verifier |
| 150 | &zctx); // encryption context |
| 151 | |
| 152 | The return value is 0 if the initialization was successful; non-zero values |
| 153 | indicate errors. Note that passwords are null-terminated ANSI strings; |
| 154 | embedded nulls must not be used. (To avoid incompatibilities between the |
| 155 | various character sets in use, especially in different versions of Windows, |
| 156 | users should be encouraged to use passwords containing only the "standard" |
| 157 | characters in the range 32-127.) |
| 158 | |
| 159 | The function returns the password verification value in achPswdVerifier, |
| 160 | which must be a 2-byte buffer. If you are encrypting, store this value in |
| 161 | the Zip file as indicated by the encryption specification. If you are |
| 162 | decrypting, compare this returned value to the value stored in the Zip |
| 163 | file. If they are different, then either the password provided by your user |
| 164 | was incorrect or the encrypted file has been altered in some way since it |
| 165 | was encrypted. (Note that if they match, there is still a 1 in 65,536 |
| 166 | chance that an incorrect password was provided.) |
| 167 | |
| 168 | The initialized encryption context (zctx) is used as a parameter to the |
| 169 | encryption/decryption functions. Therefore, its state must be maintained |
| 170 | until the "stream" is closed. |
| 171 | |
| 172 | 2. Encrypt or decrypt the data. |
| 173 | |
| 174 | To encrypt: |
| 175 | |
| 176 | fcrypt_encrypt( |
| 177 | pchData, // pointer to the data to encrypt |
| 178 | cb, // how many bytes to encrypt |
| 179 | &zctx); // encryption context |
| 180 | |
| 181 | To decrypt: |
| 182 | |
| 183 | fcrypt_decrypt( |
| 184 | pchData, // pointer to the data to decrypt |
| 185 | cb, // how many bytes to decrypt |
| 186 | &zctx); // decryption context |
| 187 | |
| 188 | You may need to call the encrypt or decrypt function multiple times, |
| 189 | passing in successive chunks of data in the buffer. For AE-1 and AE-2 |
| 190 | compatibility, the buffer size must be a multiple of 16 bytes except for |
| 191 | the last buffer, which may be smaller. For efficiency, a larger buffer size |
| 192 | such as 32,768 would generally be used. |
| 193 | |
| 194 | Note: to encrypt zero-length files, simply skip this step. You will still |
| 195 | obtain and use the password verifier (step 1) and authentication code (step |
| 196 | 3). |
| 197 | |
| 198 | 3. Close the "stream" and obtain the authentication code. |
| 199 | |
| 200 | When encryption/decryption is complete, close the "stream" as follows: |
| 201 | |
| 202 | int rc = fcrypt_end( |
| 203 | achMAC, // on return contains the authentication code |
| 204 | &zctx); // encryption context |
| 205 | |
| 206 | The return value is the size of the authentication code, which will always |
| 207 | be 10 for AE-1 and AE-2. The authentication code itself is returned in your |
| 208 | buffer at achMAC, which is an array of char, sized to hold at least 10 |
| 209 | characters. If you are encrypting, store this value in the Zip file as |
| 210 | indicated by the encryption specification; if you are decrypting, compare |
| 211 | this value to the value stored in the Zip file. If the values are |
| 212 | different, either the password is incorrect or the encrypted data has been |
| 213 | altered subsequent to storage. |
| 214 | |
| 215 | Note that decryption can fail even if the encrypted data is unaltered and |
| 216 | the password verifier was correct in step 1. The password verifier is |
| 217 | useful as a quick way to detect most incorrect passwords, but it is not |
| 218 | perfect and on rare occasions (1 out of 65,536) it will fail to detect an |
| 219 | incorrect password. It is therefore important for you to check the |
| 220 | authentication code on completion even though the password verifier was |
| 221 | correct. |
| 222 | |
| 223 | Notes |
| 224 | |
| 225 | • Dr. Gladman's AES code depends on the byte order (little-endian or |
| 226 | big-endian) used by the computing platform the code will run on. This is |
| 227 | determined by a C preprocessor constant called PLATFORM_BYTE_ORDER, which |
| 228 | is defined in the file AESOPT.H. You should be sure that |
| 229 | PLATFORM_BYTE_ORDER gets the proper value for your platform; if it does |
| 230 | not, you will need to define it yourself to the correct value. When using |
| 231 | the Microsoft compiler on Intel platforms it does get the proper value, |
| 232 | which on these platforms is AES_LITTLE_ENDIAN. We have, however, had a |
| 233 | report that it does not default properly when Borland C++ Builder is used, |
| 234 | and that manual assignment is necessary. For additional information on this |
| 235 | topic, refer to the comments within AESOPT.H. |
| 236 | |
| 237 | Change history |
| 238 | |
| 239 | Changes made in document version 1.04, July, 2008: |
| 240 | |
| 241 | A. Sample Entropy Function |
| 242 | |
| 243 | The sample entropy function was changed to include information near the |
| 244 | very beginning of the entropy stream that's unique to the day and to the |
| 245 | process and thread. |
| 246 | |
| 247 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 248 | |
| 249 | Document version: 1.04 |
| 250 | Last modified: July 21, 2008 |
| 251 | |
| 252 | Copyright(C) 2003-2016 WinZip International LLC. |
| 253 | All Rights Reserved |