// ========================================================================= // Decryption function // Note: PlaintextLength must be set to the size of the PlaintextData buffer on // entry; on exit, this will be set to the size of the buffer used. NTSTATUS ImpCypherDecryptSectorData( IN GUID* CypherGUID, IN LARGE_INTEGER SectorID, // Indexed from zero IN int SectorSize, // In bytes IN int KeyLength, // In bits IN FREEOTFEBYTE* Key, IN char* KeyASCII, // ASCII representation of "Key" IN int IVLength, // In bits IN FREEOTFEBYTE* IV, IN int CyphertextLength, // In bytes IN FREEOTFEBYTE* CyphertextData, OUT FREEOTFEBYTE* PlaintextData ) { NTSTATUS status = STATUS_SUCCESS; // Null IV in case we're not given an IV char zeroIV[FREEOTFE_MAX_CYPHER_BLOCKSIZE]; int i; INTEGER_128 blockID128; int errnum; TWOFISH(CONTEXT) context; DEBUGOUTCYPHERIMPL(DEBUGLEV_ENTER, (TEXT("ImpCypherDecryptData\n"))); if ( (IsEqualGUID(&CIPHER_GUID_TWOFISH_256_XTS, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_TWOFISH_192_XTS, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_TWOFISH_128_XTS, CypherGUID)) ) { // Generate index in correct format // XTS uses: // *) The sector index (i.e. the number of N-bit sectors) // *) The first sector is sector 0 // *) Littleendian format LARGE_INTEGER__To__INTEGER_128_LittleEndian( SectorID, blockID128 ); if ((errnum = xts_decrypt( &context, &(TWOFISH(set_key)), &(TWOFISH(encrypt)), &(TWOFISH(decrypt)), ((KeyLength/2)), Key, ((KeyLength/2)), &(Key[(KeyLength/2)/8]), CyphertextData, CyphertextLength, PlaintextData, blockID128 )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to decrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } else if ( (IsEqualGUID(&CIPHER_GUID_TWOFISH_256_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_TWOFISH_192_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_TWOFISH_128_CBC, CypherGUID)) ) { // Handle situation where a NULL/zero length IV is passed in if ( (IVLength == 0) || (IV == NULL) ) { FREEOTFE_MEMZERO(&zeroIV, sizeof(zeroIV)); IV = (char*)&zeroIV; } // Process data using CBC mode // // CBC Mode DECRYPT: // *) Setup key // *) For each block... // *) Decrypt cyphertext with key // *) XOR output plaintext with IV // *) IV = Cyphertext data // Note: "dec" specified; we're DECRYPTING TWOFISH(set_key)(&context, Key, KeyLength, dec); for (i=0; i < CyphertextLength; i += (TWOFISH_BLOCK_SIZE / 8)) { TWOFISH(decrypt)(&context, CyphertextData+i, PlaintextData+i); XORBlock(PlaintextData+i, IV, PlaintextData+i, (TWOFISH_BLOCK_SIZE / 8)); IV = CyphertextData+i; } } else { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unsupported cipher GUID passed in.\n"))); status = STATUS_INVALID_PARAMETER; } SecZeroMemory(&context, sizeof(context)); DEBUGOUTCYPHERIMPL(DEBUGLEV_EXIT, (TEXT("ImpCypherDecryptData\n"))); return status; }
// ========================================================================= // Generate PBKDF2 key based on HMAC NTSTATUS ImplKDFPBKDF2( IN PDataHashFn FnHash, IN GUID HashGUID, IN HASH HashDetails, IN unsigned int PLength, // In bits IN unsigned char* P, IN unsigned int SLength, // In bits IN unsigned char* S, IN unsigned int c, // Iterations IN int dkLenBits, // In *bits* IN OUT unsigned int* DerivedKeyLength, // In bits OUT unsigned char* DerivedKey ) { NTSTATUS status; unsigned int i; unsigned int l; // unsigned int r; // In *bytes* unsigned int dkLen; // In *bytes* unsigned int hLen; // In *bytes* unsigned char* T_; unsigned int T_SizeBytes; // In *bytes* DEBUGOUTKDFDRV(DEBUGLEV_ENTER, ("ImplKDFPBKDF2\n")); status = STATUS_SUCCESS; T_ = NULL; T_SizeBytes = 0; // Sanity check // This should be picked up by the caller; this is belt & braces if ( (HashDetails.BlockSize <= 0) || (HashDetails.Length <= 0) ) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("PBKDF2 implementation called with a hash that has an undefined/zero length/blocksize\n")); status = STATUS_INTERNAL_ERROR; } else { // This is PBKDF2 based on HMAC - the HMAC function returns the same // number of bytes as is in the hash it uses hLen = (HashDetails.Length / 8); } if (dkLenBits < 0) { dkLenBits = DEFAULT_PBKDF2_KEY_SIZE; } dkLen = (dkLenBits / 8); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLenBits = %d bits\n", dkLenBits)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLen = %d bytes\n", dkLen)); // Sanity check // This should be picked up by the caller; this is belt & braces if ((unsigned int)dkLenBits > *DerivedKeyLength) { // The output buffer can store the requested number of bits, right? // Note that dkLenBits can be -ve to indicate the full key length is to be // returned DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Requested number of bits is larger than supplied buffer\n")); status = STATUS_BUFFER_TOO_SMALL; } // Step 1 // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and // stop. DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 1: n/a; skipped...\n")); // (Step skipped - because dkLen is an integer, it can never be more // than 2^(31-1) - far less than (2^32 - 1) * hLen) // Step 2 // 2. Let l be the number of hLen-octet blocks in the derived key, // rounding up, and let r be the number of octets in the last // block: // // l = CEIL (dkLen / hLen) , // r = dkLen - (l - 1) * hLen . // // Here, CEIL (x) is the "ceiling" function, i.e. the smallest // integer greater than, or equal to, x. if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 2: Executing...\n")); // Note: div always rounds down towards zero l = (dkLen / hLen); // Because div always rounds down towards zero, we may need to increment it // by one if (dkLen > (l * hLen)) { l++; } DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("dkLen = %d bytes\n", dkLen)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("hLen = %d bytes\n", hLen)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("l = %d\n", l)); // r = dkLen - ((l - 1) * hLen); // DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("r = %d bytes\n", r)); } // Step 3 // 3. For each block of the derived key apply the function F if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 3: Executing...\n")); T_SizeBytes = (hLen * l); T_ = FREEOTFE_MEMALLOC(T_SizeBytes); for(i = 1; i<=l; i++) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Loop: i = %d\n", i)); if (!(NT_SUCCESS(PBKDF2_F( FnHash, HashGUID, HashDetails, PLength, P, SLength, S, c, i, // This is correct (-1), as our loop starts // from 1 &T_[((i-1) * hLen)] )))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Call to PBKDF2 function \"F\" failed.\n")); status = STATUS_INTERNAL_ERROR; break; } } } // Step 4 // 4. Concatenate the blocks and extract the first dkLen octets to // produce a derived key DK: // // DK = T_1 || T_2 || ... || T_l<0..r-1> if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 4: Executing...\n")); FREEOTFE_MEMCPY( DerivedKey, T_, (dkLenBits / 8) ); } // Step 5 // 5. Output the derived key DK. if (NT_SUCCESS(status)) { DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("PBKDF2 Step 5: Executing...\n")); *DerivedKeyLength = dkLenBits; } // Cleanup... DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Freeing off any used buffers...\n")); if (T_ != NULL) { SecZeroMemory(T_, T_SizeBytes); FREEOTFE_FREE(T_); } DEBUGOUTKDFDRV(DEBUGLEV_EXIT, ("ImplKDFPBKDF2\n")); return status; }
// ========================================================================= // This is the PBKDF2 PRF function "F" // The PRF used is HMAC NTSTATUS PBKDF2_F( IN PDataHashFn FnHash, IN GUID HashGUID, IN HASH HashDetails, IN unsigned int PLength, // In bits IN unsigned char* P, IN unsigned int SLength, // In bits IN unsigned char* S, IN unsigned int c, IN unsigned int i, OUT unsigned char* T_ ) { NTSTATUS status; unsigned char* U_; unsigned char* tmpU_Buffer; unsigned char* saltAndCount; unsigned int hLen; // In *bytes* unsigned int hLenBits; // In *Bits* unsigned int actualHLenBits; // In *bits* unsigned int saltAndCountSizeBytes; // In *bytes* unsigned int j, k; DEBUGOUTKDFDRV(DEBUGLEV_ENTER, ("PBKDF2_F\n")); status = STATUS_SUCCESS; U_ = NULL; tmpU_Buffer = NULL; saltAndCount = NULL; // From PKCS#5: // function F is defined as the exclusive-or sum of the // first c iterates of the underlying pseudorandom function PRF // applied to the password P and the concatenation of the salt S // and the block index i: // // F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c // // where // U_1 = PRF (P, S || INT (i)) , // U_2 = PRF (P, U_1) , // ... // U_c = PRF (P, U_{c-1}) . // // Here, INT (i) is a four-octet encoding of the integer i, most // significant octet first. DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Iterations requested: %d\n", c)); // This is PBKDF2 based on HMAC - the HMAC function returns the same // number of bytes as is in the hash it uses hLen = (HashDetails.Length / 8); hLenBits = HashDetails.Length; // Convert bits to bytes, and add 4 (specified in PKCS #5) for the "i" saltAndCountSizeBytes = ((SLength / 8) + 4); saltAndCount = FREEOTFE_MEMALLOC(saltAndCountSizeBytes); FREEOTFE_MEMCPY( saltAndCount, S, (SLength / 8) ); // Concatenate the salt // Note: *Bitwise* AND :) saltAndCount[ (SLength / 8) ] = ((i & 0xFF000000) / 0x01000000); saltAndCount[((SLength / 8)+1)] = ((i & 0x00FF0000) / 0x00010000); saltAndCount[((SLength / 8)+2)] = ((i & 0x0000FF00) / 0x00000100); saltAndCount[((SLength / 8)+3)] = ((i & 0x000000FF) / 0x00000001); U_ = FREEOTFE_MEMALLOC(hLen); tmpU_Buffer = FREEOTFE_MEMALLOC(hLen); // Process U_1 if (NT_SUCCESS(status)) { actualHLenBits = hLenBits; DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("First HMAC: SLength = %d\n", SLength)); DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("First HMAC: actualHLenBits = %d\n", actualHLenBits)); if (!(NT_SUCCESS(ImplMACHMAC( FnHash, HashGUID, HashDetails, -1, // Retrieve full HMAC PLength, P, (saltAndCountSizeBytes * 8), // In *bits* saltAndCount, &actualHLenBits, // In bits U_ )))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("First call to HMAC function \"F\" failed.\n")); status = STATUS_INTERNAL_ERROR; } else { // Sanity check if (actualHLenBits != hLenBits) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("HMAC function didn't return expected number of bits?!\n")); status = STATUS_INTERNAL_ERROR; } else { // Copy to output buffer - note that this will be XORd if // there's more c>1 FREEOTFE_MEMCPY( T_, U_, (actualHLenBits / 8) ); } } } // Process subsequent U_n if (NT_SUCCESS(status)) { for(j = 2; j <= c; j++) { actualHLenBits = hLenBits; DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Subsequent HMAC: actualHLenBits = %d\n", actualHLenBits)); if (!(NT_SUCCESS(ImplMACHMAC( FnHash, HashGUID, HashDetails, -1, // Retrieve full HMAC PLength, P, hLenBits, // In bits U_, &actualHLenBits, // In bits tmpU_Buffer )))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Call to HMAC function \"F\" failed.\n")); status = STATUS_INTERNAL_ERROR; break; } else { // Move from temp buffer to U_ buffer DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("Copying (%d)\n", hLen)); FREEOTFE_MEMCPY( U_, tmpU_Buffer, hLen ); // XOR with previous iteration DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("XORing...\n")); for(k = 0; k < hLen; k++) { T_[k] ^= U_[k]; } DEBUGOUTKDFDRV(DEBUGLEV_INFO, ("OK.\n")); } } } // Note: No need to set the output "T_" explicitly, as this is done during // processing if (tmpU_Buffer != NULL) { SecZeroMemory(tmpU_Buffer, hLen); FREEOTFE_FREE(tmpU_Buffer); } if (U_ != NULL) { SecZeroMemory(U_, hLen); FREEOTFE_FREE(U_); } if (saltAndCount != NULL) { SecZeroMemory(saltAndCount, saltAndCountSizeBytes); FREEOTFE_FREE(saltAndCount); } DEBUGOUTKDFDRV(DEBUGLEV_EXIT, ("PBKDF2_F\n")); return status; }
// ========================================================================= // Generate HMAC NTSTATUS ImplMACHMAC( IN PDataHashFn FnHash, IN GUID HashGUID, IN HASH HashDetails, IN int tBits, // In *bits* IN unsigned int KLength, // In bits IN unsigned char* K, IN unsigned int textLength, // In bits IN unsigned char* text, IN OUT unsigned int* MACLength, // In bits OUT unsigned char* MAC ) { NTSTATUS status; unsigned int KLengthBytes; // In *bytes* unsigned int B; // In *bytes* unsigned int i; unsigned char* Kzero; unsigned char* step2TmpBuf; unsigned char* step4OutBuf; unsigned char* step5OutBuf; unsigned char* step6OutBuf; unsigned char* step7OutBuf; unsigned char* step8OutBuf; unsigned char* step9OutBuf; unsigned int step2TmpBufSize; // In *bytes* unsigned int step5OutBufSize; // In *bytes* unsigned int step6OutBufSize; // In *bytes* unsigned int step8OutBufSize; // In *bytes* unsigned int step9OutBufSize; // In *bytes* unsigned int step6ValidBits; unsigned int step9ValidBits; unsigned int hashValidBits; unsigned int hashUseLenBytes; unsigned int outputBits; DEBUGOUTMACDRV(DEBUGLEV_ENTER, ("ImplMACHMAC\n")); status = STATUS_SUCCESS; Kzero = NULL; step4OutBuf = NULL; step5OutBuf = NULL; step6OutBuf = NULL; step7OutBuf = NULL; step8OutBuf = NULL; step9OutBuf = NULL; // Sanity checks // This should be picked up by the caller; this is belt & braces if ( (HashDetails.BlockSize <= 0) || (HashDetails.Length <= 0) ) { DEBUGOUTMACDRV(DEBUGLEV_ERROR, ("HMAC implementation called with a hash that has an undefined blocksize/output\n")); status = STATUS_INTERNAL_ERROR; } B = (HashDetails.BlockSize / 8); KLengthBytes = (KLength / 8); // Compute the HMAC... // --------- // FIPS-198 Steps 1-3 // i.e. Set Kzero to be a "B" length string if (NT_SUCCESS(status)) { Kzero = FREEOTFE_MEMALLOC(B); if (KLengthBytes == B) { // Step 1 DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 1: Executing...\n")); // Set K0 = K FREEOTFE_MEMCPY( Kzero, K, B ); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 2: n/a; skipped\n")); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 3: n/a; skipped\n")); } else if (KLengthBytes > B) { // Step 2 DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 1: n/a; skipped\n")); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 2: Executing...\n")); // Hash K to get a hashlength string, then pad out to B bytes // with NULLs step2TmpBufSize = (HashDetails.Length / 8); // Divide by 8 to get bytes from bits hashValidBits = HashDetails.Length; // In *bits* DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Allocating tmp buffer (%d bytes)...\n", step2TmpBufSize)); step2TmpBuf = FREEOTFE_MEMALLOC(step2TmpBufSize); status = FnHash( &HashGUID, KLength, K, &hashValidBits, step2TmpBuf ); if (!(NT_SUCCESS(status))) { DEBUGOUTMACDRV(DEBUGLEV_ERROR, ("HMAC Step 2: Call to hash driver failed\n")); status = STATUS_INTERNAL_ERROR; } else { hashUseLenBytes = min(B, (hashValidBits / 8)); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Hash output: %d bits (%d bytes)...\n", hashValidBits, (hashValidBits / 8))); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Using first %d bytes...\n", hashUseLenBytes)); // Copy the hash value to K0 FREEOTFE_MEMCPY( Kzero, step2TmpBuf, hashUseLenBytes ); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Step 2 Kzero: Padding end with NULLs (%d bytes of nulls)...\n", (B - hashUseLenBytes))); // Pad K0 with NULLs SecZeroMemory(&Kzero[hashUseLenBytes], (B - hashUseLenBytes)); } DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Zeroing and freeing hash buffer\n")); SecZeroMemory(step2TmpBuf, step2TmpBufSize); FREEOTFE_FREE(step2TmpBuf); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 3: n/a; skipped\n")); } else { // Step 3 DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 1: n/a; skipped\n")); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 2: n/a; skipped\n")); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 3: Executing...\n")); FREEOTFE_MEMCPY( Kzero, K, KLengthBytes ); DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Step 3 Kzero: Padding end with NULLs (%d bytes of nulls)...\n", (B - KLengthBytes))); // Pad K0 with NULLs SecZeroMemory(&Kzero[KLengthBytes], (B - KLengthBytes)); } } // --------- // FIPS-198 Step 4 // XOR K0 with ipad (0x36) if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 4: Executing...\n")); step4OutBuf = FREEOTFE_MEMALLOC(B); for (i = 0; i < B; i++) { step4OutBuf[i] = Kzero[i] ^ 0x36; } } // --------- // FIPS-198 Step 5 // Append input text onto the end of the output from step 4 if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 5: Executing...\n")); step5OutBufSize = (B + (textLength / 8)); step5OutBuf = FREEOTFE_MEMALLOC(step5OutBufSize); FREEOTFE_MEMCPY( step5OutBuf, step4OutBuf, B ); FREEOTFE_MEMCPY( &step5OutBuf[B], text, (textLength / 8) ); } // --------- // FIPS-198 Step 6 // Apply H to the output from step 5 if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 6: Executing...\n")); step6ValidBits = HashDetails.Length; step6OutBufSize = (step6ValidBits / 8); step6OutBuf = FREEOTFE_MEMALLOC(step6OutBufSize); status = FnHash( &HashGUID, (step5OutBufSize * 8), step5OutBuf, &step6ValidBits, step6OutBuf ); } // --------- // FIPS-198 Step 7 // XOR K0 with opad (0x5C) if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 7: Executing...\n")); step7OutBuf = FREEOTFE_MEMALLOC(B); for (i = 0; i < B; i++) { step7OutBuf[i] = Kzero[i] ^ 0x5C; } } // --------- // FIPS-198 Step 8 // Append the result from step 6 to step 7 if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 8: Executing...\n")); step8OutBufSize = (B + (step6ValidBits / 8)); step8OutBuf = FREEOTFE_MEMALLOC(step8OutBufSize); FREEOTFE_MEMCPY( step8OutBuf, step7OutBuf, B ); FREEOTFE_MEMCPY( &step8OutBuf[B], step6OutBuf, (step6ValidBits / 8) ); } // --------- // FIPS-198 Step 9 // Apply H to the output from step 8 if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 9: Executing...\n")); step9ValidBits = HashDetails.Length; step9OutBufSize = (step9ValidBits / 8); step9OutBuf = FREEOTFE_MEMALLOC(step9OutBufSize); status = FnHash( &HashGUID, (step8OutBufSize * 8), step8OutBuf, &step9ValidBits, step9OutBuf ); } // --------- // FIPS-198 Step 10 // Select the leftmost t bytes of output from step 9 if (NT_SUCCESS(status)) { DEBUGOUTMACDRV(DEBUGLEV_INFO, ("HMAC Step 10: Executing...\n")); // Truncate, if necessary, to tBits of data if (tBits >= 0) { outputBits = tBits; } else { outputBits = step9ValidBits; } // Sanity check on the buffer... if (*MACLength < outputBits) { DEBUGOUTMACDRV(DEBUGLEV_ERROR, ("Buffer too small; supplied %d bits, need %d bits\n", MACLength, outputBits)); status = STATUS_BUFFER_TOO_SMALL; } else { // Copy the appropriate number of bits to the output buffer FREEOTFE_MEMCPY( MAC, step9OutBuf, (outputBits / 8) // Convert to bytes ); *MACLength = outputBits; } } // Cleanup... DEBUGOUTMACDRV(DEBUGLEV_INFO, ("Freeing off any used buffers...\n")); if (step9OutBuf != NULL) { SecZeroMemory(step9OutBuf, step9OutBufSize); FREEOTFE_FREE(step9OutBuf); } if (step8OutBuf != NULL) { SecZeroMemory(step8OutBuf, step8OutBufSize); FREEOTFE_FREE(step8OutBuf); } if (step7OutBuf != NULL) { SecZeroMemory(step7OutBuf, B); FREEOTFE_FREE(step7OutBuf); } if (step6OutBuf != NULL) { SecZeroMemory(step6OutBuf, step6OutBufSize); FREEOTFE_FREE(step6OutBuf); } if (step5OutBuf != NULL) { SecZeroMemory(step5OutBuf, step5OutBufSize); FREEOTFE_FREE(step5OutBuf); } if (step4OutBuf != NULL) { SecZeroMemory(step4OutBuf, B); FREEOTFE_FREE(step4OutBuf); } if (Kzero != NULL) { SecZeroMemory(Kzero, B); FREEOTFE_FREE(Kzero); } DEBUGOUTMACDRV(DEBUGLEV_EXIT, ("ImplMACHMAC\n")); return status; }
// ========================================================================= // Decryption function // Note: PlaintextLength must be set to the size of the PlaintextData buffer on // entry; on exit, this will be set to the size of the buffer used. NTSTATUS ImpCypherDecryptSectorData( IN GUID* CypherGUID, IN LARGE_INTEGER SectorID, // Indexed from zero IN int SectorSize, // In bytes IN int KeyLength, // In bits IN FREEOTFEBYTE* Key, IN char* KeyASCII, // ASCII representation of "Key" IN int IVLength, // In bits IN FREEOTFEBYTE* IV, IN int CyphertextLength, // In bytes IN FREEOTFEBYTE* CyphertextData, OUT FREEOTFEBYTE* PlaintextData ) { NTSTATUS status = STATUS_SUCCESS; // libtomcrypt can't handle NULL IVs in CBC mode - it ASSERTs that IV != NULL char ltcNullIV[FREEOTFE_MAX_CYPHER_BLOCKSIZE]; int cipher; symmetric_CBC *cbc; int errnum; DEBUGOUTCYPHERIMPL(DEBUGLEV_ENTER, (TEXT("ImpCypherDecryptData\n"))); if (!( (IsEqualGUID(&CIPHER_GUID_CAST5, CypherGUID)) )) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unsupported cipher GUID passed in.\n"))); status = STATUS_INVALID_PARAMETER; } // libtomcrypt can't handle NULL IVs in CBC mode - it ASSERTs that IV != NULL if ( (IVLength == 0) || (IV == NULL) ) { FREEOTFE_MEMZERO(<cNullIV, sizeof(ltcNullIV)); IV = (char*)<cNullIV; } cbc = FREEOTFE_MEMALLOC(sizeof(symmetric_CBC)); FREEOTFE_MEMZERO(cbc, sizeof(symmetric_CBC)); if NT_SUCCESS(status) { status = InitLTCCypher(&cipher); } if NT_SUCCESS(status) { // Start a CBC session if ((errnum = cbc_start( cipher, IV, Key, (KeyLength/8), 0, cbc )) != CRYPT_OK) { status = STATUS_UNSUCCESSFUL; DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to start CBC session (errnum: %d)\n"), errnum)); } else { if ((errnum = cbc_decrypt( CyphertextData, PlaintextData, CyphertextLength, cbc )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to encrypt/decrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } cbc_done(cbc); } } SecZeroMemory(cbc, sizeof(symmetric_CBC)); FREEOTFE_FREE(cbc); DEBUGOUTCYPHERIMPL(DEBUGLEV_EXIT, (TEXT("ImpCypherDecryptData\n"))); return status; }
// ---------------------------------------------------------------------------- BOOL LUKS_DumpLUKSDataToFile( MODULE_DETAILS_MAIN* mainDriver, WCHAR* filename, unsigned char* userKey, BOOL baseIVCypherOnHashLength, WCHAR* dumpFilename ) { BOOL retval; LUKS_HEADER_EXT LUKSHeader; int i; char hashTitle[MAX_PRETTYPRINTED_TITLE]; char cypherTitle[MAX_PRETTYPRINTED_TITLE]; int keySlot; BYTE* keyMaterial; char IVHashTitle[MAX_PRETTYPRINTED_TITLE]; char IVCypherTitle[MAX_PRETTYPRINTED_TITLE]; char sectorIVGenMethodTitle[MAX_PRETTYPRINTED_TITLE]; char filenameAsChar[FREEOTFE_MAX_FILENAME_LENGTH * 4]; FILE* hFile; WCHAR* tmpStringGUID; char* prettyPrintData; int prettyPrintDataLength; retval = TRUE; // Sanity; source file must exist if (retval) { retval = ( SDUCheckFileExists(filename) && (!(SDUCheckFileExists(dumpFilename))) ); } if (retval) { hFile = _wfopen(dumpFilename, TEXT("w+")); driver_AddStdDumpHeader(hFile, "LUKS Dump"); // Get the LUKS header from the volume... if (!(LUKS_ReadLUKSHeader(filename, &LUKSHeader))) { if (LUKS_IsLUKSVolume(filename)) { fprintf(hFile, "ERROR: Unable to read LUKS header?!\n"); } else { fprintf(hFile, "Unable to read LUKS header; this does not appear to be a LUKS volume.\n"); } } else { fprintf(hFile, "\n"); fprintf(hFile, "\n"); driver_AddStdDumpSection(hFile, "cryptsetup Style Dump"); wcstombs(filenameAsChar, filename, sizeof(filenameAsChar)); fprintf(hFile, "LUKS header information for %s\n", filenameAsChar); SecZeroMemory(filenameAsChar, sizeof(filenameAsChar)); fprintf(hFile, "\n"); fprintf(hFile, "Version: %d\n", LUKSHeader.rawHeader.version); fprintf(hFile, "Cipher name: %s\n", LUKSHeader.rawHeader.cipher_name); fprintf(hFile, "Cipher mode: %s\n", LUKSHeader.rawHeader.cipher_mode); fprintf(hFile, "Hash spec: %s\n", LUKSHeader.rawHeader.hash_spec); fprintf(hFile, "Payload offset: %d\n", LUKSHeader.rawHeader.payload_offset); fprintf(hFile, "MK bits: %d\n", (LUKSHeader.rawHeader.key_bytes * 8)); // Convert from bytes to bits fprintf(hFile, "MK digest: "); LUKS_PrettyHex( hFile, (BYTE*)(&(LUKSHeader.rawHeader.mk_digest)), LUKS_DIGESTSIZE ); fprintf(hFile, "\n"); fprintf(hFile, "MK salt: "); LUKS_PrettyHex( hFile, &(LUKSHeader.rawHeader.mk_digest_salt[0]), (LUKS_SALTSIZE / 2) ); fprintf(hFile, "\n"); fprintf(hFile, " "); LUKS_PrettyHex( hFile, &(LUKSHeader.rawHeader.mk_digest_salt[(LUKS_SALTSIZE / 2)]), (LUKS_SALTSIZE / 2) ); fprintf(hFile, "\n"); fprintf(hFile, "MK iterations: %d\n", LUKSHeader.rawHeader.mk_digest_iter); fprintf(hFile, "UUID: %s\n", LUKSHeader.rawHeader.uuid); fprintf(hFile, "\n"); for (i = 0; i < LUKS_NUMKEYS; i++) { if (LUKSHeader.rawHeader.key_slot[i].active != LUKS_KEY_ENABLED) { fprintf(hFile, "Key Slot %d: DISABLED\n", i); } else { fprintf(hFile, "Key Slot %d: ENABLED\n", i); fprintf(hFile, " Iterations: %d\n", LUKSHeader.rawHeader.key_slot[i].iterations); fprintf(hFile, " Salt: "); LUKS_PrettyHex( hFile, &(LUKSHeader.rawHeader.key_slot[i].salt[0]), (LUKS_SALTSIZE / 2) ); fprintf(hFile, "\n"); fprintf(hFile, " "); LUKS_PrettyHex( hFile, &(LUKSHeader.rawHeader.key_slot[i].salt[(LUKS_SALTSIZE / 2)]), (LUKS_SALTSIZE / 2) ); fprintf(hFile, "\n"); fprintf(hFile, " Key material offset: %d\n", LUKSHeader.rawHeader.key_slot[i].key_material_offset); fprintf(hFile, " AF stripes: %d\n", LUKSHeader.rawHeader.key_slot[i].stripes); } } fprintf(hFile, "\n"); fprintf(hFile, "\n"); driver_AddStdDumpSection(hFile, "Mapped FreeOTFE Drivers"); if (!(LUKS_MapToFreeOTFE(baseIVCypherOnHashLength, &LUKSHeader))) { fprintf(hFile, "One or more of the following:\n"); fprintf(hFile, "\n"); fprintf(hFile, " *) The hash algorithm\n"); fprintf(hFile, " *) The cypher\n"); fprintf(hFile, " *) The IV generation method\n"); fprintf(hFile, "\n"); fprintf(hFile, "specified in the LUKS header could not be mapped to a FreeOTFE equivalent.\n"); fprintf(hFile, "This may be because the required FreeOTFE driver hasn''t been installed and\n"); fprintf(hFile, "started, or because the required LUKS option is not supported in this\n"); fprintf(hFile, "version of FreeOTFE\n"); } else { strcpy(hashTitle, "ERROR: Unable to determine hash title?!"); driver_HashPrettyprintAlgTechTitle_ByDriverAndGUID_char( LUKSHeader.hashKernelModeDeviceName, LUKSHeader.hashGUID, hashTitle, sizeof(hashTitle) ); strcpy(IVCypherTitle, "ERROR: Unable to determine cypher title?!"); driver_CypherPrettyprintAlgTechTitle_ByDriverAndGUID_char( LUKSHeader.cypherKernelModeDeviceName, LUKSHeader.cypherGUID, cypherTitle, sizeof(cypherTitle) ); if (LUKSHeader.sectorIVGenMethod != SCTRIVGEN_ESSIV) { strcpy(IVHashTitle, "IV hash n/a"); strcpy(IVCypherTitle, "IV cypher n/a"); } else { strcpy(IVHashTitle, "ERROR: Unable to determine IV hash title?!"); driver_HashPrettyprintAlgTechTitle_ByDriverAndGUID_char( LUKSHeader.IVHashKernelModeDeviceName, LUKSHeader.IVHashGUID, IVHashTitle, sizeof(IVHashTitle) ); strcpy(IVCypherTitle, "ERROR: Unable to determine IV cypher title?!"); driver_CypherPrettyprintAlgTechTitle_ByDriverAndGUID_char( LUKSHeader.IVCypherKernelModeDeviceName, LUKSHeader.IVCypherGUID, IVCypherTitle, sizeof(IVCypherTitle) ); } fprintf(hFile, "Hash pretty title : %s\n", hashTitle); fprintf(hFile, "Hash driver KM name : "); driver_DumpWCHAR(hFile, LUKSHeader.hashKernelModeDeviceName); fprintf(hFile, "\n"); fprintf(hFile, "Hash GUID : "); GUIDToWCHAR(&(LUKSHeader.hashGUID), &tmpStringGUID); SDUwcstoupper(tmpStringGUID, tmpStringGUID); driver_DumpWCHAR(hFile, tmpStringGUID); SecZeroAndFreeWCHARMemory(tmpStringGUID); fprintf(hFile, "\n"); fprintf(hFile, "Cypher pretty title : %s\n", cypherTitle); fprintf(hFile, "Cypher driver KM name : "); driver_DumpWCHAR(hFile, LUKSHeader.cypherKernelModeDeviceName); fprintf(hFile, "\n"); fprintf(hFile, "Cypher GUID : "); GUIDToWCHAR(&(LUKSHeader.cypherGUID), &tmpStringGUID); SDUwcstoupper(tmpStringGUID, tmpStringGUID); driver_DumpWCHAR(hFile, tmpStringGUID); SecZeroAndFreeWCHARMemory(tmpStringGUID); fprintf(hFile, "\n"); driver_PrettyprintSectorIVGenMethod_char( LUKSHeader.sectorIVGenMethod, sectorIVGenMethodTitle, sizeof(sectorIVGenMethodTitle) ); fprintf(hFile, "Sector IV generation : %s\n", sectorIVGenMethodTitle); if (LUKSHeader.sectorIVGenMethod == SCTRIVGEN_ESSIV) { fprintf(hFile, " IV hash pretty title : %s\n", IVHashTitle); fprintf(hFile, " IV hash driver KM name : "); driver_DumpWCHAR(hFile, LUKSHeader.IVHashKernelModeDeviceName); fprintf(hFile, "\n"); fprintf(hFile, " IV hash GUID : "); GUIDToWCHAR(&(LUKSHeader.IVHashGUID), &tmpStringGUID); SDUwcstoupper(tmpStringGUID, tmpStringGUID); driver_DumpWCHAR(hFile, tmpStringGUID); SecZeroAndFreeWCHARMemory(tmpStringGUID); fprintf(hFile, "\n"); if (baseIVCypherOnHashLength) { fprintf(hFile, " IV cypher : <based on IV hash length>\n"); } else { fprintf(hFile, " IV cypher : <same as main cypher>\n"); } fprintf(hFile, " IV cypher pretty title : %s\n", IVCypherTitle); fprintf(hFile, " IV cypher driver KM name: "); driver_DumpWCHAR(hFile, LUKSHeader.IVCypherKernelModeDeviceName); fprintf(hFile, "\n"); fprintf(hFile, " IV cypher GUID : "); GUIDToWCHAR(&(LUKSHeader.IVCypherGUID), &tmpStringGUID); SDUwcstoupper(tmpStringGUID, tmpStringGUID); driver_DumpWCHAR(hFile, tmpStringGUID); SecZeroAndFreeWCHARMemory(tmpStringGUID); fprintf(hFile, "\n"); } } fprintf(hFile, "\n"); fprintf(hFile, "\n"); driver_AddStdDumpSection(hFile, "Master Key"); fprintf(hFile, "User supplied password : %s\n", userKey); keyMaterial = NULL; if (!(LUKS_RecoverMasterKey( mainDriver, filename, userKey, baseIVCypherOnHashLength, &LUKSHeader, &keySlot, &keyMaterial ))) { fprintf(hFile, "No master key could be recovered with the specified password.\n"); } else { prettyPrintDataLength = SDUPrettyPrintHexBufReq( LUKSHeader.rawHeader.key_bytes, 8 ); prettyPrintData = malloc(prettyPrintDataLength); if (prettyPrintData == NULL) { fprintf(hFile, "Unable to allocate memory to store prettyprinted master key\n"); } else { SDUPrettyPrintHex( keyMaterial, 0, LUKSHeader.rawHeader.key_bytes, prettyPrintData, prettyPrintDataLength, 8 ); fprintf(hFile, "Password unlocks key slot: %d\n", keySlot); fprintf(hFile, "Recovered master key :\n"); fprintf(hFile, "%s", prettyPrintData); SecZeroAndFreeMemory(prettyPrintData, prettyPrintDataLength); } } SecZeroAndFreeMemory(keyMaterial, LUKSHeader.rawHeader.key_bytes); } retval = TRUE; fclose(hFile); } return retval; }
// ========================================================================= // Generate a key based on the hash of the salted password NTSTATUS ImplKDFHashSaltedPassword( IN PDataHashFn FnHash, IN GUID HashGUID, IN unsigned int Iterations, IN unsigned int PasswordLength, // In bits IN unsigned char* Password, IN unsigned int SaltLength, // In bits IN unsigned char* Salt, IN OUT unsigned int* DerivedKeyLength, // In bits OUT unsigned char* DerivedKey ) { NTSTATUS status; unsigned char* tmpBuffer; unsigned int tmpBufferSizeBytes; unsigned char* tmpIterateBuffer; unsigned int tmpIterateBufferSizeBytes; unsigned int i; DEBUGOUTKDFDRV(DEBUGLEV_ENTER, ("ImplKDFHashSaltedPassword\n")); tmpBufferSizeBytes = ((PasswordLength + SaltLength) / 8); tmpBuffer = FREEOTFE_MEMALLOC(tmpBufferSizeBytes); // Add the salt onto the end of the password before hashing the combined // password/salt FREEOTFE_MEMCPY( tmpBuffer, Password, (PasswordLength / 8) ); FREEOTFE_MEMCPY( &tmpBuffer[(PasswordLength / 8)], Salt, (SaltLength / 8) ); status = FnHash( &HashGUID, (tmpBufferSizeBytes * 8), // Get bits from bytes tmpBuffer, DerivedKeyLength, DerivedKey ); if (!(NT_SUCCESS(status))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("First KDF call to hash driver failed\n")); } else { // Iterate, if needed... if (Iterations >= 2) { tmpIterateBufferSizeBytes = (*DerivedKeyLength / 8); tmpIterateBuffer = FREEOTFE_MEMALLOC(tmpIterateBufferSizeBytes); for (i = 2; i <= Iterations; i++) { // Copy to temp buffer... FREEOTFE_MEMCPY( tmpIterateBuffer, DerivedKey, tmpIterateBufferSizeBytes ); // Only pass a buffer that we can copy back into // Note: This value should never be bigger than the original // value passed in, otherwise the original hash would // fail *DerivedKeyLength = (tmpIterateBufferSizeBytes * 8); // Hash temp buffer... status = FnHash( &HashGUID, (tmpIterateBufferSizeBytes * 8), // Get bits from bytes tmpIterateBuffer, DerivedKeyLength, DerivedKey ); if (!(NT_SUCCESS(status))) { DEBUGOUTKDFDRV(DEBUGLEV_ERROR, ("Subsequent KDF call to hash driver failed\n")); break; } } SecZeroMemory(tmpIterateBuffer, tmpIterateBufferSizeBytes); FREEOTFE_FREE(tmpIterateBuffer); } } // Note: No need to explicitly set *MACLength as FnHash does this for us SecZeroMemory(tmpBuffer, tmpBufferSizeBytes); FREEOTFE_FREE(tmpBuffer); DEBUGOUTKDFDRV(DEBUGLEV_EXIT, ("ImplKDFHashSaltedPassword\n")); return status; }
// ========================================================================= // Encrypt/Decrypt function // Note: PlaintextLength must be set to the size of the PlaintextData buffer on // entry; on exit, this will be set to the size of the buffer used. NTSTATUS ImpCypherCryptData( IN GUID* CypherGUID, IN LARGE_INTEGER SectorID, IN int SectorSize, // In bytes IN int KeyLength, // In bits IN FREEOTFEBYTE* Key, IN char* KeyASCII, // ASCII representation of "Key" IN int IVLength, // In bits IN FREEOTFEBYTE* IV, IN BOOLEAN encryptNotDecrypt, // TRUE = encrypt; FALSE = decrypt IN int InLength, // In bytes IN FREEOTFEBYTE* InData, OUT FREEOTFEBYTE* OutData ) { NTSTATUS status = STATUS_SUCCESS; // libtomcrypt can't handle NULL IVs in CBC mode - it ASSERTs that IV != NULL char ltcNullIV[FREEOTFE_MAX_CYPHER_BLOCKSIZE]; int cipher; symmetric_CBC *cbc; symmetric_LRW *lrw; symmetric_xts *xts; int errnum; CYPHER_MODE mode; int keySizeUnderlying; LARGE_INTEGER blockID64; INTEGER_128 blockID128; DEBUGOUTCYPHERIMPL(DEBUGLEV_ENTER, (TEXT("ImpCypherDecryptData\n"))); status = DetermineCypherDetails( CypherGUID, &keySizeUnderlying, &mode ); // libtomcrypt can't handle NULL IVs in CBC mode - it ASSERTs that IV != NULL if ( (IVLength == 0) || (IV == NULL) ) { FREEOTFE_MEMZERO(<cNullIV, sizeof(ltcNullIV)); IV = (char*)<cNullIV; } // Sanity check on key supplied if NT_SUCCESS(status) { switch (mode) { case CYPHER_MODE_CBC: { if (KeyLength != keySizeUnderlying) { status = STATUS_INVALID_PARAMETER; } break; } case CYPHER_MODE_XTS: { if (KeyLength != (2 * keySizeUnderlying)) { status = STATUS_INVALID_PARAMETER; } break; } case CYPHER_MODE_LRW: { if (KeyLength != (keySizeUnderlying + (twofish_desc.block_length * 8))) { status = STATUS_INVALID_PARAMETER; } break; } } } if NT_SUCCESS(status) { status = InitLTCCypher(&cipher); } if NT_SUCCESS(status) { switch (mode) { case CYPHER_MODE_CBC: { cbc = FREEOTFE_MEMALLOC(sizeof(symmetric_CBC)); FREEOTFE_MEMZERO(cbc, sizeof(symmetric_CBC)); // Start a CBC session if ((errnum = cbc_start( cipher, IV, Key, (keySizeUnderlying/8), 0, cbc )) != CRYPT_OK) { status = STATUS_UNSUCCESSFUL; DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to start CBC session (errnum: %d)\n"), errnum)); } else { if (encryptNotDecrypt) { if ((errnum = cbc_encrypt( InData, OutData, InLength, cbc )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to encrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } else { if ((errnum = cbc_decrypt( InData, OutData, InLength, cbc )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to decrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } cbc_done(cbc); } SecZeroMemory(cbc, sizeof(symmetric_CBC)); FREEOTFE_FREE(cbc); break; } case CYPHER_MODE_LRW: { lrw = FREEOTFE_MEMALLOC(sizeof(symmetric_LRW)); FREEOTFE_MEMZERO(lrw, sizeof(symmetric_LRW)); // Generate index in correct format // LRW uses: // *) The block index (i.e. the number of 128 bit blocks) // *) The first block has block index 1 - not 0! // *) Bigendian format // Note: LTC increments this itself as it processes each block SectorIDToBlockIdx_64Bit(SectorID, SectorSize, &blockID64); LARGE_INTEGER__To__INTEGER_128_BigEndian( blockID64, blockID128 ); IV = blockID128; // Start a LRW session if ((errnum = lrw_start( cipher, IV, Key, (keySizeUnderlying/8), // 128 bits tweak key begins after the // cypher key (Key + (keySizeUnderlying/8)), 0, lrw )) != CRYPT_OK) { status = STATUS_UNSUCCESSFUL; DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to start LRW session (errnum: %d)\n"), errnum)); } else { if (encryptNotDecrypt) { if ((errnum = lrw_encrypt( InData, OutData, InLength, lrw )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to encrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } else { if ((errnum = lrw_decrypt( InData, OutData, InLength, lrw )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to decrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } lrw_done(lrw); } SecZeroMemory(lrw, sizeof(symmetric_LRW)); FREEOTFE_FREE(lrw); break; } case CYPHER_MODE_XTS: { xts = FREEOTFE_MEMALLOC(sizeof(symmetric_xts)); FREEOTFE_MEMZERO(xts, sizeof(symmetric_xts)); // Generate index in correct format // XTS uses: // *) The sector index (i.e. the number of N-bit sectors) // *) The first sector is sector 0 // *) Littleendian format LARGE_INTEGER__To__INTEGER_128_LittleEndian( SectorID, blockID128 ); // Start an XTS session if ((errnum = xts_start( cipher, Key, &(Key[keySizeUnderlying/8]), (keySizeUnderlying/8), 0, xts )) != CRYPT_OK) { status = STATUS_UNSUCCESSFUL; DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to start XTS session (errnum: %d)\n"), errnum)); } else { if (encryptNotDecrypt) { if ((errnum = xts_encrypt( InData, InLength, OutData, blockID128, xts )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to encrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } else { if ((errnum = xts_decrypt( InData, InLength, OutData, blockID128, xts )) != CRYPT_OK) { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unable to decrypt block (errnum: %d)\n"), errnum)); status = STATUS_UNSUCCESSFUL; } } xts_done(xts); } SecZeroMemory(xts, sizeof(symmetric_xts)); FREEOTFE_FREE(xts); break; } } } DEBUGOUTCYPHERIMPL(DEBUGLEV_EXIT, (TEXT("ImpCypherDecryptData\n"))); return status; }
// ========================================================================= // IOCTL to hash data NTSTATUS IOCTL_FreeOTFEHashIOCTL_Hash( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; PIO_STACK_LOCATION irpSp; PDIOC_HASH_DATA_IN DIOCBufferIn; PDIOC_HASH_DATA_OUT DIOCBufferOut; FREEOTFEBYTE* tmpOutput; unsigned int tmpLengthBits; // In *bits* int userBufferSizeBytes; // In *bytes* DEBUGOUTHASHDRV(DEBUGLEV_ENTER, ("IOCTL_FreeOTFEHashIOCTL_Hash\n")); irpSp = IoGetCurrentIrpStackLocation(Irp); DIOCBufferIn = (PDIOC_HASH_DATA_IN)Irp->AssociatedIrp.SystemBuffer; DIOCBufferOut = (PDIOC_HASH_DATA_OUT)Irp->AssociatedIrp.SystemBuffer; // Check size of INPUT buffer // Minimum check... // (Done first in order to ensure that we can later access "length" parts // for actual check) if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*DIOCBufferIn)-sizeof(DIOCBufferIn->Data)) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("inBuffer size wrong size (expect min: %d; got: %d)\n", sizeof(*DIOCBufferIn)-sizeof(DIOCBufferIn->Data), irpSp->Parameters.DeviceIoControl.InputBufferLength )); status = STATUS_INVALID_BUFFER_SIZE; return status; } // Actual check... if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(*DIOCBufferIn)+(DIOCBufferIn->DataLength/8)-sizeof(DIOCBufferIn->Data)) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("inBuffer size wrong size (expect actual: %d; got: %d)\n", sizeof(*DIOCBufferIn)+(DIOCBufferIn->DataLength/8)-sizeof(DIOCBufferIn->Data), irpSp->Parameters.DeviceIoControl.InputBufferLength )); status = STATUS_INVALID_BUFFER_SIZE; return status; } // Check size of OUTPUT buffer // Minimum check... // (Done first in order to ensure that we can later access "length" parts // for actual check) if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*DIOCBufferOut)-sizeof(DIOCBufferOut->Hash)) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("outBuffer size wrong size (expect min: %d; got: %d)\n", sizeof(*DIOCBufferOut)-sizeof(DIOCBufferOut->Hash), irpSp->Parameters.DeviceIoControl.OutputBufferLength )); status = STATUS_INVALID_BUFFER_SIZE; return status; } // Request valid so far, process... // We have to allocate a buffer to hold the output, before it can be copied // out to the output buffer; otherwise, the input buffer gets garbled as // data is written to the output buffer // Match the user's buffer; if it's too small, the operation will kick // it out // This calculates the size of the variable part of the struct // Yes, this should be correct: The total size of the user's output buffer, // less the size of the struct - but then plus the size of // the variable parts reserved within the buffer userBufferSizeBytes = (irpSp->Parameters.DeviceIoControl.OutputBufferLength) - sizeof(*DIOCBufferOut) + sizeof(DIOCBufferOut->Hash); tmpOutput = FREEOTFE_MEMALLOC(userBufferSizeBytes); // The size of the buffer in bits; the algorithm will read this value, then // overwrite it with the actual size of the output (in bits) tmpLengthBits = (userBufferSizeBytes * 8); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Input buffer length is: %d bits\n", DIOCBufferIn->DataLength)); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Allocated output buffer for: %d bits\n", tmpLengthBits)); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("About to process...\n")); status = ImpHashHashData( &DIOCBufferIn->HashGUID, DIOCBufferIn->DataLength, // In bits (FREEOTFEBYTE*)&DIOCBufferIn->Data, &tmpLengthBits, // In bits (FREEOTFEBYTE*)tmpOutput ); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Done with status: %d (want status: %d)\n", status, STATUS_SUCCESS)); if (NT_SUCCESS(status)) { DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("tmp buffer was: %d bits, output was %d bits\n", (userBufferSizeBytes * 8), tmpLengthBits)); // Actual check... // Ensure output DIOC butter is large enough to store the output // Note that we can't carry out this check until we've created the // output value, in case the algorithm used produces variable length // output if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(*DIOCBufferOut)+(tmpLengthBits/8)-sizeof(DIOCBufferOut->Hash)) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("outBuffer size wrong size (expect actual: %d; got: %d)\n", sizeof(*DIOCBufferOut)+(tmpLengthBits/8)-sizeof(DIOCBufferOut->Hash), irpSp->Parameters.DeviceIoControl.OutputBufferLength )); status = STATUS_INVALID_BUFFER_SIZE; } else { RtlCopyMemory(&DIOCBufferOut->Hash, tmpOutput, (tmpLengthBits / 8)); DIOCBufferOut->HashLength = tmpLengthBits; Irp->IoStatus.Information = sizeof(*DIOCBufferOut)+(tmpLengthBits/8)-sizeof(DIOCBufferOut->Hash); } } else { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("FAILED")); } SecZeroMemory(tmpOutput, userBufferSizeBytes); FREEOTFE_FREE(tmpOutput); DEBUGOUTHASHDRV(DEBUGLEV_EXIT, ("IOCTL_FreeOTFEHashIOCTL_Hash\n")); return status; }
// ========================================================================= // This routine is the driver's entry point, called by the I/O system // to load the driver. The driver's entry points are initialized. // In DBG mode, this routine also examines the registry for special // debug parameters. NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING devDirName; OBJECT_ATTRIBUTES dirObjAttribs; PDEVICE_OBJECT mainDevObj; PDEVICE_EXTENSION mainDevExt; // The following debug handling was ripped from the MS DDK "floppy.c" // example #if DBG // We use this to query into the registry as to whether we // should break at driver entry. RTL_QUERY_REGISTRY_TABLE paramTable[3]; ULONG default_ShouldBreak = 0; // Default to 0; no break // ULONG default_DebugLevel = 0xFFFFFFFF; // Default to all on ULONG default_DebugLevel = 0x0000001F; // All except verbose debug ULONG debugLevel = 0; ULONG shouldBreak = 0; PWCHAR path; ULONG pathLength; // Since the registry path parameter is a "counted" UNICODE string, it // might not be zero terminated. For a very short time allocate memory // to hold the registry path zero terminated so that we can use it to // delve into the registry. // // NOTE NOTE!!!! This is not an architected way of breaking into // a driver. It happens to work for this driver because the original // DDK example code for DriverEntry happened to be written in this manner. pathLength = RegistryPath->Length + sizeof(WCHAR); if (path = FREEOTFE_MEMALLOC(pathLength)) { SecZeroMemory(¶mTable[0], sizeof(paramTable)); SecZeroMemory(path, pathLength); RtlMoveMemory( path, RegistryPath->Buffer, RegistryPath->Length ); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"BreakOnEntry"; paramTable[0].EntryContext = &shouldBreak; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &default_ShouldBreak; paramTable[0].DefaultLength = sizeof(ULONG); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"DebugLevel"; paramTable[1].EntryContext = &debugLevel; paramTable[1].DefaultType = REG_DWORD; paramTable[1].DefaultData = &default_DebugLevel; paramTable[1].DefaultLength = sizeof(ULONG); if (!(NT_SUCCESS(RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, path, ¶mTable[0], NULL, NULL)))) { shouldBreak = default_ShouldBreak; debugLevel = default_DebugLevel; } FREEOTFE_FREE(path); } FreeOTFEDebugLevel = debugLevel; DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Debug level: %d\n", FreeOTFEDebugLevel)); if (shouldBreak == 1) { DbgBreakPoint(); } #endif DEBUGOUTHASHDRV(DEBUGLEV_ENTER, ("DriverEntry\n")); // Create main device dir DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Creating dir object (main)...\n")); status = CreateDeviceDir( DEVICE_FREEOTFE_ROOT, &DirFreeOTFERoot ); if (!(NT_SUCCESS(status))) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("Unable to CreateDeviceDir (main)\n")); return status; } // Create hash device dir DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Creating dir object (hash)...\n")); status = CreateDeviceDir( DEVICE_HASH_DIR_NAME, &DirFreeOTFEHash ); if (!(NT_SUCCESS(status))) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("Unable to CreateDeviceDir (hash)\n")); ZwClose(DirFreeOTFERoot); return status; } // Create main device DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Creating main device...\n")); status = CreateDevice(DriverObject, &mainDevObj); if (!(NT_SUCCESS(status))) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("Call to CreateDevice FAILED.\n")); return status; } // Store the device dir handle for closure on unload mainDevExt = (PDEVICE_EXTENSION)mainDevObj->DeviceExtension; // Initialize the driver object with this driver's entry points. DriverObject->MajorFunction[IRP_MJ_CREATE] = FreeOTFE_MF_DispatchCreate; DriverObject->MajorFunction[IRP_MJ_CLOSE] = FreeOTFE_MF_DispatchClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FreeOTFE_MF_DispatchDeviceControl; DriverObject->DriverUnload = DriverUnload; DEBUGOUTHASHDRV(DEBUGLEV_EXIT, ("DriverEntry\n")); return status; }
// ========================================================================= // Destroy the specified device object, regardless of it's state // Note: This can be called to destroy *either* the main device, *or* a // disk device // Returns: The next device object PDEVICE_OBJECT DestroyDevice( IN PDEVICE_OBJECT devObj ) { NTSTATUS status; PDEVICE_EXTENSION devExt; PDEVICE_OBJECT nextDevObj; DEBUGOUTHASHDRV(DEBUGLEV_ENTER, ("DestroyDevice\n")); nextDevObj = devObj->NextDevice; devExt = (PDEVICE_EXTENSION)devObj->DeviceExtension; // Signal the device's thread to terminate, and wait until it does. devExt->TerminateThread = TRUE; CancelAllQueuedIRPs(devObj); if (devExt->ThreadObject != NULL) { // Make sure the thread wakes up, so it can see that it should terminate KeReleaseSemaphore(&devExt->IRPQueueSemaphore, 0, // No priority boost 1, // Increment semaphore by 1 TRUE );// WaitForXxx after this call // Wait for the thread to terminate KeWaitForSingleObject(devExt->ThreadObject, Executive, KernelMode, FALSE, NULL ); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Wait completed; thread terminated.\n")); // Release the thread object ref DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Dereferencing ThreadObject...\n")); ObDereferenceObject(devExt->ThreadObject); devExt->ThreadObject = NULL; } // Tear down any symbolic device name link DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Tearing down main device specific...\n")); if (devExt->zzSymbolicLinkName.Buffer != NULL) { DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Tearing down symbolic device link...\n")); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("About to delete symlink: %ls\n", devExt->zzSymbolicLinkName.Buffer)); status = IoDeleteSymbolicLink(&devExt->zzSymbolicLinkName); if (!NT_SUCCESS(status)) { DEBUGOUTHASHDRV(DEBUGLEV_ERROR, ("Status NOT OK\n")); } DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Freeing off symlink unicode buffer...\n")); SecZeroMemory( devExt->zzSymbolicLinkName.Buffer, devExt->zzSymbolicLinkName.MaximumLength ); FREEOTFE_FREE(devExt->zzSymbolicLinkName.Buffer); devExt->zzSymbolicLinkName.Buffer = NULL; DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("OK.\n")); } // Tear down any device name if (devExt->zzDeviceName.Buffer != NULL) { DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Freeing off devname unicode buffer...\n")); SecZeroMemory( devExt->zzDeviceName.Buffer, devExt->zzDeviceName.MaximumLength ); FREEOTFE_FREE(devExt->zzDeviceName.Buffer); devExt->zzDeviceName.Buffer = NULL; } DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("OK.\n")); // Cleardown details of hashes supported ImpHashDriverExtDetailsCleardown(&(devExt->DriverInfo)); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("OK.\n")); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Overwriting device extension memory...\n")); SecZeroMemory(devExt, sizeof(DEVICE_EXTENSION)); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("OK.\n")); // Get rid of the device object... DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("Deleting device...\n")); IoDeleteDevice(devObj); DEBUGOUTHASHDRV(DEBUGLEV_INFO, ("OK.\n")); DEBUGOUTHASHDRV(DEBUGLEV_EXIT, ("DestroyDevice\n")); return nextDevObj; }
// ========================================================================= // Decryption function // Note: PlaintextLength must be set to the size of the PlaintextData buffer on // entry; on exit, this will be set to the size of the buffer used. NTSTATUS ImpCypherDecryptSectorData( IN GUID* CypherGUID, IN LARGE_INTEGER SectorID, // Indexed from zero IN int SectorSize, // In bytes IN int KeyLength, // In bits IN FREEOTFEBYTE* Key, IN char* KeyASCII, // ASCII representation of "Key" IN int IVLength, // In bits IN FREEOTFEBYTE* IV, IN int CyphertextLength, // In bytes IN FREEOTFEBYTE* CyphertextData, OUT FREEOTFEBYTE* PlaintextData ) { NTSTATUS status = STATUS_SUCCESS; // Null IV in case we're not given an IV char zeroIV[FREEOTFE_MAX_CYPHER_BLOCKSIZE]; int i; CONTEXT context; DEBUGOUTCYPHERIMPL(DEBUGLEV_ENTER, (TEXT("ImpCypherDecryptData\n"))); if ( (IsEqualGUID(&CIPHER_GUID_CAST6_256_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_CAST6_224_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_CAST6_192_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_CAST6_160_CBC, CypherGUID)) || (IsEqualGUID(&CIPHER_GUID_CAST6_128_CBC, CypherGUID)) ) { // Handle situation where a NULL/zero length IV is passed in if ( (IVLength == 0) || (IV == NULL) ) { FREEOTFE_MEMZERO(&zeroIV, sizeof(zeroIV)); IV = (char*)&zeroIV; } // Process data using CBC mode // // CBC Mode DECRYPT: // *) Setup key // *) For each block... // *) Decrypt cyphertext with key // *) XOR output plaintext with IV // *) IV = Cyphertext data // Note: "dec" specified; we're DECRYPTING set_key(&context, (u4byte *)Key, KeyLength); for (i=0; i < CyphertextLength; i += (CAST6_BLOCK_SIZE / 8)) { decrypt(&context, (u4byte *)(CyphertextData+i), (u4byte *)(PlaintextData+i)); XORBlock(PlaintextData+i, IV, PlaintextData+i, (CAST6_BLOCK_SIZE / 8)); IV = CyphertextData+i; } } else { DEBUGOUTCYPHERIMPL(DEBUGLEV_ERROR, (TEXT("Unsupported cipher GUID passed in.\n"))); status = STATUS_INVALID_PARAMETER; } SecZeroMemory(&context, sizeof(context)); DEBUGOUTCYPHERIMPL(DEBUGLEV_EXIT, (TEXT("ImpCypherDecryptData\n"))); return status; }