| AES Coding Tips for Developers |
| |
| NOTE: WinZip^(R) users do not need to read or understand the information |
| contained on this page. It is intended for developers of Zip file utilities. |
| |
| This document contains information that may be helpful to developers and other |
| interested parties who wish to support the AE-1 and AE-2 AES encryption formats |
| in their own Zip file utilities. WinZip Computing makes no warranties regarding |
| the information provided in this document. In particular, WinZip Computing does |
| not represent or warrant that the information provided here is free from errors |
| or is suitable for any particular use, or that the file formats described here |
| will be supported in future versions of WinZip. You should test and validate |
| all code and techniques in accordance with good programming practice. |
| |
| This information supplements the basic encryption specification document found |
| here. |
| |
| This document assumes that you are using Dr. Brian Gladman's AES encryption |
| package. Dr. Gladman has generously made public a sample application that |
| demonstrates the use of his encryption/decryption and other routines, and the |
| code samples shown below are derived from this sample application. Dr. |
| Gladman's AES library and the sample application are available from the AES |
| project page on Dr. Gladman's web site. |
| |
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| |
| Generating a salt value |
| |
| Please read the discussion of salt values in the encryption specification. |
| |
| Dr. Gladman has provided a pseudo-random number generator in the files PRNG.C |
| and PRNG.H. You may find this suitable for generating salt values. These files |
| are included in the sample package available through the AES project page on |
| Dr. Gladman's web site. |
| |
| Here are guidelines for using Dr. Gladman's generator. Note that the generator |
| is used rather like an I/O stream: it is opened (initialized), used, and |
| finally closed. To obtain the best results, it is recommended that you |
| initialize the generator when your application starts and close it when your |
| application closes. (If you are coding in C++, you may wish to wrap these |
| actions in a C++ class that initializes the generator on construction and |
| closes it on destruction.) |
| |
| 1. You will need to provide an entropy function in your code for |
| initialization of the generator. The entropy function need not be |
| particularly sophisticated for this use. Here is one possibility for such a |
| function, based primarily upon the Windows performance counter: |
| |
| int entropy_fun( |
| unsigned char buf[], |
| unsigned int len) |
| { |
| unsigned __int64 pentium_tsc[1]; |
| unsigned int i; |
| static unsigned int num = 0; |
| // this sample code returns the following sequence of entropy information |
| // - the current 8-byte Windows performance counter value |
| // - an 8-byte representation of the current date/time |
| // - an 8-byte value built from the current process ID and thread ID |
| // - all subsequent calls return the then-current 8-byte performance |
| // counter value |
| switch (num) |
| { |
| case 1: |
| ++num; |
| // use a value that is unlikely to repeat across system reboots |
| GetSystemTimeAsFileTime((FILETIME *)pentium_tsc); |
| break; |
| case 2: |
| ++num; |
| { |
| // use a value that distinguishes between different instances of this |
| // code that might be running on different processors at the same time |
| unsigned __int32 processtest = GetCurrentProcessId(); |
| unsigned __int32 threadtest = GetCurrentThreadId(); |
| pentium_tsc[0] = processtest; |
| pentium_tsc[0] = (pentium_tsc[0] << 32) + threadtest; |
| } |
| break; |
| case 0: |
| ++num; |
| // fall through to default case |
| default: |
| // use a rapidly-changing value |
| // Note: check QueryPerformanceFrequency() first to |
| // ensure that QueryPerformanceCounter() will work. |
| QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc); |
| break; |
| } |
| for(i = 0; i < 8 && i < len; ++i) |
| buf[i] = ((unsigned char*)pentium_tsc)[i]; |
| return i; |
| } |
| |
| Note: the required prototype for the entropy function is defined in PRNG.H. |
| |
| 2. Initialize the generator by calling prng_init(), providing the addresses of |
| your entropy function and of an instance of a prng_ctx structure (defined |
| in PRNG.H). The prng_ctx variable maintains a context for the generator and |
| is used as a parameter for the other generator functions. Therefore, the |
| variable's state must be maintained until the generator is closed. |
| |
| prng_ctx ctx; |
| prng_init(entropy_fun, &ctx); |
| |
| You only need to do this once per application session (as long as you keep |
| the "stream" open). |
| |
| 3. To obtain a sequence of random bytes of arbitrary size, use prng_rand(). |
| This code obtains 16 random bytes, suitable for use as a salt value for |
| 256-bit AES encryption: |
| |
| unsigned char buffer[16]; |
| prng_rand(buffer, sizeof(buffer), &ctx); |
| |
| Note that the ctx parameter is the same prng_ctx variable that was used in |
| the initialization call. |
| |
| 4. When you are done with the generator (this would normally be when your |
| application closes), close it by calling prng_end: |
| |
| prng_end(&ctx); |
| |
| Again, the ctx parameter is the same prng_ctx variable that was used in the |
| initialization call. |
| |
| Encryption and decryption |
| |
| The actual encryption and decryption of data are handled quite similarly, and |
| again are rather stream-like: a stream is "opened", data is passed to it for |
| encryption or decryption, and then it is closed. The password verifier is |
| returned when the stream is opened, and the authentication code is returned |
| when the stream is closed. |
| |
| Here is the basic technique: |
| |
| 1. Initialize the "stream" for encryption or decryption and obtain the |
| password verification value. |
| |
| There is no difference in the initialization, regardless of whether you are |
| encrypting or decrypting: |
| |
| fcrypt_ctx zctx; // the encryption context |
| int rc = fcrypt_init( |
| KeySize, // extra data value indicating key size |
| pszPassword, // the password |
| strlen(pszPassword), // number of bytes in password |
| achSALT, // the salt |
| achPswdVerifier, // on return contains password verifier |
| &zctx); // encryption context |
| |
| The return value is 0 if the initialization was successful; non-zero values |
| indicate errors. Note that passwords are null-terminated ANSI strings; |
| embedded nulls must not be used. (To avoid incompatibilities between the |
| various character sets in use, especially in different versions of Windows, |
| users should be encouraged to use passwords containing only the "standard" |
| characters in the range 32-127.) |
| |
| The function returns the password verification value in achPswdVerifier, |
| which must be a 2-byte buffer. If you are encrypting, store this value in |
| the Zip file as indicated by the encryption specification. If you are |
| decrypting, compare this returned value to the value stored in the Zip |
| file. If they are different, then either the password provided by your user |
| was incorrect or the encrypted file has been altered in some way since it |
| was encrypted. (Note that if they match, there is still a 1 in 65,536 |
| chance that an incorrect password was provided.) |
| |
| The initialized encryption context (zctx) is used as a parameter to the |
| encryption/decryption functions. Therefore, its state must be maintained |
| until the "stream" is closed. |
| |
| 2. Encrypt or decrypt the data. |
| |
| To encrypt: |
| |
| fcrypt_encrypt( |
| pchData, // pointer to the data to encrypt |
| cb, // how many bytes to encrypt |
| &zctx); // encryption context |
| |
| To decrypt: |
| |
| fcrypt_decrypt( |
| pchData, // pointer to the data to decrypt |
| cb, // how many bytes to decrypt |
| &zctx); // decryption context |
| |
| You may need to call the encrypt or decrypt function multiple times, |
| passing in successive chunks of data in the buffer. For AE-1 and AE-2 |
| compatibility, the buffer size must be a multiple of 16 bytes except for |
| the last buffer, which may be smaller. For efficiency, a larger buffer size |
| such as 32,768 would generally be used. |
| |
| Note: to encrypt zero-length files, simply skip this step. You will still |
| obtain and use the password verifier (step 1) and authentication code (step |
| 3). |
| |
| 3. Close the "stream" and obtain the authentication code. |
| |
| When encryption/decryption is complete, close the "stream" as follows: |
| |
| int rc = fcrypt_end( |
| achMAC, // on return contains the authentication code |
| &zctx); // encryption context |
| |
| The return value is the size of the authentication code, which will always |
| be 10 for AE-1 and AE-2. The authentication code itself is returned in your |
| buffer at achMAC, which is an array of char, sized to hold at least 10 |
| characters. If you are encrypting, store this value in the Zip file as |
| indicated by the encryption specification; if you are decrypting, compare |
| this value to the value stored in the Zip file. If the values are |
| different, either the password is incorrect or the encrypted data has been |
| altered subsequent to storage. |
| |
| Note that decryption can fail even if the encrypted data is unaltered and |
| the password verifier was correct in step 1. The password verifier is |
| useful as a quick way to detect most incorrect passwords, but it is not |
| perfect and on rare occasions (1 out of 65,536) it will fail to detect an |
| incorrect password. It is therefore important for you to check the |
| authentication code on completion even though the password verifier was |
| correct. |
| |
| Notes |
| |
| • Dr. Gladman's AES code depends on the byte order (little-endian or |
| big-endian) used by the computing platform the code will run on. This is |
| determined by a C preprocessor constant called PLATFORM_BYTE_ORDER, which |
| is defined in the file AESOPT.H. You should be sure that |
| PLATFORM_BYTE_ORDER gets the proper value for your platform; if it does |
| not, you will need to define it yourself to the correct value. When using |
| the Microsoft compiler on Intel platforms it does get the proper value, |
| which on these platforms is AES_LITTLE_ENDIAN. We have, however, had a |
| report that it does not default properly when Borland C++ Builder is used, |
| and that manual assignment is necessary. For additional information on this |
| topic, refer to the comments within AESOPT.H. |
| |
| Change history |
| |
| Changes made in document version 1.04, July, 2008: |
| |
| A. Sample Entropy Function |
| |
| The sample entropy function was changed to include information near the |
| very beginning of the entropy stream that's unique to the day and to the |
| process and thread. |
| |
| ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| |
| Document version: 1.04 |
| Last modified: July 21, 2008 |
| |
| Copyright(C) 2003-2016 WinZip International LLC. |
| All Rights Reserved |