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 |