Chromium Code Reviews| Index: crypto/nss_util.cc |
| =================================================================== |
| --- crypto/nss_util.cc (revision 99168) |
| +++ crypto/nss_util.cc (working copy) |
| @@ -83,6 +83,148 @@ |
| return dir; |
| } |
| +#if defined(OS_CHROMEOS) |
| + |
| +static unsigned char kSupplementalUserKeyId[] = { |
|
wtc
2011/09/02 22:31:08
Nit: remove 'static' because this is in the anonym
zel
2011/09/03 01:52:22
On ChromeOS, this key will be used to perform AES
|
| + 0xCC, 0x13, 0x19, 0xDE, 0x75, 0x5E, 0xFE, 0x00, |
| + 0x5E, 0x71, 0xD4, 0xA6, 0x00, 0x00, 0x00, 0xCC |
| +}; |
| + |
| +const char kSupplementalKeyNickname[] = "ChromeOS_SupplementalUserKey"; |
| + |
| +struct SDRResult |
| +{ |
|
wtc
2011/09/02 22:31:08
Nit: put '{' on the previous line.
Document what
zel
2011/09/03 01:52:22
I have removed this code addressed in this and com
|
| + SECItem keyid; |
| + SECAlgorithmID alg; |
| + SECItem data; |
| +}; |
| +typedef struct SDRResult SDRResult; |
|
wtc
2011/09/02 22:31:08
Remove this typedef. This is not necessary in C++
zel
2011/09/03 01:52:22
Removed.
|
| + |
| +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) |
| + |
| +static SEC_ASN1Template g_encoding_template[] = { |
| + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof (SDRResult) }, |
| + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, keyid) }, |
| + { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SDRResult, alg), |
| + SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, |
| + { SEC_ASN1_OCTET_STRING, offsetof(SDRResult, data) }, |
| + { 0 } |
| +}; |
| + |
| +// Local utility functions for padding/unpadding an incoming data block |
| +// to the mechanism block size. |
| + |
| +SECStatus PadBlock(SECItem *data, unsigned int blockSize, SECItem *result) { |
|
wtc
2011/09/02 22:31:08
The function arguments and variables in these func
zel
2011/09/03 01:52:22
Removed.
|
| + SECStatus rv = SECSuccess; |
| + unsigned int padLength; |
| + unsigned int i; |
| + |
| + result->data = 0; |
| + result->len = 0; |
| + |
| + // This algorithm always adds to the block (to indicate the number |
| + // of pad bytes). So allocate a block large enough. |
| + padLength = blockSize - (data->len % blockSize); |
| + result->len = data->len + padLength; |
| + result->data = (unsigned char *) PORT_Alloc(result->len); |
|
wtc
2011/09/02 22:31:08
Nit: try to use C++ casts where possible.
This is
zel
2011/09/03 01:52:22
Removed.
|
| + |
| + // Copy the data |
| + PORT_Memcpy(result->data, data->data, data->len); |
| + |
| + // Add the pad values |
| + for(i = data->len; i < result->len; i++) |
| + result->data[i] = (unsigned char) padLength; |
| + |
| + return rv; |
| +} |
| + |
| +SECStatus UnpadBlock(SECItem *data, unsigned int blockSize, SECItem *result) { |
| + SECStatus rv = SECSuccess; |
| + unsigned int padLength; |
| + unsigned int i; |
| + |
| + result->data = 0; |
| + result->len = 0; |
| + |
| + // Remove the padding from the end if the input data. |
| + if (data->len == 0 || data->len % blockSize != 0) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + padLength = data->data[data->len-1]; |
| + if (padLength > blockSize) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + // Verify padding. |
| + for (i = data->len - padLength; i < data->len; i++) { |
| + if (data->data[i] != padLength) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + } |
| + |
| + result->len = data->len - padLength; |
| + result->data = (unsigned char *) PORT_Alloc(result->len); |
| + if (!result->data) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + PORT_Memcpy(result->data, data->data, result->len); |
| + |
| + if (padLength < 2) { |
| + return SECWouldBlock; |
| + } |
| + |
| +done: |
| + return rv; |
| +} |
| + |
| +// decrypt a block |
| +SECStatus pk11Decrypt(PK11SlotInfo *slot, PLArenaPool *arena, |
| + CK_MECHANISM_TYPE type, PK11SymKey *key, |
| + SECItem *params, SECItem *in, SECItem *result) { |
| + PK11Context *ctx = 0; |
| + SECItem paddedResult; |
| + SECStatus rv; |
| + |
| + paddedResult.len = 0; |
| + paddedResult.data = 0; |
| + |
| + ctx = PK11_CreateContextBySymKey(type, CKA_DECRYPT, key, params); |
| + if (!ctx) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + paddedResult.len = in->len; |
| + paddedResult.data = reinterpret_cast<unsigned char *>( |
| + PORT_ArenaAlloc(arena, paddedResult.len)); |
| + |
| + rv = PK11_CipherOp(ctx, paddedResult.data, |
| + reinterpret_cast<int*>(&paddedResult.len), paddedResult.len, |
| + in->data, in->len); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + PK11_Finalize(ctx); |
| + |
| + // Remove the padding. |
| + rv = UnpadBlock(&paddedResult, PK11_GetBlockSize(type, 0), result); |
| + if (rv) |
| + goto done; |
| + |
| +done: |
| + if (ctx) |
| + PK11_DestroyContext(ctx, PR_TRUE); |
| + return rv; |
| +} |
|
wtc
2011/09/02 22:31:08
If these functions are copied from NSS or Mozilla,
zel
2011/09/03 01:52:22
Removed.
|
| +#endif |
| + |
| + |
|
wtc
2011/09/02 22:31:08
Nit: remove one blank line.
|
| // On non-chromeos platforms, return the default config directory. |
| // On chromeos, return a read-only directory with fake root CA certs for testing |
| // (which will not exist on non-testing images). These root CA certs are used |
| @@ -287,7 +429,317 @@ |
| GetTPMTokenInfo(&token_name, NULL); |
| return FindSlotWithTokenName(token_name); |
| } |
| +/* |
| + bool GetSupplementalUserKey(std::string* user_key) { |
| + OpenPersistentNSSDB(); |
| + SECStatus rv = SECSuccess; |
| + PK11SlotInfo* slot = NULL; |
| + PK11SymKey *key = NULL; |
| + SECItem* keyData = NULL; |
| + SECItem keyID; |
| + CK_MECHANISM_TYPE type = CKM_DES3_CBC; |
| + PLArenaPool *arena = 0; |
| + |
| + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); |
| + if (!arena) |
| + goto done; |
| + |
| + slot = GetPublicNSSKeySlot(); |
| + if (!slot) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + rv = PK11_Authenticate(slot, PR_TRUE, NULL); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + keyID.type = siBuffer; |
| + keyID.data = kSupplementalUserKeyId; |
| + keyID.len = static_cast<int>(sizeof(kSupplementalUserKeyId)); |
| + |
| + // Find/generate AES key. |
| + key = PK11_FindFixedKey(slot, type, &keyID, NULL); |
| + if (!key) { |
| + |
| + key = PK11_TokenKeyGen(slot, type, 0, 0, &keyID, PR_TRUE, NULL); |
| + if (key) { |
| + rv = PK11_SetSymKeyNickname(key, kSupplementalKeyNickname); |
| + if (rv != SECSuccess) |
| + goto done; |
| + } else { |
| + int error = PORT_GetError(); |
| + LOG(WARNING) << "Got error: " << error; |
| + } |
| + } |
| + |
| + if (!key) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + keyData = PK11_GetKeyData(key); |
| + if (!keyData) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + *user_key = std::string( |
| + reinterpret_cast<const char*>(keyData->data), keyData->len); |
| + |
| + done: |
| + if (arena) |
| + PORT_FreeArena(arena, PR_TRUE); |
| + if (keyData) |
| + SECITEM_ZfreeItem(keyData, PR_FALSE); |
| + if (key) |
| + PK11_FreeSymKey(key); |
| + if (slot) |
| + PK11_FreeSlot(slot); |
| + |
| + return (rv == SECSuccess); |
| + } |
| +*/ |
|
wtc
2011/09/02 22:31:08
Nit: please use #if 0 to comment out a block of co
zel
2011/09/03 01:52:22
Rewritten part.
|
| + |
| + bool EncryptWithSupplementalUserKey(const std::string& raw_data, |
| + std::string* encryped_data) { |
| + DCHECK(chromeos_user_logged_in_); |
| + |
| + SECStatus rv = SECSuccess; |
| + SECItem *params = NULL; |
| + PK11SlotInfo* slot = NULL; |
| + PK11SymKey *key = NULL; |
| + PLArenaPool *arena = 0; |
| + PK11Context *ctx = 0; |
| + SDRResult sdrResult; |
| + SECItem keyID; |
| + SECItem data; |
| + SECItem paddedData = { siBuffer, NULL, 0 }; |
| + SECItem tempResults = { siBuffer, NULL, 0 }; |
| + CK_MECHANISM_TYPE type = CKM_AES_CBC; |
| + |
| + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); |
| + if (!arena) |
| + goto done; |
| + |
| + slot = GetPublicNSSKeySlot(); |
| + if (!slot) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + rv = PK11_Authenticate(slot, PR_TRUE, NULL); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + keyID.type = siBuffer; |
| + keyID.data = kSupplementalUserKeyId; |
| + keyID.len = static_cast<int>(sizeof(kSupplementalUserKeyId)); |
| + |
| + // Find/generate AES key. |
| + key = PK11_FindFixedKey(slot, type, &keyID, NULL); |
| + if (!key) { |
| + key = PK11_TokenKeyGen(slot, type, NULL, |
| + 32, /* keysize in bytes*/ |
|
wtc
2011/09/02 22:31:08
This is just a comment: 256-bit AES is extremely s
|
| + &keyID, PR_TRUE, NULL); |
| + if (key) { |
| + rv = PK11_SetSymKeyNickname(key, kSupplementalKeyNickname); |
| + if (rv != SECSuccess) |
| + goto done; |
| + } |
| + } |
| + if (!key) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + params = PK11_GenerateNewParam(type, key); |
| + if (!params) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + ctx = PK11_CreateContextBySymKey(type, CKA_ENCRYPT, key, params); |
| + if (!ctx) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + data.type = siBuffer; |
| + data.data = reinterpret_cast<unsigned char*>( |
| + const_cast<char*>(raw_data.c_str())); |
| + data.len = static_cast<int>(raw_data.length()); |
| + |
| + rv = PadBlock(&data, PK11_GetBlockSize(type, 0), &paddedData); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + sdrResult.data.len = paddedData.len; |
| + sdrResult.data.data = reinterpret_cast<unsigned char *>( |
| + PORT_ArenaAlloc(arena, sdrResult.data.len)); |
| + rv = PK11_CipherOp(ctx, sdrResult.data.data, |
| + reinterpret_cast<int*>(&sdrResult.data.len), sdrResult.data.len, |
| + paddedData.data, paddedData.len); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + PK11_Finalize(ctx); |
| + sdrResult.keyid = keyID; |
| + |
| + rv = PK11_ParamToAlgid(SEC_OID_AES_256_CBC, params, arena, &sdrResult.alg); |
| + if (rv != SECSuccess) |
| + goto done; |
| + |
| + if (!SEC_ASN1EncodeItem(0, &tempResults, &sdrResult, g_encoding_template)) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + if (rv == SECSuccess) |
| + *encryped_data = std::string( |
| + reinterpret_cast<const char*>(tempResults.data), tempResults.len); |
| + |
| + done: |
| + SECITEM_ZfreeItem(&paddedData, PR_FALSE); |
| + SECITEM_ZfreeItem(&tempResults, PR_FALSE); |
| + if (arena) |
| + PORT_FreeArena(arena, PR_TRUE); |
| + if (ctx) |
| + PK11_DestroyContext(ctx, PR_TRUE); |
| + if (params) |
| + SECITEM_ZfreeItem(params, PR_TRUE); |
| + if (key) |
| + PK11_FreeSymKey(key); |
| + if (slot) |
| + PK11_FreeSlot(slot); |
| + |
| + return (rv == SECSuccess); |
| + } |
| + |
| + bool DecryptWithSupplementalUserKey(const std::string& encrypted_data, |
| + std::string* decrypted_data) { |
| + DCHECK(chromeos_user_logged_in_); |
| + |
| + SECStatus rv = SECSuccess; |
| + PK11SlotInfo *slot = 0; |
| + PK11SymKey *key = 0; |
| + CK_MECHANISM_TYPE type; |
| + SDRResult sdrResult; |
| + SECItem *params = 0; |
| + SECItem result = { siBuffer, NULL, 0 }; |
| + SECItem possibleResult = { siBuffer, NULL, 0 }; |
| + PLArenaPool *arena = 0; |
| + void* cx = NULL; |
| + |
| + SECItem data; |
| + data.type = siBuffer; |
| + data.data = reinterpret_cast<unsigned char*>( |
| + const_cast<char*>(encrypted_data.c_str())); |
| + data.len = static_cast<int>(encrypted_data.length()); |
| + |
| + arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); |
| + if (!arena) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + // Decode the incoming data. |
| + memset(&sdrResult, 0, sizeof sdrResult); |
| + rv = SEC_QuickDERDecodeItem(arena, &sdrResult, g_encoding_template, &data); |
| + if (rv != SECSuccess) // Invalid format. |
| + goto done; |
| + |
| + // Find the slot and key for the given keyid. |
| + slot = GetPublicNSSKeySlot(); |
| + if (!slot) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + // Get the parameter values from the data. |
| + params = PK11_ParamFromAlgid(&sdrResult.alg); |
| + if (!params) { |
| + rv = SECFailure; |
| + goto done; |
| + } |
| + |
| + // Use AES. |
| + type = CKM_AES_CBC; |
| + key = PK11_FindFixedKey(slot, type, &sdrResult.keyid, cx); |
| + if (!key) { |
| + rv = SECFailure; |
| + } else { |
| + rv = pk11Decrypt(slot, arena, type, key, params, &sdrResult.data, |
| + &result); |
| + } |
| + |
| + // if the pad value was too small (1 or 2), then it's statistically |
| + // 'likely' that (1 in 256) that we may not have the correct key. |
| + // Check the other keys for a better match. If we find none, use |
| + // this result. |
| + if (rv == SECWouldBlock) |
| + possibleResult = result; |
| + |
| + // handle the case where your key indicies may have been broken. |
| + if (rv != SECSuccess) { |
| + PK11SymKey *keyList = PK11_ListFixedKeysInSlot(slot, NULL, cx); |
| + PK11SymKey *testKey = NULL; |
| + PK11SymKey *nextKey = NULL; |
| + |
| + for (testKey = keyList; testKey; testKey = PK11_GetNextSymKey(testKey)) { |
| + rv = pk11Decrypt(slot, arena, type, testKey, params, |
| + &sdrResult.data, &result); |
| + if (rv == SECSuccess) |
| + break; |
| + |
| + // found a close match. If it's our first remember it. |
| + if (rv == SECWouldBlock) { |
| + if (possibleResult.data) { |
| + // this is unlikely but possible. If we hit this condition, |
| + // we have no way of knowing which possibility to prefer. |
| + // in this case we just match the key the application |
| + // thought was the right one |
| + SECITEM_ZfreeItem(&result, PR_FALSE); |
| + } else { |
| + possibleResult = result; |
| + } |
| + } |
| + } |
| + // free the list |
| + for (testKey = keyList; testKey; testKey = nextKey) { |
| + nextKey = PK11_GetNextSymKey(testKey); |
| + PK11_FreeSymKey(testKey); |
| + } |
| + } |
| + |
| + // we didn't find a better key, use the one with a small pad value |
| + if ((rv != SECSuccess) && (possibleResult.data)) { |
| + result = possibleResult; |
| + possibleResult.data = NULL; |
| + rv = SECSuccess; |
| + } |
| + |
| + if (rv == SECSuccess) { |
| + *decrypted_data = std::string(reinterpret_cast<const char*>(result.data), |
| + result.len); |
| + } |
| + |
| + done: |
| + SECITEM_ZfreeItem(&result, PR_FALSE); |
| + if (arena) |
| + PORT_FreeArena(arena, PR_TRUE); |
| + if (key) |
| + PK11_FreeSymKey(key); |
| + if (params) |
| + SECITEM_ZfreeItem(params, PR_TRUE); |
| + if (slot) |
| + PK11_FreeSlot(slot); |
| + if (possibleResult.data) |
| + SECITEM_ZfreeItem(&possibleResult, PR_FALSE); |
| + |
| + return (rv == SECSuccess); |
| + } |
| #endif // defined(OS_CHROMEOS) |
| @@ -702,6 +1154,17 @@ |
| return g_nss_singleton.Get().EnsureTPMTokenReady(); |
| } |
| +bool EncryptWithSupplementalUserKey(const std::string& data, |
| + std::string* encryped_data) { |
| + return g_nss_singleton.Get().EncryptWithSupplementalUserKey(data, |
| + encryped_data); |
| +} |
| + |
| +bool DecryptWithSupplementalUserKey(const std::string& encryped_data, |
| + std::string* raw_data) { |
| + return g_nss_singleton.Get().DecryptWithSupplementalUserKey(encryped_data, |
| + raw_data); |
| +} |
| #endif // defined(OS_CHROMEOS) |
| // TODO(port): Implement this more simply. We can convert by subtracting an |