You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1268 lines
33 KiB
1268 lines
33 KiB
/*
|
|
File: Secure.cpp
|
|
|
|
Title: Cryptographic Funcs for Protected Storage
|
|
Author: Matt Thomlinson
|
|
Date: 11/18/96
|
|
|
|
Builds up usable cryptographic functionality and exports for
|
|
usage by storage module. Since CryptoAPI base provider may call
|
|
us, we can't use CryptXXX primitives (circular dependencies
|
|
may result). Instead, we build up functions from SHA-1
|
|
hash and DES-CBC primitives to Encrypt/Decrypt key blocks, MAC
|
|
items, and check password confirmation blocks.
|
|
|
|
|
|
**Master Key Usage**
|
|
|
|
Encryption is done in a strange way to ease password management
|
|
headaches.
|
|
|
|
// derive a key from the user password, and the password-unique salt
|
|
// Salt foils certain (dictionary) attacks on the password
|
|
|
|
// PWSalt is PASSWORD_SALT_LEN
|
|
// UserPW is SHA-1 hash of password excluding zt
|
|
// DerivedKey1 is 56-bit DES key
|
|
|
|
DerivedKey1 = DeriveKey(UserPW | PWSalt);
|
|
|
|
|
|
// using the derived key, decrypt the encrypted master keys
|
|
|
|
// encrypted master keys are E(master key bits | pwd confirm bits)
|
|
|
|
MasterBits | PwdConfirmBits = Decrypt( (MasterKey|PwdConfirmKey), DerivedKey1);
|
|
|
|
|
|
// Now we have recovered keys
|
|
// MasterKey, PwdConfirmKey are 56-bit DES keys
|
|
|
|
MasterKey
|
|
PwdConfirmKey
|
|
|
|
|
|
// check to make sure this is correct MasterKey by MACing
|
|
// the global confirmation string and checking against stored MAC
|
|
|
|
PwdConfirmMAC = HMAC(g_ConfirmBuf, PwdConfirmKey)
|
|
if (0 != memcmp(PwdConfirmMac, rgbStoredMAC, 20)
|
|
// wrong pwd!!
|
|
|
|
|
|
We've derived a master key that we can create reproducibly and
|
|
be changed without touching every item encrypted by it. That is,
|
|
If the user changes the password, we simply need to keep the
|
|
MasterBits constant and Encrypt with the new DerivedKey1 to find
|
|
the EncryptedMasterBits to write to disk. In this way, we can change
|
|
the password without changing the (exceedingly long) master key.
|
|
|
|
We also have an immediate indication of whether or not the password
|
|
used to decrypt the master key is correct.
|
|
|
|
|
|
**Encrypting/MACing a single item**
|
|
|
|
// assume we have 56-bit DES MasterKey from above
|
|
// use master key to decrypt the encrypted item keyblock
|
|
// key block holds two keys: Item key and MAC key
|
|
|
|
ItemKeyBits | MACKeyBits = Decrypt( (ItemKey|MACKey), MasterKey);
|
|
|
|
// Recovered two DES keys
|
|
// ItemKey is 56 bits
|
|
// MACKey is 56 bits
|
|
|
|
ItemKey
|
|
MACKey
|
|
|
|
// MAC the item
|
|
|
|
ItemMAC = HMAC(pbItemData, MACKey);
|
|
|
|
// Tack the items' MAC onto the item, and encrypt
|
|
|
|
EncryptedItem = ENCRYPTED_DATA_VER | Encrypt(pbItemData | ItemMAC, ItemKey);
|
|
|
|
We've succeeded in encrypting and MACing an item using the single DES
|
|
MasterKey from above. The item is both privacy and integrity protected.
|
|
|
|
|
|
**Derive Key**
|
|
**HMAC**
|
|
|
|
Above we've skimmed over the definition of key derivation and HMAC.
|
|
See primitiv.cpp for a description of how this primitive
|
|
functionality is implemented.
|
|
|
|
|
|
// Format of the encrypted item data stream:
|
|
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include <pch.cpp>
|
|
#pragma hdrstop
|
|
#include "storage.h"
|
|
|
|
|
|
|
|
|
|
// from des.h
|
|
#define DES_KEYLEN 8
|
|
|
|
|
|
// MAC buffer we use to check correct decryption
|
|
static BYTE g_rgbConfirmBuf[] = "(c) 1996 Microsoft, All Rights Reserved";
|
|
|
|
|
|
|
|
// Data Version
|
|
//
|
|
// #define ENCRYPTED_DATA_VER 0x01
|
|
// 6-12-97: version incremented. Previous versions
|
|
// should use MyOldPrimitiveHMAC -- different MAC attached to items
|
|
#define ENCRYPTED_DATA_VER 0x02
|
|
|
|
// MK Version
|
|
//
|
|
// #define ENCRYPTED_MASTERKEY_VER 0x01
|
|
// 6-12-97: version incremented. Previous versions
|
|
// should use MyOldPrimitiveHMAC -- different MAC attached to items
|
|
// #define ENCRYPTED_MASTERKEY_VER 0x02
|
|
// 5-3-99: version incremented. Previous versions
|
|
// should use sizeof(rgbpwd)
|
|
#define ENCRYPTED_MASTERKEY_VER 0x03
|
|
|
|
|
|
// given pwd, salt, and ptr to master key buffer,
|
|
// decrypts and checks MAC on master key
|
|
BOOL FMyDecryptMK(
|
|
BYTE rgbSalt[],
|
|
DWORD cbSalt,
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN],
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
|
|
PBYTE* ppbMK,
|
|
DWORD* pcbMK)
|
|
{
|
|
|
|
BOOL fResetSecurityState;
|
|
|
|
return FMyDecryptMKEx(
|
|
rgbSalt,
|
|
cbSalt,
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
ppbMK,
|
|
pcbMK,
|
|
&fResetSecurityState
|
|
);
|
|
|
|
}
|
|
|
|
BOOL
|
|
FMyDecryptMKEx(
|
|
BYTE rgbSalt[],
|
|
DWORD cbSalt,
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN],
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
|
|
PBYTE* ppbMK,
|
|
DWORD* pcbMK,
|
|
BOOL *pfResetSecurityState
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
DESKEY sDerivedKey1;
|
|
DESKEY sConfirmKey;
|
|
DWORD dwMKVersion;
|
|
PBYTE pTemp;
|
|
|
|
BYTE rgbHMACResult[A_SHA_DIGEST_LEN];
|
|
|
|
|
|
// version check!!
|
|
dwMKVersion = *(DWORD*)*ppbMK;
|
|
if (ENCRYPTED_MASTERKEY_VER < dwMKVersion)
|
|
goto Ret;
|
|
|
|
|
|
if( dwMKVersion < 0x03 ) {
|
|
|
|
*pfResetSecurityState = TRUE;
|
|
|
|
// DK1 = DeriveKey(SHA(pw), Salt)
|
|
if (!FMyPrimitiveDeriveKey(
|
|
rgbSalt,
|
|
cbSalt,
|
|
rgbPwd,
|
|
sizeof(rgbPwd),
|
|
&sDerivedKey1))
|
|
goto Ret;
|
|
|
|
} else {
|
|
|
|
*pfResetSecurityState = FALSE;
|
|
|
|
// DK1 = DeriveKey(SHA(pw), Salt)
|
|
if (!FMyPrimitiveDeriveKey(
|
|
rgbSalt,
|
|
cbSalt,
|
|
rgbPwd,
|
|
A_SHA_DIGEST_LEN,
|
|
&sDerivedKey1))
|
|
goto Ret;
|
|
}
|
|
|
|
|
|
|
|
*pcbMK -= sizeof(DWORD);
|
|
if (!(*pcbMK)) {
|
|
|
|
// Paranoid
|
|
|
|
goto Ret;
|
|
}
|
|
MoveMemory(*ppbMK, *ppbMK + sizeof(DWORD), *pcbMK); // shift data left 1 dw, splat version
|
|
pTemp = (PBYTE)SSReAlloc(*ppbMK, *pcbMK);
|
|
if (pTemp == NULL) { // check allocation
|
|
goto Ret;
|
|
}
|
|
|
|
*ppbMK = pTemp;
|
|
|
|
|
|
// Decrypt MK bits
|
|
if (!FMyPrimitiveDESDecrypt(
|
|
*ppbMK,
|
|
pcbMK,
|
|
sDerivedKey1))
|
|
goto Ret;
|
|
|
|
// assumes is at least 2*DES_KEYLEN bytes
|
|
if (*pcbMK != 2*DES_KEYLEN)
|
|
goto Ret;
|
|
|
|
if (!FMyMakeDESKey(
|
|
&sConfirmKey, // out
|
|
*ppbMK + DES_KEYLEN)) // in
|
|
goto Ret;
|
|
|
|
if (dwMKVersion == 0x01)
|
|
{
|
|
// items created with tag 0x01 used different HMACing algorithm
|
|
if (!FMyOldPrimitiveHMAC(
|
|
sConfirmKey,
|
|
g_rgbConfirmBuf,
|
|
sizeof(g_rgbConfirmBuf),
|
|
rgbHMACResult))
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
if (!FMyPrimitiveHMAC(
|
|
sConfirmKey,
|
|
g_rgbConfirmBuf,
|
|
sizeof(g_rgbConfirmBuf),
|
|
rgbHMACResult))
|
|
goto Ret;
|
|
}
|
|
|
|
if (0 != memcmp(rgbHMACResult, rgbConfirm, A_SHA_DIGEST_LEN))
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// retrieve key block and derive Item/MAC keys using MK
|
|
BOOL FMyDecryptKeyBlock(
|
|
LPCWSTR szUser,
|
|
LPCWSTR szMasterKey,
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN],
|
|
PBYTE pbKeyBlock,
|
|
DWORD cbKeyBlock,
|
|
DESKEY* psItemKey,
|
|
DESKEY* psMacKey)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
DESKEY sMK;
|
|
|
|
BYTE rgbSalt[PASSWORD_SALT_LEN];
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
|
|
|
|
PBYTE pbMK = NULL;
|
|
DWORD cbMK;
|
|
|
|
if (!FBPGetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
&pbMK,
|
|
&cbMK))
|
|
return FALSE;
|
|
|
|
// unwrap master key
|
|
if (!FMyDecryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK
|
|
))
|
|
goto Ret;
|
|
|
|
|
|
// assumes pbMK is at least 2*DES_KEYLEN bytes
|
|
if (cbMK != 2*DES_KEYLEN)
|
|
goto Ret;
|
|
|
|
if (!FMyMakeDESKey(
|
|
&sMK, // out
|
|
pbMK)) // in
|
|
goto Ret;
|
|
|
|
// use MK to decrypt key block
|
|
if (!FMyPrimitiveDESDecrypt(
|
|
pbKeyBlock,
|
|
&cbKeyBlock,
|
|
sMK))
|
|
goto Ret;
|
|
|
|
// fill in ItemKey, MacKey from decrypted key block
|
|
if (cbKeyBlock != 2*DES_KEYLEN)
|
|
goto Ret;
|
|
|
|
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
|
|
if (!FMyMakeDESKey(
|
|
psItemKey, // out
|
|
pbKeyBlock)) // in
|
|
goto Ret;
|
|
if (!FMyMakeDESKey(
|
|
psMacKey, // out
|
|
pbKeyBlock + DES_KEYLEN)) // in
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
|
|
if(pbMK != NULL) {
|
|
RtlSecureZeroMemory(pbMK, cbMK); // sfield: zero it
|
|
SSFree(pbMK); // sfield: fix illusive memory leak
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// given encrypted data and password, decrypt and check MAC on data
|
|
BOOL FProvDecryptData(
|
|
LPCWSTR szUser, // in
|
|
LPCWSTR szMasterKey, // in
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
|
|
PBYTE* ppbMyData, // in out
|
|
DWORD* pcbMyData) // in out
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
DESKEY sItemKey;
|
|
DESKEY sMacKey;
|
|
|
|
BYTE rgbHMAC[A_SHA_DIGEST_LEN];
|
|
|
|
DWORD dwDataVer;
|
|
|
|
PBYTE pTemp;
|
|
|
|
|
|
// pointers to teardown stream
|
|
PBYTE pbCurPtr = *ppbMyData;
|
|
|
|
PBYTE pbSecureData;
|
|
DWORD cbSecureData;
|
|
|
|
PBYTE pbInlineKeyBlock;
|
|
DWORD cbInlineKeyBlock;
|
|
|
|
PBYTE pbDecrypted;
|
|
DWORD cbDecrypted;
|
|
|
|
PBYTE pbMAC;
|
|
DWORD cbMAC;
|
|
|
|
// ENCRYPTED ITEM DATA FORMAT:
|
|
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
|
|
|
|
// version check -- only handle V1 data for now
|
|
dwDataVer = *(DWORD*)pbCurPtr;
|
|
if (ENCRYPTED_DATA_VER < dwDataVer)
|
|
goto Ret;
|
|
pbCurPtr += sizeof(DWORD);
|
|
|
|
// pointers to key block
|
|
cbInlineKeyBlock = *(DWORD UNALIGNED *)pbCurPtr; // keyblock size
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
pbInlineKeyBlock = pbCurPtr; // points to key block
|
|
pbCurPtr += cbInlineKeyBlock; // fwd past data
|
|
|
|
// pointers to secure data
|
|
cbSecureData = *(DWORD UNALIGNED *)pbCurPtr; // secure data size member
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
pbSecureData = pbCurPtr; // points to secure data
|
|
|
|
// retrieve key block using MK, etc
|
|
if (!FMyDecryptKeyBlock(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbPwd,
|
|
pbInlineKeyBlock,
|
|
cbInlineKeyBlock,
|
|
&sItemKey,
|
|
&sMacKey))
|
|
goto Ret;
|
|
|
|
// keys derived, now recover data inplace
|
|
if (!FMyPrimitiveDESDecrypt(
|
|
pbSecureData,
|
|
&cbSecureData,
|
|
sItemKey))
|
|
goto Ret;
|
|
|
|
cbDecrypted = *(DWORD UNALIGNED *)pbCurPtr; // plaintext size
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
pbDecrypted = pbCurPtr; // points to plaintext
|
|
pbCurPtr += cbDecrypted; // fwd past data
|
|
|
|
// pointers to HMAC
|
|
cbMAC = *(DWORD UNALIGNED *)pbCurPtr; // MAC size member
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
pbMAC = pbCurPtr; // points to MAC
|
|
pbCurPtr += cbMAC; // fwd past data
|
|
|
|
if (A_SHA_DIGEST_LEN != cbMAC) // verify HMAC size member
|
|
goto Ret;
|
|
|
|
|
|
// chk MAC
|
|
|
|
// Compute HMAC over plaintext data
|
|
|
|
if (dwDataVer == 0x01)
|
|
{
|
|
// version 0x1 used different HMAC code
|
|
if (!FMyOldPrimitiveHMAC(
|
|
sMacKey,
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
rgbHMAC))
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
if (!FMyPrimitiveHMAC(
|
|
sMacKey,
|
|
pbDecrypted,
|
|
cbDecrypted,
|
|
rgbHMAC))
|
|
goto Ret;
|
|
}
|
|
|
|
// now compare against HMAC in tail of msg
|
|
if (0 != memcmp(pbMAC, rgbHMAC, A_SHA_DIGEST_LEN))
|
|
goto Ret;
|
|
|
|
// if everything went well, return secure data (shift to far left, realloc)
|
|
MoveMemory(*ppbMyData, pbDecrypted, cbDecrypted);
|
|
|
|
pTemp = (PBYTE)SSReAlloc(*ppbMyData, cbDecrypted);
|
|
if (pTemp == NULL) // check allocation, Caller will free *ppbMyData
|
|
goto Ret;
|
|
|
|
*ppbMyData = pTemp;
|
|
|
|
*pcbMyData = cbDecrypted;
|
|
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
// TODO free ppbMyData on failure?
|
|
return fRet;
|
|
}
|
|
|
|
// given pwd, salt, and Master Key buffer, MACs and Encrypts Master Key buffer
|
|
BOOL FMyEncryptMK(
|
|
BYTE rgbSalt[],
|
|
DWORD cbSalt,
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN],
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN],
|
|
PBYTE* ppbMK,
|
|
DWORD* pcbMK)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
DESKEY sDerivedKey1;
|
|
DESKEY sConfirmKey;
|
|
PBYTE pTemp;
|
|
|
|
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
|
|
if (*pcbMK != 2*DES_KEYLEN)
|
|
goto Ret;
|
|
|
|
// confirmation key is 2nd in buffer
|
|
if (!FMyMakeDESKey(
|
|
&sConfirmKey, // out
|
|
*ppbMK + DES_KEYLEN)) // in
|
|
goto Ret;
|
|
|
|
|
|
if (!FMyPrimitiveHMAC(
|
|
sConfirmKey,
|
|
g_rgbConfirmBuf,
|
|
sizeof(g_rgbConfirmBuf),
|
|
rgbConfirm))
|
|
goto Ret;
|
|
|
|
// DK1 = DeriveKey(SHA(pw), Salt)
|
|
if (!FMyPrimitiveDeriveKey(
|
|
rgbSalt,
|
|
cbSalt,
|
|
rgbPwd,
|
|
A_SHA_DIGEST_LEN, ///sizeof(rgbPwd),
|
|
&sDerivedKey1))
|
|
goto Ret;
|
|
|
|
// Encrypt MK w/ DK1, return
|
|
if (!FMyPrimitiveDESEncrypt(
|
|
ppbMK,
|
|
pcbMK,
|
|
sDerivedKey1))
|
|
goto Ret;
|
|
|
|
// Mash version onto front!!
|
|
pTemp = (PBYTE)SSReAlloc(*ppbMK, *pcbMK+sizeof(DWORD)); // realloc bigger for ver
|
|
if (pTemp == NULL) // check allocation
|
|
goto Ret;
|
|
*ppbMK = pTemp;
|
|
|
|
MoveMemory(*ppbMK+sizeof(DWORD), *ppbMK, *pcbMK); // move data 1 dw right
|
|
*pcbMK += sizeof(DWORD); // inc size
|
|
*(DWORD*)(*ppbMK) = (DWORD)ENCRYPTED_MASTERKEY_VER; // whack version in there!
|
|
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// returns a new key block encrypted with master key
|
|
// creates and stores master key state if none exists
|
|
BOOL FMyEncryptKeyBlock(
|
|
LPCWSTR szUser,
|
|
LPCWSTR szMasterKey,
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN],
|
|
PBYTE* ppbKeyBlock,
|
|
DWORD* pcbKeyBlock,
|
|
DESKEY* psItemKey,
|
|
DESKEY* psMacKey)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
*ppbKeyBlock = NULL;
|
|
|
|
BYTE rgbSalt[PASSWORD_SALT_LEN];
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
|
|
|
|
PBYTE pbMK = NULL;
|
|
DWORD cbMK;
|
|
|
|
PBYTE pbTmp = NULL;
|
|
DWORD cbTmp;
|
|
|
|
DESKEY sMK;
|
|
|
|
// gen a random key block: 2 keys
|
|
*pcbKeyBlock = 2*DES_KEYLEN;
|
|
*ppbKeyBlock = (PBYTE) SSAlloc(*pcbKeyBlock + DES_BLOCKLEN); // performance fudge factor (realloc)
|
|
if (*ppbKeyBlock == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
if (!RtlGenRandom(*ppbKeyBlock, *pcbKeyBlock))
|
|
goto Ret;
|
|
|
|
// INSERT: FRENCH GOVT. THOUGHT CONTROL CODE
|
|
if (! FIsEncryptionPermitted())
|
|
{
|
|
// Protected Storage addition, 5/27/97
|
|
// If encryption is not allowed, pretend faulty
|
|
// RNG generated encryption key { 6d 8a 88 6a 4e aa 37 a8 }
|
|
|
|
SS_ASSERT(DES_KEYLEN == sizeof(DWORD)*2);
|
|
|
|
*(DWORD*)(*ppbKeyBlock) = 0x6d8a886a;
|
|
*(DWORD*)(*ppbKeyBlock + sizeof(DWORD)) = 0x4eaa37a8;
|
|
|
|
|
|
// PS: Remind me not to move to FRANCE
|
|
}
|
|
|
|
|
|
// assumes pbKeyBlock is at least 2*DES_KEYLEN bytes
|
|
SS_ASSERT(*pcbKeyBlock == 2*DES_KEYLEN);
|
|
|
|
if (!FMyMakeDESKey(
|
|
psItemKey, // out
|
|
*ppbKeyBlock)) // in
|
|
goto Ret;
|
|
|
|
if (!FMyMakeDESKey(
|
|
psMacKey, // out
|
|
*ppbKeyBlock + DES_KEYLEN)) // in
|
|
goto Ret;
|
|
|
|
// first derive a key from PW
|
|
if (FBPGetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
&pbMK,
|
|
&cbMK))
|
|
{
|
|
// unwrap master key
|
|
if (!FMyDecryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK))
|
|
goto Ret;
|
|
|
|
// done, have MK unwrapped.
|
|
}
|
|
else
|
|
{
|
|
// if we couldn't retrieve state, assume we must generate it
|
|
if (!RtlGenRandom(rgbSalt, PASSWORD_SALT_LEN))
|
|
goto Ret;
|
|
|
|
cbMK = 2*DES_KEYSIZE;
|
|
pbMK = (PBYTE)SSAlloc(cbMK + DES_BLOCKLEN); // performance fudge factor (realloc)
|
|
if (pbMK == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
if (!RtlGenRandom(pbMK, cbMK))
|
|
goto Ret;
|
|
|
|
// this is final MK: encrypt a copy
|
|
cbTmp = cbMK;
|
|
pbTmp = (PBYTE)SSAlloc(cbTmp);
|
|
if (pbTmp == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
CopyMemory(pbTmp, pbMK, cbMK);
|
|
|
|
|
|
// now wrap MK up and stuff in registry
|
|
if (!FMyEncryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbTmp,
|
|
&cbTmp))
|
|
goto Ret;
|
|
|
|
if (!FBPSetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
PASSWORD_SALT_LEN,
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
pbTmp,
|
|
cbTmp))
|
|
goto Ret;
|
|
|
|
}
|
|
|
|
if (cbMK != 2*DES_KEYSIZE)
|
|
goto Ret;
|
|
|
|
if (!FMyMakeDESKey(
|
|
&sMK, // out
|
|
pbMK)) // in
|
|
goto Ret;
|
|
|
|
if (*pcbKeyBlock != 2*DES_KEYLEN)
|
|
goto Ret;
|
|
|
|
if (!FMyPrimitiveDESEncrypt(
|
|
ppbKeyBlock,
|
|
pcbKeyBlock,
|
|
sMK))
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
if (!fRet)
|
|
{
|
|
if (*ppbKeyBlock) {
|
|
SSFree(*ppbKeyBlock);
|
|
*ppbKeyBlock = NULL;
|
|
}
|
|
}
|
|
|
|
if (pbMK) {
|
|
RtlSecureZeroMemory(pbMK, cbMK);
|
|
SSFree(pbMK);
|
|
}
|
|
|
|
if (pbTmp) {
|
|
RtlSecureZeroMemory(pbTmp, cbTmp); // sfield: zero memory
|
|
SSFree(pbTmp);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// given data, will generate a key block and
|
|
// return encrypt/mac'd data
|
|
BOOL FProvEncryptData(
|
|
LPCWSTR szUser, // in
|
|
LPCWSTR szMasterKey, // in
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
|
|
PBYTE* ppbMyData, // in out
|
|
DWORD* pcbMyData) // in out
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
DESKEY sItemKey;
|
|
DESKEY sMacKey;
|
|
|
|
BYTE rgbHMAC[A_SHA_DIGEST_LEN];
|
|
|
|
// helpful pointers
|
|
PBYTE pbCurPtr = *ppbMyData;
|
|
|
|
PBYTE pbKeyBlock = NULL;
|
|
DWORD cbKeyBlock = 0;
|
|
|
|
DWORD cbDataSize;
|
|
|
|
// return an item key, mac key
|
|
// store in an encrypted key block using MK, etc.
|
|
if (!FMyEncryptKeyBlock(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbPwd,
|
|
&pbKeyBlock,
|
|
&cbKeyBlock,
|
|
&sItemKey,
|
|
&sMacKey))
|
|
goto Ret;
|
|
|
|
// now secure data
|
|
|
|
// Compute HMAC
|
|
if (!FMyPrimitiveHMAC(sMacKey, *ppbMyData, *pcbMyData, rgbHMAC))
|
|
goto Ret;
|
|
|
|
// DATA FORMAT:
|
|
// version | size(keyblk) | keyblk | size(encryptedblk) | Encrypted{ size(data) | data | size(MAC) | MAC}
|
|
|
|
// lengthen data seg by data size member, MAC and MAC size member
|
|
cbDataSize = *pcbMyData; // save current size
|
|
*pcbMyData += A_SHA_DIGEST_LEN + 2*sizeof(DWORD); // sizeof(data), MAC, sizeof(MAC)
|
|
pbCurPtr = (PBYTE)SSReAlloc(*ppbMyData, *pcbMyData);
|
|
if (pbCurPtr == NULL) // check allocation
|
|
goto Ret;
|
|
*ppbMyData = pbCurPtr;
|
|
|
|
// size, data
|
|
MoveMemory(pbCurPtr+sizeof(DWORD), pbCurPtr, cbDataSize); // shift right data for size insertion
|
|
*(DWORD UNALIGNED *)pbCurPtr = cbDataSize; // size of data
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
pbCurPtr += cbDataSize; // fwd past data
|
|
|
|
// size, MAC
|
|
*(DWORD UNALIGNED *)pbCurPtr = A_SHA_DIGEST_LEN; // size of MAC
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
CopyMemory(pbCurPtr, rgbHMAC, A_SHA_DIGEST_LEN); // MAC
|
|
pbCurPtr += A_SHA_DIGEST_LEN; // fwd past MAC
|
|
|
|
if (!FMyPrimitiveDESEncrypt(
|
|
ppbMyData, // in out
|
|
pcbMyData, // in out
|
|
sItemKey))
|
|
goto Ret;
|
|
|
|
cbDataSize = *pcbMyData; // save current size
|
|
*pcbMyData += 3*sizeof(DWORD) + cbKeyBlock; // ver, sizeof(keyblk), keyblk, sizeof(encrdata)
|
|
pbCurPtr = (PBYTE)SSReAlloc(*ppbMyData, *pcbMyData);
|
|
if (pbCurPtr == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
*ppbMyData = pbCurPtr;
|
|
|
|
// shift right data for size, keyblk insertions
|
|
MoveMemory(pbCurPtr + 3*sizeof(DWORD) + cbKeyBlock, pbCurPtr, cbDataSize);
|
|
|
|
// throw version tag in front
|
|
*(DWORD UNALIGNED *)pbCurPtr = (DWORD)ENCRYPTED_DATA_VER;
|
|
pbCurPtr += sizeof(DWORD);
|
|
|
|
// insert keyblock
|
|
*(DWORD UNALIGNED *)pbCurPtr = cbKeyBlock; // size of data
|
|
pbCurPtr += sizeof(DWORD); // fwd past size
|
|
CopyMemory(pbCurPtr, pbKeyBlock, cbKeyBlock); // data
|
|
pbCurPtr += cbKeyBlock; // fwd past data
|
|
|
|
// insert sizeof encrypted blob
|
|
*(DWORD UNALIGNED *)pbCurPtr = cbDataSize; // size of data
|
|
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
RtlSecureZeroMemory(&sItemKey, sizeof(DESKEY));
|
|
RtlSecureZeroMemory(&sMacKey, sizeof(DESKEY));
|
|
|
|
if (pbKeyBlock)
|
|
SSFree(pbKeyBlock);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// given password, and master key, will decrypt and
|
|
// verify MAC on master key
|
|
BOOL FCheckPWConfirm(
|
|
LPCWSTR szUser, // in
|
|
LPCWSTR szMasterKey, // in
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN]) // in
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
BYTE rgbSalt[PASSWORD_SALT_LEN];
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
|
|
|
|
PBYTE pbMK = NULL;
|
|
DWORD cbMK;
|
|
|
|
// confirm is just get state and attempt MK decrypt
|
|
if (FBPGetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
&pbMK,
|
|
&cbMK))
|
|
{
|
|
BOOL fResetSecurityState;
|
|
|
|
// found state; is pwd correct?
|
|
if (!FMyDecryptMKEx(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK,
|
|
&fResetSecurityState
|
|
))
|
|
goto Ret;
|
|
|
|
|
|
if( fResetSecurityState )
|
|
{
|
|
|
|
// now wrap MK up and stuff in registry
|
|
|
|
if(FMyEncryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK
|
|
)) {
|
|
|
|
if (FBPSetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
pbMK,
|
|
cbMK
|
|
))
|
|
{
|
|
|
|
// found state; is pwd correct?
|
|
if (!FMyDecryptMKEx(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK,
|
|
&fResetSecurityState
|
|
)) {
|
|
OutputDebugString(TEXT("fail to dec\n"));
|
|
goto Ret;
|
|
}
|
|
}
|
|
|
|
}
|
|
} // reset security state.
|
|
}
|
|
else
|
|
{
|
|
// didn't find state; create it
|
|
// if we couldn't retrieve state, assume we must generate it
|
|
if (!RtlGenRandom(rgbSalt, PASSWORD_SALT_LEN))
|
|
goto Ret;
|
|
|
|
cbMK = 2*DES_KEYLEN;
|
|
pbMK = (PBYTE)SSAlloc(cbMK);
|
|
if (pbMK == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
if (!RtlGenRandom(pbMK, cbMK))
|
|
goto Ret;
|
|
|
|
// now wrap MK up and stuff in registry
|
|
if (!FMyEncryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK))
|
|
goto Ret;
|
|
|
|
if (!FBPSetSecurityState(
|
|
szUser,
|
|
szMasterKey,
|
|
rgbSalt,
|
|
PASSWORD_SALT_LEN,
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
pbMK,
|
|
cbMK))
|
|
goto Ret;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
if (pbMK) {
|
|
RtlSecureZeroMemory(pbMK, cbMK); // sfield: zero memory
|
|
SSFree(pbMK);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// callback for changing a password. On password change,
|
|
// MasterKey is decrypted and re-encrypted
|
|
BOOL FPasswordChangeNotify(
|
|
LPCWSTR szUser, // in
|
|
LPCWSTR szPasswordName, // in
|
|
BYTE rgbOldPwd[A_SHA_DIGEST_LEN], // in
|
|
DWORD cbOldPwd, // in
|
|
BYTE rgbNewPwd[A_SHA_DIGEST_LEN], // in
|
|
DWORD cbNewPwd) // in
|
|
{
|
|
// allows unattended pw change (callback from svr)
|
|
|
|
BOOL fRet = FALSE;
|
|
BYTE rgbSalt[PASSWORD_SALT_LEN];
|
|
BYTE rgbConfirm[A_SHA_DIGEST_LEN];
|
|
|
|
PBYTE pbMK = NULL;
|
|
DWORD cbMK;
|
|
|
|
BOOL fNewPassword = (cbOldPwd == 0);
|
|
|
|
// can't modify a non-user changable pw
|
|
// if (!FIsUserMasterKey(szPasswordName))
|
|
// goto Ret;
|
|
|
|
if (cbNewPwd != A_SHA_DIGEST_LEN)
|
|
goto Ret;
|
|
|
|
// ensure old pwd exists
|
|
if (!FBPGetSecurityState(
|
|
szUser,
|
|
szPasswordName,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
&pbMK,
|
|
&cbMK))
|
|
{
|
|
// couldn't retreive old PW, create a new one
|
|
if (!FCheckPWConfirm(
|
|
szUser,
|
|
szPasswordName,
|
|
rgbNewPwd))
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
// state was retrieved
|
|
if (fNewPassword)
|
|
{
|
|
SetLastError((DWORD)PST_E_ITEM_EXISTS);
|
|
goto Ret;
|
|
}
|
|
}
|
|
|
|
// old pwd retrieved -- time to update
|
|
if (!FMyDecryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbOldPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK))
|
|
{
|
|
SetLastError((DWORD)PST_E_WRONG_PASSWORD);
|
|
goto Ret;
|
|
}
|
|
|
|
// MK is naked here
|
|
|
|
// rewrap and save state
|
|
if (!FMyEncryptMK(
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbNewPwd,
|
|
rgbConfirm,
|
|
&pbMK,
|
|
&cbMK))
|
|
goto Ret;
|
|
if (!FBPSetSecurityState(
|
|
szUser,
|
|
szPasswordName,
|
|
rgbSalt,
|
|
sizeof(rgbSalt),
|
|
rgbConfirm,
|
|
sizeof(rgbConfirm),
|
|
pbMK,
|
|
cbMK))
|
|
goto Ret;
|
|
|
|
fRet = TRUE;
|
|
Ret:
|
|
if (pbMK) {
|
|
RtlSecureZeroMemory(pbMK, cbMK);
|
|
SSFree(pbMK);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
BOOL FHMACGeographicallySensitiveData(
|
|
LPCWSTR szUser, // in
|
|
LPCWSTR szPasswordName, // in
|
|
DWORD dwHMACVersion, // in
|
|
BYTE rgbPwd[A_SHA_DIGEST_LEN], // in
|
|
const GUID* pguidType, // in
|
|
const GUID* pguidSubtype, // in
|
|
LPCWSTR szItem, // in: may be NULL
|
|
PBYTE pbBuf, // in
|
|
DWORD cbBuf, // in
|
|
BYTE rgbHMAC[A_SHA_DIGEST_LEN]) // out
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
// helpful pointer
|
|
PBYTE pbCurrent;
|
|
|
|
PBYTE pbKeyBlock = NULL;
|
|
DWORD cbKeyBlock;
|
|
DESKEY sBogusKey;
|
|
DESKEY sMacKey;
|
|
|
|
DWORD cbTmp = (DWORD)(cbBuf + 2*sizeof(GUID) + WSZ_BYTECOUNT(szItem));
|
|
PBYTE pbTmp = (PBYTE)SSAlloc(cbTmp);
|
|
if (pbTmp == NULL) // check allocation
|
|
goto Ret;
|
|
|
|
// helpful pointer
|
|
pbCurrent = pbTmp;
|
|
|
|
// snag the MAC key
|
|
if (!FGetInternalMACKey(szUser, &pbKeyBlock, &cbKeyBlock))
|
|
{
|
|
// create a key block
|
|
if (!FMyEncryptKeyBlock(
|
|
szUser,
|
|
szPasswordName,
|
|
rgbPwd,
|
|
&pbKeyBlock,
|
|
&cbKeyBlock,
|
|
&sBogusKey,
|
|
&sMacKey))
|
|
goto Ret;
|
|
|
|
if (!FSetInternalMACKey(szUser, pbKeyBlock, cbKeyBlock))
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
// key already exists; get it
|
|
if (!FMyDecryptKeyBlock(
|
|
szUser,
|
|
szPasswordName,
|
|
rgbPwd,
|
|
pbKeyBlock,
|
|
cbKeyBlock,
|
|
&sBogusKey,
|
|
&sMacKey))
|
|
goto Ret;
|
|
}
|
|
|
|
|
|
// HMAC format:
|
|
// HMAC( guidType | guidSubtype | szItemName | pbData )
|
|
|
|
// copy type
|
|
CopyMemory(pbCurrent, pguidType, sizeof(GUID));
|
|
pbCurrent += sizeof(GUID);
|
|
|
|
// copy subtype
|
|
CopyMemory(pbCurrent, pguidSubtype, sizeof(GUID));
|
|
pbCurrent += sizeof(GUID);
|
|
|
|
// copy item name
|
|
CopyMemory(pbCurrent, szItem, WSZ_BYTECOUNT(szItem));
|
|
pbCurrent += WSZ_BYTECOUNT(szItem);
|
|
|
|
// copy actual data
|
|
CopyMemory(pbCurrent, pbBuf, cbBuf);
|
|
|
|
if (dwHMACVersion == OLD_HMAC_VERSION)
|
|
{
|
|
// now do HMAC on this
|
|
if (!FMyOldPrimitiveHMAC(
|
|
sMacKey,
|
|
pbTmp,
|
|
cbTmp,
|
|
rgbHMAC))
|
|
goto Ret;
|
|
}
|
|
else
|
|
{
|
|
// now do HMAC on this
|
|
if (!FMyPrimitiveHMAC(
|
|
sMacKey,
|
|
pbTmp,
|
|
cbTmp,
|
|
rgbHMAC))
|
|
goto Ret;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
Ret:
|
|
if (pbTmp)
|
|
SSFree(pbTmp);
|
|
|
|
if (pbKeyBlock)
|
|
SSFree(pbKeyBlock);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// lifted (nearly) directly from RSABase ntagum.c
|
|
|
|
// do locale check once
|
|
static BOOL g_fEncryptionIsPermitted;
|
|
static BOOL g_fIKnowEncryptionPermitted = FALSE;
|
|
|
|
BOOL FIsEncryptionPermitted()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether encryption is getting the system default
|
|
LCID and checking whether the country code is CTRY_FRANCE.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE - encryption is permitted
|
|
FALSE - encryption is not permitted
|
|
|
|
|
|
--*/
|
|
{
|
|
LCID DefaultLcid;
|
|
CHAR CountryCode[10];
|
|
ULONG CountryValue;
|
|
|
|
if (!g_fIKnowEncryptionPermitted)
|
|
{
|
|
// assume okay (unless found otherwise)
|
|
g_fEncryptionIsPermitted = TRUE;
|
|
|
|
DefaultLcid = GetSystemDefaultLCID();
|
|
//
|
|
// Check if the default language is Standard French
|
|
//
|
|
if (LANGIDFROMLCID(DefaultLcid) == 0x40c)
|
|
g_fEncryptionIsPermitted = FALSE;
|
|
|
|
//
|
|
// Check if the users's country is set to FRANCE
|
|
//
|
|
if (GetLocaleInfoA(DefaultLcid,LOCALE_ICOUNTRY,CountryCode,10) == 0)
|
|
g_fEncryptionIsPermitted = FALSE;
|
|
|
|
/*
|
|
CountryValue = (ULONG) atol(CountryCode);
|
|
if (CountryValue == CTRY_FRANCE)
|
|
return(FALSE);
|
|
*/
|
|
|
|
//
|
|
// begin remove dependency on atol and msvcrt
|
|
//
|
|
// from winnls.h:
|
|
// #define CTRY_FRANCE 33 // France
|
|
SS_ASSERT(CTRY_FRANCE == 33);
|
|
if (0 == lstrcmpA(CountryCode, "33"))
|
|
g_fEncryptionIsPermitted = FALSE;
|
|
//
|
|
// end remove dependency on atol and msvcrt
|
|
//
|
|
|
|
g_fIKnowEncryptionPermitted = TRUE;
|
|
}
|
|
|
|
return g_fEncryptionIsPermitted;
|
|
}
|