From 679147eead574d186ebf3069647b4c23e8ccace6 Mon Sep 17 00:00:00 2001 From: Zeno Albisser Date: Thu, 15 Aug 2013 21:46:11 +0200 Subject: Initial import. --- chromium/crypto/OWNERS | 4 + chromium/crypto/PRESUBMIT.py | 14 + chromium/crypto/apple_keychain.h | 108 ++ chromium/crypto/apple_keychain_ios.mm | 196 +++ chromium/crypto/apple_keychain_mac.mm | 144 ++ chromium/crypto/capi_util.cc | 57 + chromium/crypto/capi_util.h | 39 + chromium/crypto/crypto.gyp | 352 +++++ chromium/crypto/crypto_export.h | 38 + .../crypto_module_blocking_password_delegate.h | 33 + chromium/crypto/cssm_init.cc | 204 +++ chromium/crypto/cssm_init.h | 60 + chromium/crypto/curve25519-donna.c | 592 +++++++++ chromium/crypto/curve25519.cc | 36 + chromium/crypto/curve25519.h | 48 + chromium/crypto/curve25519_unittest.cc | 44 + chromium/crypto/ec_private_key.h | 143 ++ chromium/crypto/ec_private_key_nss.cc | 365 +++++ chromium/crypto/ec_private_key_openssl.cc | 71 + chromium/crypto/ec_private_key_unittest.cc | 107 ++ chromium/crypto/ec_signature_creator.cc | 34 + chromium/crypto/ec_signature_creator.h | 66 + chromium/crypto/ec_signature_creator_impl.h | 34 + chromium/crypto/ec_signature_creator_nss.cc | 114 ++ chromium/crypto/ec_signature_creator_openssl.cc | 31 + chromium/crypto/ec_signature_creator_unittest.cc | 84 ++ chromium/crypto/encryptor.cc | 97 ++ chromium/crypto/encryptor.h | 137 ++ chromium/crypto/encryptor_nss.cc | 193 +++ chromium/crypto/encryptor_openssl.cc | 177 +++ chromium/crypto/encryptor_unittest.cc | 532 ++++++++ chromium/crypto/ghash.cc | 257 ++++ chromium/crypto/ghash.h | 86 ++ chromium/crypto/ghash_unittest.cc | 146 ++ chromium/crypto/hkdf.cc | 99 ++ chromium/crypto/hkdf.h | 64 + chromium/crypto/hkdf_unittest.cc | 94 ++ chromium/crypto/hmac.cc | 57 + chromium/crypto/hmac.h | 93 ++ chromium/crypto/hmac_nss.cc | 117 ++ chromium/crypto/hmac_openssl.cc | 56 + chromium/crypto/hmac_unittest.cc | 277 ++++ chromium/crypto/hmac_win.cc | 209 +++ chromium/crypto/mac_security_services_lock.cc | 42 + chromium/crypto/mac_security_services_lock.h | 25 + chromium/crypto/mock_apple_keychain.cc | 61 + chromium/crypto/mock_apple_keychain.h | 251 ++++ chromium/crypto/mock_apple_keychain_ios.cc | 20 + chromium/crypto/mock_apple_keychain_mac.cc | 509 +++++++ chromium/crypto/nss_util.cc | 775 +++++++++++ chromium/crypto/nss_util.h | 173 +++ chromium/crypto/nss_util_internal.h | 43 + chromium/crypto/nss_util_unittest.cc | 40 + chromium/crypto/openpgp_symmetric_encryption.cc | 795 +++++++++++ chromium/crypto/openpgp_symmetric_encryption.h | 45 + .../openpgp_symmetric_encryption_unittest.cc | 114 ++ chromium/crypto/openssl_util.cc | 113 ++ chromium/crypto/openssl_util.h | 119 ++ chromium/crypto/p224.cc | 743 +++++++++++ chromium/crypto/p224.h | 60 + chromium/crypto/p224_spake.cc | 248 ++++ chromium/crypto/p224_spake.h | 114 ++ chromium/crypto/p224_spake_unittest.cc | 130 ++ chromium/crypto/p224_unittest.cc | 824 ++++++++++++ chromium/crypto/random.cc | 19 + chromium/crypto/random.h | 21 + chromium/crypto/random_unittest.cc | 27 + chromium/crypto/rsa_private_key.cc | 384 ++++++ chromium/crypto/rsa_private_key.h | 262 ++++ chromium/crypto/rsa_private_key_nss.cc | 284 ++++ chromium/crypto/rsa_private_key_nss_unittest.cc | 87 ++ chromium/crypto/rsa_private_key_openssl.cc | 126 ++ chromium/crypto/rsa_private_key_unittest.cc | 405 ++++++ chromium/crypto/run_all_unittests.cc | 17 + chromium/crypto/scoped_capi_types.h | 121 ++ chromium/crypto/scoped_nss_types.h | 63 + chromium/crypto/secure_hash.h | 50 + chromium/crypto/secure_hash_default.cc | 96 ++ chromium/crypto/secure_hash_openssl.cc | 102 ++ chromium/crypto/secure_hash_unittest.cc | 73 + chromium/crypto/secure_util.cc | 19 + chromium/crypto/secure_util.h | 29 + chromium/crypto/sha2.cc | 25 + chromium/crypto/sha2.h | 33 + chromium/crypto/sha2_unittest.cc | 100 ++ chromium/crypto/signature_creator.h | 67 + chromium/crypto/signature_creator_nss.cc | 97 ++ chromium/crypto/signature_creator_openssl.cc | 77 ++ chromium/crypto/signature_creator_unittest.cc | 96 ++ chromium/crypto/signature_verifier.h | 146 ++ chromium/crypto/signature_verifier_nss.cc | 226 ++++ chromium/crypto/signature_verifier_openssl.cc | 160 +++ chromium/crypto/signature_verifier_unittest.cc | 1138 ++++++++++++++++ chromium/crypto/symmetric_key.h | 105 ++ chromium/crypto/symmetric_key_nss.cc | 135 ++ chromium/crypto/symmetric_key_openssl.cc | 80 ++ chromium/crypto/symmetric_key_unittest.cc | 225 ++++ chromium/crypto/symmetric_key_win.cc | 536 ++++++++ chromium/crypto/third_party/nss/LICENSE | 35 + chromium/crypto/third_party/nss/README.chromium | 18 + chromium/crypto/third_party/nss/chromium-blapi.h | 101 ++ chromium/crypto/third_party/nss/chromium-blapit.h | 91 ++ chromium/crypto/third_party/nss/chromium-nss.h | 79 ++ chromium/crypto/third_party/nss/chromium-sha256.h | 51 + chromium/crypto/third_party/nss/pk11akey.cc | 98 ++ chromium/crypto/third_party/nss/rsawrapr.c | 160 +++ chromium/crypto/third_party/nss/secsign.cc | 132 ++ chromium/crypto/third_party/nss/sha512.cc | 1391 ++++++++++++++++++++ 108 files changed, 18540 insertions(+) create mode 100644 chromium/crypto/OWNERS create mode 100644 chromium/crypto/PRESUBMIT.py create mode 100644 chromium/crypto/apple_keychain.h create mode 100644 chromium/crypto/apple_keychain_ios.mm create mode 100644 chromium/crypto/apple_keychain_mac.mm create mode 100644 chromium/crypto/capi_util.cc create mode 100644 chromium/crypto/capi_util.h create mode 100644 chromium/crypto/crypto.gyp create mode 100644 chromium/crypto/crypto_export.h create mode 100644 chromium/crypto/crypto_module_blocking_password_delegate.h create mode 100644 chromium/crypto/cssm_init.cc create mode 100644 chromium/crypto/cssm_init.h create mode 100644 chromium/crypto/curve25519-donna.c create mode 100644 chromium/crypto/curve25519.cc create mode 100644 chromium/crypto/curve25519.h create mode 100644 chromium/crypto/curve25519_unittest.cc create mode 100644 chromium/crypto/ec_private_key.h create mode 100644 chromium/crypto/ec_private_key_nss.cc create mode 100644 chromium/crypto/ec_private_key_openssl.cc create mode 100644 chromium/crypto/ec_private_key_unittest.cc create mode 100644 chromium/crypto/ec_signature_creator.cc create mode 100644 chromium/crypto/ec_signature_creator.h create mode 100644 chromium/crypto/ec_signature_creator_impl.h create mode 100644 chromium/crypto/ec_signature_creator_nss.cc create mode 100644 chromium/crypto/ec_signature_creator_openssl.cc create mode 100644 chromium/crypto/ec_signature_creator_unittest.cc create mode 100644 chromium/crypto/encryptor.cc create mode 100644 chromium/crypto/encryptor.h create mode 100644 chromium/crypto/encryptor_nss.cc create mode 100644 chromium/crypto/encryptor_openssl.cc create mode 100644 chromium/crypto/encryptor_unittest.cc create mode 100644 chromium/crypto/ghash.cc create mode 100644 chromium/crypto/ghash.h create mode 100644 chromium/crypto/ghash_unittest.cc create mode 100644 chromium/crypto/hkdf.cc create mode 100644 chromium/crypto/hkdf.h create mode 100644 chromium/crypto/hkdf_unittest.cc create mode 100644 chromium/crypto/hmac.cc create mode 100644 chromium/crypto/hmac.h create mode 100644 chromium/crypto/hmac_nss.cc create mode 100644 chromium/crypto/hmac_openssl.cc create mode 100644 chromium/crypto/hmac_unittest.cc create mode 100644 chromium/crypto/hmac_win.cc create mode 100644 chromium/crypto/mac_security_services_lock.cc create mode 100644 chromium/crypto/mac_security_services_lock.h create mode 100644 chromium/crypto/mock_apple_keychain.cc create mode 100644 chromium/crypto/mock_apple_keychain.h create mode 100644 chromium/crypto/mock_apple_keychain_ios.cc create mode 100644 chromium/crypto/mock_apple_keychain_mac.cc create mode 100644 chromium/crypto/nss_util.cc create mode 100644 chromium/crypto/nss_util.h create mode 100644 chromium/crypto/nss_util_internal.h create mode 100644 chromium/crypto/nss_util_unittest.cc create mode 100644 chromium/crypto/openpgp_symmetric_encryption.cc create mode 100644 chromium/crypto/openpgp_symmetric_encryption.h create mode 100644 chromium/crypto/openpgp_symmetric_encryption_unittest.cc create mode 100644 chromium/crypto/openssl_util.cc create mode 100644 chromium/crypto/openssl_util.h create mode 100644 chromium/crypto/p224.cc create mode 100644 chromium/crypto/p224.h create mode 100644 chromium/crypto/p224_spake.cc create mode 100644 chromium/crypto/p224_spake.h create mode 100644 chromium/crypto/p224_spake_unittest.cc create mode 100644 chromium/crypto/p224_unittest.cc create mode 100644 chromium/crypto/random.cc create mode 100644 chromium/crypto/random.h create mode 100644 chromium/crypto/random_unittest.cc create mode 100644 chromium/crypto/rsa_private_key.cc create mode 100644 chromium/crypto/rsa_private_key.h create mode 100644 chromium/crypto/rsa_private_key_nss.cc create mode 100644 chromium/crypto/rsa_private_key_nss_unittest.cc create mode 100644 chromium/crypto/rsa_private_key_openssl.cc create mode 100644 chromium/crypto/rsa_private_key_unittest.cc create mode 100644 chromium/crypto/run_all_unittests.cc create mode 100644 chromium/crypto/scoped_capi_types.h create mode 100644 chromium/crypto/scoped_nss_types.h create mode 100644 chromium/crypto/secure_hash.h create mode 100644 chromium/crypto/secure_hash_default.cc create mode 100644 chromium/crypto/secure_hash_openssl.cc create mode 100644 chromium/crypto/secure_hash_unittest.cc create mode 100644 chromium/crypto/secure_util.cc create mode 100644 chromium/crypto/secure_util.h create mode 100644 chromium/crypto/sha2.cc create mode 100644 chromium/crypto/sha2.h create mode 100644 chromium/crypto/sha2_unittest.cc create mode 100644 chromium/crypto/signature_creator.h create mode 100644 chromium/crypto/signature_creator_nss.cc create mode 100644 chromium/crypto/signature_creator_openssl.cc create mode 100644 chromium/crypto/signature_creator_unittest.cc create mode 100644 chromium/crypto/signature_verifier.h create mode 100644 chromium/crypto/signature_verifier_nss.cc create mode 100644 chromium/crypto/signature_verifier_openssl.cc create mode 100644 chromium/crypto/signature_verifier_unittest.cc create mode 100644 chromium/crypto/symmetric_key.h create mode 100644 chromium/crypto/symmetric_key_nss.cc create mode 100644 chromium/crypto/symmetric_key_openssl.cc create mode 100644 chromium/crypto/symmetric_key_unittest.cc create mode 100644 chromium/crypto/symmetric_key_win.cc create mode 100644 chromium/crypto/third_party/nss/LICENSE create mode 100644 chromium/crypto/third_party/nss/README.chromium create mode 100644 chromium/crypto/third_party/nss/chromium-blapi.h create mode 100644 chromium/crypto/third_party/nss/chromium-blapit.h create mode 100644 chromium/crypto/third_party/nss/chromium-nss.h create mode 100644 chromium/crypto/third_party/nss/chromium-sha256.h create mode 100644 chromium/crypto/third_party/nss/pk11akey.cc create mode 100644 chromium/crypto/third_party/nss/rsawrapr.c create mode 100644 chromium/crypto/third_party/nss/secsign.cc create mode 100644 chromium/crypto/third_party/nss/sha512.cc (limited to 'chromium/crypto') diff --git a/chromium/crypto/OWNERS b/chromium/crypto/OWNERS new file mode 100644 index 00000000000..6f5cc98b802 --- /dev/null +++ b/chromium/crypto/OWNERS @@ -0,0 +1,4 @@ +agl@chromium.org +rsleevi@chromium.org +rvargas@chromium.org +wtc@chromium.org diff --git a/chromium/crypto/PRESUBMIT.py b/chromium/crypto/PRESUBMIT.py new file mode 100644 index 00000000000..7d0a13576fd --- /dev/null +++ b/chromium/crypto/PRESUBMIT.py @@ -0,0 +1,14 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Chromium presubmit script for src/net. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into gcl. +""" + +def GetPreferredTrySlaves(project, change): + # Changes in crypto often need a corresponding OpenSSL edit. + return ['linux_redux'] + diff --git a/chromium/crypto/apple_keychain.h b/chromium/crypto/apple_keychain.h new file mode 100644 index 00000000000..840ccee8c20 --- /dev/null +++ b/chromium/crypto/apple_keychain.h @@ -0,0 +1,108 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_KEYCHAIN_MAC_H_ +#define CRYPTO_KEYCHAIN_MAC_H_ + +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +#if defined (OS_IOS) +typedef void* SecKeychainRef; +typedef void* SecKeychainItemRef; +typedef void SecKeychainAttributeList; +#endif + +namespace crypto { + +// Wraps the KeychainServices API in a very thin layer, to allow it to be +// mocked out for testing. + +// See Keychain Services documentation for function documentation, as these call +// through directly to their Keychain Services equivalents (Foo -> +// SecKeychainFoo). The only exception is Free, which should be used for +// anything returned from this class that would normally be freed with +// CFRelease (to aid in testing). +class CRYPTO_EXPORT AppleKeychain { + public: + AppleKeychain(); + virtual ~AppleKeychain(); + + virtual OSStatus FindGenericPassword(CFTypeRef keychainOrArray, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32* passwordLength, + void** passwordData, + SecKeychainItemRef* itemRef) const; + + virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList, + void* data) const; + + virtual OSStatus AddGenericPassword(SecKeychainRef keychain, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const; + +#if !defined(OS_IOS) + virtual OSStatus ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, + SecKeychainAttributeInfo* info, + SecItemClass* itemClass, + SecKeychainAttributeList** attrList, + UInt32* length, + void** outData) const; + + virtual OSStatus ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, + const SecKeychainAttributeList* attrList, + UInt32 length, + const void* data) const; + + virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList, + void* data) const; + + virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const; + + virtual OSStatus SearchCreateFromAttributes( + CFTypeRef keychainOrArray, + SecItemClass itemClass, + const SecKeychainAttributeList* attrList, + SecKeychainSearchRef* searchRef) const; + + virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef* itemRef) const; + + virtual OSStatus AddInternetPassword(SecKeychainRef keychain, + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, const char* path, + UInt16 port, SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const; + + // Calls CFRelease on the given ref, after checking that |ref| is non-NULL. + virtual void Free(CFTypeRef ref) const; +#endif // !defined(OS_IOS) + + private: + DISALLOW_COPY_AND_ASSIGN(AppleKeychain); +}; + +} // namespace crypto + +#endif // CRYPTO_KEYCHAIN_MAC_H_ diff --git a/chromium/crypto/apple_keychain_ios.mm b/chromium/crypto/apple_keychain_ios.mm new file mode 100644 index 00000000000..74cf129ce1f --- /dev/null +++ b/chromium/crypto/apple_keychain_ios.mm @@ -0,0 +1,196 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/apple_keychain.h" + +#import + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/mac/scoped_nsobject.h" + +namespace { + +enum KeychainAction { + kKeychainActionCreate, + kKeychainActionUpdate +}; + +// Creates a dictionary that can be used to query the keystore. +// Ownership follows the Create rule. +CFDictionaryRef CreateGenericPasswordQuery(UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName) { + CFMutableDictionaryRef query = + CFDictionaryCreateMutable(NULL, + 5, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + // Type of element is generic password. + CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword); + + // Set the service name. + base::scoped_nsobject service_name_ns( + [[NSString alloc] initWithBytes:serviceName + length:serviceNameLength + encoding:NSUTF8StringEncoding]); + CFDictionarySetValue(query, kSecAttrService, + base::mac::NSToCFCast(service_name_ns)); + + // Set the account name. + base::scoped_nsobject account_name_ns( + [[NSString alloc] initWithBytes:accountName + length:accountNameLength + encoding:NSUTF8StringEncoding]); + CFDictionarySetValue(query, kSecAttrAccount, + base::mac::NSToCFCast(account_name_ns)); + + // Use the proper search constants, return only the data of the first match. + CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitOne); + CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue); + return query; +} + +// Creates a dictionary conatining the data to save into the keychain. +// Ownership follows the Create rule. +CFDictionaryRef CreateKeychainData(UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + KeychainAction action) { + CFMutableDictionaryRef keychain_data = + CFDictionaryCreateMutable(NULL, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + // Set the password. + NSData* password = [NSData dataWithBytes:passwordData length:passwordLength]; + CFDictionarySetValue(keychain_data, kSecValueData, + base::mac::NSToCFCast(password)); + + // If this is not a creation, no structural information is needed. + if (action != kKeychainActionCreate) + return keychain_data; + + // Set the type of the data. + CFDictionarySetValue(keychain_data, kSecClass, kSecClassGenericPassword); + + // Only allow access when the device has been unlocked. + CFDictionarySetValue(keychain_data, + kSecAttrAccessible, + kSecAttrAccessibleWhenUnlocked); + + // Set the service name. + base::scoped_nsobject service_name_ns( + [[NSString alloc] initWithBytes:serviceName + length:serviceNameLength + encoding:NSUTF8StringEncoding]); + CFDictionarySetValue(keychain_data, kSecAttrService, + base::mac::NSToCFCast(service_name_ns)); + + // Set the account name. + base::scoped_nsobject account_name_ns( + [[NSString alloc] initWithBytes:accountName + length:accountNameLength + encoding:NSUTF8StringEncoding]); + CFDictionarySetValue(keychain_data, kSecAttrAccount, + base::mac::NSToCFCast(account_name_ns)); + + return keychain_data; +} + +} // namespace + +namespace crypto { + +AppleKeychain::AppleKeychain() {} + +AppleKeychain::~AppleKeychain() {} + +OSStatus AppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList, + void* data) const { + free(data); + return noErr; +} + +OSStatus AppleKeychain::AddGenericPassword(SecKeychainRef keychain, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const { + base::ScopedCFTypeRef query(CreateGenericPasswordQuery( + serviceNameLength, serviceName, accountNameLength, accountName)); + // Check that there is not already a password. + OSStatus status = SecItemCopyMatching(query, NULL); + if (status == errSecItemNotFound) { + // A new entry must be created. + base::ScopedCFTypeRef keychain_data( + CreateKeychainData(serviceNameLength, + serviceName, + accountNameLength, + accountName, + passwordLength, + passwordData, + kKeychainActionCreate)); + status = SecItemAdd(keychain_data, NULL); + } else if (status == noErr) { + // The entry must be updated. + base::ScopedCFTypeRef keychain_data( + CreateKeychainData(serviceNameLength, + serviceName, + accountNameLength, + accountName, + passwordLength, + passwordData, + kKeychainActionUpdate)); + status = SecItemUpdate(query, keychain_data); + } + + return status; +} + +OSStatus AppleKeychain::FindGenericPassword(CFTypeRef keychainOrArray, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32* passwordLength, + void** passwordData, + SecKeychainItemRef* itemRef) const { + DCHECK((passwordData && passwordLength) || + (!passwordData && !passwordLength)); + base::ScopedCFTypeRef query(CreateGenericPasswordQuery( + serviceNameLength, serviceName, accountNameLength, accountName)); + + // Get the keychain item containing the password. + CFTypeRef resultRef = NULL; + OSStatus status = SecItemCopyMatching(query, &resultRef); + base::ScopedCFTypeRef result(resultRef); + + if (status != noErr) { + if (passwordData) { + *passwordData = NULL; + *passwordLength = 0; + } + return status; + } + + if (passwordData) { + CFDataRef data = base::mac::CFCast(result); + NSUInteger length = CFDataGetLength(data); + *passwordData = malloc(length * sizeof(UInt8)); + CFDataGetBytes(data, CFRangeMake(0, length), (UInt8*)*passwordData); + *passwordLength = length; + } + return status; +} + +} // namespace crypto diff --git a/chromium/crypto/apple_keychain_mac.mm b/chromium/crypto/apple_keychain_mac.mm new file mode 100644 index 00000000000..240c32067bc --- /dev/null +++ b/chromium/crypto/apple_keychain_mac.mm @@ -0,0 +1,144 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/apple_keychain.h" + +#import + +#include "base/synchronization/lock.h" +#include "crypto/mac_security_services_lock.h" + +namespace crypto { + +AppleKeychain::AppleKeychain() {} + +AppleKeychain::~AppleKeychain() {} + +OSStatus AppleKeychain::ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, + SecKeychainAttributeInfo* info, + SecItemClass* itemClass, + SecKeychainAttributeList** attrList, + UInt32* length, + void** outData) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainItemCopyAttributesAndData(itemRef, info, itemClass, + attrList, length, outData); +} + +OSStatus AppleKeychain::ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, + const SecKeychainAttributeList* attrList, + UInt32 length, + const void* data) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainItemModifyAttributesAndData(itemRef, attrList, length, + data); +} + +OSStatus AppleKeychain::ItemFreeAttributesAndData( + SecKeychainAttributeList* attrList, + void* data) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainItemFreeAttributesAndData(attrList, data); +} + +OSStatus AppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainItemDelete(itemRef); +} + +OSStatus AppleKeychain::SearchCreateFromAttributes( + CFTypeRef keychainOrArray, + SecItemClass itemClass, + const SecKeychainAttributeList* attrList, + SecKeychainSearchRef* searchRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, + attrList, searchRef); +} + +OSStatus AppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef* itemRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainSearchCopyNext(searchRef, itemRef); +} + +OSStatus AppleKeychain::AddInternetPassword( + SecKeychainRef keychain, + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, + const char* path, + UInt16 port, + SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainAddInternetPassword(keychain, + serverNameLength, serverName, + securityDomainLength, securityDomain, + accountNameLength, accountName, + pathLength, path, + port, protocol, authenticationType, + passwordLength, passwordData, + itemRef); +} + +OSStatus AppleKeychain::FindGenericPassword(CFTypeRef keychainOrArray, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32* passwordLength, + void** passwordData, + SecKeychainItemRef* itemRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainFindGenericPassword(keychainOrArray, + serviceNameLength, + serviceName, + accountNameLength, + accountName, + passwordLength, + passwordData, + itemRef); +} + +OSStatus AppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList, + void* data) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainItemFreeContent(attrList, data); +} + +OSStatus AppleKeychain::AddGenericPassword(SecKeychainRef keychain, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const { + base::AutoLock lock(GetMacSecurityServicesLock()); + return SecKeychainAddGenericPassword(keychain, + serviceNameLength, + serviceName, + accountNameLength, + accountName, + passwordLength, + passwordData, + itemRef); +} + +void AppleKeychain::Free(CFTypeRef ref) const { + if (ref) + CFRelease(ref); +} + +} // namespace crypto diff --git a/chromium/crypto/capi_util.cc b/chromium/crypto/capi_util.cc new file mode 100644 index 00000000000..2cf10625ec3 --- /dev/null +++ b/chromium/crypto/capi_util.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/capi_util.h" + +#include "base/basictypes.h" +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" + +namespace { + +class CAPIUtilSingleton { + public: + static CAPIUtilSingleton* GetInstance() { + return Singleton::get(); + } + + // Returns a lock to guard calls to CryptAcquireContext with + // CRYPT_DELETEKEYSET or CRYPT_NEWKEYSET. + base::Lock& acquire_context_lock() { + return acquire_context_lock_; + } + + private: + friend class Singleton; + friend struct DefaultSingletonTraits; + + CAPIUtilSingleton() {} + + base::Lock acquire_context_lock_; + + DISALLOW_COPY_AND_ASSIGN(CAPIUtilSingleton); +}; + +} // namespace + +namespace crypto { + +BOOL CryptAcquireContextLocked(HCRYPTPROV* prov, + LPCWSTR container, + LPCWSTR provider, + DWORD prov_type, + DWORD flags) { + base::AutoLock lock(CAPIUtilSingleton::GetInstance()->acquire_context_lock()); + return CryptAcquireContext(prov, container, provider, prov_type, flags); +} + +void* WINAPI CryptAlloc(size_t size) { + return malloc(size); +} + +void WINAPI CryptFree(void* p) { + free(p); +} + +} // namespace crypto diff --git a/chromium/crypto/capi_util.h b/chromium/crypto/capi_util.h new file mode 100644 index 00000000000..09a44e7634c --- /dev/null +++ b/chromium/crypto/capi_util.h @@ -0,0 +1,39 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_CAPI_UTIL_H_ +#define CRYPTO_CAPI_UTIL_H_ + +#include +#include + +#include "crypto/crypto_export.h" + +namespace crypto { + +// CryptAcquireContext when passed CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET in +// flags is not thread-safe. For such calls, we create a global lock to +// synchronize it. +// +// From "Threading Issues with Cryptographic Service Providers", +// : +// +// "The CryptAcquireContext function is generally thread safe unless +// CRYPT_NEWKEYSET or CRYPT_DELETEKEYSET is specified in the dwFlags +// parameter." +CRYPTO_EXPORT BOOL CryptAcquireContextLocked(HCRYPTPROV* prov, + LPCWSTR container, + LPCWSTR provider, + DWORD prov_type, + DWORD flags); + +// Wrappers of malloc and free for CryptoAPI routines that need memory +// allocators, such as in CRYPT_DECODE_PARA. Such routines require WINAPI +// calling conventions. +CRYPTO_EXPORT void* WINAPI CryptAlloc(size_t size); +CRYPTO_EXPORT void WINAPI CryptFree(void* p); + +} // namespace crypto + +#endif // CRYPTO_CAPI_UTIL_H_ diff --git a/chromium/crypto/crypto.gyp b/chromium/crypto/crypto.gyp new file mode 100644 index 00000000000..e2ac853b725 --- /dev/null +++ b/chromium/crypto/crypto.gyp @@ -0,0 +1,352 @@ +# Copyright (c) 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + # Put all transitive dependencies for Windows HMAC here. + # This is required so that we can build them for nacl win64. + 'hmac_win64_related_sources': [ + 'hmac.cc', + 'hmac.h', + 'hmac_win.cc', + 'secure_util.cc', + 'secure_util.h', + 'symmetric_key.h', + 'symmetric_key_win.cc', + 'third_party/nss/chromium-sha256.h', + 'third_party/nss/sha512.cc', + ], + }, + 'targets': [ + { + 'target_name': 'crypto', + 'type': '<(component)', + 'product_name': 'crcrypto', # Avoid colliding with OpenSSL's libcrypto + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + ], + 'defines': [ + 'CRYPTO_IMPLEMENTATION', + ], + 'msvs_disabled_warnings': [ + 4018, + ], + 'conditions': [ + [ 'os_posix == 1 and OS != "mac" and OS != "ios" and OS != "android"', { + 'dependencies': [ + '../build/linux/system.gyp:ssl', + ], + 'export_dependent_settings': [ + '../build/linux/system.gyp:ssl', + ], + 'conditions': [ + [ 'chromeos==1', { + 'sources/': [ ['include', '_chromeos\\.cc$'] ] + }, + ], + ], + }, { # os_posix != 1 or OS == "mac" or OS == "ios" or OS == "android" + 'sources!': [ + 'hmac_win.cc', + 'openpgp_symmetric_encryption.cc', + 'openpgp_symmetric_encryption.h', + 'symmetric_key_win.cc', + ], + }], + [ 'OS != "mac" and OS != "ios"', { + 'sources!': [ + 'apple_keychain.h', + 'mock_apple_keychain.cc', + 'mock_apple_keychain.h', + ], + }], + [ 'OS == "android"', { + 'dependencies': [ + '../third_party/openssl/openssl.gyp:openssl', + ], + 'sources/': [ + ['exclude', 'ec_private_key_nss\.cc$'], + ['exclude', 'ec_signature_creator_nss\.cc$'], + ['exclude', 'encryptor_nss\.cc$'], + ['exclude', 'hmac_nss\.cc$'], + ['exclude', 'signature_verifier_nss\.cc$'], + ['exclude', 'symmetric_key_nss\.cc$'], + ], + }], + [ 'os_bsd==1', { + 'link_settings': { + 'libraries': [ + '-L/usr/local/lib -lexecinfo', + ], + }, + }, + ], + [ 'OS == "mac"', { + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/Security.framework', + ], + }, + }, { # OS != "mac" + 'sources!': [ + 'cssm_init.cc', + 'cssm_init.h', + 'mac_security_services_lock.cc', + 'mac_security_services_lock.h', + ], + }], + [ 'OS == "mac" or OS == "ios" or OS == "win"', { + 'dependencies': [ + '../third_party/nss/nss.gyp:nspr', + '../third_party/nss/nss.gyp:nss', + ], + 'export_dependent_settings': [ + '../third_party/nss/nss.gyp:nspr', + '../third_party/nss/nss.gyp:nss', + ], + }], + [ 'OS != "win"', { + 'sources!': [ + 'capi_util.h', + 'capi_util.cc', + ], + }], + [ 'OS == "win"', { + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [4267, ], + }], + [ 'use_openssl==1', { + # TODO(joth): Use a glob to match exclude patterns once the + # OpenSSL file set is complete. + 'sources!': [ + 'ec_private_key_nss.cc', + 'ec_signature_creator_nss.cc', + 'encryptor_nss.cc', + 'hmac_nss.cc', + 'nss_util.cc', + 'nss_util.h', + 'openpgp_symmetric_encryption.cc', + 'rsa_private_key_nss.cc', + 'secure_hash_default.cc', + 'signature_creator_nss.cc', + 'signature_verifier_nss.cc', + 'symmetric_key_nss.cc', + 'third_party/nss/chromium-blapi.h', + 'third_party/nss/chromium-blapit.h', + 'third_party/nss/chromium-nss.h', + 'third_party/nss/chromium-sha256.h', + 'third_party/nss/pk11akey.cc', + 'third_party/nss/rsawrapr.c', + 'third_party/nss/secsign.cc', + 'third_party/nss/sha512.cc', + ], + }, { + 'sources!': [ + 'ec_private_key_openssl.cc', + 'ec_signature_creator_openssl.cc', + 'encryptor_openssl.cc', + 'hmac_openssl.cc', + 'openssl_util.cc', + 'openssl_util.h', + 'rsa_private_key_openssl.cc', + 'secure_hash_openssl.cc', + 'signature_creator_openssl.cc', + 'signature_verifier_openssl.cc', + 'symmetric_key_openssl.cc', + ], + },], + ], + 'sources': [ + # NOTE: all transitive dependencies of HMAC on windows need + # to be placed in the source list above. + '<@(hmac_win64_related_sources)', + 'apple_keychain.h', + 'apple_keychain_ios.mm', + 'apple_keychain_mac.mm', + 'capi_util.cc', + 'capi_util.h', + 'crypto_export.h', + 'crypto_module_blocking_password_delegate.h', + 'cssm_init.cc', + 'cssm_init.h', + 'curve25519.cc', + 'curve25519.h', + 'curve25519-donna.c', + 'ghash.cc', + 'ghash.h', + 'ec_private_key.h', + 'ec_private_key_nss.cc', + 'ec_private_key_openssl.cc', + 'ec_signature_creator.cc', + 'ec_signature_creator.h', + 'ec_signature_creator_impl.h', + 'ec_signature_creator_nss.cc', + 'ec_signature_creator_openssl.cc', + 'encryptor.cc', + 'encryptor.h', + 'encryptor_nss.cc', + 'encryptor_openssl.cc', + 'hkdf.cc', + 'hkdf.h', + 'hmac_nss.cc', + 'hmac_openssl.cc', + 'mac_security_services_lock.cc', + 'mac_security_services_lock.h', + 'mock_apple_keychain.cc', + 'mock_apple_keychain.h', + 'mock_apple_keychain_ios.cc', + 'mock_apple_keychain_mac.cc', + 'p224_spake.cc', + 'p224_spake.h', + 'nss_util.cc', + 'nss_util.h', + 'nss_util_internal.h', + 'openpgp_symmetric_encryption.cc', + 'openpgp_symmetric_encryption.h', + 'openssl_util.cc', + 'openssl_util.h', + 'p224.cc', + 'p224.h', + 'random.h', + 'random.cc', + 'rsa_private_key.cc', + 'rsa_private_key.h', + 'rsa_private_key_nss.cc', + 'rsa_private_key_openssl.cc', + 'scoped_capi_types.h', + 'scoped_nss_types.h', + 'secure_hash.h', + 'secure_hash_default.cc', + 'secure_hash_openssl.cc', + 'sha2.cc', + 'sha2.h', + 'signature_creator.h', + 'signature_creator_nss.cc', + 'signature_creator_openssl.cc', + 'signature_verifier.h', + 'signature_verifier_nss.cc', + 'signature_verifier_openssl.cc', + 'symmetric_key_nss.cc', + 'symmetric_key_openssl.cc', + 'third_party/nss/chromium-blapi.h', + 'third_party/nss/chromium-blapit.h', + 'third_party/nss/chromium-nss.h', + 'third_party/nss/pk11akey.cc', + 'third_party/nss/rsawrapr.c', + 'third_party/nss/secsign.cc', + ], + }, + { + 'target_name': 'crypto_unittests', + 'type': 'executable', + 'sources': [ + # Infrastructure files. + 'run_all_unittests.cc', + + # Tests. + 'curve25519_unittest.cc', + 'ec_private_key_unittest.cc', + 'ec_signature_creator_unittest.cc', + 'encryptor_unittest.cc', + 'ghash_unittest.cc', + 'hkdf_unittest.cc', + 'hmac_unittest.cc', + 'nss_util_unittest.cc', + 'p224_unittest.cc', + 'p224_spake_unittest.cc', + 'random_unittest.cc', + 'rsa_private_key_unittest.cc', + 'rsa_private_key_nss_unittest.cc', + 'secure_hash_unittest.cc', + 'sha2_unittest.cc', + 'signature_creator_unittest.cc', + 'signature_verifier_unittest.cc', + 'symmetric_key_unittest.cc', + 'openpgp_symmetric_encryption_unittest.cc', + ], + 'dependencies': [ + 'crypto', + '../base/base.gyp:base', + '../base/base.gyp:test_support_base', + '../testing/gmock.gyp:gmock', + '../testing/gtest.gyp:gtest', + ], + 'conditions': [ + [ 'os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', { + 'conditions': [ + [ 'linux_use_tcmalloc==1', { + 'dependencies': [ + '../base/allocator/allocator.gyp:allocator', + ], + }, + ], + ], + 'dependencies': [ + '../build/linux/system.gyp:ssl', + ], + }, { # os_posix != 1 or OS == "mac" or OS == "android" or OS == "ios" + 'sources!': [ + 'rsa_private_key_nss_unittest.cc', + 'openpgp_symmetric_encryption_unittest.cc', + ] + }], + [ 'OS == "mac" or OS == "ios" or OS == "win"', { + 'dependencies': [ + '../third_party/nss/nss.gyp:nss', + ], + }], + [ 'OS == "mac"', { + 'dependencies': [ + '../third_party/nss/nss.gyp:nspr', + ], + }], + [ 'OS == "win"', { + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + 'msvs_disabled_warnings': [4267, ], + }], + [ 'use_openssl==1', { + 'sources!': [ + 'nss_util_unittest.cc', + 'openpgp_symmetric_encryption_unittest.cc', + 'rsa_private_key_nss_unittest.cc', + ], + }], + ], + }, + ], + 'conditions': [ + ['OS == "win" and target_arch=="ia32"', { + 'targets': [ + { + 'target_name': 'crypto_nacl_win64', + # We do not want nacl_helper to depend on NSS because this would + # require including a 64-bit copy of NSS. Thus, use the native APIs + # for the helper. + 'type': '<(component)', + 'dependencies': [ + '../base/base.gyp:base_nacl_win64', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + ], + 'sources': [ + '<@(hmac_win64_related_sources)', + ], + 'defines': [ + 'CRYPTO_IMPLEMENTATION', + '<@(nacl_win64_defines)', + ], + 'msvs_disabled_warnings': [ + 4018, + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], +} diff --git a/chromium/crypto/crypto_export.h b/chromium/crypto/crypto_export.h new file mode 100644 index 00000000000..983afe6d099 --- /dev/null +++ b/chromium/crypto/crypto_export.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_CRYPTO_EXPORT_H_ +#define CRYPTO_CRYPTO_EXPORT_H_ + +// Defines CRYPTO_EXPORT so that functionality implemented by the crypto module +// can be exported to consumers, and CRYPTO_EXPORT_PRIVATE that allows unit +// tests to access features not intended to be used directly by real consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(CRYPTO_IMPLEMENTATION) +#define CRYPTO_EXPORT __declspec(dllexport) +#define CRYPTO_EXPORT_PRIVATE __declspec(dllexport) +#else +#define CRYPTO_EXPORT __declspec(dllimport) +#define CRYPTO_EXPORT_PRIVATE __declspec(dllimport) +#endif // defined(CRYPTO_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(CRYPTO_IMPLEMENTATION) +#define CRYPTO_EXPORT __attribute__((visibility("default"))) +#define CRYPTO_EXPORT_PRIVATE __attribute__((visibility("default"))) +#else +#define CRYPTO_EXPORT +#define CRYPTO_EXPORT_PRIVATE +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define CRYPTO_EXPORT +#define CRYPTO_EXPORT_PRIVATE +#endif + +#endif // CRYPTO_CRYPTO_EXPORT_H_ diff --git a/chromium/crypto/crypto_module_blocking_password_delegate.h b/chromium/crypto/crypto_module_blocking_password_delegate.h new file mode 100644 index 00000000000..c4acf1a8c55 --- /dev/null +++ b/chromium/crypto/crypto_module_blocking_password_delegate.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ +#define CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ + +#include + +namespace crypto { + +// PK11_SetPasswordFunc is a global setting. An implementation of +// CryptoModuleBlockingPasswordDelegate should be passed as the user data +// argument (|wincx|) to relevant NSS functions, which the global password +// handler will call to do the actual work. +class CryptoModuleBlockingPasswordDelegate { + public: + virtual ~CryptoModuleBlockingPasswordDelegate() {} + + // Requests a password to unlock |slot_name|. The interface is + // synchronous because NSS cannot issue an asynchronous + // request. |retry| is true if this is a request for the retry + // and we previously returned the wrong password. + // The implementation should set |*cancelled| to true if the user cancelled + // instead of entering a password, otherwise it should return the password the + // user entered. + virtual std::string RequestPassword(const std::string& slot_name, bool retry, + bool* cancelled) = 0; +}; + +} // namespace crypto + +#endif // CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ diff --git a/chromium/crypto/cssm_init.cc b/chromium/crypto/cssm_init.cc new file mode 100644 index 00000000000..208309c351e --- /dev/null +++ b/chromium/crypto/cssm_init.cc @@ -0,0 +1,204 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/cssm_init.h" + +#include + +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/memory/singleton.h" +#include "base/strings/sys_string_conversions.h" + +// When writing crypto code for Mac OS X, you may find the following +// documentation useful: +// - Common Security: CDSA and CSSM, Version 2 (with corrigenda) +// http://www.opengroup.org/security/cdsa.htm +// - Apple Cryptographic Service Provider Functional Specification +// - CryptoSample: http://developer.apple.com/SampleCode/CryptoSample/ + +namespace { + +void* CSSMMalloc(CSSM_SIZE size, void* alloc_ref) { + return malloc(size); +} + +void CSSMFree(void* mem_ptr, void* alloc_ref) { + free(mem_ptr); +} + +void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { + return realloc(ptr, size); +} + +void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { + return calloc(num, size); +} + +class CSSMInitSingleton { + public: + static CSSMInitSingleton* GetInstance() { + return Singleton >::get(); + } + + CSSM_CSP_HANDLE csp_handle() const { return csp_handle_; } + CSSM_CL_HANDLE cl_handle() const { return cl_handle_; } + CSSM_TP_HANDLE tp_handle() const { return tp_handle_; } + + private: + CSSMInitSingleton() + : inited_(false), csp_loaded_(false), cl_loaded_(false), + tp_loaded_(false), csp_handle_(CSSM_INVALID_HANDLE), + cl_handle_(CSSM_INVALID_HANDLE), tp_handle_(CSSM_INVALID_HANDLE) { + static CSSM_VERSION version = {2, 0}; + // TODO(wtc): what should our caller GUID be? + static const CSSM_GUID test_guid = { + 0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 } + }; + CSSM_RETURN crtn; + CSSM_PVC_MODE pvc_policy = CSSM_PVC_NONE; + crtn = CSSM_Init(&version, CSSM_PRIVILEGE_SCOPE_NONE, &test_guid, + CSSM_KEY_HIERARCHY_NONE, &pvc_policy, NULL); + if (crtn) { + NOTREACHED(); + return; + } + inited_ = true; + + crtn = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + csp_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509CL, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + cl_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509TP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + tp_loaded_ = true; + + const CSSM_API_MEMORY_FUNCS cssmMemoryFunctions = { + CSSMMalloc, + CSSMFree, + CSSMRealloc, + CSSMCalloc, + NULL + }; + + crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &cssmMemoryFunctions, 0, + CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &csp_handle_); + DCHECK_EQ(CSSM_OK, crtn); + crtn = CSSM_ModuleAttach(&gGuidAppleX509CL, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_CL, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &cl_handle_); + DCHECK_EQ(CSSM_OK, crtn); + crtn = CSSM_ModuleAttach(&gGuidAppleX509TP, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_TP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &tp_handle_); + DCHECK_EQ(CSSM_OK, crtn); + } + + ~CSSMInitSingleton() { + CSSM_RETURN crtn; + if (csp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); + DCHECK_EQ(CSSM_OK, crtn); + } + if (cl_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(cl_handle_); + DCHECK_EQ(CSSM_OK, crtn); + } + if (tp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(tp_handle_); + DCHECK_EQ(CSSM_OK, crtn); + } + if (csp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); + DCHECK_EQ(CSSM_OK, crtn); + } + if (cl_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL); + DCHECK_EQ(CSSM_OK, crtn); + } + if (tp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509TP, NULL, NULL); + DCHECK_EQ(CSSM_OK, crtn); + } + if (inited_) { + crtn = CSSM_Terminate(); + DCHECK_EQ(CSSM_OK, crtn); + } + } + + bool inited_; // True if CSSM_Init has been called successfully. + bool csp_loaded_; // True if gGuidAppleCSP has been loaded + bool cl_loaded_; // True if gGuidAppleX509CL has been loaded. + bool tp_loaded_; // True if gGuidAppleX509TP has been loaded. + CSSM_CSP_HANDLE csp_handle_; + CSSM_CL_HANDLE cl_handle_; + CSSM_TP_HANDLE tp_handle_; + + friend struct DefaultSingletonTraits; +}; + +} // namespace + +namespace crypto { + +void EnsureCSSMInit() { + CSSMInitSingleton::GetInstance(); +} + +CSSM_CSP_HANDLE GetSharedCSPHandle() { + return CSSMInitSingleton::GetInstance()->csp_handle(); +} + +CSSM_CL_HANDLE GetSharedCLHandle() { + return CSSMInitSingleton::GetInstance()->cl_handle(); +} + +CSSM_TP_HANDLE GetSharedTPHandle() { + return CSSMInitSingleton::GetInstance()->tp_handle(); +} + +void* CSSMMalloc(CSSM_SIZE size) { + return ::CSSMMalloc(size, NULL); +} + +void CSSMFree(void* ptr) { + ::CSSMFree(ptr, NULL); +} + +void LogCSSMError(const char* fn_name, CSSM_RETURN err) { + if (!err) + return; + base::ScopedCFTypeRef cfstr( + SecCopyErrorMessageString(err, NULL)); + LOG(ERROR) << fn_name << " returned " << err + << " (" << base::SysCFStringRefToUTF8(cfstr) << ")"; +} + +ScopedCSSMData::ScopedCSSMData() { + memset(&data_, 0, sizeof(data_)); +} + +ScopedCSSMData::~ScopedCSSMData() { + if (data_.Data) { + CSSMFree(data_.Data); + data_.Data = NULL; + } +} + +} // namespace crypto diff --git a/chromium/crypto/cssm_init.h b/chromium/crypto/cssm_init.h new file mode 100644 index 00000000000..e711099ef08 --- /dev/null +++ b/chromium/crypto/cssm_init.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_CSSM_INIT_H_ +#define CRYPTO_CSSM_INIT_H_ + +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// Initialize CSSM if it isn't already initialized. This must be called before +// any other CSSM functions. This function is thread-safe, and CSSM will only +// ever be initialized once. CSSM will be properly shut down on program exit. +CRYPTO_EXPORT void EnsureCSSMInit(); + +// Returns the shared CSP handle used by CSSM functions. +CRYPTO_EXPORT CSSM_CSP_HANDLE GetSharedCSPHandle(); + +// Returns the shared CL handle used by CSSM functions. +CRYPTO_EXPORT CSSM_CL_HANDLE GetSharedCLHandle(); + +// Returns the shared TP handle used by CSSM functions. +CRYPTO_EXPORT CSSM_TP_HANDLE GetSharedTPHandle(); + +// Set of pointers to memory function wrappers that are required for CSSM +extern const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions; + +// Utility function to log an error message including the error name. +CRYPTO_EXPORT void LogCSSMError(const char *function_name, CSSM_RETURN err); + +// Utility functions to allocate and release CSSM memory. +void* CSSMMalloc(CSSM_SIZE size); +CRYPTO_EXPORT void CSSMFree(void* ptr); + +// Wrapper class for CSSM_DATA type. This should only be used when using the +// CL/TP/CSP handles from above, since that's the only time we're guaranteed (or +// supposed to be guaranteed) that our memory management functions will be used. +// Apple's Sec* APIs manage their own memory so it shouldn't be used for those. +// The constructor initializes data_ to zero and the destructor releases the +// data properly. +class ScopedCSSMData { + public: + ScopedCSSMData(); + ~ScopedCSSMData(); + operator CSSM_DATA*() { return &data_; } + CSSM_DATA* operator ->() { return &data_; } + + private: + CSSM_DATA data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCSSMData); +}; + +} // namespace crypto + +#endif // CRYPTO_CSSM_INIT_H_ diff --git a/chromium/crypto/curve25519-donna.c b/chromium/crypto/curve25519-donna.c new file mode 100644 index 00000000000..f141ac028b0 --- /dev/null +++ b/chromium/crypto/curve25519-donna.c @@ -0,0 +1,592 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * curve25519-donna: Curve25519 elliptic curve, public key function + * + * http://code.google.com/p/curve25519-donna/ + * + * Adam Langley + * + * Derived from public domain C code by Daniel J. Bernstein + * + * More information about curve25519 can be found here + * http://cr.yp.to/ecdh.html + * + * djb's sample implementation of curve25519 is written in a special assembly + * language called qhasm and uses the floating point registers. + * + * This is, almost, a clean room reimplementation from the curve25519 paper. It + * uses many of the tricks described therein. Only the crecip function is taken + * from the sample implementation. + */ + +#include +#include + +typedef uint8_t u8; +typedef int32_t s32; +typedef int64_t limb; + +/* Field element representation: + * + * Field elements are written as an array of signed, 64-bit limbs, least + * significant first. The value of the field element is: + * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ... + * + * i.e. the limbs are 26, 25, 26, 25, ... bits wide. + */ + +/* Sum two numbers: output += in */ +static void fsum(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; i += 2) { + output[0+i] = (output[0+i] + in[0+i]); + output[1+i] = (output[1+i] + in[1+i]); + } +} + +/* Find the difference of two numbers: output = in - output + * (note the order of the arguments!) + */ +static void fdifference(limb *output, const limb *in) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = (in[i] - output[i]); + } +} + +/* Multiply a number my a scalar: output = in * scalar */ +static void fscalar_product(limb *output, const limb *in, const limb scalar) { + unsigned i; + for (i = 0; i < 10; ++i) { + output[i] = in[i] * scalar; + } +} + +/* Multiply two numbers: output = in2 * in + * + * output must be distinct to both inputs. The inputs are reduced coefficient + * form, the output is not. + */ +static void fproduct(limb *output, const limb *in2, const limb *in) { + output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); + output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + + ((limb) ((s32) in2[1])) * ((s32) in[0]); + output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[0]); + output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) + + ((limb) ((s32) in2[2])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[0]); + output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) + + 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[1])) + + ((limb) ((s32) in2[0])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[0]); + output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) + + ((limb) ((s32) in2[3])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[0]); + output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[0]); + output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) + + ((limb) ((s32) in2[4])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[0]); + output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[1])) + + ((limb) ((s32) in2[2])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[2]) + + ((limb) ((s32) in2[0])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[0]); + output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) + + ((limb) ((s32) in2[5])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[2]) + + ((limb) ((s32) in2[1])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[1]) + + ((limb) ((s32) in2[0])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[0]); + output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[3]) + + ((limb) ((s32) in2[1])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[1])) + + ((limb) ((s32) in2[4])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[4]) + + ((limb) ((s32) in2[2])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[2]); + output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) + + ((limb) ((s32) in2[6])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[4]) + + ((limb) ((s32) in2[3])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[3]) + + ((limb) ((s32) in2[2])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[2]); + output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[5]) + + ((limb) ((s32) in2[3])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[3])) + + ((limb) ((s32) in2[4])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[4]); + output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) + + ((limb) ((s32) in2[7])) * ((s32) in[6]) + + ((limb) ((s32) in2[5])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[5]) + + ((limb) ((s32) in2[4])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[4]); + output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) + + ((limb) ((s32) in2[5])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[5])) + + ((limb) ((s32) in2[6])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[6]); + output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) + + ((limb) ((s32) in2[8])) * ((s32) in[7]) + + ((limb) ((s32) in2[6])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[6]); + output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[7])); + output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) + + ((limb) ((s32) in2[9])) * ((s32) in[8]); + output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); +} + +/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */ +static void freduce_degree(limb *output) { + /* Each of these shifts and adds ends up multiplying the value by 19. */ + output[8] += output[18] << 4; + output[8] += output[18] << 1; + output[8] += output[18]; + output[7] += output[17] << 4; + output[7] += output[17] << 1; + output[7] += output[17]; + output[6] += output[16] << 4; + output[6] += output[16] << 1; + output[6] += output[16]; + output[5] += output[15] << 4; + output[5] += output[15] << 1; + output[5] += output[15]; + output[4] += output[14] << 4; + output[4] += output[14] << 1; + output[4] += output[14]; + output[3] += output[13] << 4; + output[3] += output[13] << 1; + output[3] += output[13]; + output[2] += output[12] << 4; + output[2] += output[12] << 1; + output[2] += output[12]; + output[1] += output[11] << 4; + output[1] += output[11] << 1; + output[1] += output[11]; + output[0] += output[10] << 4; + output[0] += output[10] << 1; + output[0] += output[10]; +} + +/* Reduce all coefficients of the short form input so that |x| < 2^26. + * + * On entry: |output[i]| < 2^62 + */ +static void freduce_coefficients(limb *output) { + unsigned i; + do { + output[10] = 0; + + for (i = 0; i < 10; i += 2) { + limb over = output[i] / 0x4000000l; + output[i+1] += over; + output[i] -= over * 0x4000000l; + + over = output[i+1] / 0x2000000; + output[i+2] += over; + output[i+1] -= over * 0x2000000; + } + output[0] += 19 * output[10]; + } while (output[10]); +} + +/* A helpful wrapper around fproduct: output = in * in2. + * + * output must be distinct to both inputs. The output is reduced degree and + * reduced coefficient. + */ +static void +fmul(limb *output, const limb *in, const limb *in2) { + limb t[19]; + fproduct(t, in, in2); + freduce_degree(t); + freduce_coefficients(t); + memcpy(output, t, sizeof(limb) * 10); +} + +static void fsquare_inner(limb *output, const limb *in) { + output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); + output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); + output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + + ((limb) ((s32) in[0])) * ((s32) in[2])); + output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) + + ((limb) ((s32) in[0])) * ((s32) in[3])); + output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) + + 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) + + 2 * ((limb) ((s32) in[0])) * ((s32) in[4]); + output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) + + ((limb) ((s32) in[1])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[5])); + output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) + + ((limb) ((s32) in[2])) * ((s32) in[4]) + + ((limb) ((s32) in[0])) * ((s32) in[6]) + + 2 * ((limb) ((s32) in[1])) * ((s32) in[5])); + output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) + + ((limb) ((s32) in[2])) * ((s32) in[5]) + + ((limb) ((s32) in[1])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[7])); + output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) + + 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) + + ((limb) ((s32) in[0])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[5]))); + output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) + + ((limb) ((s32) in[3])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[8]) + + ((limb) ((s32) in[0])) * ((s32) in[9])); + output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) + + ((limb) ((s32) in[4])) * ((s32) in[6]) + + ((limb) ((s32) in[2])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) + + ((limb) ((s32) in[1])) * ((s32) in[9]))); + output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) + + ((limb) ((s32) in[4])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[8]) + + ((limb) ((s32) in[2])) * ((s32) in[9])); + output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) + + 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) + + 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) + + ((limb) ((s32) in[3])) * ((s32) in[9]))); + output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) + + ((limb) ((s32) in[5])) * ((s32) in[8]) + + ((limb) ((s32) in[4])) * ((s32) in[9])); + output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) + + ((limb) ((s32) in[6])) * ((s32) in[8]) + + 2 * ((limb) ((s32) in[5])) * ((s32) in[9])); + output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) + + ((limb) ((s32) in[6])) * ((s32) in[9])); + output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) + + 4 * ((limb) ((s32) in[7])) * ((s32) in[9]); + output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]); + output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]); +} + +static void +fsquare(limb *output, const limb *in) { + limb t[19]; + fsquare_inner(t, in); + freduce_degree(t); + freduce_coefficients(t); + memcpy(output, t, sizeof(limb) * 10); +} + +/* Take a little-endian, 32-byte number and expand it into polynomial form */ +static void +fexpand(limb *output, const u8 *input) { +#define F(n,start,shift,mask) \ + output[n] = ((((limb) input[start + 0]) | \ + ((limb) input[start + 1]) << 8 | \ + ((limb) input[start + 2]) << 16 | \ + ((limb) input[start + 3]) << 24) >> shift) & mask; + F(0, 0, 0, 0x3ffffff); + F(1, 3, 2, 0x1ffffff); + F(2, 6, 3, 0x3ffffff); + F(3, 9, 5, 0x1ffffff); + F(4, 12, 6, 0x3ffffff); + F(5, 16, 0, 0x1ffffff); + F(6, 19, 1, 0x3ffffff); + F(7, 22, 3, 0x1ffffff); + F(8, 25, 4, 0x3ffffff); + F(9, 28, 6, 0x1ffffff); +#undef F +} + +/* Take a fully reduced polynomial form number and contract it into a + * little-endian, 32-byte array + */ +static void +fcontract(u8 *output, limb *input) { + int i; + + do { + for (i = 0; i < 9; ++i) { + if ((i & 1) == 1) { + while (input[i] < 0) { + input[i] += 0x2000000; + input[i + 1]--; + } + } else { + while (input[i] < 0) { + input[i] += 0x4000000; + input[i + 1]--; + } + } + } + while (input[9] < 0) { + input[9] += 0x2000000; + input[0] -= 19; + } + } while (input[0] < 0); + + input[1] <<= 2; + input[2] <<= 3; + input[3] <<= 5; + input[4] <<= 6; + input[6] <<= 1; + input[7] <<= 3; + input[8] <<= 4; + input[9] <<= 6; +#define F(i, s) \ + output[s+0] |= input[i] & 0xff; \ + output[s+1] = (input[i] >> 8) & 0xff; \ + output[s+2] = (input[i] >> 16) & 0xff; \ + output[s+3] = (input[i] >> 24) & 0xff; + output[0] = 0; + output[16] = 0; + F(0,0); + F(1,3); + F(2,6); + F(3,9); + F(4,12); + F(5,16); + F(6,19); + F(7,22); + F(8,25); + F(9,28); +#undef F +} + +/* Input: Q, Q', Q-Q' + * Output: 2Q, Q+Q' + * + * x2 z3: long form + * x3 z3: long form + * x z: short form, destroyed + * xprime zprime: short form, destroyed + * qmqp: short form, preserved + */ +static void fmonty(limb *x2, limb *z2, /* output 2Q */ + limb *x3, limb *z3, /* output Q + Q' */ + limb *x, limb *z, /* input Q */ + limb *xprime, limb *zprime, /* input Q' */ + const limb *qmqp /* input Q - Q' */) { + limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19], + zzprime[19], zzzprime[19], xxxprime[19]; + + memcpy(origx, x, 10 * sizeof(limb)); + fsum(x, z); + fdifference(z, origx); // does x - z + + memcpy(origxprime, xprime, sizeof(limb) * 10); + fsum(xprime, zprime); + fdifference(zprime, origxprime); + fproduct(xxprime, xprime, z); + fproduct(zzprime, x, zprime); + freduce_degree(xxprime); + freduce_coefficients(xxprime); + freduce_degree(zzprime); + freduce_coefficients(zzprime); + memcpy(origxprime, xxprime, sizeof(limb) * 10); + fsum(xxprime, zzprime); + fdifference(zzprime, origxprime); + fsquare(xxxprime, xxprime); + fsquare(zzzprime, zzprime); + fproduct(zzprime, zzzprime, qmqp); + freduce_degree(zzprime); + freduce_coefficients(zzprime); + memcpy(x3, xxxprime, sizeof(limb) * 10); + memcpy(z3, zzprime, sizeof(limb) * 10); + + fsquare(xx, x); + fsquare(zz, z); + fproduct(x2, xx, zz); + freduce_degree(x2); + freduce_coefficients(x2); + fdifference(zz, xx); // does zz = xx - zz + memset(zzz + 10, 0, sizeof(limb) * 9); + fscalar_product(zzz, zz, 121665); + freduce_degree(zzz); + freduce_coefficients(zzz); + fsum(zzz, xx); + fproduct(z2, zz, zzz); + freduce_degree(z2); + freduce_coefficients(z2); +} + +/* Calculates nQ where Q is the x-coordinate of a point on the curve + * + * resultx/resultz: the x coordinate of the resulting curve point (short form) + * n: a little endian, 32-byte number + * q: a point of the curve (short form) + */ +static void +cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) { + limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0}; + limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t; + limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1}; + limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h; + + unsigned i, j; + + memcpy(nqpqx, q, sizeof(limb) * 10); + + for (i = 0; i < 32; ++i) { + u8 byte = n[31 - i]; + for (j = 0; j < 8; ++j) { + if (byte & 0x80) { + fmonty(nqpqx2, nqpqz2, + nqx2, nqz2, + nqpqx, nqpqz, + nqx, nqz, + q); + } else { + fmonty(nqx2, nqz2, + nqpqx2, nqpqz2, + nqx, nqz, + nqpqx, nqpqz, + q); + } + + t = nqx; + nqx = nqx2; + nqx2 = t; + t = nqz; + nqz = nqz2; + nqz2 = t; + t = nqpqx; + nqpqx = nqpqx2; + nqpqx2 = t; + t = nqpqz; + nqpqz = nqpqz2; + nqpqz2 = t; + + byte <<= 1; + } + } + + memcpy(resultx, nqx, sizeof(limb) * 10); + memcpy(resultz, nqz, sizeof(limb) * 10); +} + +// ----------------------------------------------------------------------------- +// Shamelessly copied from djb's code +// ----------------------------------------------------------------------------- +static void +crecip(limb *out, const limb *z) { + limb z2[10]; + limb z9[10]; + limb z11[10]; + limb z2_5_0[10]; + limb z2_10_0[10]; + limb z2_20_0[10]; + limb z2_50_0[10]; + limb z2_100_0[10]; + limb t0[10]; + limb t1[10]; + int i; + + /* 2 */ fsquare(z2,z); + /* 4 */ fsquare(t1,z2); + /* 8 */ fsquare(t0,t1); + /* 9 */ fmul(z9,t0,z); + /* 11 */ fmul(z11,z9,z2); + /* 22 */ fsquare(t0,z11); + /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9); + + /* 2^6 - 2^1 */ fsquare(t0,z2_5_0); + /* 2^7 - 2^2 */ fsquare(t1,t0); + /* 2^8 - 2^3 */ fsquare(t0,t1); + /* 2^9 - 2^4 */ fsquare(t1,t0); + /* 2^10 - 2^5 */ fsquare(t0,t1); + /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0); + + /* 2^11 - 2^1 */ fsquare(t0,z2_10_0); + /* 2^12 - 2^2 */ fsquare(t1,t0); + /* 2^20 - 2^10 */ + for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0); + + /* 2^21 - 2^1 */ fsquare(t0,z2_20_0); + /* 2^22 - 2^2 */ fsquare(t1,t0); + /* 2^40 - 2^20 */ + for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0); + + /* 2^41 - 2^1 */ fsquare(t1,t0); + /* 2^42 - 2^2 */ fsquare(t0,t1); + /* 2^50 - 2^10 */ + for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0); + + /* 2^51 - 2^1 */ fsquare(t0,z2_50_0); + /* 2^52 - 2^2 */ fsquare(t1,t0); + /* 2^100 - 2^50 */ + for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0); + + /* 2^101 - 2^1 */ fsquare(t1,z2_100_0); + /* 2^102 - 2^2 */ fsquare(t0,t1); + /* 2^200 - 2^100 */ + for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); } + /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0); + + /* 2^201 - 2^1 */ fsquare(t0,t1); + /* 2^202 - 2^2 */ fsquare(t1,t0); + /* 2^250 - 2^50 */ + for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); } + /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0); + + /* 2^251 - 2^1 */ fsquare(t1,t0); + /* 2^252 - 2^2 */ fsquare(t0,t1); + /* 2^253 - 2^3 */ fsquare(t1,t0); + /* 2^254 - 2^4 */ fsquare(t0,t1); + /* 2^255 - 2^5 */ fsquare(t1,t0); + /* 2^255 - 21 */ fmul(out,t1,z11); +} + +int +curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) { + limb bp[10], x[10], z[10], zmone[10]; + uint8_t e[32]; + int i; + + for (i = 0; i < 32; ++i) e[i] = secret[i]; + e[0] &= 248; + e[31] &= 127; + e[31] |= 64; + + fexpand(bp, basepoint); + cmult(x, z, e, bp); + crecip(zmone, z); + fmul(z, x, zmone); + fcontract(mypublic, z); + return 0; +} diff --git a/chromium/crypto/curve25519.cc b/chromium/crypto/curve25519.cc new file mode 100644 index 00000000000..3346df93a1a --- /dev/null +++ b/chromium/crypto/curve25519.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/curve25519.h" + +// Curve25519 is specified in terms of byte strings, not numbers, so all +// implementations take and return the same sequence of bits. So the byte +// order is implicitly specified as in, say, SHA1. +// +// Prototype for |curve25519_donna| function in +// third_party/curve25519-donna/curve25519-donna.c +extern "C" int curve25519_donna(uint8*, const uint8*, const uint8*); + +namespace crypto { + +namespace curve25519 { + +void ScalarMult(const uint8* private_key, + const uint8* peer_public_key, + uint8* shared_key) { + curve25519_donna(shared_key, private_key, peer_public_key); +} + +// kBasePoint is the base point (generator) of the elliptic curve group. +// It is little-endian version of '9' followed by 31 zeros. +// See "Computing public keys" section of http://cr.yp.to/ecdh.html. +static const unsigned char kBasePoint[32] = {9}; + +void ScalarBaseMult(const uint8* private_key, uint8* public_key) { + curve25519_donna(public_key, private_key, kBasePoint); +} + +} // namespace curve25519 + +} // namespace crypto diff --git a/chromium/crypto/curve25519.h b/chromium/crypto/curve25519.h new file mode 100644 index 00000000000..ba24c92a877 --- /dev/null +++ b/chromium/crypto/curve25519.h @@ -0,0 +1,48 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_CURVE25519_H +#define CRYPTO_CURVE25519_H + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// Curve25519 implements the elliptic curve group known as Curve25519, as +// described in "Curve 25519: new Diffie-Hellman Speed Records", +// by D.J. Bernstein. Additional information is available at +// http://cr.yp.to/ecdh.html. +namespace curve25519 { + +// kBytes is the number of bytes in the result of the Diffie-Hellman operation, +// which is an element of GF(2^255-19). +static const size_t kBytes = 32; + +// kScalarBytes is the number of bytes in an element of the scalar field: +// GF(2^252 + 27742317777372353535851937790883648493). +static const size_t kScalarBytes = 32; + +// ScalarMult computes the |shared_key| from |private_key| and +// |peer_public_key|. This method is a wrapper for |curve25519_donna()|. It +// calls that function with |private_key| as |secret| and |peer_public_key| as +// basepoint. |private_key| should be of length |kScalarBytes| and +// |peer_public_key| should be of length |kBytes|. +// See "Computing shared secrets" section of/ http://cr.yp.to/ecdh.html. +CRYPTO_EXPORT void ScalarMult(const uint8* private_key, + const uint8* peer_public_key, + uint8* shared_key); + +// ScalarBaseMult computes the |public_key| from |private_key|. This method is a +// wrapper for |curve25519_donna()|. It calls that function with |private_key| +// as |secret| and |kBasePoint| as basepoint. |private_key| should be of length +// |kScalarBytes|. See "Computing public keys" section of +// http://cr.yp.to/ecdh.html. +CRYPTO_EXPORT void ScalarBaseMult(const uint8* private_key, uint8* public_key); + +} // namespace curve25519 + +} // namespace crypto + +#endif // CRYPTO_CURVE25519_H diff --git a/chromium/crypto/curve25519_unittest.cc b/chromium/crypto/curve25519_unittest.cc new file mode 100644 index 00000000000..0ddc4224a61 --- /dev/null +++ b/chromium/crypto/curve25519_unittest.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/curve25519.h" + +#include + +#include "crypto/random.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +// Test that the basic shared key exchange identity holds: that both parties end +// up with the same shared key. This test starts with a fixed private key for +// two parties: alice and bob. Runs ScalarBaseMult and ScalarMult to compute +// public key and shared key for alice and bob. It asserts that alice and bob +// have the same shared key. +TEST(Curve25519, SharedKeyIdentity) { + uint8 alice_private_key[curve25519::kScalarBytes] = {3}; + uint8 bob_private_key[curve25519::kScalarBytes] = {5}; + + // Get public key for alice and bob. + uint8 alice_public_key[curve25519::kBytes]; + curve25519::ScalarBaseMult(alice_private_key, alice_public_key); + + uint8 bob_public_key[curve25519::kBytes]; + curve25519::ScalarBaseMult(bob_private_key, bob_public_key); + + // Get the shared key for alice, by using alice's private key and bob's + // public key. + uint8 alice_shared_key[curve25519::kBytes]; + curve25519::ScalarMult(alice_private_key, bob_public_key, alice_shared_key); + + // Get the shared key for bob, by using bob's private key and alice's public + // key. + uint8 bob_shared_key[curve25519::kBytes]; + curve25519::ScalarMult(bob_private_key, alice_public_key, bob_shared_key); + + // Computed shared key of alice and bob should be the same. + ASSERT_EQ(0, memcmp(alice_shared_key, bob_shared_key, curve25519::kBytes)); +} + +} // namespace crypto diff --git a/chromium/crypto/ec_private_key.h b/chromium/crypto/ec_private_key.h new file mode 100644 index 00000000000..d3f5b73af60 --- /dev/null +++ b/chromium/crypto/ec_private_key.h @@ -0,0 +1,143 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_EC_PRIVATE_KEY_H_ +#define CRYPTO_EC_PRIVATE_KEY_H_ + +#include +#include + +#include "base/basictypes.h" +#include "build/build_config.h" +#include "crypto/crypto_export.h" + +#if defined(USE_OPENSSL) +// Forward declaration for openssl/*.h +typedef struct evp_pkey_st EVP_PKEY; +#else +// Forward declaration. +typedef struct CERTSubjectPublicKeyInfoStr CERTSubjectPublicKeyInfo; +typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey; +typedef struct SECKEYPublicKeyStr SECKEYPublicKey; +#endif + +namespace crypto { + +// Encapsulates an elliptic curve (EC) private key. Can be used to generate new +// keys, export keys to other formats, or to extract a public key. +// TODO(mattm): make this and RSAPrivateKey implement some PrivateKey interface. +// (The difference in types of key() and public_key() make this a little +// tricky.) +class CRYPTO_EXPORT ECPrivateKey { + public: + ~ECPrivateKey(); + + // Returns whether the system supports elliptic curve cryptography. + static bool IsSupported(); + + // Creates a new random instance. Can return NULL if initialization fails. + // The created key will use the NIST P-256 curve. + // TODO(mattm): Add a curve parameter. + static ECPrivateKey* Create(); + + // Creates a new random instance. Can return NULL if initialization fails. + // The created key is permanent and is not exportable in plaintext form. + // + // NOTE: Currently only available if USE_NSS is defined. + static ECPrivateKey* CreateSensitive(); + + // Creates a new instance by importing an existing key pair. + // The key pair is given as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo + // block and an X.509 SubjectPublicKeyInfo block. + // Returns NULL if initialization fails. + static ECPrivateKey* CreateFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info); + + // Creates a new instance by importing an existing key pair. + // The key pair is given as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo + // block and an X.509 SubjectPublicKeyInfo block. + // This can return NULL if initialization fails. The created key is permanent + // and is not exportable in plaintext form. + // + // NOTE: Currently only available if USE_NSS is defined. + static ECPrivateKey* CreateSensitiveFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info); + +#if !defined(USE_OPENSSL) + // Imports the key pair and returns in |public_key| and |key|. + // Shortcut for code that needs to keep a reference directly to NSS types + // without having to create a ECPrivateKey object and make a copy of them. + // TODO(mattm): move this function to some NSS util file. + static bool ImportFromEncryptedPrivateKeyInfo( + const std::string& password, + const uint8* encrypted_private_key_info, + size_t encrypted_private_key_info_len, + CERTSubjectPublicKeyInfo* decoded_spki, + bool permanent, + bool sensitive, + SECKEYPrivateKey** key, + SECKEYPublicKey** public_key); +#endif + +#if defined(USE_OPENSSL) + EVP_PKEY* key() { return key_; } +#else + SECKEYPrivateKey* key() { return key_; } + SECKEYPublicKey* public_key() { return public_key_; } +#endif + + // Exports the private key as an ASN.1-encoded PKCS #8 EncryptedPrivateKeyInfo + // block and the public key as an X.509 SubjectPublicKeyInfo block. + // The |password| and |iterations| are used as inputs to the key derivation + // function for generating the encryption key. PKCS #5 recommends a minimum + // of 1000 iterations, on modern systems a larger value may be preferrable. + bool ExportEncryptedPrivateKey(const std::string& password, + int iterations, + std::vector* output); + + // Exports the public key to an X.509 SubjectPublicKeyInfo block. + bool ExportPublicKey(std::vector* output); + + // Exports private key data for testing. The format of data stored into output + // doesn't matter other than that it is consistent for the same key. + bool ExportValue(std::vector* output); + bool ExportECParams(std::vector* output); + + private: + // Constructor is private. Use one of the Create*() methods above instead. + ECPrivateKey(); + + // Shared helper for Create() and CreateSensitive(). + // TODO(cmasone): consider replacing |permanent| and |sensitive| with a + // flags arg created by ORing together some enumerated values. + static ECPrivateKey* CreateWithParams(bool permanent, + bool sensitive); + + // Shared helper for CreateFromEncryptedPrivateKeyInfo() and + // CreateSensitiveFromEncryptedPrivateKeyInfo(). + static ECPrivateKey* CreateFromEncryptedPrivateKeyInfoWithParams( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info, + bool permanent, + bool sensitive); + +#if defined(USE_OPENSSL) + EVP_PKEY* key_; +#else + SECKEYPrivateKey* key_; + SECKEYPublicKey* public_key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ECPrivateKey); +}; + + +} // namespace crypto + +#endif // CRYPTO_EC_PRIVATE_KEY_H_ diff --git a/chromium/crypto/ec_private_key_nss.cc b/chromium/crypto/ec_private_key_nss.cc new file mode 100644 index 00000000000..9bb9df124e4 --- /dev/null +++ b/chromium/crypto/ec_private_key_nss.cc @@ -0,0 +1,365 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_private_key.h" + +extern "C" { +// Work around NSS missing SEC_BEGIN_PROTOS in secmodt.h. This must come before +// other NSS headers. +#include +} + +#include +#include +#include +#include + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "crypto/scoped_nss_types.h" +#include "crypto/third_party/nss/chromium-nss.h" + +namespace { + +PK11SlotInfo* GetKeySlot() { + return crypto::GetPublicNSSKeySlot(); +} + +class EllipticCurveSupportChecker { + public: + EllipticCurveSupportChecker() { + // NOTE: we can do this check here only because we use the NSS internal + // slot. If we support other slots in the future, checking whether they + // support ECDSA may block NSS, and the value may also change as devices are + // inserted/removed, so we would need to re-check on every use. + crypto::EnsureNSSInit(); + crypto::ScopedPK11Slot slot(GetKeySlot()); + supported_ = PK11_DoesMechanism(slot.get(), CKM_EC_KEY_PAIR_GEN) && + PK11_DoesMechanism(slot.get(), CKM_ECDSA); + } + + bool Supported() { + return supported_; + } + + private: + bool supported_; +}; + +static base::LazyInstance::Leaky + g_elliptic_curve_supported = LAZY_INSTANCE_INITIALIZER; + +// Copied from rsa_private_key_nss.cc. +static bool ReadAttribute(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE type, + std::vector* output) { + SECItem item; + SECStatus rv; + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); + if (rv != SECSuccess) { + DLOG(ERROR) << "PK11_ReadRawAttribute: " << PORT_GetError(); + return false; + } + + output->assign(item.data, item.data + item.len); + SECITEM_FreeItem(&item, PR_FALSE); + return true; +} + +} // namespace + +namespace crypto { + +ECPrivateKey::~ECPrivateKey() { + if (key_) + SECKEY_DestroyPrivateKey(key_); + if (public_key_) + SECKEY_DestroyPublicKey(public_key_); +} + +// static +bool ECPrivateKey::IsSupported() { + return g_elliptic_curve_supported.Get().Supported(); +} + +// static +ECPrivateKey* ECPrivateKey::Create() { + return CreateWithParams(PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitive() { +#if defined(USE_NSS) + return CreateWithParams(PR_TRUE /* permanent */, + PR_TRUE /* sensitive */); +#else + // If USE_NSS is not defined, we initialize NSS with no databases, so we can't + // create permanent keys. + NOTREACHED(); + return NULL; +#endif +} + +// static +ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info) { + return CreateFromEncryptedPrivateKeyInfoWithParams( + password, + encrypted_private_key_info, + subject_public_key_info, + PR_FALSE /* not permanent */, + PR_FALSE /* not sensitive */); +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitiveFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info) { +#if defined(USE_NSS) + return CreateFromEncryptedPrivateKeyInfoWithParams( + password, + encrypted_private_key_info, + subject_public_key_info, + PR_TRUE /* permanent */, + PR_TRUE /* sensitive */); +#else + // If USE_NSS is not defined, we initialize NSS with no databases, so we can't + // create permanent keys. + NOTREACHED(); + return NULL; +#endif +} + +// static +bool ECPrivateKey::ImportFromEncryptedPrivateKeyInfo( + const std::string& password, + const uint8* encrypted_private_key_info, + size_t encrypted_private_key_info_len, + CERTSubjectPublicKeyInfo* decoded_spki, + bool permanent, + bool sensitive, + SECKEYPrivateKey** key, + SECKEYPublicKey** public_key) { + ScopedPK11Slot slot(GetKeySlot()); + if (!slot.get()) + return false; + + *public_key = SECKEY_ExtractPublicKey(decoded_spki); + + if (!*public_key) { + DLOG(ERROR) << "SECKEY_ExtractPublicKey: " << PORT_GetError(); + return false; + } + + SECItem encoded_epki = { + siBuffer, + const_cast(encrypted_private_key_info), + static_cast(encrypted_private_key_info_len) + }; + SECKEYEncryptedPrivateKeyInfo epki; + memset(&epki, 0, sizeof(epki)); + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + + SECStatus rv = SEC_QuickDERDecodeItem( + arena.get(), + &epki, + SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate), + &encoded_epki); + if (rv != SECSuccess) { + DLOG(ERROR) << "SEC_QuickDERDecodeItem: " << PORT_GetError(); + SECKEY_DestroyPublicKey(*public_key); + *public_key = NULL; + return false; + } + + SECItem password_item = { + siBuffer, + reinterpret_cast(const_cast(password.data())), + static_cast(password.size()) + }; + + rv = ImportEncryptedECPrivateKeyInfoAndReturnKey( + slot.get(), + &epki, + &password_item, + NULL, // nickname + &(*public_key)->u.ec.publicValue, + permanent, + sensitive, + key, + NULL); // wincx + if (rv != SECSuccess) { + DLOG(ERROR) << "ImportEncryptedECPrivateKeyInfoAndReturnKey: " + << PORT_GetError(); + SECKEY_DestroyPublicKey(*public_key); + *public_key = NULL; + return false; + } + + return true; +} + +bool ECPrivateKey::ExportEncryptedPrivateKey( + const std::string& password, + int iterations, + std::vector* output) { + // We export as an EncryptedPrivateKeyInfo bundle instead of a plain PKCS #8 + // PrivateKeyInfo because PK11_ImportDERPrivateKeyInfoAndReturnKey doesn't + // support EC keys. + // https://bugzilla.mozilla.org/show_bug.cgi?id=327773 + SECItem password_item = { + siBuffer, + reinterpret_cast(const_cast(password.data())), + static_cast(password.size()) + }; + + SECKEYEncryptedPrivateKeyInfo* encrypted = PK11_ExportEncryptedPrivKeyInfo( + NULL, // Slot, optional. + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, + &password_item, + key_, + iterations, + NULL); // wincx. + + if (!encrypted) { + DLOG(ERROR) << "PK11_ExportEncryptedPrivKeyInfo: " << PORT_GetError(); + return false; + } + + ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); + SECItem der_key = {siBuffer, NULL, 0}; + SECItem* encoded_item = SEC_ASN1EncodeItem( + arena.get(), + &der_key, + encrypted, + SEC_ASN1_GET(SECKEY_EncryptedPrivateKeyInfoTemplate)); + SECKEY_DestroyEncryptedPrivateKeyInfo(encrypted, PR_TRUE); + if (!encoded_item) { + DLOG(ERROR) << "SEC_ASN1EncodeItem: " << PORT_GetError(); + return false; + } + + output->assign(der_key.data, der_key.data + der_key.len); + + return true; +} + +bool ECPrivateKey::ExportPublicKey(std::vector* output) { + ScopedSECItem der_pubkey( + SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); + if (!der_pubkey.get()) { + return false; + } + + output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len); + return true; +} + +bool ECPrivateKey::ExportValue(std::vector* output) { + return ReadAttribute(key_, CKA_VALUE, output); +} + +bool ECPrivateKey::ExportECParams(std::vector* output) { + return ReadAttribute(key_, CKA_EC_PARAMS, output); +} + +ECPrivateKey::ECPrivateKey() : key_(NULL), public_key_(NULL) {} + +// static +ECPrivateKey* ECPrivateKey::CreateWithParams(bool permanent, + bool sensitive) { + EnsureNSSInit(); + + scoped_ptr result(new ECPrivateKey); + + ScopedPK11Slot slot(GetKeySlot()); + if (!slot.get()) + return NULL; + + SECOidData* oid_data = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); + if (!oid_data) { + DLOG(ERROR) << "SECOID_FindOIDByTag: " << PORT_GetError(); + return NULL; + } + + // SECKEYECParams is a SECItem containing the DER encoded ASN.1 ECParameters + // value. For a named curve, that is just the OBJECT IDENTIFIER of the curve. + // In addition to the oid data, the encoding requires one byte for the ASN.1 + // tag and one byte for the length (assuming the length is <= 127). + DCHECK_LE(oid_data->oid.len, 127U); + std::vector parameters_buf(2 + oid_data->oid.len); + SECKEYECParams ec_parameters = { + siDEROID, ¶meters_buf[0], + static_cast(parameters_buf.size()) + }; + + ec_parameters.data[0] = SEC_ASN1_OBJECT_ID; + ec_parameters.data[1] = oid_data->oid.len; + memcpy(ec_parameters.data + 2, oid_data->oid.data, oid_data->oid.len); + + result->key_ = PK11_GenerateKeyPair(slot.get(), + CKM_EC_KEY_PAIR_GEN, + &ec_parameters, + &result->public_key_, + permanent, + sensitive, + NULL); + if (!result->key_) { + DLOG(ERROR) << "PK11_GenerateKeyPair: " << PORT_GetError(); + return NULL; + } + + return result.release(); +} + +// static +ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfoWithParams( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info, + bool permanent, + bool sensitive) { + EnsureNSSInit(); + + scoped_ptr result(new ECPrivateKey); + + SECItem encoded_spki = { + siBuffer, + const_cast(&subject_public_key_info[0]), + static_cast(subject_public_key_info.size()) + }; + CERTSubjectPublicKeyInfo* decoded_spki = SECKEY_DecodeDERSubjectPublicKeyInfo( + &encoded_spki); + if (!decoded_spki) { + DLOG(ERROR) << "SECKEY_DecodeDERSubjectPublicKeyInfo: " << PORT_GetError(); + return NULL; + } + + bool success = ECPrivateKey::ImportFromEncryptedPrivateKeyInfo( + password, + &encrypted_private_key_info[0], + encrypted_private_key_info.size(), + decoded_spki, + permanent, + sensitive, + &result->key_, + &result->public_key_); + + SECKEY_DestroySubjectPublicKeyInfo(decoded_spki); + + if (success) + return result.release(); + + return NULL; +} + +} // namespace crypto diff --git a/chromium/crypto/ec_private_key_openssl.cc b/chromium/crypto/ec_private_key_openssl.cc new file mode 100644 index 00000000000..20214e6c83d --- /dev/null +++ b/chromium/crypto/ec_private_key_openssl.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_private_key.h" + +#include "base/logging.h" + +namespace crypto { + +ECPrivateKey::~ECPrivateKey() {} + +// static +bool ECPrivateKey::IsSupported() { + return false; +} + +// static +ECPrivateKey* ECPrivateKey::Create() { + NOTIMPLEMENTED(); + return NULL; +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitive() { + NOTIMPLEMENTED(); + return NULL; +} + +// static +ECPrivateKey* ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info) { + NOTIMPLEMENTED(); + return NULL; +} + +// static +ECPrivateKey* ECPrivateKey::CreateSensitiveFromEncryptedPrivateKeyInfo( + const std::string& password, + const std::vector& encrypted_private_key_info, + const std::vector& subject_public_key_info) { + NOTIMPLEMENTED(); + return NULL; +} + +bool ECPrivateKey::ExportEncryptedPrivateKey( + const std::string& password, + int iterations, + std::vector* output) { + NOTIMPLEMENTED(); + return false; +} + +bool ECPrivateKey::ExportPublicKey(std::vector* output) { + NOTIMPLEMENTED(); + return false; +} + +bool ECPrivateKey::ExportValue(std::vector* output) { + NOTIMPLEMENTED(); + return false; +} + +bool ECPrivateKey::ExportECParams(std::vector* output) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace crypto diff --git a/chromium/crypto/ec_private_key_unittest.cc b/chromium/crypto/ec_private_key_unittest.cc new file mode 100644 index 00000000000..d2ec256deda --- /dev/null +++ b/chromium/crypto/ec_private_key_unittest.cc @@ -0,0 +1,107 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_private_key.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(USE_OPENSSL) +// Once ECPrivateKey is implemented for OpenSSL, remove this #if block. +// TODO(mattm): When that happens, also add some exported keys from each to test +// interop between NSS and OpenSSL. +TEST(ECPrivateKeyUnitTest, OpenSSLStub) { + scoped_ptr keypair1( + crypto::ECPrivateKey::Create()); + ASSERT_FALSE(keypair1.get()); +} +#else +// Generate random private keys. Export, then re-import. We should get +// back the same exact public key, and the private key should have the same +// value and elliptic curve params. +TEST(ECPrivateKeyUnitTest, InitRandomTest) { + const std::string password1; + const std::string password2 = "test"; + + scoped_ptr keypair1( + crypto::ECPrivateKey::Create()); + scoped_ptr keypair2( + crypto::ECPrivateKey::Create()); + ASSERT_TRUE(keypair1.get()); + ASSERT_TRUE(keypair2.get()); + + std::vector key1value; + std::vector key2value; + std::vector key1params; + std::vector key2params; + EXPECT_TRUE(keypair1->ExportValue(&key1value)); + EXPECT_TRUE(keypair2->ExportValue(&key2value)); + EXPECT_TRUE(keypair1->ExportECParams(&key1params)); + EXPECT_TRUE(keypair2->ExportECParams(&key2params)); + + std::vector privkey1; + std::vector privkey2; + std::vector pubkey1; + std::vector pubkey2; + ASSERT_TRUE(keypair1->ExportEncryptedPrivateKey( + password1, 1, &privkey1)); + ASSERT_TRUE(keypair2->ExportEncryptedPrivateKey( + password2, 1, &privkey2)); + EXPECT_TRUE(keypair1->ExportPublicKey(&pubkey1)); + EXPECT_TRUE(keypair2->ExportPublicKey(&pubkey2)); + + scoped_ptr keypair3( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + password1, privkey1, pubkey1)); + scoped_ptr keypair4( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + password2, privkey2, pubkey2)); + ASSERT_TRUE(keypair3.get()); + ASSERT_TRUE(keypair4.get()); + + std::vector key3value; + std::vector key4value; + std::vector key3params; + std::vector key4params; + EXPECT_TRUE(keypair3->ExportValue(&key3value)); + EXPECT_TRUE(keypair4->ExportValue(&key4value)); + EXPECT_TRUE(keypair3->ExportECParams(&key3params)); + EXPECT_TRUE(keypair4->ExportECParams(&key4params)); + + EXPECT_EQ(key1value, key3value); + EXPECT_EQ(key2value, key4value); + EXPECT_EQ(key1params, key3params); + EXPECT_EQ(key2params, key4params); + + std::vector pubkey3; + std::vector pubkey4; + EXPECT_TRUE(keypair3->ExportPublicKey(&pubkey3)); + EXPECT_TRUE(keypair4->ExportPublicKey(&pubkey4)); + + EXPECT_EQ(pubkey1, pubkey3); + EXPECT_EQ(pubkey2, pubkey4); +} + +TEST(ECPrivateKeyUnitTest, BadPasswordTest) { + const std::string password1; + const std::string password2 = "test"; + + scoped_ptr keypair1( + crypto::ECPrivateKey::Create()); + ASSERT_TRUE(keypair1.get()); + + std::vector privkey1; + std::vector pubkey1; + ASSERT_TRUE(keypair1->ExportEncryptedPrivateKey( + password1, 1, &privkey1)); + ASSERT_TRUE(keypair1->ExportPublicKey(&pubkey1)); + + scoped_ptr keypair2( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + password2, privkey1, pubkey1)); + ASSERT_FALSE(keypair2.get()); +} +#endif // !defined(USE_OPENSSL) diff --git a/chromium/crypto/ec_signature_creator.cc b/chromium/crypto/ec_signature_creator.cc new file mode 100644 index 00000000000..a6887bc117b --- /dev/null +++ b/chromium/crypto/ec_signature_creator.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_signature_creator.h" + +#include "base/logging.h" +#include "crypto/ec_signature_creator_impl.h" + +namespace crypto { + +namespace { + +ECSignatureCreatorFactory* g_factory_ = NULL; + +} // namespace + +// static +ECSignatureCreator* ECSignatureCreator::Create(ECPrivateKey* key) { + if (g_factory_) + return g_factory_->Create(key); + return new ECSignatureCreatorImpl(key); +} + +// static +void ECSignatureCreator::SetFactoryForTesting( + ECSignatureCreatorFactory* factory) { + // We should always clear the factory after each test to avoid + // use-after-free problems. + DCHECK(!g_factory_ || !factory); + g_factory_ = factory; +} + +} // namespace crypto diff --git a/chromium/crypto/ec_signature_creator.h b/chromium/crypto/ec_signature_creator.h new file mode 100644 index 00000000000..16e64f5753c --- /dev/null +++ b/chromium/crypto/ec_signature_creator.h @@ -0,0 +1,66 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_EC_SIGNATURE_CREATOR_H_ +#define CRYPTO_EC_SIGNATURE_CREATOR_H_ + +#include +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +class ECPrivateKey; +class ECSignatureCreator; + +class CRYPTO_EXPORT ECSignatureCreatorFactory { + public: + virtual ~ECSignatureCreatorFactory() {} + + virtual ECSignatureCreator* Create(ECPrivateKey* key) = 0; +}; + +// Signs data using a bare private key (as opposed to a full certificate). +// We need this class because SignatureCreator is hardcoded to use +// RSAPrivateKey. +class CRYPTO_EXPORT ECSignatureCreator { + public: + virtual ~ECSignatureCreator() {} + + // Create an instance. The caller must ensure that the provided PrivateKey + // instance outlives the created ECSignatureCreator. + // TODO(rch): This is currently hard coded to use SHA256. Ideally, we should + // pass in the hash algorithm identifier. + static ECSignatureCreator* Create(ECPrivateKey* key); + + // Set a factory to make the Create function return non-standard + // ECSignatureCreator objects. Because the ECDSA algorithm involves + // randomness, this is useful for higher-level tests that want to have + // deterministic mocked output to compare. + static void SetFactoryForTesting(ECSignatureCreatorFactory* factory); + + // Signs |data_len| bytes from |data| and writes the results into + // |signature| as a DER encoded ECDSA-Sig-Value from RFC 3279. + // + // ECDSA-Sig-Value ::= SEQUENCE { + // r INTEGER, + // s INTEGER } + virtual bool Sign(const uint8* data, + int data_len, + std::vector* signature) = 0; + + // DecodeSignature converts from a DER encoded ECDSA-Sig-Value (as produced + // by Sign) to a `raw' ECDSA signature which consists of a pair of + // big-endian, zero-padded, 256-bit integers, r and s. On success it returns + // true and puts the raw signature into |out_raw_sig|. + // (Only P-256 signatures are supported.) + virtual bool DecodeSignature(const std::vector& signature, + std::vector* out_raw_sig) = 0; +}; + +} // namespace crypto + +#endif // CRYPTO_EC_SIGNATURE_CREATOR_H_ diff --git a/chromium/crypto/ec_signature_creator_impl.h b/chromium/crypto/ec_signature_creator_impl.h new file mode 100644 index 00000000000..8ef67cd7f3d --- /dev/null +++ b/chromium/crypto/ec_signature_creator_impl.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_EC_SIGNATURE_CREATOR_IMPL_H_ +#define CRYPTO_EC_SIGNATURE_CREATOR_IMPL_H_ + +#include "base/compiler_specific.h" +#include "crypto/ec_signature_creator.h" + +namespace crypto { + +class ECSignatureCreatorImpl : public ECSignatureCreator { + public: + explicit ECSignatureCreatorImpl(ECPrivateKey* key); + virtual ~ECSignatureCreatorImpl(); + + virtual bool Sign(const uint8* data, + int data_len, + std::vector* signature) OVERRIDE; + + virtual bool DecodeSignature(const std::vector& der_sig, + std::vector* out_raw_sig) OVERRIDE; + + private: + ECPrivateKey* key_; + size_t signature_len_; + + DISALLOW_COPY_AND_ASSIGN(ECSignatureCreatorImpl); +}; + +} // namespace crypto + +#endif // CRYPTO_EC_SIGNATURE_CREATOR_IMPL_H_ diff --git a/chromium/crypto/ec_signature_creator_nss.cc b/chromium/crypto/ec_signature_creator_nss.cc new file mode 100644 index 00000000000..3e3626f4499 --- /dev/null +++ b/chromium/crypto/ec_signature_creator_nss.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_signature_creator_impl.h" + +#include +#include +#include +#include +#if defined(OS_POSIX) +#include +#endif + +#include "base/logging.h" +#include "crypto/ec_private_key.h" +#include "crypto/nss_util.h" +#include "crypto/scoped_nss_types.h" + +namespace crypto { + +namespace { + +SECStatus SignData(SECItem* result, + SECItem* input, + SECKEYPrivateKey* key, + HASH_HashType hash_type, + size_t* out_signature_len) { + if (key->keyType != ecKey) { + DLOG(FATAL) << "Should be using an EC key."; + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // Hash the input. + std::vector hash_data(HASH_ResultLen(hash_type)); + SECStatus rv = HASH_HashBuf( + hash_type, &hash_data[0], input->data, input->len); + if (rv != SECSuccess) + return rv; + SECItem hash = {siBuffer, &hash_data[0], + static_cast(hash_data.size())}; + + // Compute signature of hash. + int signature_len = PK11_SignatureLen(key); + std::vector signature_data(signature_len); + SECItem sig = {siBuffer, &signature_data[0], + static_cast(signature_len)}; + rv = PK11_Sign(key, &sig, &hash); + if (rv != SECSuccess) + return rv; + + *out_signature_len = sig.len; + + // DER encode the signature. + return DSAU_EncodeDerSigWithLen(result, &sig, sig.len); +} + +} // namespace + +ECSignatureCreatorImpl::ECSignatureCreatorImpl(ECPrivateKey* key) + : key_(key), + signature_len_(0) { + EnsureNSSInit(); +} + +ECSignatureCreatorImpl::~ECSignatureCreatorImpl() {} + +bool ECSignatureCreatorImpl::Sign(const uint8* data, + int data_len, + std::vector* signature) { + // Data to be signed + SECItem secret; + secret.type = siBuffer; + secret.len = data_len; + secret.data = const_cast(data); + + // SECItem to receive the output buffer. + SECItem result; + result.type = siBuffer; + result.len = 0; + result.data = NULL; + + // Sign the secret data and save it to |result|. + SECStatus rv = + SignData(&result, &secret, key_->key(), HASH_AlgSHA256, &signature_len_); + if (rv != SECSuccess) { + DLOG(ERROR) << "DerSignData: " << PORT_GetError(); + return false; + } + + // Copy the signed data into the output vector. + signature->assign(result.data, result.data + result.len); + SECITEM_FreeItem(&result, PR_FALSE /* only free |result.data| */); + return true; +} + +bool ECSignatureCreatorImpl::DecodeSignature( + const std::vector& der_sig, + std::vector* out_raw_sig) { + SECItem der_sig_item; + der_sig_item.type = siBuffer; + der_sig_item.len = der_sig.size(); + der_sig_item.data = const_cast(&der_sig[0]); + + SECItem* raw_sig = DSAU_DecodeDerSigToLen(&der_sig_item, signature_len_); + if (!raw_sig) + return false; + out_raw_sig->assign(raw_sig->data, raw_sig->data + raw_sig->len); + SECITEM_FreeItem(raw_sig, PR_TRUE /* free SECItem structure itself. */); + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/ec_signature_creator_openssl.cc b/chromium/crypto/ec_signature_creator_openssl.cc new file mode 100644 index 00000000000..8854f5ed464 --- /dev/null +++ b/chromium/crypto/ec_signature_creator_openssl.cc @@ -0,0 +1,31 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_signature_creator_impl.h" + +#include "base/logging.h" + +namespace crypto { + +ECSignatureCreatorImpl::ECSignatureCreatorImpl(ECPrivateKey* key) + : key_(key) { + NOTIMPLEMENTED(); +} + +ECSignatureCreatorImpl::~ECSignatureCreatorImpl() {} + +bool ECSignatureCreatorImpl::Sign(const uint8* data, + int data_len, + std::vector* signature) { + NOTIMPLEMENTED(); + return false; +} + +bool ECSignatureCreatorImpl::DecodeSignature(const std::vector& der_sig, + std::vector* out_raw_sig) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace crypto diff --git a/chromium/crypto/ec_signature_creator_unittest.cc b/chromium/crypto/ec_signature_creator_unittest.cc new file mode 100644 index 00000000000..bc0cb4a6b60 --- /dev/null +++ b/chromium/crypto/ec_signature_creator_unittest.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ec_signature_creator.h" + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "crypto/ec_private_key.h" +#include "crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(USE_OPENSSL) +// Once ECSignatureCreator is implemented for OpenSSL, remove this #if block. +// TODO(rch): When that happens, also add some exported keys from each to +// test interop between NSS and OpenSSL. +TEST(ECSignatureCreatorTest, OpenSSLStub) { + scoped_ptr signer( + crypto::ECSignatureCreator::Create(NULL)); + ASSERT_TRUE(signer.get()); + EXPECT_FALSE(signer->Sign(NULL, 0, NULL)); +} +#else +TEST(ECSignatureCreatorTest, BasicTest) { + // Do a verify round trip. + scoped_ptr key_original( + crypto::ECPrivateKey::Create()); + ASSERT_TRUE(key_original.get()); + + std::vector key_info; + ASSERT_TRUE( + key_original->ExportEncryptedPrivateKey(std::string(), 1000, &key_info)); + std::vector pubkey_info; + ASSERT_TRUE(key_original->ExportPublicKey(&pubkey_info)); + + scoped_ptr key( + crypto::ECPrivateKey::CreateFromEncryptedPrivateKeyInfo( + std::string(), key_info, pubkey_info)); + ASSERT_TRUE(key.get()); + ASSERT_TRUE(key->key() != NULL); + + scoped_ptr signer( + crypto::ECSignatureCreator::Create(key.get())); + ASSERT_TRUE(signer.get()); + + std::string data("Hello, World!"); + std::vector signature; + ASSERT_TRUE(signer->Sign(reinterpret_cast(data.c_str()), + data.size(), + &signature)); + + std::vector public_key_info; + ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); + + // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT. + // RFC 5758: + // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } + // ... + // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or + // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field + // as an AlgorithmIdentifier, the encoding MUST omit the parameters + // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one + // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with- + // SHA384, or ecdsa-with-SHA512. + // See also RFC 5480, Appendix A. + const uint8 kECDSAWithSHA256AlgorithmID[] = { + 0x30, 0x0a, + 0x06, 0x08, + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, + }; + crypto::SignatureVerifier verifier; + ASSERT_TRUE(verifier.VerifyInit( + kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID), + &signature[0], signature.size(), + &public_key_info.front(), public_key_info.size())); + + verifier.VerifyUpdate(reinterpret_cast(data.c_str()), + data.size()); + ASSERT_TRUE(verifier.VerifyFinal()); +} +#endif // !defined(USE_OPENSSL) diff --git a/chromium/crypto/encryptor.cc b/chromium/crypto/encryptor.cc new file mode 100644 index 00000000000..a673f81c178 --- /dev/null +++ b/chromium/crypto/encryptor.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/encryptor.h" + +#include "base/logging.h" +#include "base/sys_byteorder.h" + +namespace crypto { + +///////////////////////////////////////////////////////////////////////////// +// Encyptor::Counter Implementation. +Encryptor::Counter::Counter(const base::StringPiece& counter) { + CHECK(sizeof(counter_) == counter.length()); + + memcpy(&counter_, counter.data(), sizeof(counter_)); +} + +Encryptor::Counter::~Counter() { +} + +bool Encryptor::Counter::Increment() { + uint64 low_num = base::NetToHost64(counter_.components64[1]); + uint64 new_low_num = low_num + 1; + counter_.components64[1] = base::HostToNet64(new_low_num); + + // If overflow occured then increment the most significant component. + if (new_low_num < low_num) { + counter_.components64[0] = + base::HostToNet64(base::NetToHost64(counter_.components64[0]) + 1); + } + + // TODO(hclam): Return false if counter value overflows. + return true; +} + +void Encryptor::Counter::Write(void* buf) { + uint8* buf_ptr = reinterpret_cast(buf); + memcpy(buf_ptr, &counter_, sizeof(counter_)); +} + +size_t Encryptor::Counter::GetLengthInBytes() const { + return sizeof(counter_); +} + +///////////////////////////////////////////////////////////////////////////// +// Partial Encryptor Implementation. + +bool Encryptor::SetCounter(const base::StringPiece& counter) { + if (mode_ != CTR) + return false; + if (counter.length() != 16u) + return false; + + counter_.reset(new Counter(counter)); + return true; +} + +bool Encryptor::GenerateCounterMask(size_t plaintext_len, + uint8* mask, + size_t* mask_len) { + DCHECK_EQ(CTR, mode_); + CHECK(mask); + CHECK(mask_len); + + const size_t kBlockLength = counter_->GetLengthInBytes(); + size_t blocks = (plaintext_len + kBlockLength - 1) / kBlockLength; + CHECK(blocks); + + *mask_len = blocks * kBlockLength; + + for (size_t i = 0; i < blocks; ++i) { + counter_->Write(mask); + mask += kBlockLength; + + bool ret = counter_->Increment(); + if (!ret) + return false; + } + return true; +} + +void Encryptor::MaskMessage(const void* plaintext, + size_t plaintext_len, + const void* mask, + void* ciphertext) const { + DCHECK_EQ(CTR, mode_); + const uint8* plaintext_ptr = reinterpret_cast(plaintext); + const uint8* mask_ptr = reinterpret_cast(mask); + uint8* ciphertext_ptr = reinterpret_cast(ciphertext); + + for (size_t i = 0; i < plaintext_len; ++i) + ciphertext_ptr[i] = plaintext_ptr[i] ^ mask_ptr[i]; +} + +} // namespace crypto diff --git a/chromium/crypto/encryptor.h b/chromium/crypto/encryptor.h new file mode 100644 index 00000000000..bcc647bbc07 --- /dev/null +++ b/chromium/crypto/encryptor.h @@ -0,0 +1,137 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_ENCRYPTOR_H_ +#define CRYPTO_ENCRYPTOR_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" +#include "crypto/crypto_export.h" + +#if defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) +#include "crypto/scoped_nss_types.h" +#endif + +namespace crypto { + +class SymmetricKey; + +class CRYPTO_EXPORT Encryptor { + public: + enum Mode { + CBC, + CTR, + }; + + // This class implements a 128-bits counter to be used in AES-CTR encryption. + // Only 128-bits counter is supported in this class. + class CRYPTO_EXPORT Counter { + public: + explicit Counter(const base::StringPiece& counter); + ~Counter(); + + // Increment the counter value. + bool Increment(); + + // Write the content of the counter to |buf|. |buf| should have enough + // space for |GetLengthInBytes()|. + void Write(void* buf); + + // Return the length of this counter. + size_t GetLengthInBytes() const; + + private: + union { + uint32 components32[4]; + uint64 components64[2]; + } counter_; + }; + + Encryptor(); + virtual ~Encryptor(); + + // Initializes the encryptor using |key| and |iv|. Returns false if either the + // key or the initialization vector cannot be used. + // + // If |mode| is CBC, |iv| must not be empty; if it is CTR, then |iv| must be + // empty. + bool Init(SymmetricKey* key, Mode mode, const base::StringPiece& iv); + + // Encrypts |plaintext| into |ciphertext|. |plaintext| may only be empty if + // the mode is CBC. + bool Encrypt(const base::StringPiece& plaintext, std::string* ciphertext); + + // Decrypts |ciphertext| into |plaintext|. |ciphertext| must not be empty. + // + // WARNING: In CBC mode, Decrypt() returns false if it detects the padding + // in the decrypted plaintext is wrong. Padding errors can result from + // tampered ciphertext or a wrong decryption key. But successful decryption + // does not imply the authenticity of the data. The caller of Decrypt() + // must either authenticate the ciphertext before decrypting it, or take + // care to not report decryption failure. Otherwise it could inadvertently + // be used as a padding oracle to attack the cryptosystem. + bool Decrypt(const base::StringPiece& ciphertext, std::string* plaintext); + + // Sets the counter value when in CTR mode. Currently only 128-bits + // counter value is supported. + // + // Returns true only if update was successful. + bool SetCounter(const base::StringPiece& counter); + + // TODO(albertb): Support streaming encryption. + + private: + // Generates a mask using |counter_| to be used for encryption in CTR mode. + // Resulting mask will be written to |mask| with |mask_len| bytes. + // + // Make sure there's enough space in mask when calling this method. + // Reserve at least |plaintext_len| + 16 bytes for |mask|. + // + // The generated mask will always have at least |plaintext_len| bytes and + // will be a multiple of the counter length. + // + // This method is used only in CTR mode. + // + // Returns false if this call failed. + bool GenerateCounterMask(size_t plaintext_len, + uint8* mask, + size_t* mask_len); + + // Mask the |plaintext| message using |mask|. The output will be written to + // |ciphertext|. |ciphertext| must have at least |plaintext_len| bytes. + void MaskMessage(const void* plaintext, + size_t plaintext_len, + const void* mask, + void* ciphertext) const; + + SymmetricKey* key_; + Mode mode_; + scoped_ptr counter_; + +#if defined(USE_OPENSSL) + bool Crypt(bool do_encrypt, // Pass true to encrypt, false to decrypt. + const base::StringPiece& input, + std::string* output); + bool CryptCTR(bool do_encrypt, + const base::StringPiece& input, + std::string* output); + std::string iv_; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + bool Crypt(PK11Context* context, + const base::StringPiece& input, + std::string* output); + bool CryptCTR(PK11Context* context, + const base::StringPiece& input, + std::string* output); + ScopedSECItem param_; +#endif +}; + +} // namespace crypto + +#endif // CRYPTO_ENCRYPTOR_H_ diff --git a/chromium/crypto/encryptor_nss.cc b/chromium/crypto/encryptor_nss.cc new file mode 100644 index 00000000000..280e38be46d --- /dev/null +++ b/chromium/crypto/encryptor_nss.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/encryptor.h" + +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +namespace { + +inline CK_MECHANISM_TYPE GetMechanism(Encryptor::Mode mode) { + switch (mode) { + case Encryptor::CBC: + return CKM_AES_CBC_PAD; + case Encryptor::CTR: + // AES-CTR encryption uses ECB encryptor as a building block since + // NSS doesn't support CTR encryption mode. + return CKM_AES_ECB; + default: + NOTREACHED() << "Unsupported mode of operation"; + break; + } + return static_cast(-1); +} + +} // namespace + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { + EnsureNSSInit(); +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, + Mode mode, + const base::StringPiece& iv) { + DCHECK(key); + DCHECK(CBC == mode || CTR == mode) << "Unsupported mode of operation"; + + key_ = key; + mode_ = mode; + + if (mode == CBC && iv.size() != AES_BLOCK_SIZE) + return false; + + switch (mode) { + case CBC: + SECItem iv_item; + iv_item.type = siBuffer; + iv_item.data = reinterpret_cast( + const_cast(iv.data())); + iv_item.len = iv.size(); + + param_.reset(PK11_ParamFromIV(GetMechanism(mode), &iv_item)); + break; + case CTR: + param_.reset(PK11_ParamFromIV(GetMechanism(mode), NULL)); + break; + } + + return param_ != NULL; +} + +bool Encryptor::Encrypt(const base::StringPiece& plaintext, + std::string* ciphertext) { + CHECK(!plaintext.empty() || (mode_ == CBC)); + ScopedPK11Context context(PK11_CreateContextBySymKey(GetMechanism(mode_), + CKA_ENCRYPT, + key_->key(), + param_.get())); + if (!context.get()) + return false; + + return (mode_ == CTR) ? + CryptCTR(context.get(), plaintext, ciphertext) : + Crypt(context.get(), plaintext, ciphertext); +} + +bool Encryptor::Decrypt(const base::StringPiece& ciphertext, + std::string* plaintext) { + CHECK(!ciphertext.empty()); + ScopedPK11Context context(PK11_CreateContextBySymKey( + GetMechanism(mode_), (mode_ == CTR ? CKA_ENCRYPT : CKA_DECRYPT), + key_->key(), param_.get())); + if (!context.get()) + return false; + + return (mode_ == CTR) ? + CryptCTR(context.get(), ciphertext, plaintext) : + Crypt(context.get(), ciphertext, plaintext); +} + +bool Encryptor::Crypt(PK11Context* context, + const base::StringPiece& input, + std::string* output) { + size_t output_len = input.size() + AES_BLOCK_SIZE; + CHECK_GT(output_len, input.size()); + + output->resize(output_len); + uint8* output_data = + reinterpret_cast(const_cast(output->data())); + + int input_len = input.size(); + uint8* input_data = + reinterpret_cast(const_cast(input.data())); + + int op_len; + SECStatus rv = PK11_CipherOp(context, + output_data, + &op_len, + output_len, + input_data, + input_len); + + if (SECSuccess != rv) { + output->clear(); + return false; + } + + unsigned int digest_len; + rv = PK11_DigestFinal(context, + output_data + op_len, + &digest_len, + output_len - op_len); + if (SECSuccess != rv) { + output->clear(); + return false; + } + + output->resize(op_len + digest_len); + return true; +} + +bool Encryptor::CryptCTR(PK11Context* context, + const base::StringPiece& input, + std::string* output) { + if (!counter_.get()) { + LOG(ERROR) << "Counter value not set in CTR mode."; + return false; + } + + size_t output_len = ((input.size() + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE) * + AES_BLOCK_SIZE; + CHECK_GE(output_len, input.size()); + output->resize(output_len); + uint8* output_data = + reinterpret_cast(const_cast(output->data())); + + size_t mask_len; + bool ret = GenerateCounterMask(input.size(), output_data, &mask_len); + if (!ret) + return false; + + CHECK_EQ(mask_len, output_len); + int op_len; + SECStatus rv = PK11_CipherOp(context, + output_data, + &op_len, + output_len, + output_data, + mask_len); + if (SECSuccess != rv) + return false; + CHECK_EQ(static_cast(mask_len), op_len); + + unsigned int digest_len; + rv = PK11_DigestFinal(context, + NULL, + &digest_len, + 0); + if (SECSuccess != rv) + return false; + CHECK(!digest_len); + + // Use |output_data| to mask |input|. + MaskMessage( + reinterpret_cast(const_cast(input.data())), + input.length(), output_data, output_data); + output->resize(input.length()); + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/encryptor_openssl.cc b/chromium/crypto/encryptor_openssl.cc new file mode 100644 index 00000000000..0bf96b79df2 --- /dev/null +++ b/chromium/crypto/encryptor_openssl.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/encryptor.h" + +#include +#include + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "crypto/openssl_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +namespace { + +const EVP_CIPHER* GetCipherForKey(SymmetricKey* key) { + switch (key->key().length()) { + case 16: return EVP_aes_128_cbc(); + case 24: return EVP_aes_192_cbc(); + case 32: return EVP_aes_256_cbc(); + default: return NULL; + } +} + +// On destruction this class will cleanup the ctx, and also clear the OpenSSL +// ERR stack as a convenience. +class ScopedCipherCTX { + public: + explicit ScopedCipherCTX() { + EVP_CIPHER_CTX_init(&ctx_); + } + ~ScopedCipherCTX() { + EVP_CIPHER_CTX_cleanup(&ctx_); + ClearOpenSSLERRStack(FROM_HERE); + } + EVP_CIPHER_CTX* get() { return &ctx_; } + + private: + EVP_CIPHER_CTX ctx_; +}; + +} // namespace + +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { +} + +Encryptor::~Encryptor() { +} + +bool Encryptor::Init(SymmetricKey* key, + Mode mode, + const base::StringPiece& iv) { + DCHECK(key); + DCHECK(mode == CBC || mode == CTR); + + EnsureOpenSSLInit(); + if (mode == CBC && iv.size() != AES_BLOCK_SIZE) + return false; + + if (GetCipherForKey(key) == NULL) + return false; + + key_ = key; + mode_ = mode; + iv.CopyToString(&iv_); + return true; +} + +bool Encryptor::Encrypt(const base::StringPiece& plaintext, + std::string* ciphertext) { + CHECK(!plaintext.empty() || (mode_ == CBC)); + return (mode_ == CTR) ? + CryptCTR(true, plaintext, ciphertext) : + Crypt(true, plaintext, ciphertext); +} + +bool Encryptor::Decrypt(const base::StringPiece& ciphertext, + std::string* plaintext) { + CHECK(!ciphertext.empty()); + return (mode_ == CTR) ? + CryptCTR(false, ciphertext, plaintext) : + Crypt(false, ciphertext, plaintext); +} + +bool Encryptor::Crypt(bool do_encrypt, + const base::StringPiece& input, + std::string* output) { + DCHECK(key_); // Must call Init() before En/De-crypt. + // Work on the result in a local variable, and then only transfer it to + // |output| on success to ensure no partial data is returned. + std::string result; + output->clear(); + + const EVP_CIPHER* cipher = GetCipherForKey(key_); + DCHECK(cipher); // Already handled in Init(); + + const std::string& key = key_->key(); + DCHECK_EQ(EVP_CIPHER_iv_length(cipher), static_cast(iv_.length())); + DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast(key.length())); + + ScopedCipherCTX ctx; + if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL, + reinterpret_cast(key.data()), + reinterpret_cast(iv_.data()), + do_encrypt)) + return false; + + // When encrypting, add another block size of space to allow for any padding. + const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0); + CHECK_GT(output_size, 0u); + CHECK_GT(output_size + 1, input.size()); + uint8* out_ptr = reinterpret_cast(WriteInto(&result, + output_size + 1)); + int out_len; + if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len, + reinterpret_cast(input.data()), + input.length())) + return false; + + // Write out the final block plus padding (if any) to the end of the data + // just written. + int tail_len; + if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len)) + return false; + + out_len += tail_len; + DCHECK_LE(out_len, static_cast(output_size)); + result.resize(out_len); + + output->swap(result); + return true; +} + +bool Encryptor::CryptCTR(bool do_encrypt, + const base::StringPiece& input, + std::string* output) { + if (!counter_.get()) { + LOG(ERROR) << "Counter value not set in CTR mode."; + return false; + } + + AES_KEY aes_key; + if (AES_set_encrypt_key(reinterpret_cast(key_->key().data()), + key_->key().size() * 8, &aes_key) != 0) { + return false; + } + + const size_t out_size = input.size(); + CHECK_GT(out_size, 0u); + CHECK_GT(out_size + 1, input.size()); + + std::string result; + uint8* out_ptr = reinterpret_cast(WriteInto(&result, out_size + 1)); + + uint8_t ivec[AES_BLOCK_SIZE] = { 0 }; + uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 }; + unsigned int block_offset = 0; + + counter_->Write(ivec); + + AES_ctr128_encrypt(reinterpret_cast(input.data()), out_ptr, + input.size(), &aes_key, ivec, ecount_buf, &block_offset); + + // AES_ctr128_encrypt() updates |ivec|. Update the |counter_| here. + SetCounter(base::StringPiece(reinterpret_cast(ivec), + AES_BLOCK_SIZE)); + + output->swap(result); + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/encryptor_unittest.cc b/chromium/crypto/encryptor_unittest.cc new file mode 100644 index 00000000000..2f569e19e0f --- /dev/null +++ b/chromium/crypto/encryptor_unittest.cc @@ -0,0 +1,532 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/encryptor.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "crypto/symmetric_key.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(EncryptorTest, EncryptDecrypt) { + scoped_ptr key( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "password", "saltiest", 1000, 256)); + EXPECT_TRUE(key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long as the cipher block size. + std::string iv("the iv: 16 bytes"); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(key.get(), crypto::Encryptor::CBC, iv)); + + std::string plaintext("this is the plaintext"); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + + EXPECT_LT(0U, ciphertext.size()); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + + EXPECT_EQ(plaintext, decypted); +} + +TEST(EncryptorTest, DecryptWrongKey) { + scoped_ptr key( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "password", "saltiest", 1000, 256)); + EXPECT_TRUE(key.get()); + + // A wrong key that can be detected by implementations that validate every + // byte in the padding. + scoped_ptr wrong_key( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "wrongword", "sweetest", 1000, 256)); + EXPECT_TRUE(wrong_key.get()); + + // A wrong key that can't be detected by any implementation. The password + // "wrongword;" would also work. + scoped_ptr wrong_key2( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "wrongword+", "sweetest", 1000, 256)); + EXPECT_TRUE(wrong_key2.get()); + + // A wrong key that can be detected by all implementations. + scoped_ptr wrong_key3( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::AES, "wrongwordx", "sweetest", 1000, 256)); + EXPECT_TRUE(wrong_key3.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long as the cipher block size. + std::string iv("the iv: 16 bytes"); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(key.get(), crypto::Encryptor::CBC, iv)); + + std::string plaintext("this is the plaintext"); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + + static const unsigned char expected_ciphertext[] = { + 0x7D, 0x67, 0x5B, 0x53, 0xE6, 0xD8, 0x0F, 0x27, + 0x74, 0xB1, 0x90, 0xFE, 0x6E, 0x58, 0x4A, 0xA0, + 0x0E, 0x35, 0xE3, 0x01, 0xC0, 0xFE, 0x9A, 0xD8, + 0x48, 0x1D, 0x42, 0xB0, 0xBA, 0x21, 0xB2, 0x0C + }; + + ASSERT_EQ(arraysize(expected_ciphertext), ciphertext.size()); + for (size_t i = 0; i < ciphertext.size(); ++i) { + ASSERT_EQ(expected_ciphertext[i], + static_cast(ciphertext[i])); + } + + std::string decypted; + + // This wrong key causes the last padding byte to be 5, which is a valid + // padding length, and the second to last padding byte to be 137, which is + // invalid. If an implementation simply uses the last padding byte to + // determine the padding length without checking every padding byte, + // Encryptor::Decrypt() will still return true. This is the case for NSS + // (crbug.com/124434). +#if !defined(USE_NSS) && !defined(OS_WIN) && !defined(OS_MACOSX) + crypto::Encryptor decryptor; + EXPECT_TRUE(decryptor.Init(wrong_key.get(), crypto::Encryptor::CBC, iv)); + EXPECT_FALSE(decryptor.Decrypt(ciphertext, &decypted)); +#endif + + // This demonstrates that not all wrong keys can be detected by padding + // error. This wrong key causes the last padding byte to be 1, which is + // a valid padding block of length 1. + crypto::Encryptor decryptor2; + EXPECT_TRUE(decryptor2.Init(wrong_key2.get(), crypto::Encryptor::CBC, iv)); + EXPECT_TRUE(decryptor2.Decrypt(ciphertext, &decypted)); + + // This wrong key causes the last padding byte to be 253, which should be + // rejected by all implementations. + crypto::Encryptor decryptor3; + EXPECT_TRUE(decryptor3.Init(wrong_key3.get(), crypto::Encryptor::CBC, iv)); + EXPECT_FALSE(decryptor3.Decrypt(ciphertext, &decypted)); +} + +namespace { + +// From NIST SP 800-38a test cast: +// - F.5.1 CTR-AES128.Encrypt +// - F.5.6 CTR-AES256.Encrypt +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +const unsigned char kAES128CTRKey[] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c +}; + +const unsigned char kAES256CTRKey[] = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 +}; + +const unsigned char kAESCTRInitCounter[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +const unsigned char kAESCTRPlaintext[] = { + // Block #1 + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + // Block #2 + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + // Block #3 + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + // Block #4 + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 +}; + +const unsigned char kAES128CTRCiphertext[] = { + // Block #1 + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, + 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, + // Block #2 + 0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, + 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff, + // Block #3 + 0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, + 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab, + // Block #4 + 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, + 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee +}; + +const unsigned char kAES256CTRCiphertext[] = { + // Block #1 + 0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, + 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28, + // Block #2 + 0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, + 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5, + // Block #3 + 0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, + 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d, + // Block #4 + 0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, + 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6 +}; + +void TestAESCTREncrypt( + const unsigned char* key, size_t key_size, + const unsigned char* init_counter, size_t init_counter_size, + const unsigned char* plaintext, size_t plaintext_size, + const unsigned char* ciphertext, size_t ciphertext_size) { + std::string key_str(reinterpret_cast(key), key_size); + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key_str)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CTR, "")); + + base::StringPiece init_counter_str( + reinterpret_cast(init_counter), init_counter_size); + base::StringPiece plaintext_str( + reinterpret_cast(plaintext), plaintext_size); + + EXPECT_TRUE(encryptor.SetCounter(init_counter_str)); + std::string encrypted; + EXPECT_TRUE(encryptor.Encrypt(plaintext_str, &encrypted)); + + EXPECT_EQ(ciphertext_size, encrypted.size()); + EXPECT_EQ(0, memcmp(encrypted.data(), ciphertext, encrypted.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.SetCounter(init_counter_str)); + EXPECT_TRUE(encryptor.Decrypt(encrypted, &decypted)); + + EXPECT_EQ(plaintext_str, decypted); +} + +void TestAESCTRMultipleDecrypt( + const unsigned char* key, size_t key_size, + const unsigned char* init_counter, size_t init_counter_size, + const unsigned char* plaintext, size_t plaintext_size, + const unsigned char* ciphertext, size_t ciphertext_size) { + std::string key_str(reinterpret_cast(key), key_size); + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key_str)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CTR, "")); + + // Counter is set only once. + EXPECT_TRUE(encryptor.SetCounter(base::StringPiece( + reinterpret_cast(init_counter), init_counter_size))); + + std::string ciphertext_str(reinterpret_cast(ciphertext), + ciphertext_size); + + int kTestDecryptSizes[] = { 32, 16, 8 }; + + int offset = 0; + for (size_t i = 0; i < arraysize(kTestDecryptSizes); ++i) { + std::string decypted; + size_t len = kTestDecryptSizes[i]; + EXPECT_TRUE( + encryptor.Decrypt(ciphertext_str.substr(offset, len), &decypted)); + EXPECT_EQ(len, decypted.size()); + EXPECT_EQ(0, memcmp(decypted.data(), plaintext + offset, len)); + offset += len; + } +} + +} // namespace + +TEST(EncryptorTest, EncryptAES128CTR) { + TestAESCTREncrypt( + kAES128CTRKey, arraysize(kAES128CTRKey), + kAESCTRInitCounter, arraysize(kAESCTRInitCounter), + kAESCTRPlaintext, arraysize(kAESCTRPlaintext), + kAES128CTRCiphertext, arraysize(kAES128CTRCiphertext)); +} + +TEST(EncryptorTest, EncryptAES256CTR) { + TestAESCTREncrypt( + kAES256CTRKey, arraysize(kAES256CTRKey), + kAESCTRInitCounter, arraysize(kAESCTRInitCounter), + kAESCTRPlaintext, arraysize(kAESCTRPlaintext), + kAES256CTRCiphertext, arraysize(kAES256CTRCiphertext)); +} + +TEST(EncryptorTest, EncryptAES128CTR_MultipleDecrypt) { + TestAESCTRMultipleDecrypt( + kAES128CTRKey, arraysize(kAES128CTRKey), + kAESCTRInitCounter, arraysize(kAESCTRInitCounter), + kAESCTRPlaintext, arraysize(kAESCTRPlaintext), + kAES128CTRCiphertext, arraysize(kAES128CTRCiphertext)); +} + +TEST(EncryptorTest, EncryptAES256CTR_MultipleDecrypt) { + TestAESCTRMultipleDecrypt( + kAES256CTRKey, arraysize(kAES256CTRKey), + kAESCTRInitCounter, arraysize(kAESCTRInitCounter), + kAESCTRPlaintext, arraysize(kAESCTRPlaintext), + kAES256CTRCiphertext, arraysize(kAES256CTRCiphertext)); +} + +TEST(EncryptorTest, EncryptDecryptCTR) { + scoped_ptr key( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 128)); + + EXPECT_TRUE(key.get()); + const std::string kInitialCounter = "0000000000000000"; + + crypto::Encryptor encryptor; + EXPECT_TRUE(encryptor.Init(key.get(), crypto::Encryptor::CTR, "")); + EXPECT_TRUE(encryptor.SetCounter(kInitialCounter)); + + std::string plaintext("normal plaintext of random length"); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_LT(0U, ciphertext.size()); + + std::string decypted; + EXPECT_TRUE(encryptor.SetCounter(kInitialCounter)); + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); + + plaintext = "0123456789012345"; + EXPECT_TRUE(encryptor.SetCounter(kInitialCounter)); + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_LT(0U, ciphertext.size()); + + EXPECT_TRUE(encryptor.SetCounter(kInitialCounter)); + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); +} + +TEST(EncryptorTest, CTRCounter) { + const int kCounterSize = 16; + const unsigned char kTest1[] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + unsigned char buf[16]; + + // Increment 10 times. + crypto::Encryptor::Counter counter1( + std::string(reinterpret_cast(kTest1), kCounterSize)); + for (int i = 0; i < 10; ++i) + counter1.Increment(); + counter1.Write(buf); + EXPECT_EQ(0, memcmp(buf, kTest1, 15)); + EXPECT_TRUE(buf[15] == 10); + + // Check corner cases. + const unsigned char kTest2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + const unsigned char kExpect2[] = + {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}; + crypto::Encryptor::Counter counter2( + std::string(reinterpret_cast(kTest2), kCounterSize)); + counter2.Increment(); + counter2.Write(buf); + EXPECT_EQ(0, memcmp(buf, kExpect2, kCounterSize)); + + const unsigned char kTest3[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + const unsigned char kExpect3[] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + crypto::Encryptor::Counter counter3( + std::string(reinterpret_cast(kTest3), kCounterSize)); + counter3.Increment(); + counter3.Write(buf); + EXPECT_EQ(0, memcmp(buf, kExpect3, kCounterSize)); +} + +// TODO(wtc): add more known-answer tests. Test vectors are available from +// http://www.ietf.org/rfc/rfc3602 +// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf +// http://gladman.plushost.co.uk/oldsite/AES/index.php +// http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip + +// NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt. +TEST(EncryptorTest, EncryptAES256CBC) { + // From NIST SP 800-38a test cast F.2.5 CBC-AES256.Encrypt. + static const unsigned char kRawKey[] = { + 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, + 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, + 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, + 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 + }; + static const unsigned char kRawIv[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const unsigned char kRawPlaintext[] = { + // Block #1 + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, + // Block #2 + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, + // Block #3 + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, + // Block #4 + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10, + }; + static const unsigned char kRawCiphertext[] = { + // Block #1 + 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, + 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + // Block #2 + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, + 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + // Block #3 + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, + 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + // Block #4 + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, + 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b, + // PKCS #5 padding, encrypted. + 0x3f, 0x46, 0x17, 0x96, 0xd6, 0xb0, 0xd6, 0xb2, + 0xe0, 0xc2, 0xa7, 0x2b, 0x4d, 0x80, 0xe6, 0x44 + }; + + std::string key(reinterpret_cast(kRawKey), sizeof(kRawKey)); + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + std::string iv(reinterpret_cast(kRawIv), sizeof(kRawIv)); + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string plaintext(reinterpret_cast(kRawPlaintext), + sizeof(kRawPlaintext)); + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + + EXPECT_EQ(sizeof(kRawCiphertext), ciphertext.size()); + EXPECT_EQ(0, memcmp(ciphertext.data(), kRawCiphertext, ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + + EXPECT_EQ(plaintext, decypted); +} + +// Expected output derived from the NSS implementation. +TEST(EncryptorTest, EncryptAES128CBCRegression) { + std::string key = "128=SixteenBytes"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext = "Plain text with a g-clef U+1D11E \360\235\204\236"; + std::string expected_ciphertext_hex = + "D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A" + "C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); +} + +// Expected output derived from the NSS implementation. +TEST(EncryptorTest, EncryptAES192CBCRegression) { + std::string key = "192bitsIsTwentyFourByte!"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext = "Small text"; + std::string expected_ciphertext_hex = "78DE5D7C2714FC5C61346C5416F6C89A"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); + + std::string decypted; + EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decypted)); + EXPECT_EQ(plaintext, decypted); +} + +// Not all platforms allow import/generation of symmetric keys with an +// unsupported size. +#if !defined(USE_NSS) && !defined(OS_WIN) && !defined(OS_MACOSX) +TEST(EncryptorTest, UnsupportedKeySize) { + std::string key = "7 = bad"; + std::string iv = "Sweet Sixteen IV"; + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); +} +#endif // unsupported platforms. + +TEST(EncryptorTest, UnsupportedIV) { + std::string key = "128=SixteenBytes"; + std::string iv = "OnlyForteen :("; + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); +} + +TEST(EncryptorTest, EmptyEncrypt) { + std::string key = "128=SixteenBytes"; + std::string iv = "Sweet Sixteen IV"; + std::string plaintext; + std::string expected_ciphertext_hex = "8518B8878D34E7185E300D0FCC426396"; + + scoped_ptr sym_key(crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, key)); + ASSERT_TRUE(sym_key.get()); + + crypto::Encryptor encryptor; + // The IV must be exactly as long a the cipher block size. + EXPECT_EQ(16U, iv.size()); + EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)); + + std::string ciphertext; + EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext)); + EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext.data(), + ciphertext.size())); +} diff --git a/chromium/crypto/ghash.cc b/chromium/crypto/ghash.cc new file mode 100644 index 00000000000..5b28c442b89 --- /dev/null +++ b/chromium/crypto/ghash.cc @@ -0,0 +1,257 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ghash.h" + +#include "base/logging.h" +#include "base/sys_byteorder.h" + +namespace crypto { + +// GaloisHash is a polynomial authenticator that works in GF(2^128). +// +// Elements of the field are represented in `little-endian' order (which +// matches the description in the paper[1]), thus the most significant bit is +// the right-most bit. (This is backwards from the way that everybody else does +// it.) +// +// We store field elements in a pair of such `little-endian' uint64s. So the +// value one is represented by {low = 2**63, high = 0} and doubling a value +// involves a *right* shift. +// +// [1] http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf + +namespace { + +// Get64 reads a 64-bit, big-endian number from |bytes|. +uint64 Get64(const uint8 bytes[8]) { + uint64 t; + memcpy(&t, bytes, sizeof(t)); + return base::NetToHost64(t); +} + +// Put64 writes |x| to |bytes| as a 64-bit, big-endian number. +void Put64(uint8 bytes[8], uint64 x) { + x = base::HostToNet64(x); + memcpy(bytes, &x, sizeof(x)); +} + +// Reverse reverses the order of the bits of 4-bit number in |i|. +int Reverse(int i) { + i = ((i << 2) & 0xc) | ((i >> 2) & 0x3); + i = ((i << 1) & 0xa) | ((i >> 1) & 0x5); + return i; +} + +} // namespace + +GaloisHash::GaloisHash(const uint8 key[16]) { + Reset(); + + // We precompute 16 multiples of |key|. However, when we do lookups into this + // table we'll be using bits from a field element and therefore the bits will + // be in the reverse order. So normally one would expect, say, 4*key to be in + // index 4 of the table but due to this bit ordering it will actually be in + // index 0010 (base 2) = 2. + FieldElement x = {Get64(key), Get64(key+8)}; + product_table_[0].low = 0; + product_table_[0].hi = 0; + product_table_[Reverse(1)] = x; + + for (int i = 0; i < 16; i += 2) { + product_table_[Reverse(i)] = Double(product_table_[Reverse(i/2)]); + product_table_[Reverse(i+1)] = Add(product_table_[Reverse(i)], x); + } +} + +void GaloisHash::Reset() { + state_ = kHashingAdditionalData; + additional_bytes_ = 0; + ciphertext_bytes_ = 0; + buf_used_ = 0; + y_.low = 0; + y_.hi = 0; +} + +void GaloisHash::UpdateAdditional(const uint8* data, size_t length) { + DCHECK_EQ(state_, kHashingAdditionalData); + additional_bytes_ += length; + Update(data, length); +} + +void GaloisHash::UpdateCiphertext(const uint8* data, size_t length) { + if (state_ == kHashingAdditionalData) { + // If there's any remaining additional data it's zero padded to the next + // full block. + if (buf_used_ > 0) { + memset(&buf_[buf_used_], 0, sizeof(buf_)-buf_used_); + UpdateBlocks(buf_, 1); + buf_used_ = 0; + } + state_ = kHashingCiphertext; + } + + DCHECK_EQ(state_, kHashingCiphertext); + ciphertext_bytes_ += length; + Update(data, length); +} + +void GaloisHash::Finish(void* output, size_t len) { + DCHECK(state_ != kComplete); + + if (buf_used_ > 0) { + // If there's any remaining data (additional data or ciphertext), it's zero + // padded to the next full block. + memset(&buf_[buf_used_], 0, sizeof(buf_)-buf_used_); + UpdateBlocks(buf_, 1); + buf_used_ = 0; + } + + state_ = kComplete; + + // The lengths of the additional data and ciphertext are included as the last + // block. The lengths are the number of bits. + y_.low ^= additional_bytes_*8; + y_.hi ^= ciphertext_bytes_*8; + MulAfterPrecomputation(product_table_, &y_); + + uint8 *result, result_tmp[16]; + if (len >= 16) { + result = reinterpret_cast(output); + } else { + result = result_tmp; + } + + Put64(result, y_.low); + Put64(result + 8, y_.hi); + + if (len < 16) + memcpy(output, result_tmp, len); +} + +// static +GaloisHash::FieldElement GaloisHash::Add( + const FieldElement& x, + const FieldElement& y) { + // Addition in a characteristic 2 field is just XOR. + FieldElement z = {x.low^y.low, x.hi^y.hi}; + return z; +} + +// static +GaloisHash::FieldElement GaloisHash::Double(const FieldElement& x) { + const bool msb_set = x.hi & 1; + + FieldElement xx; + // Because of the bit-ordering, doubling is actually a right shift. + xx.hi = x.hi >> 1; + xx.hi |= x.low << 63; + xx.low = x.low >> 1; + + // If the most-significant bit was set before shifting then it, conceptually, + // becomes a term of x^128. This is greater than the irreducible polynomial + // so the result has to be reduced. The irreducible polynomial is + // 1+x+x^2+x^7+x^128. We can subtract that to eliminate the term at x^128 + // which also means subtracting the other four terms. In characteristic 2 + // fields, subtraction == addition == XOR. + if (msb_set) + xx.low ^= 0xe100000000000000ULL; + + return xx; +} + +void GaloisHash::MulAfterPrecomputation(const FieldElement* table, + FieldElement* x) { + FieldElement z = {0, 0}; + + // In order to efficiently multiply, we use the precomputed table of i*key, + // for i in 0..15, to handle four bits at a time. We could obviously use + // larger tables for greater speedups but the next convenient table size is + // 4K, which is a little large. + // + // In other fields one would use bit positions spread out across the field in + // order to reduce the number of doublings required. However, in + // characteristic 2 fields, repeated doublings are exceptionally cheap and + // it's not worth spending more precomputation time to eliminate them. + for (unsigned i = 0; i < 2; i++) { + uint64 word; + if (i == 0) { + word = x->hi; + } else { + word = x->low; + } + + for (unsigned j = 0; j < 64; j += 4) { + Mul16(&z); + // the values in |table| are ordered for little-endian bit positions. See + // the comment in the constructor. + const FieldElement& t = table[word & 0xf]; + z.low ^= t.low; + z.hi ^= t.hi; + word >>= 4; + } + } + + *x = z; +} + +// kReductionTable allows for rapid multiplications by 16. A multiplication by +// 16 is a right shift by four bits, which results in four bits at 2**128. +// These terms have to be eliminated by dividing by the irreducible polynomial. +// In GHASH, the polynomial is such that all the terms occur in the +// least-significant 8 bits, save for the term at x^128. Therefore we can +// precompute the value to be added to the field element for each of the 16 bit +// patterns at 2**128 and the values fit within 12 bits. +static const uint16 kReductionTable[16] = { + 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, +}; + +// static +void GaloisHash::Mul16(FieldElement* x) { + const unsigned msw = x->hi & 0xf; + x->hi >>= 4; + x->hi |= x->low << 60; + x->low >>= 4; + x->low ^= static_cast(kReductionTable[msw]) << 48; +} + +void GaloisHash::UpdateBlocks(const uint8* bytes, size_t num_blocks) { + for (size_t i = 0; i < num_blocks; i++) { + y_.low ^= Get64(bytes); + bytes += 8; + y_.hi ^= Get64(bytes); + bytes += 8; + MulAfterPrecomputation(product_table_, &y_); + } +} + +void GaloisHash::Update(const uint8* data, size_t length) { + if (buf_used_ > 0) { + const size_t n = std::min(length, sizeof(buf_) - buf_used_); + memcpy(&buf_[buf_used_], data, n); + buf_used_ += n; + length -= n; + data += n; + + if (buf_used_ == sizeof(buf_)) { + UpdateBlocks(buf_, 1); + buf_used_ = 0; + } + } + + if (length >= 16) { + const size_t n = length / 16; + UpdateBlocks(data, n); + length -= n*16; + data += n*16; + } + + if (length > 0) { + memcpy(buf_, data, length); + buf_used_ = length; + } +} + +} // namespace crypto diff --git a/chromium/crypto/ghash.h b/chromium/crypto/ghash.h new file mode 100644 index 00000000000..6dc247be28e --- /dev/null +++ b/chromium/crypto/ghash.h @@ -0,0 +1,86 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// GaloisHash implements the polynomial authenticator part of GCM as specified +// in http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf +// Specifically it implements the GHASH function, defined in section 2.3 of +// that document. +// +// In SP-800-38D, GHASH is defined differently and takes only a single data +// argument. But it is always called with an argument of a certain form: +// GHASH_H (A || 0^v || C || 0^u || [len(A)]_64 || [len(C)]_64) +// This mirrors how the gcm-revised-spec.pdf version of GHASH handles its two +// data arguments. The two GHASH functions therefore differ only in whether the +// data is formatted inside or outside of the function. +// +// WARNING: do not use this as a generic authenticator. Polynomial +// authenticators must be used in the correct manner and any use outside of GCM +// requires careful consideration. +// +// WARNING: this code is not constant time. However, in all likelihood, nor is +// the implementation of AES that is used. +class CRYPTO_EXPORT_PRIVATE GaloisHash { + public: + explicit GaloisHash(const uint8 key[16]); + + // Reset prepares to digest a fresh message with the same key. This is more + // efficient than creating a fresh object. + void Reset(); + + // UpdateAdditional hashes in `additional' data. This is data that is not + // encrypted, but is covered by the authenticator. All additional data must + // be written before any ciphertext is written. + void UpdateAdditional(const uint8* data, size_t length); + + // UpdateCiphertext hashes in ciphertext to be authenticated. + void UpdateCiphertext(const uint8* data, size_t length); + + // Finish completes the hash computation and writes at most |len| bytes of + // the result to |output|. + void Finish(void* output, size_t len); + + private: + enum State { + kHashingAdditionalData, + kHashingCiphertext, + kComplete, + }; + + struct FieldElement { + uint64 low, hi; + }; + + // Add returns |x|+|y|. + static FieldElement Add(const FieldElement& x, const FieldElement& y); + // Double returns 2*|x|. + static FieldElement Double(const FieldElement& x); + // MulAfterPrecomputation sets |x| = |x|*h where h is |table[1]| and + // table[i] = i*h for i=0..15. + static void MulAfterPrecomputation(const FieldElement* table, + FieldElement* x); + // Mul16 sets |x| = 16*|x|. + static void Mul16(FieldElement* x); + + // UpdateBlocks processes |num_blocks| 16-bytes blocks from |bytes|. + void UpdateBlocks(const uint8* bytes, size_t num_blocks); + // Update processes |length| bytes from |bytes| and calls UpdateBlocks on as + // much data as possible. It uses |buf_| to buffer any remaining data and + // always consumes all of |bytes|. + void Update(const uint8* bytes, size_t length); + + FieldElement y_; + State state_; + size_t additional_bytes_; + size_t ciphertext_bytes_; + uint8 buf_[16]; + size_t buf_used_; + FieldElement product_table_[16]; +}; + +} // namespace crypto diff --git a/chromium/crypto/ghash_unittest.cc b/chromium/crypto/ghash_unittest.cc new file mode 100644 index 00000000000..459e177c817 --- /dev/null +++ b/chromium/crypto/ghash_unittest.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/ghash.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +namespace { + +// Test vectors are taken from Appendix B of +// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf + +static const uint8 kKey1[16] = { + 0x66, 0xe9, 0x4b, 0xd4, 0xef, 0x8a, 0x2c, 0x3b, + 0x88, 0x4c, 0xfa, 0x59, 0xca, 0x34, 0x2b, 0x2e, +}; + +static const uint8 kCiphertext2[] = { + 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, + 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78, +}; + +static const uint8 kKey3[16] = { + 0xb8, 0x3b, 0x53, 0x37, 0x08, 0xbf, 0x53, 0x5d, + 0x0a, 0xa6, 0xe5, 0x29, 0x80, 0xd5, 0x3b, 0x78, +}; + +static const uint8 kCiphertext3[] = { + 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85, +}; + +static const uint8 kAdditional4[] = { + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xad, 0xda, 0xd2, +}; + +struct TestCase { + const uint8* key; + const uint8* additional; + unsigned additional_length; + const uint8* ciphertext; + unsigned ciphertext_length; + const uint8 expected[16]; +}; + +static const TestCase kTestCases[] = { + { + kKey1, + NULL, + 0, + NULL, + 0, + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, + { + kKey1, + NULL, + 0, + kCiphertext2, + sizeof(kCiphertext2), + { + 0xf3, 0x8c, 0xbb, 0x1a, 0xd6, 0x92, 0x23, 0xdc, + 0xc3, 0x45, 0x7a, 0xe5, 0xb6, 0xb0, 0xf8, 0x85, + }, + }, + { + kKey3, + NULL, + 0, + kCiphertext3, + sizeof(kCiphertext3), + { + 0x7f, 0x1b, 0x32, 0xb8, 0x1b, 0x82, 0x0d, 0x02, + 0x61, 0x4f, 0x88, 0x95, 0xac, 0x1d, 0x4e, 0xac, + }, + }, + { + kKey3, + kAdditional4, + sizeof(kAdditional4), + kCiphertext3, + sizeof(kCiphertext3) - 4, + { + 0x69, 0x8e, 0x57, 0xf7, 0x0e, 0x6e, 0xcc, 0x7f, + 0xd9, 0x46, 0x3b, 0x72, 0x60, 0xa9, 0xae, 0x5f, + }, + }, +}; + +TEST(GaloisHash, TestCases) { + uint8 out[16]; + + for (size_t i = 0; i < arraysize(kTestCases); ++i) { + const TestCase& test = kTestCases[i]; + + GaloisHash hash(test.key); + if (test.additional_length) + hash.UpdateAdditional(test.additional, test.additional_length); + if (test.ciphertext_length) + hash.UpdateCiphertext(test.ciphertext, test.ciphertext_length); + hash.Finish(out, sizeof(out)); + EXPECT_TRUE(0 == memcmp(out, test.expected, 16)); + } +} + +TEST(GaloisHash, VaryLengths) { + uint8 out[16]; + + for (size_t chunk_size = 1; chunk_size < 16; chunk_size++) { + for (size_t i = 0; i < arraysize(kTestCases); ++i) { + const TestCase& test = kTestCases[i]; + + GaloisHash hash(test.key); + for (size_t i = 0; i < test.additional_length;) { + size_t n = std::min(test.additional_length - i, chunk_size); + hash.UpdateAdditional(test.additional + i, n); + i += n; + } + for (size_t i = 0; i < test.ciphertext_length;) { + size_t n = std::min(test.ciphertext_length - i, chunk_size); + hash.UpdateCiphertext(test.ciphertext + i, n); + i += n; + } + hash.Finish(out, sizeof(out)); + EXPECT_TRUE(0 == memcmp(out, test.expected, 16)); + } + } +} + +} // namespace + +} // namespace crypto diff --git a/chromium/crypto/hkdf.cc b/chromium/crypto/hkdf.cc new file mode 100644 index 00000000000..1cd8468977e --- /dev/null +++ b/chromium/crypto/hkdf.cc @@ -0,0 +1,99 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hkdf.h" + +#include "base/logging.h" +#include "crypto/hmac.h" + +namespace crypto { + +const size_t kSHA256HashLength = 32; + +HKDF::HKDF(const base::StringPiece& secret, + const base::StringPiece& salt, + const base::StringPiece& info, + size_t key_bytes_to_generate, + size_t iv_bytes_to_generate) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + base::StringPiece actual_salt = salt; + char zeros[kSHA256HashLength]; + if (actual_salt.empty()) { + // If salt is not given, HashLength zeros are used. + memset(zeros, 0, sizeof(zeros)); + actual_salt.set(zeros, sizeof(zeros)); + } + + // Perform the Extract step to transform the input key and + // salt into the pseudorandom key (PRK) used for Expand. + HMAC prk_hmac(HMAC::SHA256); + bool result = prk_hmac.Init(actual_salt); + DCHECK(result); + + // |prk| is a pseudorandom key (of kSHA256HashLength octets). + uint8 prk[kSHA256HashLength]; + DCHECK_EQ(sizeof(prk), prk_hmac.DigestLength()); + result = prk_hmac.Sign(secret, prk, sizeof(prk)); + DCHECK(result); + + // https://tools.ietf.org/html/rfc5869#section-2.3 + // Perform the Expand phase to turn the pseudorandom key + // and info into the output keying material. + const size_t material_length = + 2*key_bytes_to_generate + 2*iv_bytes_to_generate; + const size_t n = (material_length + kSHA256HashLength-1) / + kSHA256HashLength; + DCHECK_LT(n, 256u); + + output_.resize(n * kSHA256HashLength); + base::StringPiece previous; + + scoped_ptr buf(new char[kSHA256HashLength + info.size() + 1]); + uint8 digest[kSHA256HashLength]; + + HMAC hmac(HMAC::SHA256); + result = hmac.Init(prk, sizeof(prk)); + DCHECK(result); + + for (size_t i = 0; i < n; i++) { + memcpy(buf.get(), previous.data(), previous.size()); + size_t j = previous.size(); + memcpy(buf.get() + j, info.data(), info.size()); + j += info.size(); + buf[j++] = static_cast(i + 1); + + result = hmac.Sign(base::StringPiece(buf.get(), j), digest, sizeof(digest)); + DCHECK(result); + + memcpy(&output_[i*sizeof(digest)], digest, sizeof(digest)); + previous = base::StringPiece(reinterpret_cast(digest), + sizeof(digest)); + } + + size_t j = 0; + // On Windows, when the size of output_ is zero, dereference of 0'th element + // results in a crash. C++11 solves this problem by adding a data() getter + // method to std::vector. + if (key_bytes_to_generate) { + client_write_key_ = base::StringPiece(reinterpret_cast(&output_[j]), + key_bytes_to_generate); + j += key_bytes_to_generate; + server_write_key_ = base::StringPiece(reinterpret_cast(&output_[j]), + key_bytes_to_generate); + j += key_bytes_to_generate; + } + + if (iv_bytes_to_generate) { + client_write_iv_ = base::StringPiece(reinterpret_cast(&output_[j]), + iv_bytes_to_generate); + j += iv_bytes_to_generate; + server_write_iv_ = base::StringPiece(reinterpret_cast(&output_[j]), + iv_bytes_to_generate); + } +} + +HKDF::~HKDF() { +} + +} // namespace crypto diff --git a/chromium/crypto/hkdf.h b/chromium/crypto/hkdf.h new file mode 100644 index 00000000000..1d7a87673a1 --- /dev/null +++ b/chromium/crypto/hkdf.h @@ -0,0 +1,64 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_HKDF_H_ +#define CRYPTO_HKDF_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// HKDF implements the key derivation function specified in RFC 5869 (using +// SHA-256) and outputs key material, as needed by QUIC. +// See https://tools.ietf.org/html/rfc5869 for details. +class CRYPTO_EXPORT HKDF { + public: + // |secret|: The input shared secret (or, from RFC 5869, the IKM). + // |salt|: an (optional) public salt / non-secret random value. While + // optional, callers are strongly recommended to provide a salt. There is no + // added security value in making this larger than the SHA-256 block size of + // 64 bytes. + // |info|: an (optional) label to distinguish different uses of HKDF. It is + // optional context and application specific information (can be a zero-length + // string). + // |key_bytes_to_generate|: the number of bytes of key material to generate. + // |iv_bytes_to_generate|: the number of bytes of IV to generate. + HKDF(const base::StringPiece& secret, + const base::StringPiece& salt, + const base::StringPiece& info, + size_t key_bytes_to_generate, + size_t iv_bytes_to_generate); + ~HKDF(); + + base::StringPiece client_write_key() const { + return client_write_key_; + } + base::StringPiece client_write_iv() const { + return client_write_iv_; + } + base::StringPiece server_write_key() const { + return server_write_key_; + } + base::StringPiece server_write_iv() const { + return server_write_iv_; + } + + private: + std::vector output_; + + base::StringPiece client_write_key_; + base::StringPiece server_write_key_; + base::StringPiece client_write_iv_; + base::StringPiece server_write_iv_; +}; + +} // namespace crypto + +#endif // CRYPTO_HKDF_H_ diff --git a/chromium/crypto/hkdf_unittest.cc b/chromium/crypto/hkdf_unittest.cc new file mode 100644 index 00000000000..dc369d1b94f --- /dev/null +++ b/chromium/crypto/hkdf_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hkdf.h" + +#include + +#include "base/strings/string_number_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +using crypto::HKDF; + +namespace test { +namespace { + +struct HKDFTest { + const char* key_hex; + const char* salt_hex; + const char* info_hex; + const char* output_hex; +}; + +// These test cases are taken from +// https://tools.ietf.org/html/rfc5869#appendix-A. +static const HKDFTest kHKDFTests[] = {{ + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5" + "b887185865", + }, { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324" + "25262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546474849" + "4a4b4c4d4e4f", + "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081828384" + "85868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9" + "aaabacadaeaf", + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4" + "d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9" + "fafbfcfdfeff", + "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99ca" + "c7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c" + "01d5c1f3434f1d87", + }, { + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", + "", + "", + "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395fa" + "a4b61a96c8", + }, +}; + +TEST(HKDFTest, HKDF) { + for (size_t i = 0; i < arraysize(kHKDFTests); i++) { + const HKDFTest& test(kHKDFTests[i]); + SCOPED_TRACE(i); + + std::vector data; + ASSERT_TRUE(base::HexStringToBytes(test.key_hex, &data)); + const std::string key(reinterpret_cast(&data[0]), data.size()); + + data.clear(); + // |salt_hex| is optional and may be empty. + std::string salt(test.salt_hex); + if (!salt.empty()) { + ASSERT_TRUE(base::HexStringToBytes(salt, &data)); + salt.assign(reinterpret_cast(&data[0]), data.size()); + } + + data.clear(); + // |info_hex| is optional and may be empty. + std::string info(test.info_hex); + if (!info.empty()) { + ASSERT_TRUE(base::HexStringToBytes(info, &data)); + info.assign(reinterpret_cast(&data[0]), data.size()); + } + + data.clear(); + ASSERT_TRUE(base::HexStringToBytes(test.output_hex, &data)); + const std::string expected(reinterpret_cast(&data[0]), data.size()); + + // We set the key_length to the length of the expected output and then take + // the result from the first key, which is the client write key. + HKDF hkdf(key, salt, info, expected.size(), 0); + + ASSERT_EQ(expected.size(), hkdf.client_write_key().size()); + EXPECT_EQ(0, memcmp(expected.data(), hkdf.client_write_key().data(), + expected.size())); + } +} + +} // namespace +} // namespace test diff --git a/chromium/crypto/hmac.cc b/chromium/crypto/hmac.cc new file mode 100644 index 00000000000..c9a2b74d983 --- /dev/null +++ b/chromium/crypto/hmac.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hmac.h" + +#include + +#include "base/logging.h" +#include "crypto/secure_util.h" +#include "crypto/symmetric_key.h" + +namespace crypto { + +bool HMAC::Init(SymmetricKey* key) { + std::string raw_key; + bool result = key->GetRawKey(&raw_key) && Init(raw_key); + // Zero out key copy. This might get optimized away, but one can hope. + // Using std::string to store key info at all is a larger problem. + std::fill(raw_key.begin(), raw_key.end(), 0); + return result; +} + +size_t HMAC::DigestLength() const { + switch (hash_alg_) { + case SHA1: + return 20; + case SHA256: + return 32; + default: + NOTREACHED(); + return 0; + } +} + +bool HMAC::Verify(const base::StringPiece& data, + const base::StringPiece& digest) const { + if (digest.size() != DigestLength()) + return false; + return VerifyTruncated(data, digest); +} + +bool HMAC::VerifyTruncated(const base::StringPiece& data, + const base::StringPiece& digest) const { + if (digest.empty()) + return false; + size_t digest_length = DigestLength(); + scoped_ptr computed_digest( + new unsigned char[digest_length]); + if (!Sign(data, computed_digest.get(), digest_length)) + return false; + + return SecureMemEqual(digest.data(), computed_digest.get(), + std::min(digest.size(), digest_length)); +} + +} // namespace crypto diff --git a/chromium/crypto/hmac.h b/chromium/crypto/hmac.h new file mode 100644 index 00000000000..c7b22fa1bdf --- /dev/null +++ b/chromium/crypto/hmac.h @@ -0,0 +1,93 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Utility class for calculating the HMAC for a given message. We currently +// only support SHA1 for the hash algorithm, but this can be extended easily. + +#ifndef CRYPTO_HMAC_H_ +#define CRYPTO_HMAC_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_piece.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// Simplify the interface and reduce includes by abstracting out the internals. +struct HMACPlatformData; +class SymmetricKey; + +class CRYPTO_EXPORT HMAC { + public: + // The set of supported hash functions. Extend as required. + enum HashAlgorithm { + SHA1, + SHA256, + }; + + explicit HMAC(HashAlgorithm hash_alg); + ~HMAC(); + + // Returns the length of digest that this HMAC will create. + size_t DigestLength() const; + + // TODO(abarth): Add a PreferredKeyLength() member function. + + // Initializes this instance using |key| of the length |key_length|. Call Init + // only once. It returns false on the second or later calls. + // + // NOTE: the US Federal crypto standard FIPS 198, Section 3 says: + // The size of the key, K, shall be equal to or greater than L/2, where L + // is the size of the hash function output. + // In FIPS 198-1 (and SP-800-107, which describes key size recommendations), + // this requirement is gone. But a system crypto library may still enforce + // this old requirement. If the key is shorter than this recommended value, + // Init() may fail. + bool Init(const unsigned char* key, size_t key_length) WARN_UNUSED_RESULT; + + // Initializes this instance using |key|. Call Init + // only once. It returns false on the second or later calls. + bool Init(SymmetricKey* key) WARN_UNUSED_RESULT; + + // Initializes this instance using |key|. Call Init only once. It returns + // false on the second or later calls. + bool Init(const base::StringPiece& key) WARN_UNUSED_RESULT { + return Init(reinterpret_cast(key.data()), + key.size()); + } + + // Calculates the HMAC for the message in |data| using the algorithm supplied + // to the constructor and the key supplied to the Init method. The HMAC is + // returned in |digest|, which has |digest_length| bytes of storage available. + bool Sign(const base::StringPiece& data, unsigned char* digest, + size_t digest_length) const WARN_UNUSED_RESULT; + + // Verifies that the HMAC for the message in |data| equals the HMAC provided + // in |digest|, using the algorithm supplied to the constructor and the key + // supplied to the Init method. Use of this method is strongly recommended + // over using Sign() with a manual comparison (such as memcmp), as such + // comparisons may result in side-channel disclosures, such as timing, that + // undermine the cryptographic integrity. |digest| must be exactly + // |DigestLength()| bytes long. + bool Verify(const base::StringPiece& data, + const base::StringPiece& digest) const WARN_UNUSED_RESULT; + + // Verifies a truncated HMAC, behaving identical to Verify(), except + // that |digest| is allowed to be smaller than |DigestLength()|. + bool VerifyTruncated( + const base::StringPiece& data, + const base::StringPiece& digest) const WARN_UNUSED_RESULT; + + private: + HashAlgorithm hash_alg_; + scoped_ptr plat_; + + DISALLOW_COPY_AND_ASSIGN(HMAC); +}; + +} // namespace crypto + +#endif // CRYPTO_HMAC_H_ diff --git a/chromium/crypto/hmac_nss.cc b/chromium/crypto/hmac_nss.cc new file mode 100644 index 00000000000..e14282c9166 --- /dev/null +++ b/chromium/crypto/hmac_nss.cc @@ -0,0 +1,117 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hmac.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "crypto/scoped_nss_types.h" + +namespace crypto { + +struct HMACPlatformData { + CK_MECHANISM_TYPE mechanism_; + ScopedPK11Slot slot_; + ScopedPK11SymKey sym_key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported. + switch (hash_alg_) { + case SHA1: + plat_->mechanism_ = CKM_SHA_1_HMAC; + break; + case SHA256: + plat_->mechanism_ = CKM_SHA256_HMAC; + break; + default: + NOTREACHED() << "Unsupported hash algorithm"; + break; + } +} + +HMAC::~HMAC() { +} + +bool HMAC::Init(const unsigned char *key, size_t key_length) { + EnsureNSSInit(); + + if (plat_->slot_.get()) { + // Init must not be called more than twice on the same HMAC object. + NOTREACHED(); + return false; + } + + plat_->slot_.reset(PK11_GetInternalSlot()); + if (!plat_->slot_.get()) { + NOTREACHED(); + return false; + } + + SECItem key_item; + key_item.type = siBuffer; + key_item.data = const_cast(key); // NSS API isn't const. + key_item.len = key_length; + + plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(), + plat_->mechanism_, + PK11_OriginUnwrap, + CKA_SIGN, + &key_item, + NULL)); + if (!plat_->sym_key_.get()) { + NOTREACHED(); + return false; + } + + return true; +} + +bool HMAC::Sign(const base::StringPiece& data, + unsigned char* digest, + size_t digest_length) const { + if (!plat_->sym_key_.get()) { + // Init has not been called before Sign. + NOTREACHED(); + return false; + } + + SECItem param = { siBuffer, NULL, 0 }; + ScopedPK11Context context(PK11_CreateContextBySymKey(plat_->mechanism_, + CKA_SIGN, + plat_->sym_key_.get(), + ¶m)); + if (!context.get()) { + NOTREACHED(); + return false; + } + + if (PK11_DigestBegin(context.get()) != SECSuccess) { + NOTREACHED(); + return false; + } + + if (PK11_DigestOp(context.get(), + reinterpret_cast(data.data()), + data.length()) != SECSuccess) { + NOTREACHED(); + return false; + } + + unsigned int len = 0; + if (PK11_DigestFinal(context.get(), + digest, &len, digest_length) != SECSuccess) { + NOTREACHED(); + return false; + } + + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/hmac_openssl.cc b/chromium/crypto/hmac_openssl.cc new file mode 100644 index 00000000000..f7010c81bc6 --- /dev/null +++ b/chromium/crypto/hmac_openssl.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hmac.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +struct HMACPlatformData { + std::vector key; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char* key, size_t key_length) { + // Init must not be called more than once on the same HMAC object. + DCHECK(plat_->key.empty()); + + plat_->key.assign(key, key + key_length); + return true; +} + +HMAC::~HMAC() { + // Zero out key copy. + plat_->key.assign(plat_->key.size(), 0); + STLClearObject(&plat_->key); +} + +bool HMAC::Sign(const base::StringPiece& data, + unsigned char* digest, + size_t digest_length) const { + DCHECK(!plat_->key.empty()); // Init must be called before Sign. + + ScopedOpenSSLSafeSizeBuffer result(digest, digest_length); + return ::HMAC(hash_alg_ == SHA1 ? EVP_sha1() : EVP_sha256(), + &plat_->key[0], plat_->key.size(), + reinterpret_cast(data.data()), + data.size(), + result.safe_buffer(), NULL); +} + +} // namespace crypto diff --git a/chromium/crypto/hmac_unittest.cc b/chromium/crypto/hmac_unittest.cc new file mode 100644 index 00000000000..f0844a9186c --- /dev/null +++ b/chromium/crypto/hmac_unittest.cc @@ -0,0 +1,277 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "crypto/hmac.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const size_t kSHA1DigestSize = 20; +static const size_t kSHA256DigestSize = 32; + +static const char* kSimpleKey = + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"; +static const size_t kSimpleKeyLength = 80; + +static const struct { + const char *data; + const int data_len; + const char *digest; +} kSimpleHmacCases[] = { + { "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } +}; + +TEST(HMACTest, HmacSafeBrowsingResponseTest) { + const int kKeySize = 16; + + // Client key. + const unsigned char kClientKey[kKeySize] = + { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd, + 0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 }; + + // Expected HMAC result using kMessage and kClientKey. + const unsigned char kReceivedHmac[kSHA1DigestSize] = + { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52, + 0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad, + 0x86, 0xd2, 0x48, 0x85 }; + + const char kMessage[] = +"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s" +".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi" +"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh" +"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y" +"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing" +"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c" +"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go" +"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4" +"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf" +"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411" +"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb" +"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa" +"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s." +"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro" +"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925" +"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis" +"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co" +"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog" +"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2" +"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n"; + + std::string message_data(kMessage); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(kClientKey, kKeySize)); + unsigned char calculated_hmac[kSHA1DigestSize]; + + EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(kReceivedHmac, calculated_hmac, kSHA1DigestSize)); +} + +// Test cases from RFC 2202 section 3 +TEST(HMACTest, RFC2202TestCases) { + const struct { + const char *key; + const int key_len; + const char *data; + const int data_len; + const char *digest; + } cases[] = { + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B", 20, + "Hi There", 8, + "\xB6\x17\x31\x86\x55\x05\x72\x64\xE2\x8B\xC0\xB6\xFB\x37\x8C\x8E" + "\xF1\x46\xBE\x00" }, + { "Jefe", 4, + "what do ya want for nothing?", 28, + "\xEF\xFC\xDF\x6A\xE5\xEB\x2F\xA2\xD2\x74\x16\xD5\xF1\x84\xDF\x9C" + "\x25\x9A\x7C\x79" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA", 20, + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD", 50, + "\x12\x5D\x73\x42\xB9\xAC\x11\xCD\x91\xA3\x9A\xF4\x8A\xA1\x7B\x4F" + "\x63\xF1\x75\xD3" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25, + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD", 50, + "\x4C\x90\x07\xF4\x02\x62\x50\xC6\xBC\x84\x14\xF9\xBF\x50\xC8\x6C" + "\x2D\x72\x35\xDA" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C", 20, + "Test With Truncation", 20, + "\x4C\x1A\x03\x42\x4B\x55\xE0\x7F\xE7\xF2\x7B\xE1\xD5\x8B\xB9\x32" + "\x4A\x9A\x5A\x04" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(reinterpret_cast(cases[i].key), + cases[i].key_len)); + std::string data_string(cases[i].data, cases[i].data_len); + unsigned char digest[kSHA1DigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(cases[i].digest, digest, kSHA1DigestSize)); + } +} + +// TODO(wtc): add other test vectors from RFC 4231. +TEST(HMACTest, RFC4231TestCase6) { + unsigned char key[131]; + for (size_t i = 0; i < sizeof(key); ++i) + key[i] = 0xaa; + + std::string data = "Test Using Larger Than Block-Size Key - Hash Key First"; + ASSERT_EQ(54U, data.size()); + + static unsigned char kKnownHMACSHA256[] = { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }; + + crypto::HMAC hmac(crypto::HMAC::SHA256); + ASSERT_TRUE(hmac.Init(key, sizeof(key))); + unsigned char calculated_hmac[kSHA256DigestSize]; + + EXPECT_EQ(kSHA256DigestSize, hmac.DigestLength()); + EXPECT_TRUE(hmac.Sign(data, calculated_hmac, kSHA256DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac, kSHA256DigestSize)); +} + +// Based on NSS's FIPS HMAC power-up self-test. +TEST(HMACTest, NSSFIPSPowerUpSelfTest) { + static const char kKnownMessage[] = + "The test message for the MD2, MD5, and SHA-1 hashing algorithms."; + + static const unsigned char kKnownSecretKey[] = { + 0x46, 0x69, 0x72, 0x65, 0x66, 0x6f, 0x78, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x54, 0x68, 0x75, 0x6e, + 0x64, 0x65, 0x72, 0x42, 0x69, 0x72, 0x64, 0x20, + 0x61, 0x72, 0x65, 0x20, 0x61, 0x77, 0x65, 0x73, + 0x6f, 0x6d, 0x65, 0x21, 0x00 + }; + + static const size_t kKnownSecretKeySize = sizeof(kKnownSecretKey); + + // HMAC-SHA-1 known answer (20 bytes). + static const unsigned char kKnownHMACSHA1[] = { + 0xd5, 0x85, 0xf6, 0x5b, 0x39, 0xfa, 0xb9, 0x05, + 0x3b, 0x57, 0x1d, 0x61, 0xe7, 0xb8, 0x84, 0x1e, + 0x5d, 0x0e, 0x1e, 0x11 + }; + + // HMAC-SHA-256 known answer (32 bytes). + static const unsigned char kKnownHMACSHA256[] = { + 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44, + 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14, 0x22, 0xe0, + 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9, + 0xa7, 0x1b, 0x56, 0x7d, 0x1d, 0x29, 0x40, 0x48 + }; + + std::string message_data(kKnownMessage); + + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(kKnownSecretKey, kKnownSecretKeySize)); + unsigned char calculated_hmac[kSHA1DigestSize]; + + EXPECT_EQ(kSHA1DigestSize, hmac.DigestLength()); + EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA1, calculated_hmac, kSHA1DigestSize)); + EXPECT_TRUE(hmac.Verify( + message_data, + base::StringPiece(reinterpret_cast(kKnownHMACSHA1), + kSHA1DigestSize))); + EXPECT_TRUE(hmac.VerifyTruncated( + message_data, + base::StringPiece(reinterpret_cast(kKnownHMACSHA1), + kSHA1DigestSize / 2))); + + crypto::HMAC hmac2(crypto::HMAC::SHA256); + ASSERT_TRUE(hmac2.Init(kKnownSecretKey, kKnownSecretKeySize)); + unsigned char calculated_hmac2[kSHA256DigestSize]; + + EXPECT_TRUE(hmac2.Sign(message_data, calculated_hmac2, kSHA256DigestSize)); + EXPECT_EQ(0, memcmp(kKnownHMACSHA256, calculated_hmac2, kSHA256DigestSize)); +} + +TEST(HMACTest, HMACObjectReuse) { + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE( + hmac.Init(reinterpret_cast(kSimpleKey), + kSimpleKeyLength)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSimpleHmacCases); ++i) { + std::string data_string(kSimpleHmacCases[i].data, + kSimpleHmacCases[i].data_len); + unsigned char digest[kSHA1DigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kSHA1DigestSize)); + EXPECT_EQ(0, memcmp(kSimpleHmacCases[i].digest, digest, kSHA1DigestSize)); + } +} + +TEST(HMACTest, Verify) { + crypto::HMAC hmac(crypto::HMAC::SHA1); + ASSERT_TRUE( + hmac.Init(reinterpret_cast(kSimpleKey), + kSimpleKeyLength)); + const char empty_digest[kSHA1DigestSize] = { 0 }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSimpleHmacCases); ++i) { + // Expected results + EXPECT_TRUE(hmac.Verify( + base::StringPiece(kSimpleHmacCases[i].data, + kSimpleHmacCases[i].data_len), + base::StringPiece(kSimpleHmacCases[i].digest, + kSHA1DigestSize))); + // Mismatched size + EXPECT_FALSE(hmac.Verify( + base::StringPiece(kSimpleHmacCases[i].data, + kSimpleHmacCases[i].data_len), + base::StringPiece(kSimpleHmacCases[i].data, + kSimpleHmacCases[i].data_len))); + + // Expected size, mismatched data + EXPECT_FALSE(hmac.Verify( + base::StringPiece(kSimpleHmacCases[i].data, + kSimpleHmacCases[i].data_len), + base::StringPiece(empty_digest, kSHA1DigestSize))); + } +} diff --git a/chromium/crypto/hmac_win.cc b/chromium/crypto/hmac_win.cc new file mode 100644 index 00000000000..ba7d8275035 --- /dev/null +++ b/chromium/crypto/hmac_win.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/hmac.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "crypto/scoped_capi_types.h" +#include "crypto/third_party/nss/chromium-blapi.h" +#include "crypto/third_party/nss/chromium-sha256.h" + +namespace crypto { + +namespace { + +// Implementation of HMAC-SHA-256: +// +// SHA-256 is supported in Windows XP SP3 or later. We still need to support +// Windows XP SP2, so unfortunately we have to implement HMAC-SHA-256 here. + +enum { + SHA256_BLOCK_SIZE = 64 // Block size (in bytes) of the input to SHA-256. +}; + +// NSS doesn't accept size_t for text size, divide the data into smaller +// chunks as needed. +void Wrapped_SHA256_Update(SHA256Context* ctx, const unsigned char* text, + size_t text_len) { + const unsigned int kChunkSize = 1 << 30; + while (text_len > kChunkSize) { + SHA256_Update(ctx, text, kChunkSize); + text += kChunkSize; + text_len -= kChunkSize; + } + SHA256_Update(ctx, text, (unsigned int)text_len); +} + +// See FIPS 198: The Keyed-Hash Message Authentication Code (HMAC). +void ComputeHMACSHA256(const unsigned char* key, size_t key_len, + const unsigned char* text, size_t text_len, + unsigned char* output, size_t output_len) { + SHA256Context ctx; + + // Pre-process the key, if necessary. + unsigned char key0[SHA256_BLOCK_SIZE]; + if (key_len > SHA256_BLOCK_SIZE) { + SHA256_Begin(&ctx); + Wrapped_SHA256_Update(&ctx, key, key_len); + SHA256_End(&ctx, key0, NULL, SHA256_LENGTH); + memset(key0 + SHA256_LENGTH, 0, SHA256_BLOCK_SIZE - SHA256_LENGTH); + } else { + memcpy(key0, key, key_len); + if (key_len < SHA256_BLOCK_SIZE) + memset(key0 + key_len, 0, SHA256_BLOCK_SIZE - key_len); + } + + unsigned char padded_key[SHA256_BLOCK_SIZE]; + unsigned char inner_hash[SHA256_LENGTH]; + + // XOR key0 with ipad. + for (int i = 0; i < SHA256_BLOCK_SIZE; ++i) + padded_key[i] = key0[i] ^ 0x36; + + // Compute the inner hash. + SHA256_Begin(&ctx); + SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); + Wrapped_SHA256_Update(&ctx, text, text_len); + SHA256_End(&ctx, inner_hash, NULL, SHA256_LENGTH); + + // XOR key0 with opad. + for (int i = 0; i < SHA256_BLOCK_SIZE; ++i) + padded_key[i] = key0[i] ^ 0x5c; + + // Compute the outer hash. + SHA256_Begin(&ctx); + SHA256_Update(&ctx, padded_key, SHA256_BLOCK_SIZE); + SHA256_Update(&ctx, inner_hash, SHA256_LENGTH); + SHA256_End(&ctx, output, NULL, (unsigned int) output_len); +} + +} // namespace + +struct HMACPlatformData { + ~HMACPlatformData() { + if (!raw_key_.empty()) { + SecureZeroMemory(&raw_key_[0], raw_key_.size()); + } + + // Destroy the key before releasing the provider. + key_.reset(); + } + + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; + + // For HMAC-SHA-256 only. + std::vector raw_key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 and SHA-256 hash algorithms are supported now. + DCHECK(hash_alg_ == SHA1 || hash_alg_ == SHA256); +} + +bool HMAC::Init(const unsigned char* key, size_t key_length) { + if (plat_->provider_ || plat_->key_ || !plat_->raw_key_.empty()) { + // Init must not be called more than once on the same HMAC object. + NOTREACHED(); + return false; + } + + if (hash_alg_ == SHA256) { + plat_->raw_key_.assign(key, key + key_length); + return true; + } + + if (!CryptAcquireContext(plat_->provider_.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + NOTREACHED(); + return false; + } + + // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and + // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB + // allows the import of an unencrypted key. For Win2k support, a cubmbersome + // exponent-of-one key procedure must be used: + // http://support.microsoft.com/kb/228786/en-us + // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes. + + struct KeyBlob { + BLOBHEADER header; + DWORD key_size; + BYTE key_data[1]; + }; + size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length, + sizeof(KeyBlob)); + std::vector key_blob_storage = std::vector(key_blob_size); + KeyBlob* key_blob = reinterpret_cast(&key_blob_storage[0]); + key_blob->header.bType = PLAINTEXTKEYBLOB; + key_blob->header.bVersion = CUR_BLOB_VERSION; + key_blob->header.reserved = 0; + key_blob->header.aiKeyAlg = CALG_RC2; + key_blob->key_size = static_cast(key_length); + memcpy(key_blob->key_data, key, key_length); + + if (!CryptImportKey(plat_->provider_, &key_blob_storage[0], + (DWORD)key_blob_storage.size(), 0, + CRYPT_IPSEC_HMAC_KEY, plat_->key_.receive())) { + NOTREACHED(); + return false; + } + + // Destroy the copy of the key. + SecureZeroMemory(key_blob->key_data, key_length); + + return true; +} + +HMAC::~HMAC() { +} + +bool HMAC::Sign(const base::StringPiece& data, + unsigned char* digest, + size_t digest_length) const { + if (hash_alg_ == SHA256) { + if (plat_->raw_key_.empty()) + return false; + ComputeHMACSHA256(&plat_->raw_key_[0], plat_->raw_key_.size(), + reinterpret_cast(data.data()), + data.size(), digest, digest_length); + return true; + } + + if (!plat_->provider_ || !plat_->key_) + return false; + + if (hash_alg_ != SHA1) { + NOTREACHED(); + return false; + } + + ScopedHCRYPTHASH hash; + if (!CryptCreateHash(plat_->provider_, CALG_HMAC, plat_->key_, 0, + hash.receive())) + return false; + + HMAC_INFO hmac_info; + memset(&hmac_info, 0, sizeof(hmac_info)); + hmac_info.HashAlgid = CALG_SHA1; + if (!CryptSetHashParam(hash, HP_HMAC_INFO, + reinterpret_cast(&hmac_info), 0)) + return false; + + if (!CryptHashData(hash, reinterpret_cast(data.data()), + static_cast(data.size()), 0)) + return false; + + DWORD sha1_size = static_cast(digest_length); + return !!CryptGetHashParam(hash, HP_HASHVAL, digest, &sha1_size, 0); +} + +} // namespace crypto diff --git a/chromium/crypto/mac_security_services_lock.cc b/chromium/crypto/mac_security_services_lock.cc new file mode 100644 index 00000000000..c0b8712089e --- /dev/null +++ b/chromium/crypto/mac_security_services_lock.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/mac_security_services_lock.h" + +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" + +namespace { + +// This singleton pertains to Apple's wrappers over their own CSSM handles, +// as opposed to our own CSSM_CSP_HANDLE in cssm_init.cc. +class SecurityServicesSingleton { + public: + static SecurityServicesSingleton* GetInstance() { + return Singleton >::get(); + } + + base::Lock& lock() { return lock_; } + + private: + friend struct DefaultSingletonTraits; + + SecurityServicesSingleton() {} + ~SecurityServicesSingleton() {} + + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(SecurityServicesSingleton); +}; + +} // namespace + +namespace crypto { + +base::Lock& GetMacSecurityServicesLock() { + return SecurityServicesSingleton::GetInstance()->lock(); +} + +} // namespace crypto diff --git a/chromium/crypto/mac_security_services_lock.h b/chromium/crypto/mac_security_services_lock.h new file mode 100644 index 00000000000..fe56c6f38e2 --- /dev/null +++ b/chromium/crypto/mac_security_services_lock.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ +#define CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ + +#include "crypto/crypto_export.h" + +namespace base { +class Lock; +} + +namespace crypto { + +// The Mac OS X certificate and key management wrappers over CSSM are not +// thread-safe. In particular, code that accesses the CSSM database is +// problematic. +// +// http://developer.apple.com/mac/library/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html +CRYPTO_EXPORT base::Lock& GetMacSecurityServicesLock(); + +} // namespace crypto + +#endif // CRYPTO_MAC_SECURITY_SERVICES_LOCK_H_ diff --git a/chromium/crypto/mock_apple_keychain.cc b/chromium/crypto/mock_apple_keychain.cc new file mode 100644 index 00000000000..1ddfc867946 --- /dev/null +++ b/chromium/crypto/mock_apple_keychain.cc @@ -0,0 +1,61 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/time/time.h" +#include "crypto/mock_apple_keychain.h" + +namespace crypto { + +OSStatus MockAppleKeychain::FindGenericPassword( + CFTypeRef keychainOrArray, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32* passwordLength, + void** passwordData, + SecKeychainItemRef* itemRef) const { + // When simulating |noErr|, return canned |passwordData| and + // |passwordLength|. Otherwise, just return given code. + if (find_generic_result_ == noErr) { + static const char kPassword[] = "my_password"; + DCHECK(passwordData); + // The function to free this data is mocked so the cast is fine. + *passwordData = const_cast(kPassword); + DCHECK(passwordLength); + *passwordLength = arraysize(kPassword); + password_data_count_++; + } + + return find_generic_result_; +} + +OSStatus MockAppleKeychain::ItemFreeContent(SecKeychainAttributeList* attrList, + void* data) const { + // No-op. + password_data_count_--; + return noErr; +} + +OSStatus MockAppleKeychain::AddGenericPassword( + SecKeychainRef keychain, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const { + called_add_generic_ = true; + + DCHECK_GT(passwordLength, 0U); + DCHECK(passwordData); + add_generic_password_ = + std::string(const_cast(static_cast(passwordData)), + passwordLength); + return noErr; +} + +} // namespace crypto diff --git a/chromium/crypto/mock_apple_keychain.h b/chromium/crypto/mock_apple_keychain.h new file mode 100644 index 00000000000..efbb438aa6a --- /dev/null +++ b/chromium/crypto/mock_apple_keychain.h @@ -0,0 +1,251 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_MOCK_KEYCHAIN_MAC_H_ +#define CRYPTO_MOCK_KEYCHAIN_MAC_H_ + +#include + +#include +#include +#include +#include + +#include "base/compiler_specific.h" +#include "crypto/apple_keychain.h" + +namespace crypto { + +// Mock Keychain wrapper for testing code that interacts with the OS X +// Keychain. Implemented by storing SecKeychainAttributeList and +// KeychainPasswordData values in separate mutable containers and +// mapping them to integer keys. +// +// Note that "const" is pretty much meaningless for this class; the const-ness +// of AppleKeychain doesn't apply to the actual keychain data, so all of the +// Mock data is mutable; don't assume that it won't change over the life of +// tests. +class CRYPTO_EXPORT MockAppleKeychain : public AppleKeychain { + public: + MockAppleKeychain(); + virtual ~MockAppleKeychain(); + + // AppleKeychain implementation. + virtual OSStatus FindGenericPassword( + CFTypeRef keychainOrArray, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32* passwordLength, + void** passwordData, + SecKeychainItemRef* itemRef) const OVERRIDE; + virtual OSStatus ItemFreeContent(SecKeychainAttributeList* attrList, + void* data) const OVERRIDE; + virtual OSStatus AddGenericPassword( + SecKeychainRef keychain, + UInt32 serviceNameLength, + const char* serviceName, + UInt32 accountNameLength, + const char* accountName, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const OVERRIDE; + +#if !defined(OS_IOS) + virtual OSStatus ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, + SecKeychainAttributeInfo* info, + SecItemClass* itemClass, + SecKeychainAttributeList** attrList, + UInt32* length, + void** outData) const OVERRIDE; + // Pass "fail_me" as the data to get errSecAuthFailed. + virtual OSStatus ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, + const SecKeychainAttributeList* attrList, + UInt32 length, + const void* data) const OVERRIDE; + virtual OSStatus ItemFreeAttributesAndData(SecKeychainAttributeList* attrList, + void* data) const OVERRIDE; + virtual OSStatus ItemDelete(SecKeychainItemRef itemRef) const OVERRIDE; + virtual OSStatus SearchCreateFromAttributes( + CFTypeRef keychainOrArray, + SecItemClass itemClass, + const SecKeychainAttributeList* attrList, + SecKeychainSearchRef* searchRef) const OVERRIDE; + virtual OSStatus SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef* itemRef) const OVERRIDE; + // Pass "some.domain.com" as the serverName to get errSecDuplicateItem. + virtual OSStatus AddInternetPassword( + SecKeychainRef keychain, + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, const char* path, + UInt16 port, SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const OVERRIDE; + virtual void Free(CFTypeRef ref) const OVERRIDE; + + // Return the counts of objects returned by Create/Copy functions but never + // Free'd as they should have been. + int UnfreedSearchCount() const; + int UnfreedKeychainItemCount() const; + int UnfreedAttributeDataCount() const; + + // Returns true if all items added with AddInternetPassword have a creator + // code set. + bool CreatorCodesSetForAddedItems() const; + + struct KeychainTestData { + const SecAuthenticationType auth_type; + const char* server; + const SecProtocolType protocol; + const char* path; + const UInt32 port; + const char* security_domain; + const char* creation_date; + const char* username; + const char* password; + const bool negative_item; + }; + // Adds a keychain item with the given info to the test set. + void AddTestItem(const KeychainTestData& item_data); +#endif // !defined(OS_IOS) + + // |FindGenericPassword()| can return different results depending on user + // interaction with the system Keychain. For mocking purposes we allow the + // user of this class to specify the result code of the + // |FindGenericPassword()| call so we can simulate the result of different + // user interactions. + void set_find_generic_result(OSStatus result) { + find_generic_result_ = result; + } + + // Returns the true if |AddGenericPassword()| was called. + bool called_add_generic() const { return called_add_generic_; } + + // Returns the value of the password set when |AddGenericPassword()| was + // called. + std::string add_generic_password() const { return add_generic_password_; } + + // Returns the number of allocations - deallocations for password data. + int password_data_count() const { return password_data_count_; } + + private: + + // Type used for the keys in the std::map(s) and MockAppleKeychain items. + typedef uintptr_t MockKeychainItemType; + + // Type of the map holding the mock keychain attributes. + typedef std::map + MockKeychainAttributesMap; + +#if !defined(OS_IOS) + // Returns true if the keychain already contains a password that matches the + // attributes provided. + bool AlreadyContainsInternetPassword( + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, + const char* path, + UInt16 port, + SecProtocolType protocol, + SecAuthenticationType authenticationType) const; + // Initializes storage for keychain data at |key|. + void InitializeKeychainData(MockKeychainItemType key) const; + // Sets the data and length of |tag| in the item-th test item. + void SetTestDataBytes( + MockKeychainItemType item, + UInt32 tag, + const void* data, + size_t length); + // Sets the data and length of |tag| in the item-th test item based on + // |value|. The null-terminator will not be included; the Keychain Services + // docs don't indicate whether it is or not, so clients should not assume + // that it will be. + void SetTestDataString(MockKeychainItemType item, + UInt32 tag, + const char* value); + // Sets the data of the corresponding attribute of the item-th test item to + // |value|. Assumes that the space has alread been allocated, and the length + // set. + void SetTestDataPort(MockKeychainItemType item, UInt32 value); + void SetTestDataProtocol(MockKeychainItemType item, SecProtocolType value); + void SetTestDataAuthType(MockKeychainItemType item, + SecAuthenticationType value); + void SetTestDataNegativeItem(MockKeychainItemType item, Boolean value); + void SetTestDataCreator(MockKeychainItemType item, OSType value); + // Sets the password data and length for the item-th test item. + void SetTestDataPasswordBytes(MockKeychainItemType item, + const void* data, + size_t length); + // Sets the password for the item-th test item. As with SetTestDataString, + // the data will not be null-terminated. + void SetTestDataPasswordString(MockKeychainItemType item, const char* value); + + // Returns the address of the attribute in attribute_list with tag |tag|. + static SecKeychainAttribute* AttributeWithTag( + const SecKeychainAttributeList& attribute_list, + UInt32 tag); + + static const SecKeychainSearchRef kDummySearchRef; + + typedef struct KeychainPasswordData { + KeychainPasswordData() : data(NULL), length(0) {} + void* data; + UInt32 length; + } KeychainPasswordData; + + // Mutable because the MockAppleKeychain API requires its internal keychain + // storage to be modifiable by users of this class. + mutable MockKeychainAttributesMap keychain_attr_list_; + mutable std::map keychain_data_; + mutable MockKeychainItemType next_item_key_; + + // Tracks the items that should be returned in subsequent calls to + // SearchCopyNext, based on the last call to SearchCreateFromAttributes. + // We can't handle multiple active searches, since we don't track the search + // ref we return, but we don't need to for our mocking. + mutable std::vector remaining_search_results_; + + // Track copies and releases to make sure they balance. Really these should + // be maps to track per item, but this should be good enough to catch + // real mistakes. + mutable int search_copy_count_; + mutable int keychain_item_copy_count_; + mutable int attribute_data_copy_count_; + + // Tracks which items (by key) were added with AddInternetPassword. + mutable std::set added_via_api_; +#endif // !defined(OS_IOS) + + // Result code for the |FindGenericPassword()| method. + OSStatus find_generic_result_; + + // Records whether |AddGenericPassword()| gets called. + mutable bool called_add_generic_; + + // Tracks the allocations and frees of password data in |FindGenericPassword| + // and |ItemFreeContent|. + mutable int password_data_count_; + + // Records the password being set when |AddGenericPassword()| gets called. + mutable std::string add_generic_password_; +}; + +} // namespace crypto + +#endif // CRYPTO_MOCK_KEYCHAIN_MAC_H_ diff --git a/chromium/crypto/mock_apple_keychain_ios.cc b/chromium/crypto/mock_apple_keychain_ios.cc new file mode 100644 index 00000000000..9e8a1646685 --- /dev/null +++ b/chromium/crypto/mock_apple_keychain_ios.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/time/time.h" +#include "crypto/mock_apple_keychain.h" + +namespace crypto { + +MockAppleKeychain::MockAppleKeychain() + : find_generic_result_(noErr), + called_add_generic_(false), + password_data_count_(0) { +} + +MockAppleKeychain::~MockAppleKeychain() { +} + +} // namespace crypto diff --git a/chromium/crypto/mock_apple_keychain_mac.cc b/chromium/crypto/mock_apple_keychain_mac.cc new file mode 100644 index 00000000000..d586f70b363 --- /dev/null +++ b/chromium/crypto/mock_apple_keychain_mac.cc @@ -0,0 +1,509 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/time/time.h" +#include "crypto/mock_apple_keychain.h" + +namespace crypto { + +// static +const SecKeychainSearchRef MockAppleKeychain::kDummySearchRef = + reinterpret_cast(1000); + +MockAppleKeychain::MockAppleKeychain() + : next_item_key_(0), + search_copy_count_(0), + keychain_item_copy_count_(0), + attribute_data_copy_count_(0), + find_generic_result_(noErr), + called_add_generic_(false), + password_data_count_(0) {} + +void MockAppleKeychain::InitializeKeychainData(MockKeychainItemType key) const { + UInt32 tags[] = { kSecAccountItemAttr, + kSecServerItemAttr, + kSecPortItemAttr, + kSecPathItemAttr, + kSecProtocolItemAttr, + kSecAuthenticationTypeItemAttr, + kSecSecurityDomainItemAttr, + kSecCreationDateItemAttr, + kSecNegativeItemAttr, + kSecCreatorItemAttr }; + keychain_attr_list_[key] = SecKeychainAttributeList(); + keychain_data_[key] = KeychainPasswordData(); + keychain_attr_list_[key].count = arraysize(tags); + keychain_attr_list_[key].attr = static_cast( + calloc(keychain_attr_list_[key].count, sizeof(SecKeychainAttribute))); + for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) { + keychain_attr_list_[key].attr[i].tag = tags[i]; + size_t data_size = 0; + switch (tags[i]) { + case kSecPortItemAttr: + data_size = sizeof(UInt32); + break; + case kSecProtocolItemAttr: + data_size = sizeof(SecProtocolType); + break; + case kSecAuthenticationTypeItemAttr: + data_size = sizeof(SecAuthenticationType); + break; + case kSecNegativeItemAttr: + data_size = sizeof(Boolean); + break; + case kSecCreatorItemAttr: + data_size = sizeof(OSType); + break; + } + if (data_size > 0) { + keychain_attr_list_[key].attr[i].length = data_size; + keychain_attr_list_[key].attr[i].data = calloc(1, data_size); + } + } +} + +MockAppleKeychain::~MockAppleKeychain() { + for (MockKeychainAttributesMap::iterator it = keychain_attr_list_.begin(); + it != keychain_attr_list_.end(); + ++it) { + for (unsigned int i = 0; i < it->second.count; ++i) { + if (it->second.attr[i].data) + free(it->second.attr[i].data); + } + free(it->second.attr); + if (keychain_data_[it->first].data) + free(keychain_data_[it->first].data); + } + keychain_attr_list_.clear(); + keychain_data_.clear(); +} + +SecKeychainAttribute* MockAppleKeychain::AttributeWithTag( + const SecKeychainAttributeList& attribute_list, + UInt32 tag) { + int attribute_index = -1; + for (unsigned int i = 0; i < attribute_list.count; ++i) { + if (attribute_list.attr[i].tag == tag) { + attribute_index = i; + break; + } + } + if (attribute_index == -1) { + NOTREACHED() << "Unsupported attribute: " << tag; + return NULL; + } + return &(attribute_list.attr[attribute_index]); +} + +void MockAppleKeychain::SetTestDataBytes(MockKeychainItemType item, + UInt32 tag, + const void* data, + size_t length) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item], + tag); + attribute->length = length; + if (length > 0) { + if (attribute->data) + free(attribute->data); + attribute->data = malloc(length); + CHECK(attribute->data); + memcpy(attribute->data, data, length); + } else { + attribute->data = NULL; + } +} + +void MockAppleKeychain::SetTestDataString(MockKeychainItemType item, + UInt32 tag, + const char* value) { + SetTestDataBytes(item, tag, value, value ? strlen(value) : 0); +} + +void MockAppleKeychain::SetTestDataPort(MockKeychainItemType item, + UInt32 value) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item], + kSecPortItemAttr); + UInt32* data = static_cast(attribute->data); + *data = value; +} + +void MockAppleKeychain::SetTestDataProtocol(MockKeychainItemType item, + SecProtocolType value) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item], + kSecProtocolItemAttr); + SecProtocolType* data = static_cast(attribute->data); + *data = value; +} + +void MockAppleKeychain::SetTestDataAuthType(MockKeychainItemType item, + SecAuthenticationType value) { + SecKeychainAttribute* attribute = AttributeWithTag( + keychain_attr_list_[item], kSecAuthenticationTypeItemAttr); + SecAuthenticationType* data = static_cast( + attribute->data); + *data = value; +} + +void MockAppleKeychain::SetTestDataNegativeItem(MockKeychainItemType item, + Boolean value) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item], + kSecNegativeItemAttr); + Boolean* data = static_cast(attribute->data); + *data = value; +} + +void MockAppleKeychain::SetTestDataCreator(MockKeychainItemType item, + OSType value) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[item], + kSecCreatorItemAttr); + OSType* data = static_cast(attribute->data); + *data = value; +} + +void MockAppleKeychain::SetTestDataPasswordBytes(MockKeychainItemType item, + const void* data, + size_t length) { + keychain_data_[item].length = length; + if (length > 0) { + if (keychain_data_[item].data) + free(keychain_data_[item].data); + keychain_data_[item].data = malloc(length); + memcpy(keychain_data_[item].data, data, length); + } else { + keychain_data_[item].data = NULL; + } +} + +void MockAppleKeychain::SetTestDataPasswordString(MockKeychainItemType item, + const char* value) { + SetTestDataPasswordBytes(item, value, value ? strlen(value) : 0); +} + +OSStatus MockAppleKeychain::ItemCopyAttributesAndData( + SecKeychainItemRef itemRef, + SecKeychainAttributeInfo* info, + SecItemClass* itemClass, + SecKeychainAttributeList** attrList, + UInt32* length, + void** outData) const { + DCHECK(itemRef); + MockKeychainItemType key = + reinterpret_cast(itemRef) - 1; + if (keychain_attr_list_.find(key) == keychain_attr_list_.end()) + return errSecInvalidItemRef; + + DCHECK(!itemClass); // itemClass not implemented in the Mock. + if (attrList) + *attrList = &(keychain_attr_list_[key]); + if (outData) { + *outData = keychain_data_[key].data; + DCHECK(length); + *length = keychain_data_[key].length; + } + + ++attribute_data_copy_count_; + return noErr; +} + +OSStatus MockAppleKeychain::ItemModifyAttributesAndData( + SecKeychainItemRef itemRef, + const SecKeychainAttributeList* attrList, + UInt32 length, + const void* data) const { + DCHECK(itemRef); + const char* fail_trigger = "fail_me"; + if (length == strlen(fail_trigger) && + memcmp(data, fail_trigger, length) == 0) { + return errSecAuthFailed; + } + + MockKeychainItemType key = + reinterpret_cast(itemRef) - 1; + if (keychain_attr_list_.find(key) == keychain_attr_list_.end()) + return errSecInvalidItemRef; + + MockAppleKeychain* mutable_this = const_cast(this); + if (attrList) { + for (UInt32 change_attr = 0; change_attr < attrList->count; ++change_attr) { + if (attrList->attr[change_attr].tag == kSecCreatorItemAttr) { + void* data = attrList->attr[change_attr].data; + mutable_this->SetTestDataCreator(key, *(static_cast(data))); + } else { + NOTIMPLEMENTED(); + } + } + } + if (data) + mutable_this->SetTestDataPasswordBytes(key, data, length); + return noErr; +} + +OSStatus MockAppleKeychain::ItemFreeAttributesAndData( + SecKeychainAttributeList* attrList, + void* data) const { + --attribute_data_copy_count_; + return noErr; +} + +OSStatus MockAppleKeychain::ItemDelete(SecKeychainItemRef itemRef) const { + MockKeychainItemType key = + reinterpret_cast(itemRef) - 1; + + for (unsigned int i = 0; i < keychain_attr_list_[key].count; ++i) { + if (keychain_attr_list_[key].attr[i].data) + free(keychain_attr_list_[key].attr[i].data); + } + free(keychain_attr_list_[key].attr); + if (keychain_data_[key].data) + free(keychain_data_[key].data); + + keychain_attr_list_.erase(key); + keychain_data_.erase(key); + added_via_api_.erase(key); + return noErr; +} + +OSStatus MockAppleKeychain::SearchCreateFromAttributes( + CFTypeRef keychainOrArray, + SecItemClass itemClass, + const SecKeychainAttributeList* attrList, + SecKeychainSearchRef* searchRef) const { + // Figure out which of our mock items matches, and set up the array we'll use + // to generate results out of SearchCopyNext. + remaining_search_results_.clear(); + for (MockKeychainAttributesMap::const_iterator it = + keychain_attr_list_.begin(); + it != keychain_attr_list_.end(); + ++it) { + bool mock_item_matches = true; + for (UInt32 search_attr = 0; search_attr < attrList->count; ++search_attr) { + SecKeychainAttribute* mock_attribute = + AttributeWithTag(it->second, attrList->attr[search_attr].tag); + if (mock_attribute->length != attrList->attr[search_attr].length || + memcmp(mock_attribute->data, attrList->attr[search_attr].data, + attrList->attr[search_attr].length) != 0) { + mock_item_matches = false; + break; + } + } + if (mock_item_matches) + remaining_search_results_.push_back(it->first); + } + + DCHECK(searchRef); + *searchRef = kDummySearchRef; + ++search_copy_count_; + return noErr; +} + +bool MockAppleKeychain::AlreadyContainsInternetPassword( + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, + const char* path, + UInt16 port, + SecProtocolType protocol, + SecAuthenticationType authenticationType) const { + for (MockKeychainAttributesMap::const_iterator it = + keychain_attr_list_.begin(); + it != keychain_attr_list_.end(); + ++it) { + SecKeychainAttribute* attribute; + attribute = AttributeWithTag(it->second, kSecServerItemAttr); + if ((attribute->length != serverNameLength) || + (attribute->data == NULL && *serverName != '\0') || + (attribute->data != NULL && *serverName == '\0') || + strncmp(serverName, + (const char*) attribute->data, + serverNameLength) != 0) { + continue; + } + attribute = AttributeWithTag(it->second, kSecSecurityDomainItemAttr); + if ((attribute->length != securityDomainLength) || + (attribute->data == NULL && *securityDomain != '\0') || + (attribute->data != NULL && *securityDomain == '\0') || + strncmp(securityDomain, + (const char*) attribute->data, + securityDomainLength) != 0) { + continue; + } + attribute = AttributeWithTag(it->second, kSecAccountItemAttr); + if ((attribute->length != accountNameLength) || + (attribute->data == NULL && *accountName != '\0') || + (attribute->data != NULL && *accountName == '\0') || + strncmp(accountName, + (const char*) attribute->data, + accountNameLength) != 0) { + continue; + } + attribute = AttributeWithTag(it->second, kSecPathItemAttr); + if ((attribute->length != pathLength) || + (attribute->data == NULL && *path != '\0') || + (attribute->data != NULL && *path == '\0') || + strncmp(path, + (const char*) attribute->data, + pathLength) != 0) { + continue; + } + attribute = AttributeWithTag(it->second, kSecPortItemAttr); + if ((attribute->data == NULL) || + (port != *(static_cast(attribute->data)))) { + continue; + } + attribute = AttributeWithTag(it->second, kSecProtocolItemAttr); + if ((attribute->data == NULL) || + (protocol != *(static_cast(attribute->data)))) { + continue; + } + attribute = AttributeWithTag(it->second, kSecAuthenticationTypeItemAttr); + if ((attribute->data == NULL) || + (authenticationType != + *(static_cast(attribute->data)))) { + continue; + } + // The keychain already has this item, since all fields other than the + // password match. + return true; + } + return false; +} + +OSStatus MockAppleKeychain::AddInternetPassword( + SecKeychainRef keychain, + UInt32 serverNameLength, + const char* serverName, + UInt32 securityDomainLength, + const char* securityDomain, + UInt32 accountNameLength, + const char* accountName, + UInt32 pathLength, + const char* path, + UInt16 port, + SecProtocolType protocol, + SecAuthenticationType authenticationType, + UInt32 passwordLength, + const void* passwordData, + SecKeychainItemRef* itemRef) const { + + // Check for the magic duplicate item trigger. + if (strcmp(serverName, "some.domain.com") == 0) + return errSecDuplicateItem; + + // If the account already exists in the keychain, we don't add it. + if (AlreadyContainsInternetPassword(serverNameLength, serverName, + securityDomainLength, securityDomain, + accountNameLength, accountName, + pathLength, path, + port, protocol, + authenticationType)) { + return errSecDuplicateItem; + } + + // Pick the next unused slot. + MockKeychainItemType key = next_item_key_++; + + // Initialize keychain data storage at the target location. + InitializeKeychainData(key); + + MockAppleKeychain* mutable_this = const_cast(this); + mutable_this->SetTestDataBytes(key, kSecServerItemAttr, serverName, + serverNameLength); + mutable_this->SetTestDataBytes(key, kSecSecurityDomainItemAttr, + securityDomain, securityDomainLength); + mutable_this->SetTestDataBytes(key, kSecAccountItemAttr, accountName, + accountNameLength); + mutable_this->SetTestDataBytes(key, kSecPathItemAttr, path, pathLength); + mutable_this->SetTestDataPort(key, port); + mutable_this->SetTestDataProtocol(key, protocol); + mutable_this->SetTestDataAuthType(key, authenticationType); + mutable_this->SetTestDataPasswordBytes(key, passwordData, + passwordLength); + base::Time::Exploded exploded_time; + base::Time::Now().UTCExplode(&exploded_time); + char time_string[128]; + snprintf(time_string, sizeof(time_string), "%04d%02d%02d%02d%02d%02dZ", + exploded_time.year, exploded_time.month, exploded_time.day_of_month, + exploded_time.hour, exploded_time.minute, exploded_time.second); + mutable_this->SetTestDataString(key, kSecCreationDateItemAttr, time_string); + + added_via_api_.insert(key); + + if (itemRef) { + *itemRef = reinterpret_cast(key + 1); + ++keychain_item_copy_count_; + } + return noErr; +} + +OSStatus MockAppleKeychain::SearchCopyNext(SecKeychainSearchRef searchRef, + SecKeychainItemRef* itemRef) const { + if (remaining_search_results_.empty()) + return errSecItemNotFound; + MockKeychainItemType key = remaining_search_results_.front(); + remaining_search_results_.erase(remaining_search_results_.begin()); + *itemRef = reinterpret_cast(key + 1); + ++keychain_item_copy_count_; + return noErr; +} + +void MockAppleKeychain::Free(CFTypeRef ref) const { + if (!ref) + return; + + if (ref == kDummySearchRef) { + --search_copy_count_; + } else { + --keychain_item_copy_count_; + } +} + +int MockAppleKeychain::UnfreedSearchCount() const { + return search_copy_count_; +} + +int MockAppleKeychain::UnfreedKeychainItemCount() const { + return keychain_item_copy_count_; +} + +int MockAppleKeychain::UnfreedAttributeDataCount() const { + return attribute_data_copy_count_; +} + +bool MockAppleKeychain::CreatorCodesSetForAddedItems() const { + for (std::set::const_iterator + i = added_via_api_.begin(); + i != added_via_api_.end(); + ++i) { + SecKeychainAttribute* attribute = AttributeWithTag(keychain_attr_list_[*i], + kSecCreatorItemAttr); + OSType* data = static_cast(attribute->data); + if (*data == 0) + return false; + } + return true; +} + +void MockAppleKeychain::AddTestItem(const KeychainTestData& item_data) { + MockKeychainItemType key = next_item_key_++; + + InitializeKeychainData(key); + SetTestDataAuthType(key, item_data.auth_type); + SetTestDataString(key, kSecServerItemAttr, item_data.server); + SetTestDataProtocol(key, item_data.protocol); + SetTestDataString(key, kSecPathItemAttr, item_data.path); + SetTestDataPort(key, item_data.port); + SetTestDataString(key, kSecSecurityDomainItemAttr, + item_data.security_domain); + SetTestDataString(key, kSecCreationDateItemAttr, item_data.creation_date); + SetTestDataString(key, kSecAccountItemAttr, item_data.username); + SetTestDataPasswordString(key, item_data.password); + SetTestDataNegativeItem(key, item_data.negative_item); +} + +} // namespace crypto diff --git a/chromium/crypto/nss_util.cc b/chromium/crypto/nss_util.cc new file mode 100644 index 00000000000..27541cad5dc --- /dev/null +++ b/chromium/crypto/nss_util.cc @@ -0,0 +1,775 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(OS_LINUX) +#include +#include +#elif defined(OS_OPENBSD) +#include +#include +#endif + +#include + +#include "base/debug/alias.h" +#include "base/environment.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/histogram.h" +#include "base/native_library.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_restrictions.h" +#include "build/build_config.h" + +// USE_NSS means we use NSS for everything crypto-related. If USE_NSS is not +// defined, such as on Mac and Windows, we use NSS for SSL only -- we don't +// use NSS for crypto or certificate verification, and we don't use the NSS +// certificate and key databases. +#if defined(USE_NSS) +#include "base/synchronization/lock.h" +#include "crypto/crypto_module_blocking_password_delegate.h" +#endif // defined(USE_NSS) + +namespace crypto { + +namespace { + +#if defined(OS_CHROMEOS) +const char kNSSDatabaseName[] = "Real NSS database"; + +// Constants for loading the Chrome OS TPM-backed PKCS #11 library. +const char kChapsModuleName[] = "Chaps"; +const char kChapsPath[] = "libchaps.so"; + +// Fake certificate authority database used for testing. +static const base::FilePath::CharType kReadOnlyCertDB[] = + FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb"); +#endif // defined(OS_CHROMEOS) + +std::string GetNSSErrorMessage() { + std::string result; + if (PR_GetErrorTextLength()) { + scoped_ptr error_text(new char[PR_GetErrorTextLength() + 1]); + PRInt32 copied = PR_GetErrorText(error_text.get()); + result = std::string(error_text.get(), copied); + } else { + result = base::StringPrintf("NSS error code: %d", PR_GetError()); + } + return result; +} + +#if defined(USE_NSS) +base::FilePath GetDefaultConfigDirectory() { + base::FilePath dir = file_util::GetHomeDir(); + if (dir.empty()) { + LOG(ERROR) << "Failed to get home directory."; + return dir; + } + dir = dir.AppendASCII(".pki").AppendASCII("nssdb"); + if (!file_util::CreateDirectory(dir)) { + LOG(ERROR) << "Failed to create " << dir.value() << " directory."; + dir.clear(); + } + return dir; +} + +// 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 +// by the local Google Accounts server mock we use when testing our login code. +// If this directory is not present, NSS_Init() will fail. It is up to the +// caller to failover to NSS_NoDB_Init() at that point. +base::FilePath GetInitialConfigDirectory() { +#if defined(OS_CHROMEOS) + return base::FilePath(kReadOnlyCertDB); +#else + return GetDefaultConfigDirectory(); +#endif // defined(OS_CHROMEOS) +} + +// This callback for NSS forwards all requests to a caller-specified +// CryptoModuleBlockingPasswordDelegate object. +char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { +#if defined(OS_CHROMEOS) + // If we get asked for a password for the TPM, then return the + // well known password we use, as long as the TPM slot has been + // initialized. + if (crypto::IsTPMTokenReady()) { + std::string token_name; + std::string user_pin; + crypto::GetTPMTokenInfo(&token_name, &user_pin); + if (PK11_GetTokenName(slot) == token_name) + return PORT_Strdup(user_pin.c_str()); + } +#endif + crypto::CryptoModuleBlockingPasswordDelegate* delegate = + reinterpret_cast(arg); + if (delegate) { + bool cancelled = false; + std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), + retry != PR_FALSE, + &cancelled); + if (cancelled) + return NULL; + char* result = PORT_Strdup(password.c_str()); + password.replace(0, password.size(), password.size(), 0); + return result; + } + DLOG(ERROR) << "PK11 password requested with NULL arg"; + return NULL; +} + +// NSS creates a local cache of the sqlite database if it detects that the +// filesystem the database is on is much slower than the local disk. The +// detection doesn't work with the latest versions of sqlite, such as 3.6.22 +// (NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=578561). So we set +// the NSS environment variable NSS_SDB_USE_CACHE to "yes" to override NSS's +// detection when database_dir is on NFS. See http://crbug.com/48585. +// +// TODO(wtc): port this function to other USE_NSS platforms. It is defined +// only for OS_LINUX and OS_OPENBSD simply because the statfs structure +// is OS-specific. +// +// Because this function sets an environment variable it must be run before we +// go multi-threaded. +void UseLocalCacheOfNSSDatabaseIfNFS(const base::FilePath& database_dir) { +#if defined(OS_LINUX) || defined(OS_OPENBSD) + struct statfs buf; + if (statfs(database_dir.value().c_str(), &buf) == 0) { +#if defined(OS_LINUX) + if (buf.f_type == NFS_SUPER_MAGIC) { +#elif defined(OS_OPENBSD) + if (strcmp(buf.f_fstypename, MOUNT_NFS) == 0) { +#endif + scoped_ptr env(base::Environment::Create()); + const char* use_cache_env_var = "NSS_SDB_USE_CACHE"; + if (!env->HasVar(use_cache_env_var)) + env->SetVar(use_cache_env_var, "yes"); + } + } +#endif // defined(OS_LINUX) || defined(OS_OPENBSD) +} + +PK11SlotInfo* FindSlotWithTokenName(const std::string& token_name) { + AutoSECMODListReadLock auto_lock; + SECMODModuleList* head = SECMOD_GetDefaultModuleList(); + for (SECMODModuleList* item = head; item != NULL; item = item->next) { + int slot_count = item->module->loaded ? item->module->slotCount : 0; + for (int i = 0; i < slot_count; i++) { + PK11SlotInfo* slot = item->module->slots[i]; + if (PK11_GetTokenName(slot) == token_name) + return PK11_ReferenceSlot(slot); + } + } + return NULL; +} + +#endif // defined(USE_NSS) + +// A singleton to initialize/deinitialize NSPR. +// Separate from the NSS singleton because we initialize NSPR on the UI thread. +// Now that we're leaking the singleton, we could merge back with the NSS +// singleton. +class NSPRInitSingleton { + private: + friend struct base::DefaultLazyInstanceTraits; + + NSPRInitSingleton() { + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + } + + // NOTE(willchan): We don't actually execute this code since we leak NSS to + // prevent non-joinable threads from using NSS after it's already been shut + // down. + ~NSPRInitSingleton() { + PL_ArenaFinish(); + PRStatus prstatus = PR_Cleanup(); + if (prstatus != PR_SUCCESS) + LOG(ERROR) << "PR_Cleanup failed; was NSPR initialized on wrong thread?"; + } +}; + +base::LazyInstance::Leaky + g_nspr_singleton = LAZY_INSTANCE_INITIALIZER; + +// This is a LazyInstance so that it will be deleted automatically when the +// unittest exits. NSSInitSingleton is a LeakySingleton, so it would not be +// deleted if it were a regular member. +base::LazyInstance g_test_nss_db_dir = + LAZY_INSTANCE_INITIALIZER; + +// Force a crash with error info on NSS_NoDB_Init failure. +void CrashOnNSSInitFailure() { + int nss_error = PR_GetError(); + int os_error = PR_GetOSError(); + base::debug::Alias(&nss_error); + base::debug::Alias(&os_error); + LOG(ERROR) << "Error initializing NSS without a persistent database: " + << GetNSSErrorMessage(); + LOG(FATAL) << "nss_error=" << nss_error << ", os_error=" << os_error; +} + +class NSSInitSingleton { + public: +#if defined(OS_CHROMEOS) + void OpenPersistentNSSDB() { + if (!chromeos_user_logged_in_) { + // GetDefaultConfigDirectory causes us to do blocking IO on UI thread. + // Temporarily allow it until we fix http://crbug.com/70119 + base::ThreadRestrictions::ScopedAllowIO allow_io; + chromeos_user_logged_in_ = true; + + // This creates another DB slot in NSS that is read/write, unlike + // the fake root CA cert DB and the "default" crypto key + // provider, which are still read-only (because we initialized + // NSS before we had a cryptohome mounted). + software_slot_ = OpenUserDB(GetDefaultConfigDirectory(), + kNSSDatabaseName); + } + } + + void EnableTPMTokenForNSS() { + tpm_token_enabled_for_nss_ = true; + } + + bool InitializeTPMToken(const std::string& token_name, + const std::string& user_pin) { + // If EnableTPMTokenForNSS hasn't been called, return false. + if (!tpm_token_enabled_for_nss_) + return false; + + // If everything is already initialized, then return true. + if (chaps_module_ && tpm_slot_) + return true; + + tpm_token_name_ = token_name; + tpm_user_pin_ = user_pin; + + // This tries to load the Chaps module so NSS can talk to the hardware + // TPM. + if (!chaps_module_) { + chaps_module_ = LoadModule( + kChapsModuleName, + kChapsPath, + // For more details on these parameters, see: + // https://developer.mozilla.org/en/PKCS11_Module_Specs + // slotFlags=[PublicCerts] -- Certificates and public keys can be + // read from this slot without requiring a call to C_Login. + // askpw=only -- Only authenticate to the token when necessary. + "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\""); + } + if (chaps_module_){ + // If this gets set, then we'll use the TPM for certs with + // private keys, otherwise we'll fall back to the software + // implementation. + tpm_slot_ = GetTPMSlot(); + + return tpm_slot_ != NULL; + } + return false; + } + + void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) { + if (!tpm_token_enabled_for_nss_) { + LOG(ERROR) << "GetTPMTokenInfo called before TPM Token is ready."; + return; + } + if (token_name) + *token_name = tpm_token_name_; + if (user_pin) + *user_pin = tpm_user_pin_; + } + + bool IsTPMTokenReady() { + return tpm_slot_ != NULL; + } + + PK11SlotInfo* GetTPMSlot() { + std::string token_name; + GetTPMTokenInfo(&token_name, NULL); + return FindSlotWithTokenName(token_name); + } +#endif // defined(OS_CHROMEOS) + + + bool OpenTestNSSDB() { + if (test_slot_) + return true; + if (!g_test_nss_db_dir.Get().CreateUniqueTempDir()) + return false; + test_slot_ = OpenUserDB(g_test_nss_db_dir.Get().path(), kTestTPMTokenName); + return !!test_slot_; + } + + void CloseTestNSSDB() { + if (!test_slot_) + return; + SECStatus status = SECMOD_CloseUserDB(test_slot_); + if (status != SECSuccess) + PLOG(ERROR) << "SECMOD_CloseUserDB failed: " << PORT_GetError(); + PK11_FreeSlot(test_slot_); + test_slot_ = NULL; + ignore_result(g_test_nss_db_dir.Get().Delete()); + } + + PK11SlotInfo* GetPublicNSSKeySlot() { + if (test_slot_) + return PK11_ReferenceSlot(test_slot_); + if (software_slot_) + return PK11_ReferenceSlot(software_slot_); + return PK11_GetInternalKeySlot(); + } + + PK11SlotInfo* GetPrivateNSSKeySlot() { + if (test_slot_) + return PK11_ReferenceSlot(test_slot_); + +#if defined(OS_CHROMEOS) + if (tpm_token_enabled_for_nss_) { + if (IsTPMTokenReady()) { + return PK11_ReferenceSlot(tpm_slot_); + } else { + // If we were supposed to get the hardware token, but were + // unable to, return NULL rather than fall back to sofware. + return NULL; + } + } +#endif + // If we weren't supposed to enable the TPM for NSS, then return + // the software slot. + if (software_slot_) + return PK11_ReferenceSlot(software_slot_); + return PK11_GetInternalKeySlot(); + } + +#if defined(USE_NSS) + base::Lock* write_lock() { + return &write_lock_; + } +#endif // defined(USE_NSS) + + // This method is used to force NSS to be initialized without a DB. + // Call this method before NSSInitSingleton() is constructed. + static void ForceNoDBInit() { + force_nodb_init_ = true; + } + + private: + friend struct base::DefaultLazyInstanceTraits; + + NSSInitSingleton() + : tpm_token_enabled_for_nss_(false), + chaps_module_(NULL), + software_slot_(NULL), + test_slot_(NULL), + tpm_slot_(NULL), + root_(NULL), + chromeos_user_logged_in_(false) { + base::TimeTicks start_time = base::TimeTicks::Now(); + EnsureNSPRInit(); + + // We *must* have NSS >= 3.14.3. + COMPILE_ASSERT( + (NSS_VMAJOR == 3 && NSS_VMINOR == 14 && NSS_VPATCH >= 3) || + (NSS_VMAJOR == 3 && NSS_VMINOR > 14) || + (NSS_VMAJOR > 3), + nss_version_check_failed); + // Also check the run-time NSS version. + // NSS_VersionCheck is a >= check, not strict equality. + if (!NSS_VersionCheck("3.14.3")) { + LOG(FATAL) << "NSS_VersionCheck(\"3.14.3\") failed. NSS >= 3.14.3 is " + "required. Please upgrade to the latest NSS, and if you " + "still get this error, contact your distribution " + "maintainer."; + } + + SECStatus status = SECFailure; + bool nodb_init = force_nodb_init_; + +#if !defined(USE_NSS) + // Use the system certificate store, so initialize NSS without database. + nodb_init = true; +#endif + + if (nodb_init) { + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + CrashOnNSSInitFailure(); + return; + } +#if defined(OS_IOS) + root_ = InitDefaultRootCerts(); +#endif // defined(OS_IOS) + } else { +#if defined(USE_NSS) + base::FilePath database_dir = GetInitialConfigDirectory(); + if (!database_dir.empty()) { + // This duplicates the work which should have been done in + // EarlySetupForNSSInit. However, this function is idempotent so + // there's no harm done. + UseLocalCacheOfNSSDatabaseIfNFS(database_dir); + + // Initialize with a persistent database (likely, ~/.pki/nssdb). + // Use "sql:" which can be shared by multiple processes safely. + std::string nss_config_dir = + base::StringPrintf("sql:%s", database_dir.value().c_str()); +#if defined(OS_CHROMEOS) + status = NSS_Init(nss_config_dir.c_str()); +#else + status = NSS_InitReadWrite(nss_config_dir.c_str()); +#endif + if (status != SECSuccess) { + LOG(ERROR) << "Error initializing NSS with a persistent " + "database (" << nss_config_dir + << "): " << GetNSSErrorMessage(); + } + } + if (status != SECSuccess) { + VLOG(1) << "Initializing NSS without a persistent database."; + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + CrashOnNSSInitFailure(); + return; + } + } + + PK11_SetPasswordFunc(PKCS11PasswordFunc); + + // If we haven't initialized the password for the NSS databases, + // initialize an empty-string password so that we don't need to + // log in. + PK11SlotInfo* slot = PK11_GetInternalKeySlot(); + if (slot) { + // PK11_InitPin may write to the keyDB, but no other thread can use NSS + // yet, so we don't need to lock. + if (PK11_NeedUserInit(slot)) + PK11_InitPin(slot, NULL, NULL); + PK11_FreeSlot(slot); + } + + root_ = InitDefaultRootCerts(); +#endif // defined(USE_NSS) + } + + // Disable MD5 certificate signatures. (They are disabled by default in + // NSS 3.14.) + NSS_SetAlgorithmPolicy(SEC_OID_MD5, 0, NSS_USE_ALG_IN_CERT_SIGNATURE); + NSS_SetAlgorithmPolicy(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, + 0, NSS_USE_ALG_IN_CERT_SIGNATURE); + + // The UMA bit is conditionally set for this histogram in + // chrome/common/startup_metric_utils.cc . + HISTOGRAM_CUSTOM_TIMES("Startup.SlowStartupNSSInit", + base::TimeTicks::Now() - start_time, + base::TimeDelta::FromMilliseconds(10), + base::TimeDelta::FromHours(1), + 50); + } + + // NOTE(willchan): We don't actually execute this code since we leak NSS to + // prevent non-joinable threads from using NSS after it's already been shut + // down. + ~NSSInitSingleton() { + if (tpm_slot_) { + PK11_FreeSlot(tpm_slot_); + tpm_slot_ = NULL; + } + if (software_slot_) { + SECMOD_CloseUserDB(software_slot_); + PK11_FreeSlot(software_slot_); + software_slot_ = NULL; + } + CloseTestNSSDB(); + if (root_) { + SECMOD_UnloadUserModule(root_); + SECMOD_DestroyModule(root_); + root_ = NULL; + } + if (chaps_module_) { + SECMOD_UnloadUserModule(chaps_module_); + SECMOD_DestroyModule(chaps_module_); + chaps_module_ = NULL; + } + + SECStatus status = NSS_Shutdown(); + if (status != SECSuccess) { + // We VLOG(1) because this failure is relatively harmless (leaking, but + // we're shutting down anyway). + VLOG(1) << "NSS_Shutdown failed; see http://crbug.com/4609"; + } + } + +#if defined(USE_NSS) || defined(OS_IOS) + // Load nss's built-in root certs. + SECMODModule* InitDefaultRootCerts() { + SECMODModule* root = LoadModule("Root Certs", "libnssckbi.so", NULL); + if (root) + return root; + + // Aw, snap. Can't find/load root cert shared library. + // This will make it hard to talk to anybody via https. + NOTREACHED(); + return NULL; + } + + // Load the given module for this NSS session. + SECMODModule* LoadModule(const char* name, + const char* library_path, + const char* params) { + std::string modparams = base::StringPrintf( + "name=\"%s\" library=\"%s\" %s", + name, library_path, params ? params : ""); + + // Shouldn't need to const_cast here, but SECMOD doesn't properly + // declare input string arguments as const. Bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=642546 was filed + // on NSS codebase to address this. + SECMODModule* module = SECMOD_LoadUserModule( + const_cast(modparams.c_str()), NULL, PR_FALSE); + if (!module) { + LOG(ERROR) << "Error loading " << name << " module into NSS: " + << GetNSSErrorMessage(); + return NULL; + } + return module; + } +#endif + + static PK11SlotInfo* OpenUserDB(const base::FilePath& path, + const char* description) { + const std::string modspec = + base::StringPrintf("configDir='sql:%s' tokenDescription='%s'", + path.value().c_str(), description); + PK11SlotInfo* db_slot = SECMOD_OpenUserDB(modspec.c_str()); + if (db_slot) { + if (PK11_NeedUserInit(db_slot)) + PK11_InitPin(db_slot, NULL, NULL); + } + else { + LOG(ERROR) << "Error opening persistent database (" << modspec + << "): " << GetNSSErrorMessage(); + } + return db_slot; + } + + // If this is set to true NSS is forced to be initialized without a DB. + static bool force_nodb_init_; + + bool tpm_token_enabled_for_nss_; + std::string tpm_token_name_; + std::string tpm_user_pin_; + SECMODModule* chaps_module_; + PK11SlotInfo* software_slot_; + PK11SlotInfo* test_slot_; + PK11SlotInfo* tpm_slot_; + SECMODModule* root_; + bool chromeos_user_logged_in_; +#if defined(USE_NSS) + // TODO(davidben): When https://bugzilla.mozilla.org/show_bug.cgi?id=564011 + // is fixed, we will no longer need the lock. + base::Lock write_lock_; +#endif // defined(USE_NSS) +}; + +// static +bool NSSInitSingleton::force_nodb_init_ = false; + +base::LazyInstance::Leaky + g_nss_singleton = LAZY_INSTANCE_INITIALIZER; +} // namespace + +const char kTestTPMTokenName[] = "Test DB"; + +#if defined(USE_NSS) +void EarlySetupForNSSInit() { + base::FilePath database_dir = GetInitialConfigDirectory(); + if (!database_dir.empty()) + UseLocalCacheOfNSSDatabaseIfNFS(database_dir); +} +#endif + +void EnsureNSPRInit() { + g_nspr_singleton.Get(); +} + +void InitNSSSafely() { + // We might fork, but we haven't loaded any security modules. + DisableNSSForkCheck(); + // If we're sandboxed, we shouldn't be able to open user security modules, + // but it's more correct to tell NSS to not even try. + // Loading user security modules would have security implications. + ForceNSSNoDBInit(); + // Initialize NSS. + EnsureNSSInit(); +} + +void EnsureNSSInit() { + // Initializing SSL causes us to do blocking IO. + // Temporarily allow it until we fix + // http://code.google.com/p/chromium/issues/detail?id=59847 + base::ThreadRestrictions::ScopedAllowIO allow_io; + g_nss_singleton.Get(); +} + +void ForceNSSNoDBInit() { + NSSInitSingleton::ForceNoDBInit(); +} + +void DisableNSSForkCheck() { + scoped_ptr env(base::Environment::Create()); + env->SetVar("NSS_STRICT_NOFORK", "DISABLED"); +} + +void LoadNSSLibraries() { + // Some NSS libraries are linked dynamically so load them here. +#if defined(USE_NSS) + // Try to search for multiple directories to load the libraries. + std::vector paths; + + // Use relative path to Search PATH for the library files. + paths.push_back(base::FilePath()); + + // For Debian derivatives NSS libraries are located here. + paths.push_back(base::FilePath("/usr/lib/nss")); + + // Ubuntu 11.10 (Oneiric) and Debian Wheezy place the libraries here. +#if defined(ARCH_CPU_X86_64) + paths.push_back(base::FilePath("/usr/lib/x86_64-linux-gnu/nss")); +#elif defined(ARCH_CPU_X86) + paths.push_back(base::FilePath("/usr/lib/i386-linux-gnu/nss")); +#elif defined(ARCH_CPU_ARMEL) + paths.push_back(base::FilePath("/usr/lib/arm-linux-gnueabi/nss")); +#elif defined(ARCH_CPU_MIPSEL) + paths.push_back(base::FilePath("/usr/lib/mipsel-linux-gnu/nss")); +#endif + + // A list of library files to load. + std::vector libs; + libs.push_back("libsoftokn3.so"); + libs.push_back("libfreebl3.so"); + + // For each combination of library file and path, check for existence and + // then load. + size_t loaded = 0; + for (size_t i = 0; i < libs.size(); ++i) { + for (size_t j = 0; j < paths.size(); ++j) { + base::FilePath path = paths[j].Append(libs[i]); + base::NativeLibrary lib = base::LoadNativeLibrary(path, NULL); + if (lib) { + ++loaded; + break; + } + } + } + + if (loaded == libs.size()) { + VLOG(3) << "NSS libraries loaded."; + } else { + LOG(ERROR) << "Failed to load NSS libraries."; + } +#endif +} + +bool CheckNSSVersion(const char* version) { + return !!NSS_VersionCheck(version); +} + +#if defined(USE_NSS) +ScopedTestNSSDB::ScopedTestNSSDB() + : is_open_(g_nss_singleton.Get().OpenTestNSSDB()) { +} + +ScopedTestNSSDB::~ScopedTestNSSDB() { + // Don't close when NSS is < 3.15.1, because it would require an additional + // sleep for 1 second after closing the database, due to + // http://bugzil.la/875601. + if (NSS_VersionCheck("3.15.1")) { + g_nss_singleton.Get().CloseTestNSSDB(); + } +} + +base::Lock* GetNSSWriteLock() { + return g_nss_singleton.Get().write_lock(); +} + +AutoNSSWriteLock::AutoNSSWriteLock() : lock_(GetNSSWriteLock()) { + // May be NULL if the lock is not needed in our version of NSS. + if (lock_) + lock_->Acquire(); +} + +AutoNSSWriteLock::~AutoNSSWriteLock() { + if (lock_) { + lock_->AssertAcquired(); + lock_->Release(); + } +} + +AutoSECMODListReadLock::AutoSECMODListReadLock() + : lock_(SECMOD_GetDefaultModuleListLock()) { + SECMOD_GetReadLock(lock_); + } + +AutoSECMODListReadLock::~AutoSECMODListReadLock() { + SECMOD_ReleaseReadLock(lock_); +} + +#endif // defined(USE_NSS) + +#if defined(OS_CHROMEOS) +void OpenPersistentNSSDB() { + g_nss_singleton.Get().OpenPersistentNSSDB(); +} + +void EnableTPMTokenForNSS() { + g_nss_singleton.Get().EnableTPMTokenForNSS(); +} + +void GetTPMTokenInfo(std::string* token_name, std::string* user_pin) { + g_nss_singleton.Get().GetTPMTokenInfo(token_name, user_pin); +} + +bool IsTPMTokenReady() { + return g_nss_singleton.Get().IsTPMTokenReady(); +} + +bool InitializeTPMToken(const std::string& token_name, + const std::string& user_pin) { + return g_nss_singleton.Get().InitializeTPMToken(token_name, user_pin); +} +#endif // defined(OS_CHROMEOS) + +base::Time PRTimeToBaseTime(PRTime prtime) { + return base::Time::FromInternalValue( + prtime + base::Time::UnixEpoch().ToInternalValue()); +} + +PRTime BaseTimeToPRTime(base::Time time) { + return time.ToInternalValue() - base::Time::UnixEpoch().ToInternalValue(); +} + +PK11SlotInfo* GetPublicNSSKeySlot() { + return g_nss_singleton.Get().GetPublicNSSKeySlot(); +} + +PK11SlotInfo* GetPrivateNSSKeySlot() { + return g_nss_singleton.Get().GetPrivateNSSKeySlot(); +} + +} // namespace crypto diff --git a/chromium/crypto/nss_util.h b/chromium/crypto/nss_util.h new file mode 100644 index 00000000000..1d7503d8131 --- /dev/null +++ b/chromium/crypto/nss_util.h @@ -0,0 +1,173 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_NSS_UTIL_H_ +#define CRYPTO_NSS_UTIL_H_ + +#include +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +namespace base { +class FilePath; +class Lock; +class Time; +} // namespace base + +// This file specifically doesn't depend on any NSS or NSPR headers because it +// is included by various (non-crypto) parts of chrome to call the +// initialization functions. +namespace crypto { + +// The TPMToken name used for the NSS slot opened by ScopedTestNSSDB. +CRYPTO_EXPORT extern const char kTestTPMTokenName[]; + +#if defined(USE_NSS) +// EarlySetupForNSSInit performs lightweight setup which must occur before the +// process goes multithreaded. This does not initialise NSS. For test, see +// EnsureNSSInit. +CRYPTO_EXPORT void EarlySetupForNSSInit(); +#endif + +// Initialize NRPR if it isn't already initialized. This function is +// thread-safe, and NSPR will only ever be initialized once. +CRYPTO_EXPORT void EnsureNSPRInit(); + +// Initialize NSS safely for strict sandboxing. This function tells NSS to not +// load user security modules, and makes sure NSS will have proper entropy in a +// restricted, sandboxed environment. +// +// As a defense in depth measure, this function should be called in a sandboxed +// environment. That way, in the event of a bug, NSS will still not be able to +// load security modules that could expose private data and keys. +// +// Make sure to get an LGTM from the Chrome Security Team if you use this. +CRYPTO_EXPORT void InitNSSSafely(); + +// Initialize NSS if it isn't already initialized. This must be called before +// any other NSS functions. This function is thread-safe, and NSS will only +// ever be initialized once. +CRYPTO_EXPORT void EnsureNSSInit(); + +// Call this before calling EnsureNSSInit() will force NSS to initialize +// without a persistent DB. This is used for the special case where access of +// persistent DB is prohibited. +// +// TODO(hclam): Isolate loading default root certs. +// +// NSS will be initialized without loading any user security modules, including +// the built-in root certificates module. User security modules need to be +// loaded manually after NSS initialization. +// +// If EnsureNSSInit() is called before then this function has no effect. +// +// Calling this method only has effect on Linux. +// +// WARNING: Use this with caution. +CRYPTO_EXPORT void ForceNSSNoDBInit(); + +// This method is used to disable checks in NSS when used in a forked process. +// NSS checks whether it is running a forked process to avoid problems when +// using user security modules in a forked process. However if we are sure +// there are no modules loaded before the process is forked then there is no +// harm disabling the check. +// +// This method must be called before EnsureNSSInit() to take effect. +// +// WARNING: Use this with caution. +CRYPTO_EXPORT void DisableNSSForkCheck(); + +// Load NSS library files. This function has no effect on Mac and Windows. +// This loads the necessary NSS library files so that NSS can be initialized +// after loading additional library files is disallowed, for example when the +// sandbox is active. +// +// Note that this does not load libnssckbi.so which contains the root +// certificates. +CRYPTO_EXPORT void LoadNSSLibraries(); + +// Check if the current NSS version is greater than or equals to |version|. +// A sample version string is "3.12.3". +bool CheckNSSVersion(const char* version); + +#if defined(OS_CHROMEOS) +// Open the r/w nssdb that's stored inside the user's encrypted home +// directory. This is the default slot returned by +// GetPublicNSSKeySlot(). +CRYPTO_EXPORT void OpenPersistentNSSDB(); + +// Indicates that NSS should load the Chaps library so that we +// can access the TPM through NSS. Once this is called, +// GetPrivateNSSKeySlot() will return the TPM slot if one was found. +CRYPTO_EXPORT void EnableTPMTokenForNSS(); + +// Get name and user PIN for the built-in TPM token on ChromeOS. +// Either one can safely be NULL. Should only be called after +// EnableTPMTokenForNSS has been called with a non-null delegate. +CRYPTO_EXPORT void GetTPMTokenInfo(std::string* token_name, + std::string* user_pin); + +// Returns true if the TPM is owned and PKCS#11 initialized with the +// user and security officer PINs, and has been enabled in NSS by +// calling EnableTPMForNSS, and Chaps has been successfully +// loaded into NSS. +CRYPTO_EXPORT bool IsTPMTokenReady(); + +// Initialize the TPM token. Does nothing if it is already initialized. +CRYPTO_EXPORT bool InitializeTPMToken(const std::string& token_name, + const std::string& user_pin); +#endif + +// Convert a NSS PRTime value into a base::Time object. +// We use a int64 instead of PRTime here to avoid depending on NSPR headers. +CRYPTO_EXPORT base::Time PRTimeToBaseTime(int64 prtime); + +// Convert a base::Time object into a PRTime value. +// We use a int64 instead of PRTime here to avoid depending on NSPR headers. +CRYPTO_EXPORT int64 BaseTimeToPRTime(base::Time time); + +#if defined(USE_NSS) +// Exposed for unittests only. +// TODO(mattm): When NSS 3.14 is the minimum version required, +// switch back to using a separate user DB for each test. +// Because of https://bugzilla.mozilla.org/show_bug.cgi?id=588269 , the +// opened user DB is not automatically closed. +class CRYPTO_EXPORT_PRIVATE ScopedTestNSSDB { + public: + ScopedTestNSSDB(); + ~ScopedTestNSSDB(); + + bool is_open() { return is_open_; } + + private: + bool is_open_; + DISALLOW_COPY_AND_ASSIGN(ScopedTestNSSDB); +}; + +// NSS has a bug which can cause a deadlock or stall in some cases when writing +// to the certDB and keyDB. It also has a bug which causes concurrent key pair +// generations to scribble over each other. To work around this, we synchronize +// writes to the NSS databases with a global lock. The lock is hidden beneath a +// function for easy disabling when the bug is fixed. Callers should allow for +// it to return NULL in the future. +// +// See https://bugzilla.mozilla.org/show_bug.cgi?id=564011 +base::Lock* GetNSSWriteLock(); + +// A helper class that acquires the NSS write Lock while the AutoNSSWriteLock +// is in scope. +class CRYPTO_EXPORT AutoNSSWriteLock { + public: + AutoNSSWriteLock(); + ~AutoNSSWriteLock(); + private: + base::Lock *lock_; + DISALLOW_COPY_AND_ASSIGN(AutoNSSWriteLock); +}; + +#endif // defined(USE_NSS) + +} // namespace crypto + +#endif // CRYPTO_NSS_UTIL_H_ diff --git a/chromium/crypto/nss_util_internal.h b/chromium/crypto/nss_util_internal.h new file mode 100644 index 00000000000..056ec28c165 --- /dev/null +++ b/chromium/crypto/nss_util_internal.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_NSS_UTIL_INTERNAL_H_ +#define CRYPTO_NSS_UTIL_INTERNAL_H_ + +#include + +#include "crypto/crypto_export.h" + +// These functions return a type defined in an NSS header, and so cannot be +// declared in nss_util.h. Hence, they are declared here. + +namespace crypto { + +// Returns a reference to the default NSS key slot for storing +// public-key data only (e.g. server certs). Caller must release +// returned reference with PK11_FreeSlot. +CRYPTO_EXPORT PK11SlotInfo* GetPublicNSSKeySlot(); + +// Returns a reference to the default slot for storing private-key and +// mixed private-key/public-key data. Returns a hardware (TPM) NSS +// key slot if on ChromeOS and EnableTPMForNSS() has been called +// successfully. Caller must release returned reference with +// PK11_FreeSlot. +CRYPTO_EXPORT PK11SlotInfo* GetPrivateNSSKeySlot(); + +// A helper class that acquires the SECMOD list read lock while the +// AutoSECMODListReadLock is in scope. +class AutoSECMODListReadLock { + public: + AutoSECMODListReadLock(); + ~AutoSECMODListReadLock(); + + private: + SECMODListLock* lock_; + DISALLOW_COPY_AND_ASSIGN(AutoSECMODListReadLock); +}; + +} // namespace crypto + +#endif // CRYPTO_NSS_UTIL_INTERNAL_H_ diff --git a/chromium/crypto/nss_util_unittest.cc b/chromium/crypto/nss_util_unittest.cc new file mode 100644 index 00000000000..b68788513c6 --- /dev/null +++ b/chromium/crypto/nss_util_unittest.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/nss_util.h" + +#include + +#include "base/time/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +TEST(NSSUtilTest, PRTimeConversion) { + EXPECT_EQ(base::Time::UnixEpoch(), PRTimeToBaseTime(0)); + EXPECT_EQ(0, BaseTimeToPRTime(base::Time::UnixEpoch())); + + PRExplodedTime prxtime; + prxtime.tm_params.tp_gmt_offset = 0; + prxtime.tm_params.tp_dst_offset = 0; + base::Time::Exploded exploded; + prxtime.tm_year = exploded.year = 2011; + exploded.month = 12; + prxtime.tm_month = 11; + prxtime.tm_wday = exploded.day_of_week = 0; // Should be unusued. + prxtime.tm_mday = exploded.day_of_month = 10; + prxtime.tm_hour = exploded.hour = 2; + prxtime.tm_min = exploded.minute = 52; + prxtime.tm_sec = exploded.second = 19; + exploded.millisecond = 342; + prxtime.tm_usec = 342000; + + PRTime pr_time = PR_ImplodeTime(&prxtime); + base::Time base_time = base::Time::FromUTCExploded(exploded); + + EXPECT_EQ(base_time, PRTimeToBaseTime(pr_time)); + EXPECT_EQ(pr_time, BaseTimeToPRTime(base_time)); +} + +} // namespace crypto diff --git a/chromium/crypto/openpgp_symmetric_encryption.cc b/chromium/crypto/openpgp_symmetric_encryption.cc new file mode 100644 index 00000000000..702952b0187 --- /dev/null +++ b/chromium/crypto/openpgp_symmetric_encryption.cc @@ -0,0 +1,795 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/openpgp_symmetric_encryption.h" + +#include + +#include +#include + +#include + +#include "base/logging.h" +#include "crypto/random.h" +#include "crypto/scoped_nss_types.h" +#include "crypto/nss_util.h" + +namespace crypto { + +namespace { + +// Reader wraps a StringPiece and provides methods to read several datatypes +// while advancing the StringPiece. +class Reader { + public: + Reader(base::StringPiece input) + : data_(input) { + } + + bool U8(uint8* out) { + if (data_.size() < 1) + return false; + *out = static_cast(data_[0]); + data_.remove_prefix(1); + return true; + } + + bool U32(uint32* out) { + if (data_.size() < 4) + return false; + *out = static_cast(data_[0]) << 24 | + static_cast(data_[1]) << 16 | + static_cast(data_[2]) << 8 | + static_cast(data_[3]); + data_.remove_prefix(4); + return true; + } + + // Prefix sets |*out| to the first |n| bytes of the StringPiece and advances + // the StringPiece by |n|. + bool Prefix(size_t n, base::StringPiece *out) { + if (data_.size() < n) + return false; + *out = base::StringPiece(data_.data(), n); + data_.remove_prefix(n); + return true; + } + + // Remainder returns the remainer of the StringPiece and advances it to the + // end. + base::StringPiece Remainder() { + base::StringPiece ret = data_; + data_ = base::StringPiece(); + return ret; + } + + typedef base::StringPiece Position; + + Position tell() const { + return data_; + } + + void Seek(Position p) { + data_ = p; + } + + bool Skip(size_t n) { + if (data_.size() < n) + return false; + data_.remove_prefix(n); + return true; + } + + bool empty() const { + return data_.empty(); + } + + size_t size() const { + return data_.size(); + } + + private: + base::StringPiece data_; +}; + +// SaltedIteratedS2K implements the salted and iterated string-to-key +// convertion. See RFC 4880, section 3.7.1.3. +void SaltedIteratedS2K(unsigned cipher_key_length, + HASH_HashType hash_function, + base::StringPiece passphrase, + base::StringPiece salt, + unsigned count, + uint8 *out_key) { + const std::string combined = salt.as_string() + passphrase.as_string(); + const size_t combined_len = combined.size(); + + unsigned done = 0; + uint8 zero[1] = {0}; + + HASHContext* hash_context = HASH_Create(hash_function); + + for (unsigned i = 0; done < cipher_key_length; i++) { + HASH_Begin(hash_context); + + for (unsigned j = 0; j < i; j++) + HASH_Update(hash_context, zero, sizeof(zero)); + + unsigned written = 0; + while (written < count) { + if (written + combined_len > count) { + unsigned todo = count - written; + HASH_Update(hash_context, + reinterpret_cast(combined.data()), + todo); + written = count; + } else { + HASH_Update(hash_context, + reinterpret_cast(combined.data()), + combined_len); + written += combined_len; + } + } + + unsigned num_hash_bytes; + uint8 digest[HASH_LENGTH_MAX]; + HASH_End(hash_context, digest, &num_hash_bytes, sizeof(digest)); + + unsigned todo = cipher_key_length - done; + if (todo > num_hash_bytes) + todo = num_hash_bytes; + memcpy(out_key + done, digest, todo); + done += todo; + } + + HASH_Destroy(hash_context); +} + +// CreateAESContext sets up |out_key| to be an AES context, with the given key, +// in ECB mode and with no IV. +bool CreateAESContext(const uint8* key, unsigned key_len, + ScopedPK11Context* out_decryption_context) { + ScopedPK11Slot slot(PK11_GetInternalSlot()); + if (!slot.get()) + return false; + SECItem key_item; + key_item.type = siBuffer; + key_item.data = const_cast(key); + key_item.len = key_len; + ScopedPK11SymKey pk11_key(PK11_ImportSymKey( + slot.get(), CKM_AES_ECB, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, + NULL)); + if (!pk11_key.get()) + return false; + ScopedSECItem iv_param(PK11_ParamFromIV(CKM_AES_ECB, NULL)); + out_decryption_context->reset( + PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_ENCRYPT, pk11_key.get(), + iv_param.get())); + return out_decryption_context->get() != NULL; +} + + +// These constants are the tag numbers for the various packet types that we +// use. +static const unsigned kSymmetricKeyEncryptedTag = 3; +static const unsigned kSymmetricallyEncryptedTag = 18; +static const unsigned kCompressedTag = 8; +static const unsigned kLiteralDataTag = 11; + +class Decrypter { + public: + ~Decrypter() { + for (std::vector::iterator + i = arena_.begin(); i != arena_.end(); i++) { + free(*i); + } + arena_.clear(); + } + + OpenPGPSymmetricEncrytion::Result Decrypt(base::StringPiece in, + base::StringPiece passphrase, + base::StringPiece *out_contents) { + Reader reader(in); + unsigned tag; + base::StringPiece contents; + ScopedPK11Context decryption_context; + + if (!ParsePacket(&reader, &tag, &contents)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + if (tag != kSymmetricKeyEncryptedTag) + return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED; + Reader inner(contents); + OpenPGPSymmetricEncrytion::Result result = + ParseSymmetricKeyEncrypted(&inner, passphrase, &decryption_context); + if (result != OpenPGPSymmetricEncrytion::OK) + return result; + + if (!ParsePacket(&reader, &tag, &contents)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + if (tag != kSymmetricallyEncryptedTag) + return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED; + if (!reader.empty()) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + inner = Reader(contents); + if (!ParseSymmetricallyEncrypted(&inner, &decryption_context, &contents)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + + reader = Reader(contents); + if (!ParsePacket(&reader, &tag, &contents)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + if (tag == kCompressedTag) + return OpenPGPSymmetricEncrytion::COMPRESSED; + if (tag != kLiteralDataTag) + return OpenPGPSymmetricEncrytion::NOT_SYMMETRICALLY_ENCRYPTED; + inner = Reader(contents); + if (!ParseLiteralData(&inner, out_contents)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + + return OpenPGPSymmetricEncrytion::OK; + } + + private: + // ParsePacket parses an OpenPGP packet from reader. See RFC 4880, section + // 4.2.2. + bool ParsePacket(Reader *reader, + unsigned *out_tag, + base::StringPiece *out_contents) { + uint8 header; + if (!reader->U8(&header)) + return false; + if ((header & 0x80) == 0) { + // Tag byte must have MSB set. + return false; + } + + if ((header & 0x40) == 0) { + // Old format packet. + *out_tag = (header & 0x3f) >> 2; + + uint8 length_type = header & 3; + if (length_type == 3) { + *out_contents = reader->Remainder(); + return true; + } + + const unsigned length_bytes = 1 << length_type; + size_t length = 0; + for (unsigned i = 0; i < length_bytes; i++) { + uint8 length_byte; + if (!reader->U8(&length_byte)) + return false; + length <<= 8; + length |= length_byte; + } + + return reader->Prefix(length, out_contents); + } + + // New format packet. + *out_tag = header & 0x3f; + size_t length; + bool is_partial; + if (!ParseLength(reader, &length, &is_partial)) + return false; + if (is_partial) + return ParseStreamContents(reader, length, out_contents); + return reader->Prefix(length, out_contents); + } + + // ParseStreamContents parses all the chunks of a partial length stream from + // reader. See http://tools.ietf.org/html/rfc4880#section-4.2.2.4 + bool ParseStreamContents(Reader *reader, + size_t length, + base::StringPiece *out_contents) { + const Reader::Position beginning_of_stream = reader->tell(); + const size_t first_chunk_length = length; + + // First we parse the stream to find its length. + if (!reader->Skip(length)) + return false; + + for (;;) { + size_t chunk_length; + bool is_partial; + + if (!ParseLength(reader, &chunk_length, &is_partial)) + return false; + if (length + chunk_length < length) + return false; + length += chunk_length; + if (!reader->Skip(chunk_length)) + return false; + if (!is_partial) + break; + } + + // Now we have the length of the whole stream in |length|. + char* buf = reinterpret_cast(malloc(length)); + arena_.push_back(buf); + size_t j = 0; + reader->Seek(beginning_of_stream); + + base::StringPiece first_chunk; + if (!reader->Prefix(first_chunk_length, &first_chunk)) + return false; + memcpy(buf + j, first_chunk.data(), first_chunk_length); + j += first_chunk_length; + + // Now we parse the stream again, this time copying into |buf| + for (;;) { + size_t chunk_length; + bool is_partial; + + if (!ParseLength(reader, &chunk_length, &is_partial)) + return false; + base::StringPiece chunk; + if (!reader->Prefix(chunk_length, &chunk)) + return false; + memcpy(buf + j, chunk.data(), chunk_length); + j += chunk_length; + if (!is_partial) + break; + } + + *out_contents = base::StringPiece(buf, length); + return true; + } + + // ParseLength parses an OpenPGP length from reader. See RFC 4880, section + // 4.2.2. + bool ParseLength(Reader *reader, size_t *out_length, bool *out_is_prefix) { + uint8 length_spec; + if (!reader->U8(&length_spec)) + return false; + + *out_is_prefix = false; + if (length_spec < 192) { + *out_length = length_spec; + return true; + } else if (length_spec < 224) { + uint8 next_byte; + if (!reader->U8(&next_byte)) + return false; + + *out_length = (length_spec - 192) << 8; + *out_length += next_byte; + return true; + } else if (length_spec < 255) { + *out_length = 1u << (length_spec & 0x1f); + *out_is_prefix = true; + return true; + } else { + uint32 length32; + if (!reader->U32(&length32)) + return false; + *out_length = length32; + return true; + } + } + + // ParseSymmetricKeyEncrypted parses a passphrase protected session key. See + // RFC 4880, section 5.3. + OpenPGPSymmetricEncrytion::Result ParseSymmetricKeyEncrypted( + Reader *reader, + base::StringPiece passphrase, + ScopedPK11Context *decryption_context) { + uint8 version, cipher, s2k_type, hash_func_id; + if (!reader->U8(&version) || version != 4) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + + if (!reader->U8(&cipher) || + !reader->U8(&s2k_type) || + !reader->U8(&hash_func_id)) { + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + } + + uint8 cipher_key_length = OpenPGPCipherIdToKeyLength(cipher); + if (cipher_key_length == 0) + return OpenPGPSymmetricEncrytion::UNKNOWN_CIPHER; + + HASH_HashType hash_function; + switch (hash_func_id) { + case 2: // SHA-1 + hash_function = HASH_AlgSHA1; + break; + case 8: // SHA-256 + hash_function = HASH_AlgSHA256; + break; + default: + return OpenPGPSymmetricEncrytion::UNKNOWN_HASH; + } + + // This chunk of code parses the S2K specifier. See RFC 4880, section 3.7.1. + base::StringPiece salt; + uint8 key[32]; + uint8 count_spec; + switch (s2k_type) { + case 1: + if (!reader->Prefix(8, &salt)) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + // Fall through. + case 0: + SaltedIteratedS2K(cipher_key_length, hash_function, passphrase, salt, + passphrase.size() + salt.size(), key); + break; + case 3: + if (!reader->Prefix(8, &salt) || + !reader->U8(&count_spec)) { + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + } + SaltedIteratedS2K( + cipher_key_length, hash_function, passphrase, salt, + static_cast( + 16 + (count_spec&15)) << ((count_spec >> 4) + 6), key); + break; + default: + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + } + + if (!CreateAESContext(key, cipher_key_length, decryption_context)) + return OpenPGPSymmetricEncrytion::INTERNAL_ERROR; + + if (reader->empty()) { + // The resulting key is used directly. + return OpenPGPSymmetricEncrytion::OK; + } + + // The S2K derived key encrypts another key that follows: + base::StringPiece encrypted_key = reader->Remainder(); + if (encrypted_key.size() < 1) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + + uint8* plaintext_key = reinterpret_cast( + malloc(encrypted_key.size())); + arena_.push_back(plaintext_key); + + CFBDecrypt(encrypted_key, decryption_context, plaintext_key); + + cipher_key_length = OpenPGPCipherIdToKeyLength(plaintext_key[0]); + if (cipher_key_length == 0) + return OpenPGPSymmetricEncrytion::UNKNOWN_CIPHER; + if (encrypted_key.size() != 1u + cipher_key_length) + return OpenPGPSymmetricEncrytion::PARSE_ERROR; + if (!CreateAESContext(plaintext_key + 1, cipher_key_length, + decryption_context)) { + return OpenPGPSymmetricEncrytion::INTERNAL_ERROR; + } + return OpenPGPSymmetricEncrytion::OK; + } + + // CFBDecrypt decrypts the cipher-feedback encrypted data in |in| to |out| + // using |decryption_context| and assumes an IV of all zeros. + void CFBDecrypt(base::StringPiece in, ScopedPK11Context* decryption_context, + uint8* out) { + // We need this for PK11_CipherOp to write to, but we never check it as we + // work in ECB mode, one block at a time. + int out_len; + + uint8 mask[AES_BLOCK_SIZE]; + memset(mask, 0, sizeof(mask)); + + unsigned used = AES_BLOCK_SIZE; + + for (size_t i = 0; i < in.size(); i++) { + if (used == AES_BLOCK_SIZE) { + PK11_CipherOp(decryption_context->get(), mask, &out_len, sizeof(mask), + mask, AES_BLOCK_SIZE); + used = 0; + } + + uint8 t = in[i]; + out[i] = t ^ mask[used]; + mask[used] = t; + used++; + } + } + + // OpenPGPCipherIdToKeyLength converts an OpenPGP cipher id (see RFC 4880, + // section 9.2) to the key length of that cipher. It returns 0 on error. + unsigned OpenPGPCipherIdToKeyLength(uint8 cipher) { + switch (cipher) { + case 7: // AES-128 + return 16; + case 8: // AES-192 + return 24; + case 9: // AES-256 + return 32; + default: + return 0; + } + } + + // ParseSymmetricallyEncrypted parses a Symmetrically Encrypted packet. See + // RFC 4880, sections 5.7 and 5.13. + bool ParseSymmetricallyEncrypted(Reader *reader, + ScopedPK11Context *decryption_context, + base::StringPiece *out_plaintext) { + // We need this for PK11_CipherOp to write to, but we never check it as we + // work in ECB mode, one block at a time. + int out_len; + + uint8 version; + if (!reader->U8(&version) || version != 1) + return false; + + base::StringPiece prefix_sp; + if (!reader->Prefix(AES_BLOCK_SIZE + 2, &prefix_sp)) + return false; + uint8 prefix[AES_BLOCK_SIZE + 2]; + memcpy(prefix, prefix_sp.data(), sizeof(prefix)); + + uint8 prefix_copy[AES_BLOCK_SIZE + 2]; + uint8 fre[AES_BLOCK_SIZE]; + + memset(prefix_copy, 0, AES_BLOCK_SIZE); + PK11_CipherOp(decryption_context->get(), fre, &out_len, sizeof(fre), + prefix_copy, AES_BLOCK_SIZE); + for (unsigned i = 0; i < AES_BLOCK_SIZE; i++) + prefix_copy[i] = fre[i] ^ prefix[i]; + PK11_CipherOp(decryption_context->get(), fre, &out_len, sizeof(fre), prefix, + AES_BLOCK_SIZE); + prefix_copy[AES_BLOCK_SIZE] = prefix[AES_BLOCK_SIZE] ^ fre[0]; + prefix_copy[AES_BLOCK_SIZE + 1] = prefix[AES_BLOCK_SIZE + 1] ^ fre[1]; + + if (prefix_copy[AES_BLOCK_SIZE - 2] != prefix_copy[AES_BLOCK_SIZE] || + prefix_copy[AES_BLOCK_SIZE - 1] != prefix_copy[AES_BLOCK_SIZE + 1]) { + return false; + } + + fre[0] = prefix[AES_BLOCK_SIZE]; + fre[1] = prefix[AES_BLOCK_SIZE + 1]; + + unsigned out_used = 2; + + const size_t plaintext_size = reader->size(); + if (plaintext_size < SHA1_LENGTH + 2) { + // Too small to contain an MDC trailer. + return false; + } + + uint8* plaintext = reinterpret_cast(malloc(plaintext_size)); + arena_.push_back(plaintext); + + for (size_t i = 0; i < plaintext_size; i++) { + uint8 b; + if (!reader->U8(&b)) + return false; + if (out_used == AES_BLOCK_SIZE) { + PK11_CipherOp(decryption_context->get(), fre, &out_len, sizeof(fre), + fre, AES_BLOCK_SIZE); + out_used = 0; + } + + plaintext[i] = b ^ fre[out_used]; + fre[out_used++] = b; + } + + // The plaintext should be followed by a Modification Detection Code + // packet. This packet is specified such that the header is always + // serialized as exactly these two bytes: + if (plaintext[plaintext_size - SHA1_LENGTH - 2] != 0xd3 || + plaintext[plaintext_size - SHA1_LENGTH - 1] != 0x14) { + return false; + } + + HASHContext* hash_context = HASH_Create(HASH_AlgSHA1); + HASH_Begin(hash_context); + HASH_Update(hash_context, prefix_copy, sizeof(prefix_copy)); + HASH_Update(hash_context, plaintext, plaintext_size - SHA1_LENGTH); + uint8 digest[SHA1_LENGTH]; + unsigned num_hash_bytes; + HASH_End(hash_context, digest, &num_hash_bytes, sizeof(digest)); + HASH_Destroy(hash_context); + + if (memcmp(digest, &plaintext[plaintext_size - SHA1_LENGTH], + SHA1_LENGTH) != 0) { + return false; + } + + *out_plaintext = base::StringPiece(reinterpret_cast(plaintext), + plaintext_size - SHA1_LENGTH); + return true; + } + + // ParseLiteralData parses a Literal Data packet. See RFC 4880, section 5.9. + bool ParseLiteralData(Reader *reader, base::StringPiece *out_data) { + uint8 is_binary, filename_len; + if (!reader->U8(&is_binary) || + !reader->U8(&filename_len) || + !reader->Skip(filename_len) || + !reader->Skip(sizeof(uint32) /* mtime */)) { + return false; + } + + *out_data = reader->Remainder(); + return true; + } + + // arena_ contains malloced pointers that are used as temporary space during + // the decryption. + std::vector arena_; +}; + +class Encrypter { + public: + // ByteString is used throughout in order to avoid signedness issues with a + // std::string. + typedef std::basic_string ByteString; + + static ByteString Encrypt(base::StringPiece plaintext, + base::StringPiece passphrase) { + ByteString key; + ByteString ske = SerializeSymmetricKeyEncrypted(passphrase, &key); + + ByteString literal_data = SerializeLiteralData(plaintext); + ByteString se = SerializeSymmetricallyEncrypted(literal_data, key); + return ske + se; + } + + private: + // MakePacket returns an OpenPGP packet tagged as type |tag|. It always uses + // new-format headers. See RFC 4880, section 4.2. + static ByteString MakePacket(unsigned tag, const ByteString& contents) { + ByteString header; + header.push_back(0x80 | 0x40 | tag); + + if (contents.size() < 192) { + header.push_back(contents.size()); + } else if (contents.size() < 8384) { + size_t length = contents.size(); + length -= 192; + header.push_back(192 + (length >> 8)); + header.push_back(length & 0xff); + } else { + size_t length = contents.size(); + header.push_back(255); + header.push_back(length >> 24); + header.push_back(length >> 16); + header.push_back(length >> 8); + header.push_back(length); + } + + return header + contents; + } + + // SerializeLiteralData returns a Literal Data packet containing |contents| + // as binary data with no filename nor mtime specified. See RFC 4880, section + // 5.9. + static ByteString SerializeLiteralData(base::StringPiece contents) { + ByteString literal_data; + literal_data.push_back(0x74); // text mode + literal_data.push_back(0x00); // no filename + literal_data.push_back(0x00); // zero mtime + literal_data.push_back(0x00); + literal_data.push_back(0x00); + literal_data.push_back(0x00); + literal_data += ByteString(reinterpret_cast(contents.data()), + contents.size()); + return MakePacket(kLiteralDataTag, literal_data); + } + + // SerializeSymmetricKeyEncrypted generates a random AES-128 key from + // |passphrase|, sets |out_key| to it and returns a Symmetric Key Encrypted + // packet. See RFC 4880, section 5.3. + static ByteString SerializeSymmetricKeyEncrypted(base::StringPiece passphrase, + ByteString *out_key) { + ByteString ske; + ske.push_back(4); // version 4 + ske.push_back(7); // AES-128 + ske.push_back(3); // iterated and salted S2K + ske.push_back(2); // SHA-1 + + uint64 salt64; + crypto::RandBytes(&salt64, sizeof(salt64)); + ByteString salt(sizeof(salt64), 0); + + // It's a random value, so endianness doesn't matter. + ske += ByteString(reinterpret_cast(&salt64), sizeof(salt64)); + ske.push_back(96); // iteration count of 65536 + + uint8 key[16]; + SaltedIteratedS2K( + sizeof(key), HASH_AlgSHA1, passphrase, + base::StringPiece(reinterpret_cast(&salt64), sizeof(salt64)), + 65536, key); + *out_key = ByteString(key, sizeof(key)); + return MakePacket(kSymmetricKeyEncryptedTag, ske); + } + + // SerializeSymmetricallyEncrypted encrypts |plaintext| with |key| and + // returns a Symmetrically Encrypted packet containing the ciphertext. See + // RFC 4880, section 5.7. + static ByteString SerializeSymmetricallyEncrypted(ByteString plaintext, + const ByteString& key) { + // We need this for PK11_CipherOp to write to, but we never check it as we + // work in ECB mode, one block at a time. + int out_len; + + ByteString packet; + packet.push_back(1); // version 1 + static const unsigned kBlockSize = 16; // AES block size + + uint8 prefix[kBlockSize + 2], fre[kBlockSize], iv[kBlockSize]; + crypto::RandBytes(iv, kBlockSize); + memset(fre, 0, sizeof(fre)); + + ScopedPK11Context aes_context; + CHECK(CreateAESContext(key.data(), key.size(), &aes_context)); + + PK11_CipherOp(aes_context.get(), fre, &out_len, sizeof(fre), fre, + AES_BLOCK_SIZE); + for (unsigned i = 0; i < 16; i++) + prefix[i] = iv[i] ^ fre[i]; + PK11_CipherOp(aes_context.get(), fre, &out_len, sizeof(fre), prefix, + AES_BLOCK_SIZE); + prefix[kBlockSize] = iv[kBlockSize - 2] ^ fre[0]; + prefix[kBlockSize + 1] = iv[kBlockSize - 1] ^ fre[1]; + + packet += ByteString(prefix, sizeof(prefix)); + + ByteString plaintext_copy = plaintext; + plaintext_copy.push_back(0xd3); // MDC packet + plaintext_copy.push_back(20); // packet length (20 bytes) + + HASHContext* hash_context = HASH_Create(HASH_AlgSHA1); + HASH_Begin(hash_context); + HASH_Update(hash_context, iv, sizeof(iv)); + HASH_Update(hash_context, iv + kBlockSize - 2, 2); + HASH_Update(hash_context, plaintext_copy.data(), plaintext_copy.size()); + uint8 digest[SHA1_LENGTH]; + unsigned num_hash_bytes; + HASH_End(hash_context, digest, &num_hash_bytes, sizeof(digest)); + HASH_Destroy(hash_context); + + plaintext_copy += ByteString(digest, sizeof(digest)); + + fre[0] = prefix[kBlockSize]; + fre[1] = prefix[kBlockSize+1]; + unsigned out_used = 2; + + for (size_t i = 0; i < plaintext_copy.size(); i++) { + if (out_used == kBlockSize) { + PK11_CipherOp(aes_context.get(), fre, &out_len, sizeof(fre), fre, + AES_BLOCK_SIZE); + out_used = 0; + } + + uint8 c = plaintext_copy[i] ^ fre[out_used]; + fre[out_used++] = c; + packet.push_back(c); + } + + return MakePacket(kSymmetricallyEncryptedTag, packet); + } +}; + +} // anonymous namespace + +// static +OpenPGPSymmetricEncrytion::Result OpenPGPSymmetricEncrytion::Decrypt( + base::StringPiece encrypted, + base::StringPiece passphrase, + std::string *out) { + EnsureNSSInit(); + + Decrypter decrypter; + base::StringPiece result; + Result reader = decrypter.Decrypt(encrypted, passphrase, &result); + if (reader == OK) + *out = result.as_string(); + return reader; +} + +// static +std::string OpenPGPSymmetricEncrytion::Encrypt( + base::StringPiece plaintext, + base::StringPiece passphrase) { + EnsureNSSInit(); + + Encrypter::ByteString b = + Encrypter::Encrypt(plaintext, passphrase); + return std::string(reinterpret_cast(b.data()), b.size()); +} + +} // namespace crypto diff --git a/chromium/crypto/openpgp_symmetric_encryption.h b/chromium/crypto/openpgp_symmetric_encryption.h new file mode 100644 index 00000000000..bcc81d68d47 --- /dev/null +++ b/chromium/crypto/openpgp_symmetric_encryption.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_OPENPGP_SYMMETRIC_ENCRYPTION_H_ +#define CRYPTO_OPENPGP_SYMMETRIC_ENCRYPTION_H_ + +#include + +#include "base/strings/string_piece.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// OpenPGPSymmetricEncrytion implements enough of RFC 4880 to read and write +// uncompressed, symmetrically encrypted data. You can create ciphertext +// compatable with this code from the command line with: +// gpg --compress-algo=NONE --cipher-algo=AES -c +// +// Likewise, the output of this can be decrypted on the command line with: +// gpg < input +class CRYPTO_EXPORT OpenPGPSymmetricEncrytion { + public: + enum Result { + OK, + UNKNOWN_CIPHER, // you forgot to pass --cipher-algo=AES to gpg + UNKNOWN_HASH, + NOT_SYMMETRICALLY_ENCRYPTED, // it's OpenPGP data, but not correct form + COMPRESSED, // you forgot to pass --compress-algo=NONE + PARSE_ERROR, // it's not OpenPGP data. + INTERNAL_ERROR, + }; + + static Result Decrypt(base::StringPiece encrypted, + base::StringPiece passphrase, + std::string *out); + + static std::string Encrypt(base::StringPiece plaintext, + base::StringPiece passphrase); +}; + +} // namespace crypto + +#endif // CRYPTO_OPENPGP_SYMMETRIC_ENCRYPTION_H_ + diff --git a/chromium/crypto/openpgp_symmetric_encryption_unittest.cc b/chromium/crypto/openpgp_symmetric_encryption_unittest.cc new file mode 100644 index 00000000000..61857195635 --- /dev/null +++ b/chromium/crypto/openpgp_symmetric_encryption_unittest.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/openpgp_symmetric_encryption.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +// These test vectors were created by hand using either GPG or the Go OpenPGP +// library. + +// AES-128, GPG +static const uint8 kTestMessage1[] = { + 0x8c, 0x0d, 0x04, 0x07, 0x03, 0x02, 0x69, 0x24, 0xaf, 0xbf, 0x0b, 0x31, 0x98, + 0x6d, 0x60, 0xd2, 0x3d, 0x01, 0xc4, 0x29, 0xab, 0xec, 0x1b, 0xdf, 0xfa, 0x90, + 0x86, 0x92, 0x94, 0xc7, 0xa5, 0xe7, 0xd8, 0x80, 0x0a, 0x55, 0x3e, 0xbd, 0x10, + 0xef, 0x40, 0xfe, 0xb7, 0x39, 0x83, 0x4e, 0x5e, 0x77, 0x9d, 0x57, 0x94, 0xb6, + 0xe4, 0x59, 0xa7, 0x32, 0x76, 0x22, 0x48, 0xed, 0x37, 0xe8, 0x6f, 0xf9, 0x80, + 0x04, 0xa1, 0xe4, 0xbf, 0x40, 0xa6, 0x9b, 0xd1, 0x3e, 0xba, 0xaa, 0x52, 0xd0, +}; + +// AES-256, GPG +static const uint8 kTestMessage2[] = { + 0x8c, 0x0d, 0x04, 0x09, 0x03, 0x02, 0xd6, 0xb0, 0x34, 0xa0, 0xb8, 0x6c, 0x15, + 0xb8, 0x60, 0xd2, 0x3d, 0x01, 0x97, 0xe4, 0x46, 0x56, 0xc5, 0xc9, 0xc7, 0x81, + 0xd1, 0x09, 0xf3, 0xa0, 0x5d, 0x3b, 0xa7, 0xe3, 0x68, 0xb4, 0x19, 0xd2, 0x76, + 0x83, 0x38, 0x13, 0x98, 0xb8, 0xaf, 0x54, 0x51, 0x52, 0xbb, 0xc5, 0x7d, 0x8a, + 0x70, 0x66, 0x40, 0x0b, 0xb5, 0x92, 0xc3, 0xd3, 0x51, 0x63, 0x5d, 0x99, 0x9b, + 0x96, 0x82, 0xe1, 0xfe, 0xac, 0xa8, 0xa5, 0x87, 0x8b, 0x3f, 0xd1, 0x90, 0x70, +}; + +// AES-128, Go +static const uint8 kTestMessage3[] = { + 0xc3, 0x1e, 0x04, 0x07, 0x03, 0x02, 0x74, 0x1e, 0x2d, 0x7d, 0x2e, 0xdf, 0x20, + 0xdb, 0x60, 0xb1, 0x22, 0xca, 0x39, 0x74, 0x2f, 0xe8, 0x2f, 0x09, 0xf8, 0xa4, + 0x13, 0x76, 0x14, 0x65, 0x6e, 0xb9, 0xd2, 0xe0, 0x01, 0xe4, 0x40, 0x83, 0x90, + 0x3c, 0x76, 0xac, 0x23, 0x3e, 0xd2, 0xb0, 0xb2, 0x1c, 0x9d, 0x24, 0xcb, 0x7b, + 0xe1, 0x0c, 0x6b, 0xe0, 0x86, 0xe0, 0x22, 0xe1, 0xf2, 0xcb, 0xe0, 0xf3, 0xe2, + 0xbf, 0xdd, 0x89, 0xda, 0xe0, 0x83, 0xe3, 0xd9, 0x34, 0xf9, 0xaa, 0x17, 0x96, + 0x6a, 0x28, 0xe0, 0xca, 0xe2, 0x78, 0xca, 0x0e, 0x7d, 0xe0, 0xeb, 0xe4, 0xca, + 0xe6, 0xf0, 0x90, 0x4f, 0x22, 0x91, 0xf9, 0xb2, 0xbb, 0x19, 0x0b, 0x45, 0xc0, + 0x16, 0x9e, 0xe2, 0xdb, 0x30, 0xad, 0xbb, 0xe1, 0xb8, 0x12, 0x00, +}; + +// AES-128, salted S2K, GPG +static const uint8 kTestMessage4[] = { + 0x8c, 0x0c, 0x04, 0x07, 0x01, 0x02, 0xf8, 0x42, 0x78, 0x07, 0x04, 0xaa, 0x54, + 0xcc, 0xd2, 0x3d, 0x01, 0xcf, 0xb7, 0x30, 0xe1, 0xed, 0xb2, 0x53, 0x6e, 0x4a, + 0xbc, 0x49, 0x27, 0x45, 0xde, 0x1d, 0x5b, 0xe2, 0x17, 0x43, 0x39, 0x79, 0xdc, + 0xa5, 0xb7, 0x1a, 0x1b, 0xb7, 0x29, 0x9c, 0xb5, 0x69, 0x2f, 0x42, 0xc5, 0xe5, + 0x0c, 0x78, 0x57, 0x16, 0xa6, 0x46, 0x22, 0x18, 0x0c, 0xa2, 0xb3, 0x8c, 0xee, + 0xa1, 0xde, 0x38, 0xf1, 0xca, 0x73, 0xd3, 0xd6, 0xa3, 0x61, 0x47, 0xe2, +}; + +TEST(OpenPGPSymmetricEncrytionTest, AES128GPG) { + base::StringPiece encrypted(reinterpret_cast(kTestMessage1), + sizeof(kTestMessage1)); + std::string out; + + OpenPGPSymmetricEncrytion::Result r = + OpenPGPSymmetricEncrytion::Decrypt(encrypted, "testing", &out); + EXPECT_EQ(OpenPGPSymmetricEncrytion::OK, r); + EXPECT_EQ("Hello world\n", out); +} + +TEST(OpenPGPSymmetricEncrytionTest, AES256GPG) { + base::StringPiece encrypted(reinterpret_cast(kTestMessage2), + sizeof(kTestMessage2)); + std::string out; + + OpenPGPSymmetricEncrytion::Result r = + OpenPGPSymmetricEncrytion::Decrypt(encrypted, "testing", &out); + EXPECT_EQ(OpenPGPSymmetricEncrytion::OK, r); + EXPECT_EQ("Hello world\n", out); +} + +TEST(OpenPGPSymmetricEncrytionTest, AES128Go) { + base::StringPiece encrypted(reinterpret_cast(kTestMessage3), + sizeof(kTestMessage3)); + std::string out; + + OpenPGPSymmetricEncrytion::Result r = + OpenPGPSymmetricEncrytion::Decrypt(encrypted, "testing", &out); + EXPECT_EQ(OpenPGPSymmetricEncrytion::OK, r); + EXPECT_EQ("Hello world\n", out); +} + +TEST(OpenPGPSymmetricEncrytionTest, SaltedS2K) { + base::StringPiece encrypted(reinterpret_cast(kTestMessage4), + sizeof(kTestMessage4)); + std::string out; + + OpenPGPSymmetricEncrytion::Result r = + OpenPGPSymmetricEncrytion::Decrypt(encrypted, "testing", &out); + EXPECT_EQ(OpenPGPSymmetricEncrytion::OK, r); + EXPECT_EQ("Hello world\n", out); +} + +TEST(OpenPGPSymmetricEncrytionTest, Encrypt) { + for (unsigned i = 0; i < 16; i++) { + std::string encrypted = + OpenPGPSymmetricEncrytion::Encrypt("Hello world\n", "testing"); + + std::string out; + OpenPGPSymmetricEncrytion::Result r = + OpenPGPSymmetricEncrytion::Decrypt(encrypted, "testing", &out); + EXPECT_EQ(OpenPGPSymmetricEncrytion::OK, r); + EXPECT_EQ("Hello world\n", out); + } +} + +} // namespace crypto diff --git a/chromium/crypto/openssl_util.cc b/chromium/crypto/openssl_util.cc new file mode 100644 index 00000000000..5bc3ce53cd3 --- /dev/null +++ b/chromium/crypto/openssl_util.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/openssl_util.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/memory/singleton.h" +#include "base/strings/string_piece.h" +#include "base/synchronization/lock.h" + +namespace crypto { + +namespace { + +unsigned long CurrentThreadId() { + return static_cast(base::PlatformThread::CurrentId()); +} + +// Singleton for initializing and cleaning up the OpenSSL library. +class OpenSSLInitSingleton { + public: + static OpenSSLInitSingleton* GetInstance() { + // We allow the SSL environment to leak for multiple reasons: + // - it is used from a non-joinable worker thread that is not stopped on + // shutdown, hence may still be using OpenSSL library after the AtExit + // runner has completed. + // - There are other OpenSSL related singletons (e.g. the client socket + // context) who's cleanup depends on the global environment here, but + // we can't control the order the AtExit handlers will run in so + // allowing the global environment to leak at least ensures it is + // available for those other singletons to reliably cleanup. + return Singleton >::get(); + } + private: + friend struct DefaultSingletonTraits; + OpenSSLInitSingleton() { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + int num_locks = CRYPTO_num_locks(); + locks_.reserve(num_locks); + for (int i = 0; i < num_locks; ++i) + locks_.push_back(new base::Lock()); + CRYPTO_set_locking_callback(LockingCallback); + CRYPTO_set_id_callback(CurrentThreadId); + } + + ~OpenSSLInitSingleton() { + CRYPTO_set_locking_callback(NULL); + EVP_cleanup(); + ERR_free_strings(); + } + + static void LockingCallback(int mode, int n, const char* file, int line) { + OpenSSLInitSingleton::GetInstance()->OnLockingCallback(mode, n, file, line); + } + + void OnLockingCallback(int mode, int n, const char* file, int line) { + CHECK_LT(static_cast(n), locks_.size()); + if (mode & CRYPTO_LOCK) + locks_[n]->Acquire(); + else + locks_[n]->Release(); + } + + // These locks are used and managed by OpenSSL via LockingCallback(). + ScopedVector locks_; + + DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton); +}; + +// Callback routine for OpenSSL to print error messages. |str| is a +// NULL-terminated string of length |len| containing diagnostic information +// such as the library, function and reason for the error, the file and line +// where the error originated, plus potentially any context-specific +// information about the error. |context| contains a pointer to user-supplied +// data, which is currently unused. +// If this callback returns a value <= 0, OpenSSL will stop processing the +// error queue and return, otherwise it will continue calling this function +// until all errors have been removed from the queue. +int OpenSSLErrorCallback(const char* str, size_t len, void* context) { + DVLOG(1) << "\t" << base::StringPiece(str, len); + return 1; +} + +} // namespace + +void EnsureOpenSSLInit() { + (void)OpenSSLInitSingleton::GetInstance(); +} + +void ClearOpenSSLERRStack(const tracked_objects::Location& location) { + if (logging::DEBUG_MODE && VLOG_IS_ON(1)) { + int error_num = ERR_peek_error(); + if (error_num == 0) + return; + + std::string message; + location.Write(true, true, &message); + DVLOG(1) << "OpenSSL ERR_get_error stack from " << message; + ERR_print_errors_cb(&OpenSSLErrorCallback, NULL); + } else { + ERR_clear_error(); + } +} + +} // namespace crypto diff --git a/chromium/crypto/openssl_util.h b/chromium/crypto/openssl_util.h new file mode 100644 index 00000000000..e8483c9633e --- /dev/null +++ b/chromium/crypto/openssl_util.h @@ -0,0 +1,119 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_OPENSSL_UTIL_H_ +#define CRYPTO_OPENSSL_UTIL_H_ + +#include "base/basictypes.h" +#include "base/location.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// A helper class that takes care of destroying OpenSSL objects when they go out +// of scope. +template +class ScopedOpenSSL { + public: + ScopedOpenSSL() : ptr_(NULL) { } + explicit ScopedOpenSSL(T* ptr) : ptr_(ptr) { } + ~ScopedOpenSSL() { + reset(NULL); + } + + T* get() const { return ptr_; } + T* release() { + T* ptr = ptr_; + ptr_ = NULL; + return ptr; + } + void reset(T* ptr) { + if (ptr != ptr_) { + if (ptr_) (*destructor)(ptr_); + ptr_ = ptr; + } + } + + private: + T* ptr_; + + DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSL); +}; + +// Provides a buffer of at least MIN_SIZE bytes, for use when calling OpenSSL's +// SHA256, HMAC, etc functions, adapting the buffer sizing rules to meet those +// of the our base wrapper APIs. +// This allows the library to write directly to the caller's buffer if it is of +// sufficient size, but if not it will write to temporary |min_sized_buffer_| +// of required size and then its content is automatically copied out on +// destruction, with truncation as appropriate. +template +class ScopedOpenSSLSafeSizeBuffer { + public: + ScopedOpenSSLSafeSizeBuffer(unsigned char* output, size_t output_len) + : output_(output), + output_len_(output_len) { + } + + ~ScopedOpenSSLSafeSizeBuffer() { + if (output_len_ < MIN_SIZE) { + // Copy the temporary buffer out, truncating as needed. + memcpy(output_, min_sized_buffer_, output_len_); + } + // else... any writing already happened directly into |output_|. + } + + unsigned char* safe_buffer() { + return output_len_ < MIN_SIZE ? min_sized_buffer_ : output_; + } + + private: + // Pointer to the caller's data area and its associated size, where data + // written via safe_buffer() will [eventually] end up. + unsigned char* output_; + size_t output_len_; + + // Temporary buffer writen into in the case where the caller's + // buffer is not of sufficient size. + unsigned char min_sized_buffer_[MIN_SIZE]; + + DISALLOW_COPY_AND_ASSIGN(ScopedOpenSSLSafeSizeBuffer); +}; + +// Initialize OpenSSL if it isn't already initialized. This must be called +// before any other OpenSSL functions. +// This function is thread-safe, and OpenSSL will only ever be initialized once. +// OpenSSL will be properly shut down on program exit. +void CRYPTO_EXPORT EnsureOpenSSLInit(); + +// Drains the OpenSSL ERR_get_error stack. On a debug build the error codes +// are send to VLOG(1), on a release build they are disregarded. In most +// cases you should pass FROM_HERE as the |location|. +void CRYPTO_EXPORT ClearOpenSSLERRStack( + const tracked_objects::Location& location); + +// Place an instance of this class on the call stack to automatically clear +// the OpenSSL error stack on function exit. +class OpenSSLErrStackTracer { + public: + // Pass FROM_HERE as |location|, to help track the source of OpenSSL error + // messages. Note any diagnostic emitted will be tagged with the location of + // the constructor call as it's not possible to trace a destructor's callsite. + explicit OpenSSLErrStackTracer(const tracked_objects::Location& location) + : location_(location) { + EnsureOpenSSLInit(); + } + ~OpenSSLErrStackTracer() { + ClearOpenSSLERRStack(location_); + } + + private: + const tracked_objects::Location location_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(OpenSSLErrStackTracer); +}; + +} // namespace crypto + +#endif // CRYPTO_OPENSSL_UTIL_H_ diff --git a/chromium/crypto/p224.cc b/chromium/crypto/p224.cc new file mode 100644 index 00000000000..ac0a081037f --- /dev/null +++ b/chromium/crypto/p224.cc @@ -0,0 +1,743 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is an implementation of the P224 elliptic curve group. It's written to +// be short and simple rather than fast, although it's still constant-time. +// +// See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background. + +#include "crypto/p224.h" + +#include + +#include "base/sys_byteorder.h" + +namespace { + +using base::HostToNet32; +using base::NetToHost32; + +// Field element functions. +// +// The field that we're dealing with is ℤ/pℤ where p = 2**224 - 2**96 + 1. +// +// Field elements are represented by a FieldElement, which is a typedef to an +// array of 8 uint32's. The value of a FieldElement, a, is: +// a[0] + 2**28·a[1] + 2**56·a[1] + ... + 2**196·a[7] +// +// Using 28-bit limbs means that there's only 4 bits of headroom, which is less +// than we would really like. But it has the useful feature that we hit 2**224 +// exactly, making the reflections during a reduce much nicer. + +using crypto::p224::FieldElement; + +// kP is the P224 prime. +const FieldElement kP = { + 1, 0, 0, 268431360, + 268435455, 268435455, 268435455, 268435455, +}; + +void Contract(FieldElement* inout); + +// IsZero returns 0xffffffff if a == 0 mod p and 0 otherwise. +uint32 IsZero(const FieldElement& a) { + FieldElement minimal; + memcpy(&minimal, &a, sizeof(minimal)); + Contract(&minimal); + + uint32 is_zero = 0, is_p = 0; + for (unsigned i = 0; i < 8; i++) { + is_zero |= minimal[i]; + is_p |= minimal[i] - kP[i]; + } + + // If either is_zero or is_p is 0, then we should return 1. + is_zero |= is_zero >> 16; + is_zero |= is_zero >> 8; + is_zero |= is_zero >> 4; + is_zero |= is_zero >> 2; + is_zero |= is_zero >> 1; + + is_p |= is_p >> 16; + is_p |= is_p >> 8; + is_p |= is_p >> 4; + is_p |= is_p >> 2; + is_p |= is_p >> 1; + + // For is_zero and is_p, the LSB is 0 iff all the bits are zero. + is_zero &= is_p & 1; + is_zero = (~is_zero) << 31; + is_zero = static_cast(is_zero) >> 31; + return is_zero; +} + +// Add computes *out = a+b +// +// a[i] + b[i] < 2**32 +void Add(FieldElement* out, const FieldElement& a, const FieldElement& b) { + for (int i = 0; i < 8; i++) { + (*out)[i] = a[i] + b[i]; + } +} + +static const uint32 kTwo31p3 = (1u<<31) + (1u<<3); +static const uint32 kTwo31m3 = (1u<<31) - (1u<<3); +static const uint32 kTwo31m15m3 = (1u<<31) - (1u<<15) - (1u<<3); +// kZero31ModP is 0 mod p where bit 31 is set in all limbs so that we can +// subtract smaller amounts without underflow. See the section "Subtraction" in +// [1] for why. +static const FieldElement kZero31ModP = { + kTwo31p3, kTwo31m3, kTwo31m3, kTwo31m15m3, + kTwo31m3, kTwo31m3, kTwo31m3, kTwo31m3 +}; + +// Subtract computes *out = a-b +// +// a[i], b[i] < 2**30 +// out[i] < 2**32 +void Subtract(FieldElement* out, const FieldElement& a, const FieldElement& b) { + for (int i = 0; i < 8; i++) { + // See the section on "Subtraction" in [1] for details. + (*out)[i] = a[i] + kZero31ModP[i] - b[i]; + } +} + +static const uint64 kTwo63p35 = (1ull<<63) + (1ull<<35); +static const uint64 kTwo63m35 = (1ull<<63) - (1ull<<35); +static const uint64 kTwo63m35m19 = (1ull<<63) - (1ull<<35) - (1ull<<19); +// kZero63ModP is 0 mod p where bit 63 is set in all limbs. See the section +// "Subtraction" in [1] for why. +static const uint64 kZero63ModP[8] = { + kTwo63p35, kTwo63m35, kTwo63m35, kTwo63m35, + kTwo63m35m19, kTwo63m35, kTwo63m35, kTwo63m35, +}; + +static const uint32 kBottom28Bits = 0xfffffff; + +// LargeFieldElement also represents an element of the field. The limbs are +// still spaced 28-bits apart and in little-endian order. So the limbs are at +// 0, 28, 56, ..., 392 bits, each 64-bits wide. +typedef uint64 LargeFieldElement[15]; + +// ReduceLarge converts a LargeFieldElement to a FieldElement. +// +// in[i] < 2**62 +void ReduceLarge(FieldElement* out, LargeFieldElement* inptr) { + LargeFieldElement& in(*inptr); + + for (int i = 0; i < 8; i++) { + in[i] += kZero63ModP[i]; + } + + // Eliminate the coefficients at 2**224 and greater while maintaining the + // same value mod p. + for (int i = 14; i >= 8; i--) { + in[i-8] -= in[i]; // reflection off the "+1" term of p. + in[i-5] += (in[i] & 0xffff) << 12; // part of the "-2**96" reflection. + in[i-4] += in[i] >> 16; // the rest of the "-2**96" reflection. + } + in[8] = 0; + // in[0..8] < 2**64 + + // As the values become small enough, we start to store them in |out| and use + // 32-bit operations. + for (int i = 1; i < 8; i++) { + in[i+1] += in[i] >> 28; + (*out)[i] = static_cast(in[i] & kBottom28Bits); + } + // Eliminate the term at 2*224 that we introduced while keeping the same + // value mod p. + in[0] -= in[8]; // reflection off the "+1" term of p. + (*out)[3] += static_cast(in[8] & 0xffff) << 12; // "-2**96" term + (*out)[4] += static_cast(in[8] >> 16); // rest of "-2**96" term + // in[0] < 2**64 + // out[3] < 2**29 + // out[4] < 2**29 + // out[1,2,5..7] < 2**28 + + (*out)[0] = static_cast(in[0] & kBottom28Bits); + (*out)[1] += static_cast((in[0] >> 28) & kBottom28Bits); + (*out)[2] += static_cast(in[0] >> 56); + // out[0] < 2**28 + // out[1..4] < 2**29 + // out[5..7] < 2**28 +} + +// Mul computes *out = a*b +// +// a[i] < 2**29, b[i] < 2**30 (or vice versa) +// out[i] < 2**29 +void Mul(FieldElement* out, const FieldElement& a, const FieldElement& b) { + LargeFieldElement tmp; + memset(&tmp, 0, sizeof(tmp)); + + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + tmp[i+j] += static_cast(a[i]) * static_cast(b[j]); + } + } + + ReduceLarge(out, &tmp); +} + +// Square computes *out = a*a +// +// a[i] < 2**29 +// out[i] < 2**29 +void Square(FieldElement* out, const FieldElement& a) { + LargeFieldElement tmp; + memset(&tmp, 0, sizeof(tmp)); + + for (int i = 0; i < 8; i++) { + for (int j = 0; j <= i; j++) { + uint64 r = static_cast(a[i]) * static_cast(a[j]); + if (i == j) { + tmp[i+j] += r; + } else { + tmp[i+j] += r << 1; + } + } + } + + ReduceLarge(out, &tmp); +} + +// Reduce reduces the coefficients of in_out to smaller bounds. +// +// On entry: a[i] < 2**31 + 2**30 +// On exit: a[i] < 2**29 +void Reduce(FieldElement* in_out) { + FieldElement& a = *in_out; + + for (int i = 0; i < 7; i++) { + a[i+1] += a[i] >> 28; + a[i] &= kBottom28Bits; + } + uint32 top = a[7] >> 28; + a[7] &= kBottom28Bits; + + // top < 2**4 + // Constant-time: mask = (top != 0) ? 0xffffffff : 0 + uint32 mask = top; + mask |= mask >> 2; + mask |= mask >> 1; + mask <<= 31; + mask = static_cast(static_cast(mask) >> 31); + + // Eliminate top while maintaining the same value mod p. + a[0] -= top; + a[3] += top << 12; + + // We may have just made a[0] negative but, if we did, then we must + // have added something to a[3], thus it's > 2**12. Therefore we can + // carry down to a[0]. + a[3] -= 1 & mask; + a[2] += mask & ((1<<28) - 1); + a[1] += mask & ((1<<28) - 1); + a[0] += mask & (1<<28); +} + +// Invert calcuates *out = in**-1 by computing in**(2**224 - 2**96 - 1), i.e. +// Fermat's little theorem. +void Invert(FieldElement* out, const FieldElement& in) { + FieldElement f1, f2, f3, f4; + + Square(&f1, in); // 2 + Mul(&f1, f1, in); // 2**2 - 1 + Square(&f1, f1); // 2**3 - 2 + Mul(&f1, f1, in); // 2**3 - 1 + Square(&f2, f1); // 2**4 - 2 + Square(&f2, f2); // 2**5 - 4 + Square(&f2, f2); // 2**6 - 8 + Mul(&f1, f1, f2); // 2**6 - 1 + Square(&f2, f1); // 2**7 - 2 + for (int i = 0; i < 5; i++) { // 2**12 - 2**6 + Square(&f2, f2); + } + Mul(&f2, f2, f1); // 2**12 - 1 + Square(&f3, f2); // 2**13 - 2 + for (int i = 0; i < 11; i++) { // 2**24 - 2**12 + Square(&f3, f3); + } + Mul(&f2, f3, f2); // 2**24 - 1 + Square(&f3, f2); // 2**25 - 2 + for (int i = 0; i < 23; i++) { // 2**48 - 2**24 + Square(&f3, f3); + } + Mul(&f3, f3, f2); // 2**48 - 1 + Square(&f4, f3); // 2**49 - 2 + for (int i = 0; i < 47; i++) { // 2**96 - 2**48 + Square(&f4, f4); + } + Mul(&f3, f3, f4); // 2**96 - 1 + Square(&f4, f3); // 2**97 - 2 + for (int i = 0; i < 23; i++) { // 2**120 - 2**24 + Square(&f4, f4); + } + Mul(&f2, f4, f2); // 2**120 - 1 + for (int i = 0; i < 6; i++) { // 2**126 - 2**6 + Square(&f2, f2); + } + Mul(&f1, f1, f2); // 2**126 - 1 + Square(&f1, f1); // 2**127 - 2 + Mul(&f1, f1, in); // 2**127 - 1 + for (int i = 0; i < 97; i++) { // 2**224 - 2**97 + Square(&f1, f1); + } + Mul(out, f1, f3); // 2**224 - 2**96 - 1 +} + +// Contract converts a FieldElement to its minimal, distinguished form. +// +// On entry, in[i] < 2**29 +// On exit, in[i] < 2**28 +void Contract(FieldElement* inout) { + FieldElement& out = *inout; + + // Reduce the coefficients to < 2**28. + for (int i = 0; i < 7; i++) { + out[i+1] += out[i] >> 28; + out[i] &= kBottom28Bits; + } + uint32 top = out[7] >> 28; + out[7] &= kBottom28Bits; + + // Eliminate top while maintaining the same value mod p. + out[0] -= top; + out[3] += top << 12; + + // We may just have made out[0] negative. So we carry down. If we made + // out[0] negative then we know that out[3] is sufficiently positive + // because we just added to it. + for (int i = 0; i < 3; i++) { + uint32 mask = static_cast(static_cast(out[i]) >> 31); + out[i] += (1 << 28) & mask; + out[i+1] -= 1 & mask; + } + + // We might have pushed out[3] over 2**28 so we perform another, partial + // carry chain. + for (int i = 3; i < 7; i++) { + out[i+1] += out[i] >> 28; + out[i] &= kBottom28Bits; + } + top = out[7] >> 28; + out[7] &= kBottom28Bits; + + // Eliminate top while maintaining the same value mod p. + out[0] -= top; + out[3] += top << 12; + + // There are two cases to consider for out[3]: + // 1) The first time that we eliminated top, we didn't push out[3] over + // 2**28. In this case, the partial carry chain didn't change any values + // and top is zero. + // 2) We did push out[3] over 2**28 the first time that we eliminated top. + // The first value of top was in [0..16), therefore, prior to eliminating + // the first top, 0xfff1000 <= out[3] <= 0xfffffff. Therefore, after + // overflowing and being reduced by the second carry chain, out[3] <= + // 0xf000. Thus it cannot have overflowed when we eliminated top for the + // second time. + + // Again, we may just have made out[0] negative, so do the same carry down. + // As before, if we made out[0] negative then we know that out[3] is + // sufficiently positive. + for (int i = 0; i < 3; i++) { + uint32 mask = static_cast(static_cast(out[i]) >> 31); + out[i] += (1 << 28) & mask; + out[i+1] -= 1 & mask; + } + + // The value is < 2**224, but maybe greater than p. In order to reduce to a + // unique, minimal value we see if the value is >= p and, if so, subtract p. + + // First we build a mask from the top four limbs, which must all be + // equal to bottom28Bits if the whole value is >= p. If top_4_all_ones + // ends up with any zero bits in the bottom 28 bits, then this wasn't + // true. + uint32 top_4_all_ones = 0xffffffffu; + for (int i = 4; i < 8; i++) { + top_4_all_ones &= out[i]; + } + top_4_all_ones |= 0xf0000000; + // Now we replicate any zero bits to all the bits in top_4_all_ones. + top_4_all_ones &= top_4_all_ones >> 16; + top_4_all_ones &= top_4_all_ones >> 8; + top_4_all_ones &= top_4_all_ones >> 4; + top_4_all_ones &= top_4_all_ones >> 2; + top_4_all_ones &= top_4_all_ones >> 1; + top_4_all_ones = + static_cast(static_cast(top_4_all_ones << 31) >> 31); + + // Now we test whether the bottom three limbs are non-zero. + uint32 bottom_3_non_zero = out[0] | out[1] | out[2]; + bottom_3_non_zero |= bottom_3_non_zero >> 16; + bottom_3_non_zero |= bottom_3_non_zero >> 8; + bottom_3_non_zero |= bottom_3_non_zero >> 4; + bottom_3_non_zero |= bottom_3_non_zero >> 2; + bottom_3_non_zero |= bottom_3_non_zero >> 1; + bottom_3_non_zero = + static_cast(static_cast(bottom_3_non_zero) >> 31); + + // Everything depends on the value of out[3]. + // If it's > 0xffff000 and top_4_all_ones != 0 then the whole value is >= p + // If it's = 0xffff000 and top_4_all_ones != 0 and bottom_3_non_zero != 0, + // then the whole value is >= p + // If it's < 0xffff000, then the whole value is < p + uint32 n = out[3] - 0xffff000; + uint32 out_3_equal = n; + out_3_equal |= out_3_equal >> 16; + out_3_equal |= out_3_equal >> 8; + out_3_equal |= out_3_equal >> 4; + out_3_equal |= out_3_equal >> 2; + out_3_equal |= out_3_equal >> 1; + out_3_equal = + ~static_cast(static_cast(out_3_equal << 31) >> 31); + + // If out[3] > 0xffff000 then n's MSB will be zero. + uint32 out_3_gt = ~static_cast(static_cast(n << 31) >> 31); + + uint32 mask = top_4_all_ones & ((out_3_equal & bottom_3_non_zero) | out_3_gt); + out[0] -= 1 & mask; + out[3] -= 0xffff000 & mask; + out[4] -= 0xfffffff & mask; + out[5] -= 0xfffffff & mask; + out[6] -= 0xfffffff & mask; + out[7] -= 0xfffffff & mask; +} + + +// Group element functions. +// +// These functions deal with group elements. The group is an elliptic curve +// group with a = -3 defined in FIPS 186-3, section D.2.2. + +using crypto::p224::Point; + +// kB is parameter of the elliptic curve. +const FieldElement kB = { + 55967668, 11768882, 265861671, 185302395, + 39211076, 180311059, 84673715, 188764328, +}; + +void CopyConditional(Point* out, const Point& a, uint32 mask); +void DoubleJacobian(Point* out, const Point& a); + +// AddJacobian computes *out = a+b where a != b. +void AddJacobian(Point *out, + const Point& a, + const Point& b) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl + FieldElement z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v; + + uint32 z1_is_zero = IsZero(a.z); + uint32 z2_is_zero = IsZero(b.z); + + // Z1Z1 = Z1² + Square(&z1z1, a.z); + + // Z2Z2 = Z2² + Square(&z2z2, b.z); + + // U1 = X1*Z2Z2 + Mul(&u1, a.x, z2z2); + + // U2 = X2*Z1Z1 + Mul(&u2, b.x, z1z1); + + // S1 = Y1*Z2*Z2Z2 + Mul(&s1, b.z, z2z2); + Mul(&s1, a.y, s1); + + // S2 = Y2*Z1*Z1Z1 + Mul(&s2, a.z, z1z1); + Mul(&s2, b.y, s2); + + // H = U2-U1 + Subtract(&h, u2, u1); + Reduce(&h); + uint32 x_equal = IsZero(h); + + // I = (2*H)² + for (int j = 0; j < 8; j++) { + i[j] = h[j] << 1; + } + Reduce(&i); + Square(&i, i); + + // J = H*I + Mul(&j, h, i); + // r = 2*(S2-S1) + Subtract(&r, s2, s1); + Reduce(&r); + uint32 y_equal = IsZero(r); + + if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) { + // The two input points are the same therefore we must use the dedicated + // doubling function as the slope of the line is undefined. + DoubleJacobian(out, a); + return; + } + + for (int i = 0; i < 8; i++) { + r[i] <<= 1; + } + Reduce(&r); + + // V = U1*I + Mul(&v, u1, i); + + // Z3 = ((Z1+Z2)²-Z1Z1-Z2Z2)*H + Add(&z1z1, z1z1, z2z2); + Add(&z2z2, a.z, b.z); + Reduce(&z2z2); + Square(&z2z2, z2z2); + Subtract(&out->z, z2z2, z1z1); + Reduce(&out->z); + Mul(&out->z, out->z, h); + + // X3 = r²-J-2*V + for (int i = 0; i < 8; i++) { + z1z1[i] = v[i] << 1; + } + Add(&z1z1, j, z1z1); + Reduce(&z1z1); + Square(&out->x, r); + Subtract(&out->x, out->x, z1z1); + Reduce(&out->x); + + // Y3 = r*(V-X3)-2*S1*J + for (int i = 0; i < 8; i++) { + s1[i] <<= 1; + } + Mul(&s1, s1, j); + Subtract(&z1z1, v, out->x); + Reduce(&z1z1); + Mul(&z1z1, z1z1, r); + Subtract(&out->y, z1z1, s1); + Reduce(&out->y); + + CopyConditional(out, a, z2_is_zero); + CopyConditional(out, b, z1_is_zero); +} + +// DoubleJacobian computes *out = a+a. +void DoubleJacobian(Point* out, const Point& a) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b + FieldElement delta, gamma, beta, alpha, t; + + Square(&delta, a.z); + Square(&gamma, a.y); + Mul(&beta, a.x, gamma); + + // alpha = 3*(X1-delta)*(X1+delta) + Add(&t, a.x, delta); + for (int i = 0; i < 8; i++) { + t[i] += t[i] << 1; + } + Reduce(&t); + Subtract(&alpha, a.x, delta); + Reduce(&alpha); + Mul(&alpha, alpha, t); + + // Z3 = (Y1+Z1)²-gamma-delta + Add(&out->z, a.y, a.z); + Reduce(&out->z); + Square(&out->z, out->z); + Subtract(&out->z, out->z, gamma); + Reduce(&out->z); + Subtract(&out->z, out->z, delta); + Reduce(&out->z); + + // X3 = alpha²-8*beta + for (int i = 0; i < 8; i++) { + delta[i] = beta[i] << 3; + } + Reduce(&delta); + Square(&out->x, alpha); + Subtract(&out->x, out->x, delta); + Reduce(&out->x); + + // Y3 = alpha*(4*beta-X3)-8*gamma² + for (int i = 0; i < 8; i++) { + beta[i] <<= 2; + } + Reduce(&beta); + Subtract(&beta, beta, out->x); + Reduce(&beta); + Square(&gamma, gamma); + for (int i = 0; i < 8; i++) { + gamma[i] <<= 3; + } + Reduce(&gamma); + Mul(&out->y, alpha, beta); + Subtract(&out->y, out->y, gamma); + Reduce(&out->y); +} + +// CopyConditional sets *out=a if mask is 0xffffffff. mask must be either 0 of +// 0xffffffff. +void CopyConditional(Point* out, + const Point& a, + uint32 mask) { + for (int i = 0; i < 8; i++) { + out->x[i] ^= mask & (a.x[i] ^ out->x[i]); + out->y[i] ^= mask & (a.y[i] ^ out->y[i]); + out->z[i] ^= mask & (a.z[i] ^ out->z[i]); + } +} + +// ScalarMult calculates *out = a*scalar where scalar is a big-endian number of +// length scalar_len and != 0. +void ScalarMult(Point* out, const Point& a, + const uint8* scalar, size_t scalar_len) { + memset(out, 0, sizeof(*out)); + Point tmp; + + for (size_t i = 0; i < scalar_len; i++) { + for (unsigned int bit_num = 0; bit_num < 8; bit_num++) { + DoubleJacobian(out, *out); + uint32 bit = static_cast(static_cast( + (((scalar[i] >> (7 - bit_num)) & 1) << 31) >> 31)); + AddJacobian(&tmp, a, *out); + CopyConditional(out, tmp, bit); + } + } +} + +// Get224Bits reads 7 words from in and scatters their contents in +// little-endian form into 8 words at out, 28 bits per output word. +void Get224Bits(uint32* out, const uint32* in) { + out[0] = NetToHost32(in[6]) & kBottom28Bits; + out[1] = ((NetToHost32(in[5]) << 4) | + (NetToHost32(in[6]) >> 28)) & kBottom28Bits; + out[2] = ((NetToHost32(in[4]) << 8) | + (NetToHost32(in[5]) >> 24)) & kBottom28Bits; + out[3] = ((NetToHost32(in[3]) << 12) | + (NetToHost32(in[4]) >> 20)) & kBottom28Bits; + out[4] = ((NetToHost32(in[2]) << 16) | + (NetToHost32(in[3]) >> 16)) & kBottom28Bits; + out[5] = ((NetToHost32(in[1]) << 20) | + (NetToHost32(in[2]) >> 12)) & kBottom28Bits; + out[6] = ((NetToHost32(in[0]) << 24) | + (NetToHost32(in[1]) >> 8)) & kBottom28Bits; + out[7] = (NetToHost32(in[0]) >> 4) & kBottom28Bits; +} + +// Put224Bits performs the inverse operation to Get224Bits: taking 28 bits from +// each of 8 input words and writing them in big-endian order to 7 words at +// out. +void Put224Bits(uint32* out, const uint32* in) { + out[6] = HostToNet32((in[0] >> 0) | (in[1] << 28)); + out[5] = HostToNet32((in[1] >> 4) | (in[2] << 24)); + out[4] = HostToNet32((in[2] >> 8) | (in[3] << 20)); + out[3] = HostToNet32((in[3] >> 12) | (in[4] << 16)); + out[2] = HostToNet32((in[4] >> 16) | (in[5] << 12)); + out[1] = HostToNet32((in[5] >> 20) | (in[6] << 8)); + out[0] = HostToNet32((in[6] >> 24) | (in[7] << 4)); +} + +} // anonymous namespace + +namespace crypto { + +namespace p224 { + +bool Point::SetFromString(const base::StringPiece& in) { + if (in.size() != 2*28) + return false; + const uint32* inwords = reinterpret_cast(in.data()); + Get224Bits(x, inwords); + Get224Bits(y, inwords + 7); + memset(&z, 0, sizeof(z)); + z[0] = 1; + + // Check that the point is on the curve, i.e. that y² = x³ - 3x + b. + FieldElement lhs; + Square(&lhs, y); + Contract(&lhs); + + FieldElement rhs; + Square(&rhs, x); + Mul(&rhs, x, rhs); + + FieldElement three_x; + for (int i = 0; i < 8; i++) { + three_x[i] = x[i] * 3; + } + Reduce(&three_x); + Subtract(&rhs, rhs, three_x); + Reduce(&rhs); + + ::Add(&rhs, rhs, kB); + Contract(&rhs); + return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; +} + +std::string Point::ToString() const { + FieldElement zinv, zinv_sq, x, y; + + // If this is the point at infinity we return a string of all zeros. + if (IsZero(this->z)) { + static const char zeros[56] = {0}; + return std::string(zeros, sizeof(zeros)); + } + + Invert(&zinv, this->z); + Square(&zinv_sq, zinv); + Mul(&x, this->x, zinv_sq); + Mul(&zinv_sq, zinv_sq, zinv); + Mul(&y, this->y, zinv_sq); + + Contract(&x); + Contract(&y); + + uint32 outwords[14]; + Put224Bits(outwords, x); + Put224Bits(outwords + 7, y); + return std::string(reinterpret_cast(outwords), sizeof(outwords)); +} + +void ScalarMult(const Point& in, const uint8* scalar, Point* out) { + ::ScalarMult(out, in, scalar, 28); +} + +// kBasePoint is the base point (generator) of the elliptic curve group. +static const Point kBasePoint = { + {22813985, 52956513, 34677300, 203240812, + 12143107, 133374265, 225162431, 191946955}, + {83918388, 223877528, 122119236, 123340192, + 266784067, 263504429, 146143011, 198407736}, + {1, 0, 0, 0, 0, 0, 0, 0}, +}; + +void ScalarBaseMult(const uint8* scalar, Point* out) { + ::ScalarMult(out, kBasePoint, scalar, 28); +} + +void Add(const Point& a, const Point& b, Point* out) { + AddJacobian(out, a, b); +} + +void Negate(const Point& in, Point* out) { + // Guide to elliptic curve cryptography, page 89 suggests that (X : X+Y : Z) + // is the negative in Jacobian coordinates, but it doesn't actually appear to + // be true in testing so this performs the negation in affine coordinates. + FieldElement zinv, zinv_sq, y; + Invert(&zinv, in.z); + Square(&zinv_sq, zinv); + Mul(&out->x, in.x, zinv_sq); + Mul(&zinv_sq, zinv_sq, zinv); + Mul(&y, in.y, zinv_sq); + + Subtract(&out->y, kP, y); + Reduce(&out->y); + + memset(&out->z, 0, sizeof(out->z)); + out->z[0] = 1; +} + +} // namespace p224 + +} // namespace crypto diff --git a/chromium/crypto/p224.h b/chromium/crypto/p224.h new file mode 100644 index 00000000000..2efecfab559 --- /dev/null +++ b/chromium/crypto/p224.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_P224_H_ +#define CRYPTO_P224_H_ + +#include + +#include "base/basictypes.h" +#include "base/strings/string_piece.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// P224 implements an elliptic curve group, commonly known as P224 and defined +// in FIPS 186-3, section D.2.2. +namespace p224 { + +// An element of the field (ℤ/pℤ) is represented with 8, 28-bit limbs in +// little endian order. +typedef uint32 FieldElement[8]; + +struct CRYPTO_EXPORT Point { + // SetFromString the value of the point from the 56 byte, external + // representation. The external point representation is an (x, y) pair of a + // point on the curve. Each field element is represented as a big-endian + // number < p. + bool SetFromString(const base::StringPiece& in); + + // ToString returns an external representation of the Point. + std::string ToString() const; + + // An Point is represented in Jacobian form (x/z², y/z³). + FieldElement x, y, z; +}; + +// kScalarBytes is the number of bytes needed to represent an element of the +// P224 field. +static const size_t kScalarBytes = 28; + +// ScalarMult computes *out = in*scalar where scalar is a 28-byte, big-endian +// number. +void CRYPTO_EXPORT ScalarMult(const Point& in, const uint8* scalar, Point* out); + +// ScalarBaseMult computes *out = g*scalar where g is the base point of the +// curve and scalar is a 28-byte, big-endian number. +void CRYPTO_EXPORT ScalarBaseMult(const uint8* scalar, Point* out); + +// Add computes *out = a+b. +void CRYPTO_EXPORT Add(const Point& a, const Point& b, Point* out); + +// Negate calculates out = -a; +void CRYPTO_EXPORT Negate(const Point& a, Point* out); + +} // namespace p224 + +} // namespace crypto + +#endif // CRYPTO_P224_H_ diff --git a/chromium/crypto/p224_spake.cc b/chromium/crypto/p224_spake.cc new file mode 100644 index 00000000000..31109a43503 --- /dev/null +++ b/chromium/crypto/p224_spake.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This code implements SPAKE2, a variant of EKE: +// http://www.di.ens.fr/~pointche/pub.php?reference=AbPo04 + +#include + +#include +#include +#include +#include + +namespace { + +// The following two points (M and N in the protocol) are verifiable random +// points on the curve and can be generated with the following code: + +// #include +// #include +// #include +// +// #include +// #include +// #include +// +// static const char kSeed1[] = "P224 point generation seed (M)"; +// static const char kSeed2[] = "P224 point generation seed (N)"; +// +// void find_seed(const char* seed) { +// SHA256_CTX sha256; +// uint8_t digest[SHA256_DIGEST_LENGTH]; +// +// SHA256_Init(&sha256); +// SHA256_Update(&sha256, seed, strlen(seed)); +// SHA256_Final(digest, &sha256); +// +// BIGNUM x, y; +// EC_GROUP* p224 = EC_GROUP_new_by_curve_name(NID_secp224r1); +// EC_POINT* p = EC_POINT_new(p224); +// +// for (unsigned i = 0;; i++) { +// BN_init(&x); +// BN_bin2bn(digest, 28, &x); +// +// if (EC_POINT_set_compressed_coordinates_GFp( +// p224, p, &x, digest[28] & 1, NULL)) { +// BN_init(&y); +// EC_POINT_get_affine_coordinates_GFp(p224, p, &x, &y, NULL); +// char* x_str = BN_bn2hex(&x); +// char* y_str = BN_bn2hex(&y); +// printf("Found after %u iterations:\n%s\n%s\n", i, x_str, y_str); +// OPENSSL_free(x_str); +// OPENSSL_free(y_str); +// BN_free(&x); +// BN_free(&y); +// break; +// } +// +// SHA256_Init(&sha256); +// SHA256_Update(&sha256, digest, sizeof(digest)); +// SHA256_Final(digest, &sha256); +// +// BN_free(&x); +// } +// +// EC_POINT_free(p); +// EC_GROUP_free(p224); +// } +// +// int main() { +// find_seed(kSeed1); +// find_seed(kSeed2); +// return 0; +// } + +const crypto::p224::Point kM = { + {174237515, 77186811, 235213682, 33849492, + 33188520, 48266885, 177021753, 81038478}, + {104523827, 245682244, 266509668, 236196369, + 28372046, 145351378, 198520366, 113345994}, + {1, 0, 0, 0, 0, 0, 0}, +}; + +const crypto::p224::Point kN = { + {136176322, 263523628, 251628795, 229292285, + 5034302, 185981975, 171998428, 11653062}, + {197567436, 51226044, 60372156, 175772188, + 42075930, 8083165, 160827401, 65097570}, + {1, 0, 0, 0, 0, 0, 0}, +}; + +} // anonymous namespace + +namespace crypto { + +P224EncryptedKeyExchange::P224EncryptedKeyExchange( + PeerType peer_type, const base::StringPiece& password) + : state_(kStateInitial), + is_server_(peer_type == kPeerTypeServer) { + memset(&x_, 0, sizeof(x_)); + memset(&expected_authenticator_, 0, sizeof(expected_authenticator_)); + + // x_ is a random scalar. + RandBytes(x_, sizeof(x_)); + + // X = g**x_ + p224::Point X; + p224::ScalarBaseMult(x_, &X); + + // Calculate |password| hash to get SPAKE password value. + SHA256HashString(std::string(password.data(), password.length()), + pw_, sizeof(pw_)); + + // The client masks the Diffie-Hellman value, X, by adding M**pw and the + // server uses N**pw. + p224::Point MNpw; + p224::ScalarMult(is_server_ ? kN : kM, pw_, &MNpw); + + // X* = X + (N|M)**pw + p224::Point Xstar; + p224::Add(X, MNpw, &Xstar); + + next_message_ = Xstar.ToString(); +} + +const std::string& P224EncryptedKeyExchange::GetMessage() { + if (state_ == kStateInitial) { + state_ = kStateRecvDH; + return next_message_; + } else if (state_ == kStateSendHash) { + state_ = kStateRecvHash; + return next_message_; + } + + LOG(FATAL) << "P224EncryptedKeyExchange::GetMessage called in" + " bad state " << state_; + next_message_ = ""; + return next_message_; +} + +P224EncryptedKeyExchange::Result P224EncryptedKeyExchange::ProcessMessage( + const base::StringPiece& message) { + if (state_ == kStateRecvHash) { + // This is the final state of the protocol: we are reading the peer's + // authentication hash and checking that it matches the one that we expect. + if (message.size() != sizeof(expected_authenticator_)) { + error_ = "peer's hash had an incorrect size"; + return kResultFailed; + } + if (!SecureMemEqual(message.data(), expected_authenticator_, + message.size())) { + error_ = "peer's hash had incorrect value"; + return kResultFailed; + } + state_ = kStateDone; + return kResultSuccess; + } + + if (state_ != kStateRecvDH) { + LOG(FATAL) << "P224EncryptedKeyExchange::ProcessMessage called in" + " bad state " << state_; + error_ = "internal error"; + return kResultFailed; + } + + // Y* is the other party's masked, Diffie-Hellman value. + p224::Point Ystar; + if (!Ystar.SetFromString(message)) { + error_ = "failed to parse peer's masked Diffie-Hellman value"; + return kResultFailed; + } + + // We calculate the mask value: (N|M)**pw + p224::Point MNpw, minus_MNpw, Y, k; + p224::ScalarMult(is_server_ ? kM : kN, pw_, &MNpw); + p224::Negate(MNpw, &minus_MNpw); + + // Y = Y* - (N|M)**pw + p224::Add(Ystar, minus_MNpw, &Y); + + // K = Y**x_ + p224::ScalarMult(Y, x_, &k); + + // If everything worked out, then K is the same for both parties. + key_ = k.ToString(); + + std::string client_masked_dh, server_masked_dh; + if (is_server_) { + client_masked_dh = message.as_string(); + server_masked_dh = next_message_; + } else { + client_masked_dh = next_message_; + server_masked_dh = message.as_string(); + } + + // Now we calculate the hashes that each side will use to prove to the other + // that they derived the correct value for K. + uint8 client_hash[kSHA256Length], server_hash[kSHA256Length]; + CalculateHash(kPeerTypeClient, client_masked_dh, server_masked_dh, key_, + client_hash); + CalculateHash(kPeerTypeServer, client_masked_dh, server_masked_dh, key_, + server_hash); + + const uint8* my_hash = is_server_ ? server_hash : client_hash; + const uint8* their_hash = is_server_ ? client_hash : server_hash; + + next_message_ = + std::string(reinterpret_cast(my_hash), kSHA256Length); + memcpy(expected_authenticator_, their_hash, kSHA256Length); + state_ = kStateSendHash; + return kResultPending; +} + +void P224EncryptedKeyExchange::CalculateHash( + PeerType peer_type, + const std::string& client_masked_dh, + const std::string& server_masked_dh, + const std::string& k, + uint8* out_digest) { + std::string hash_contents; + + if (peer_type == kPeerTypeServer) { + hash_contents = "server"; + } else { + hash_contents = "client"; + } + + hash_contents += client_masked_dh; + hash_contents += server_masked_dh; + hash_contents += + std::string(reinterpret_cast(pw_), sizeof(pw_)); + hash_contents += k; + + SHA256HashString(hash_contents, out_digest, kSHA256Length); +} + +const std::string& P224EncryptedKeyExchange::error() const { + return error_; +} + +const std::string& P224EncryptedKeyExchange::GetKey() { + DCHECK_EQ(state_, kStateDone); + return key_; +} + +} // namespace crypto diff --git a/chromium/crypto/p224_spake.h b/chromium/crypto/p224_spake.h new file mode 100644 index 00000000000..6905ef2c220 --- /dev/null +++ b/chromium/crypto/p224_spake.h @@ -0,0 +1,114 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_P224_SPAKE_H_ +#define CRYPTO_P224_SPAKE_H_ + +#include +#include +#include + +namespace crypto { + +// P224EncryptedKeyExchange implements SPAKE2, a variant of Encrypted +// Key Exchange. It allows two parties that have a secret common +// password to establish a common secure key by exchanging messages +// over unsecure channel without disclosing the password. +// +// The password can be low entropy as authenticating with an attacker only +// gives the attacker a one-shot password oracle. No other information about +// the password is leaked. (However, you must be sure to limit the number of +// permitted authentication attempts otherwise they get many one-shot oracles.) +// +// The protocol requires several RTTs (actually two, but you shouldn't assume +// that.) To use the object, call GetMessage() and pass that message to the +// peer. Get a message from the peer and feed it into ProcessMessage. Then +// examine the return value of ProcessMessage: +// kResultPending: Another round is required. Call GetMessage and repeat. +// kResultFailed: The authentication has failed. You can get a human readable +// error message by calling error(). +// kResultSuccess: The authentication was successful. +// +// In each exchange, each peer always sends a message. +class CRYPTO_EXPORT P224EncryptedKeyExchange { + public: + enum Result { + kResultPending, + kResultFailed, + kResultSuccess, + }; + + // PeerType's values are named client and server due to convention. But + // they could be called "A" and "B" as far as the protocol is concerned so + // long as the two parties don't both get the same label. + enum PeerType { + kPeerTypeClient, + kPeerTypeServer, + }; + + // peer_type: the type of the local authentication party. + // password: secret session password. Both parties to the + // authentication must pass the same value. For the case of a + // TLS connection, see RFC 5705. + P224EncryptedKeyExchange(PeerType peer_type, + const base::StringPiece& password); + + // GetMessage returns a byte string which must be passed to the other party + // in the authentication. + const std::string& GetMessage(); + + // ProcessMessage processes a message which must have been generated by a + // call to GetMessage() by the other party. + Result ProcessMessage(const base::StringPiece& message); + + // In the event that ProcessMessage() returns kResultFailed, error will + // return a human readable error message. + const std::string& error() const; + + // The key established as result of the key exchange. Must be called + // at then end after ProcessMessage() returns kResultSuccess. + const std::string& GetKey(); + + private: + // The authentication state machine is very simple and each party proceeds + // through each of these states, in order. + enum State { + kStateInitial, + kStateRecvDH, + kStateSendHash, + kStateRecvHash, + kStateDone, + }; + + State state_; + const bool is_server_; + // next_message_ contains a value for GetMessage() to return. + std::string next_message_; + std::string error_; + + // CalculateHash computes the verification hash for the given peer and writes + // |kSHA256Length| bytes at |out_digest|. + void CalculateHash( + PeerType peer_type, + const std::string& client_masked_dh, + const std::string& server_masked_dh, + const std::string& k, + uint8* out_digest); + + // x_ is the secret Diffie-Hellman exponent (see paper referenced in .cc + // file). + uint8 x_[p224::kScalarBytes]; + // pw_ is SHA256(P(password), P(session))[:28] where P() prepends a uint32, + // big-endian length prefix (see paper refereneced in .cc file). + uint8 pw_[p224::kScalarBytes]; + // expected_authenticator_ is used to store the hash value expected from the + // other party. + uint8 expected_authenticator_[kSHA256Length]; + + std::string key_; +}; + +} // namespace crypto + +#endif // CRYPTO_P224_SPAKE_H_ diff --git a/chromium/crypto/p224_spake_unittest.cc b/chromium/crypto/p224_spake_unittest.cc new file mode 100644 index 00000000000..ca35f7245b9 --- /dev/null +++ b/chromium/crypto/p224_spake_unittest.cc @@ -0,0 +1,130 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +namespace { + +bool RunExchange(P224EncryptedKeyExchange* client, + P224EncryptedKeyExchange* server) { + + for (;;) { + std::string client_message, server_message; + client_message = client->GetMessage(); + server_message = server->GetMessage(); + + P224EncryptedKeyExchange::Result client_result, server_result; + client_result = client->ProcessMessage(server_message); + server_result = server->ProcessMessage(client_message); + + // Check that we never hit the case where only one succeeds. + if ((client_result == P224EncryptedKeyExchange::kResultSuccess) ^ + (server_result == P224EncryptedKeyExchange::kResultSuccess)) { + CHECK(false) << "Parties differ on whether authentication was successful"; + } + + if (client_result == P224EncryptedKeyExchange::kResultFailed || + server_result == P224EncryptedKeyExchange::kResultFailed) { + return false; + } + + if (client_result == P224EncryptedKeyExchange::kResultSuccess && + server_result == P224EncryptedKeyExchange::kResultSuccess) { + return true; + } + + CHECK_EQ(P224EncryptedKeyExchange::kResultPending, client_result); + CHECK_EQ(P224EncryptedKeyExchange::kResultPending, server_result); + } +} + +const char kPassword[] = "foo"; + +} // namespace + +TEST(MutualAuth, CorrectAuth) { + P224EncryptedKeyExchange client( + P224EncryptedKeyExchange::kPeerTypeClient, kPassword); + P224EncryptedKeyExchange server( + P224EncryptedKeyExchange::kPeerTypeServer, kPassword); + + EXPECT_TRUE(RunExchange(&client, &server)); + EXPECT_EQ(client.GetKey(), server.GetKey()); +} + +TEST(MutualAuth, IncorrectPassword) { + P224EncryptedKeyExchange client( + P224EncryptedKeyExchange::kPeerTypeClient, + kPassword); + P224EncryptedKeyExchange server( + P224EncryptedKeyExchange::kPeerTypeServer, + "wrongpassword"); + + EXPECT_FALSE(RunExchange(&client, &server)); +} + +TEST(MutualAuth, Fuzz) { + static const unsigned kIterations = 40; + + for (unsigned i = 0; i < kIterations; i++) { + P224EncryptedKeyExchange client( + P224EncryptedKeyExchange::kPeerTypeClient, kPassword); + P224EncryptedKeyExchange server( + P224EncryptedKeyExchange::kPeerTypeServer, kPassword); + + // We'll only be testing small values of i, but we don't want that to bias + // the test coverage. So we disperse the value of i by multiplying by the + // FNV, 32-bit prime, producing a poor-man's PRNG. + const uint32 rand = i * 16777619; + + for (unsigned round = 0;; round++) { + std::string client_message, server_message; + client_message = client.GetMessage(); + server_message = server.GetMessage(); + + if ((rand & 1) == round) { + const bool server_or_client = rand & 2; + std::string* m = server_or_client ? &server_message : &client_message; + if (rand & 4) { + // Truncate + *m = m->substr(0, (i >> 3) % m->size()); + } else { + // Corrupt + const size_t bits = m->size() * 8; + const size_t bit_to_corrupt = (rand >> 3) % bits; + const_cast(m->data())[bit_to_corrupt / 8] ^= + 1 << (bit_to_corrupt % 8); + } + } + + P224EncryptedKeyExchange::Result client_result, server_result; + client_result = client.ProcessMessage(server_message); + server_result = server.ProcessMessage(client_message); + + // If we have corrupted anything, we expect the authentication to fail, + // although one side can succeed if we happen to corrupt the second round + // message to the other. + ASSERT_FALSE( + client_result == P224EncryptedKeyExchange::kResultSuccess && + server_result == P224EncryptedKeyExchange::kResultSuccess); + + if (client_result == P224EncryptedKeyExchange::kResultFailed || + server_result == P224EncryptedKeyExchange::kResultFailed) { + break; + } + + ASSERT_EQ(P224EncryptedKeyExchange::kResultPending, + client_result); + ASSERT_EQ(P224EncryptedKeyExchange::kResultPending, + server_result); + } + } +} + +} // namespace crypto diff --git a/chromium/crypto/p224_unittest.cc b/chromium/crypto/p224_unittest.cc new file mode 100644 index 00000000000..ef9e305e943 --- /dev/null +++ b/chromium/crypto/p224_unittest.cc @@ -0,0 +1,824 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "crypto/p224.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +using p224::Point; + +// kBasePointExternal is the P224 base point in external representation. +static const uint8 kBasePointExternal[56] = { + 0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f, + 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3, + 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6, + 0x11, 0x5c, 0x1d, 0x21, 0xbd, 0x37, 0x63, 0x88, + 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6, + 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64, + 0x44, 0xd5, 0x81, 0x99, 0x85, 0x00, 0x7e, 0x34, +}; + +// TestVector represents a test of scalar multiplication of the base point. +// |scalar| is a big-endian scalar and |affine| is the external representation +// of g*scalar. +struct TestVector { + uint8 scalar[28]; + uint8 affine[28*2]; +}; + +static const int kNumNISTTestVectors = 52; + +// kNISTTestVectors are the NIST test vectors for P224. +static const TestVector kNISTTestVectors[kNumNISTTestVectors] = { + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01}, + {0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f, + 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3, + 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6, + 0x11, 0x5c, 0x1d, 0x21, 0xbd, 0x37, 0x63, 0x88, + 0xb5, 0xf7, 0x23, 0xfb, 0x4c, 0x22, 0xdf, 0xe6, + 0xcd, 0x43, 0x75, 0xa0, 0x5a, 0x07, 0x47, 0x64, + 0x44, 0xd5, 0x81, 0x99, 0x85, 0x00, 0x7e, 0x34 + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, }, + + {0x70, 0x6a, 0x46, 0xdc, 0x76, 0xdc, 0xb7, 0x67, + 0x98, 0xe6, 0x0e, 0x6d, 0x89, 0x47, 0x47, 0x88, + 0xd1, 0x6d, 0xc1, 0x80, 0x32, 0xd2, 0x68, 0xfd, + 0x1a, 0x70, 0x4f, 0xa6, 0x1c, 0x2b, 0x76, 0xa7, + 0xbc, 0x25, 0xe7, 0x70, 0x2a, 0x70, 0x4f, 0xa9, + 0x86, 0x89, 0x28, 0x49, 0xfc, 0xa6, 0x29, 0x48, + 0x7a, 0xcf, 0x37, 0x09, 0xd2, 0xe4, 0xe8, 0xbb, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, }, + {0xdf, 0x1b, 0x1d, 0x66, 0xa5, 0x51, 0xd0, 0xd3, + 0x1e, 0xff, 0x82, 0x25, 0x58, 0xb9, 0xd2, 0xcc, + 0x75, 0xc2, 0x18, 0x02, 0x79, 0xfe, 0x0d, 0x08, + 0xfd, 0x89, 0x6d, 0x04, 0xa3, 0xf7, 0xf0, 0x3c, + 0xad, 0xd0, 0xbe, 0x44, 0x4c, 0x0a, 0xa5, 0x68, + 0x30, 0x13, 0x0d, 0xdf, 0x77, 0xd3, 0x17, 0x34, + 0x4e, 0x1a, 0xf3, 0x59, 0x19, 0x81, 0xa9, 0x25, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, }, + {0xae, 0x99, 0xfe, 0xeb, 0xb5, 0xd2, 0x69, 0x45, + 0xb5, 0x48, 0x92, 0x09, 0x2a, 0x8a, 0xee, 0x02, + 0x91, 0x29, 0x30, 0xfa, 0x41, 0xcd, 0x11, 0x4e, + 0x40, 0x44, 0x73, 0x01, 0x04, 0x82, 0x58, 0x0a, + 0x0e, 0xc5, 0xbc, 0x47, 0xe8, 0x8b, 0xc8, 0xc3, + 0x78, 0x63, 0x2c, 0xd1, 0x96, 0xcb, 0x3f, 0xa0, + 0x58, 0xa7, 0x11, 0x4e, 0xb0, 0x30, 0x54, 0xc9, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, }, + {0x31, 0xc4, 0x9a, 0xe7, 0x5b, 0xce, 0x78, 0x07, + 0xcd, 0xff, 0x22, 0x05, 0x5d, 0x94, 0xee, 0x90, + 0x21, 0xfe, 0xdb, 0xb5, 0xab, 0x51, 0xc5, 0x75, + 0x26, 0xf0, 0x11, 0xaa, 0x27, 0xe8, 0xbf, 0xf1, + 0x74, 0x56, 0x35, 0xec, 0x5b, 0xa0, 0xc9, 0xf1, + 0xc2, 0xed, 0xe1, 0x54, 0x14, 0xc6, 0x50, 0x7d, + 0x29, 0xff, 0xe3, 0x7e, 0x79, 0x0a, 0x07, 0x9b, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, }, + {0x1f, 0x24, 0x83, 0xf8, 0x25, 0x72, 0x25, 0x1f, + 0xca, 0x97, 0x5f, 0xea, 0x40, 0xdb, 0x82, 0x1d, + 0xf8, 0xad, 0x82, 0xa3, 0xc0, 0x02, 0xee, 0x6c, + 0x57, 0x11, 0x24, 0x08, 0x89, 0xfa, 0xf0, 0xcc, + 0xb7, 0x50, 0xd9, 0x9b, 0x55, 0x3c, 0x57, 0x4f, + 0xad, 0x7e, 0xcf, 0xb0, 0x43, 0x85, 0x86, 0xeb, + 0x39, 0x52, 0xaf, 0x5b, 0x4b, 0x15, 0x3c, 0x7e, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, }, + {0xdb, 0x2f, 0x6b, 0xe6, 0x30, 0xe2, 0x46, 0xa5, + 0xcf, 0x7d, 0x99, 0xb8, 0x51, 0x94, 0xb1, 0x23, + 0xd4, 0x87, 0xe2, 0xd4, 0x66, 0xb9, 0x4b, 0x24, + 0xa0, 0x3c, 0x3e, 0x28, 0x0f, 0x3a, 0x30, 0x08, + 0x54, 0x97, 0xf2, 0xf6, 0x11, 0xee, 0x25, 0x17, + 0xb1, 0x63, 0xef, 0x8c, 0x53, 0xb7, 0x15, 0xd1, + 0x8b, 0xb4, 0xe4, 0x80, 0x8d, 0x02, 0xb9, 0x63, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, }, + {0x85, 0x8e, 0x6f, 0x9c, 0xc6, 0xc1, 0x2c, 0x31, + 0xf5, 0xdf, 0x12, 0x4a, 0xa7, 0x77, 0x67, 0xb0, + 0x5c, 0x8b, 0xc0, 0x21, 0xbd, 0x68, 0x3d, 0x2b, + 0x55, 0x57, 0x15, 0x50, 0x04, 0x6d, 0xcd, 0x3e, + 0xa5, 0xc4, 0x38, 0x98, 0xc5, 0xc5, 0xfc, 0x4f, + 0xda, 0xc7, 0xdb, 0x39, 0xc2, 0xf0, 0x2e, 0xbe, + 0xe4, 0xe3, 0x54, 0x1d, 0x1e, 0x78, 0x04, 0x7a, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, }, + {0x2f, 0xdc, 0xcc, 0xfe, 0xe7, 0x20, 0xa7, 0x7e, + 0xf6, 0xcb, 0x3b, 0xfb, 0xb4, 0x47, 0xf9, 0x38, + 0x31, 0x17, 0xe3, 0xda, 0xa4, 0xa0, 0x7e, 0x36, + 0xed, 0x15, 0xf7, 0x8d, 0x37, 0x17, 0x32, 0xe4, + 0xf4, 0x1b, 0xf4, 0xf7, 0x88, 0x30, 0x35, 0xe6, + 0xa7, 0x9f, 0xce, 0xdc, 0x0e, 0x19, 0x6e, 0xb0, + 0x7b, 0x48, 0x17, 0x16, 0x97, 0x51, 0x74, 0x63, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, }, + {0xae, 0xa9, 0xe1, 0x7a, 0x30, 0x65, 0x17, 0xeb, + 0x89, 0x15, 0x2a, 0xa7, 0x09, 0x6d, 0x2c, 0x38, + 0x1e, 0xc8, 0x13, 0xc5, 0x1a, 0xa8, 0x80, 0xe7, + 0xbe, 0xe2, 0xc0, 0xfd, 0x39, 0xbb, 0x30, 0xea, + 0xb3, 0x37, 0xe0, 0xa5, 0x21, 0xb6, 0xcb, 0xa1, + 0xab, 0xe4, 0xb2, 0xb3, 0xa3, 0xe5, 0x24, 0xc1, + 0x4a, 0x3f, 0xe3, 0xeb, 0x11, 0x6b, 0x65, 0x5f, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, }, + {0xef, 0x53, 0xb6, 0x29, 0x4a, 0xca, 0x43, 0x1f, + 0x0f, 0x3c, 0x22, 0xdc, 0x82, 0xeb, 0x90, 0x50, + 0x32, 0x4f, 0x1d, 0x88, 0xd3, 0x77, 0xe7, 0x16, + 0x44, 0x8e, 0x50, 0x7c, 0x20, 0xb5, 0x10, 0x00, + 0x40, 0x92, 0xe9, 0x66, 0x36, 0xcf, 0xb7, 0xe3, + 0x2e, 0xfd, 0xed, 0x82, 0x65, 0xc2, 0x66, 0xdf, + 0xb7, 0x54, 0xfa, 0x6d, 0x64, 0x91, 0xa6, 0xda, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, }, + {0x6e, 0x31, 0xee, 0x1d, 0xc1, 0x37, 0xf8, 0x1b, + 0x05, 0x67, 0x52, 0xe4, 0xde, 0xab, 0x14, 0x43, + 0xa4, 0x81, 0x03, 0x3e, 0x9b, 0x4c, 0x93, 0xa3, + 0x04, 0x4f, 0x4f, 0x7a, 0x20, 0x7d, 0xdd, 0xf0, + 0x38, 0x5b, 0xfd, 0xea, 0xb6, 0xe9, 0xac, 0xda, + 0x8d, 0xa0, 0x6b, 0x3b, 0xbe, 0xf2, 0x24, 0xa9, + 0x3a, 0xb1, 0xe9, 0xe0, 0x36, 0x10, 0x9d, 0x13, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0d, }, + {0x34, 0xe8, 0xe1, 0x7a, 0x43, 0x0e, 0x43, 0x28, + 0x97, 0x93, 0xc3, 0x83, 0xfa, 0xc9, 0x77, 0x42, + 0x47, 0xb4, 0x0e, 0x9e, 0xbd, 0x33, 0x66, 0x98, + 0x1f, 0xcf, 0xae, 0xca, 0x25, 0x28, 0x19, 0xf7, + 0x1c, 0x7f, 0xb7, 0xfb, 0xcb, 0x15, 0x9b, 0xe3, + 0x37, 0xd3, 0x7d, 0x33, 0x36, 0xd7, 0xfe, 0xb9, + 0x63, 0x72, 0x4f, 0xdf, 0xb0, 0xec, 0xb7, 0x67, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, }, + {0xa5, 0x36, 0x40, 0xc8, 0x3d, 0xc2, 0x08, 0x60, + 0x3d, 0xed, 0x83, 0xe4, 0xec, 0xf7, 0x58, 0xf2, + 0x4c, 0x35, 0x7d, 0x7c, 0xf4, 0x80, 0x88, 0xb2, + 0xce, 0x01, 0xe9, 0xfa, 0xd5, 0x81, 0x4c, 0xd7, + 0x24, 0x19, 0x9c, 0x4a, 0x5b, 0x97, 0x4a, 0x43, + 0x68, 0x5f, 0xbf, 0x5b, 0x8b, 0xac, 0x69, 0x45, + 0x9c, 0x94, 0x69, 0xbc, 0x8f, 0x23, 0xcc, 0xaf, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, }, + {0xba, 0xa4, 0xd8, 0x63, 0x55, 0x11, 0xa7, 0xd2, + 0x88, 0xae, 0xbe, 0xed, 0xd1, 0x2c, 0xe5, 0x29, + 0xff, 0x10, 0x2c, 0x91, 0xf9, 0x7f, 0x86, 0x7e, + 0x21, 0x91, 0x6b, 0xf9, 0x97, 0x9a, 0x5f, 0x47, + 0x59, 0xf8, 0x0f, 0x4f, 0xb4, 0xec, 0x2e, 0x34, + 0xf5, 0x56, 0x6d, 0x59, 0x56, 0x80, 0xa1, 0x17, + 0x35, 0xe7, 0xb6, 0x10, 0x46, 0x12, 0x79, 0x89, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, }, + {0x0b, 0x6e, 0xc4, 0xfe, 0x17, 0x77, 0x38, 0x24, + 0x04, 0xef, 0x67, 0x99, 0x97, 0xba, 0x8d, 0x1c, + 0xc5, 0xcd, 0x8e, 0x85, 0x34, 0x92, 0x59, 0xf5, + 0x90, 0xc4, 0xc6, 0x6d, 0x33, 0x99, 0xd4, 0x64, + 0x34, 0x59, 0x06, 0xb1, 0x1b, 0x00, 0xe3, 0x63, + 0xef, 0x42, 0x92, 0x21, 0xf2, 0xec, 0x72, 0x0d, + 0x2f, 0x66, 0x5d, 0x7d, 0xea, 0xd5, 0xb4, 0x82, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, }, + {0xb8, 0x35, 0x7c, 0x3a, 0x6c, 0xee, 0xf2, 0x88, + 0x31, 0x0e, 0x17, 0xb8, 0xbf, 0xef, 0xf9, 0x20, + 0x08, 0x46, 0xca, 0x8c, 0x19, 0x42, 0x49, 0x7c, + 0x48, 0x44, 0x03, 0xbc, 0xff, 0x14, 0x9e, 0xfa, + 0x66, 0x06, 0xa6, 0xbd, 0x20, 0xef, 0x7d, 0x1b, + 0x06, 0xbd, 0x92, 0xf6, 0x90, 0x46, 0x39, 0xdc, + 0xe5, 0x17, 0x4d, 0xb6, 0xcc, 0x55, 0x4a, 0x26, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, }, + {0xc9, 0xff, 0x61, 0xb0, 0x40, 0x87, 0x4c, 0x05, + 0x68, 0x47, 0x92, 0x16, 0x82, 0x4a, 0x15, 0xea, + 0xb1, 0xa8, 0x38, 0xa7, 0x97, 0xd1, 0x89, 0x74, + 0x62, 0x26, 0xe4, 0xcc, 0xea, 0x98, 0xd6, 0x0e, + 0x5f, 0xfc, 0x9b, 0x8f, 0xcf, 0x99, 0x9f, 0xab, + 0x1d, 0xf7, 0xe7, 0xef, 0x70, 0x84, 0xf2, 0x0d, + 0xdb, 0x61, 0xbb, 0x04, 0x5a, 0x6c, 0xe0, 0x02, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x13, }, + {0xa1, 0xe8, 0x1c, 0x04, 0xf3, 0x0c, 0xe2, 0x01, + 0xc7, 0xc9, 0xac, 0xe7, 0x85, 0xed, 0x44, 0xcc, + 0x33, 0xb4, 0x55, 0xa0, 0x22, 0xf2, 0xac, 0xdb, + 0xc6, 0xca, 0xe8, 0x3c, 0xdc, 0xf1, 0xf6, 0xc3, + 0xdb, 0x09, 0xc7, 0x0a, 0xcc, 0x25, 0x39, 0x1d, + 0x49, 0x2f, 0xe2, 0x5b, 0x4a, 0x18, 0x0b, 0xab, + 0xd6, 0xce, 0xa3, 0x56, 0xc0, 0x47, 0x19, 0xcd, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x14, }, + {0xfc, 0xc7, 0xf2, 0xb4, 0x5d, 0xf1, 0xcd, 0x5a, + 0x3c, 0x0c, 0x07, 0x31, 0xca, 0x47, 0xa8, 0xaf, + 0x75, 0xcf, 0xb0, 0x34, 0x7e, 0x83, 0x54, 0xee, + 0xfe, 0x78, 0x24, 0x55, 0x0d, 0x5d, 0x71, 0x10, + 0x27, 0x4c, 0xba, 0x7c, 0xde, 0xe9, 0x0e, 0x1a, + 0x8b, 0x0d, 0x39, 0x4c, 0x37, 0x6a, 0x55, 0x73, + 0xdb, 0x6b, 0xe0, 0xbf, 0x27, 0x47, 0xf5, 0x30, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x8e, 0xbb, 0xb9, + 0x5e, 0xed, 0x0e, 0x13, }, + {0x61, 0xf0, 0x77, 0xc6, 0xf6, 0x2e, 0xd8, 0x02, + 0xda, 0xd7, 0xc2, 0xf3, 0x8f, 0x5c, 0x67, 0xf2, + 0xcc, 0x45, 0x36, 0x01, 0xe6, 0x1b, 0xd0, 0x76, + 0xbb, 0x46, 0x17, 0x9e, 0x22, 0x72, 0xf9, 0xe9, + 0xf5, 0x93, 0x3e, 0x70, 0x38, 0x8e, 0xe6, 0x52, + 0x51, 0x34, 0x43, 0xb5, 0xe2, 0x89, 0xdd, 0x13, + 0x5d, 0xcc, 0x0d, 0x02, 0x99, 0xb2, 0x25, 0xe4, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x9d, 0x89, + 0x3d, 0x4c, 0xdd, 0x74, 0x72, 0x46, 0xcd, 0xca, + 0x43, 0x59, 0x0e, 0x13, }, + {0x02, 0x98, 0x95, 0xf0, 0xaf, 0x49, 0x6b, 0xfc, + 0x62, 0xb6, 0xef, 0x8d, 0x8a, 0x65, 0xc8, 0x8c, + 0x61, 0x39, 0x49, 0xb0, 0x36, 0x68, 0xaa, 0xb4, + 0xf0, 0x42, 0x9e, 0x35, 0x3e, 0xa6, 0xe5, 0x3f, + 0x9a, 0x84, 0x1f, 0x20, 0x19, 0xec, 0x24, 0xbd, + 0xe1, 0xa7, 0x56, 0x77, 0xaa, 0x9b, 0x59, 0x02, + 0xe6, 0x10, 0x81, 0xc0, 0x10, 0x64, 0xde, 0x93, + }, + }, + { + {0x41, 0xff, 0xc1, 0xff, 0xff, 0xfe, 0x01, 0xff, + 0xfc, 0x00, 0x03, 0xff, 0xfe, 0x00, 0x07, 0xc0, + 0x01, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xf0, 0x7f, + 0xfe, 0x00, 0x07, 0xc0, }, + {0xab, 0x68, 0x99, 0x30, 0xbc, 0xae, 0x4a, 0x4a, + 0xa5, 0xf5, 0xcb, 0x08, 0x5e, 0x82, 0x3e, 0x8a, + 0xe3, 0x0f, 0xd3, 0x65, 0xeb, 0x1d, 0xa4, 0xab, + 0xa9, 0xcf, 0x03, 0x79, 0x33, 0x45, 0xa1, 0x21, + 0xbb, 0xd2, 0x33, 0x54, 0x8a, 0xf0, 0xd2, 0x10, + 0x65, 0x4e, 0xb4, 0x0b, 0xab, 0x78, 0x8a, 0x03, + 0x66, 0x64, 0x19, 0xbe, 0x6f, 0xbd, 0x34, 0xe7, + }, + }, + { + {0x7f, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xc0, 0x03, + 0xff, 0xff, 0xfc, 0x00, 0x7f, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0xff, }, + {0xbd, 0xb6, 0xa8, 0x81, 0x7c, 0x1f, 0x89, 0xda, + 0x1c, 0x2f, 0x3d, 0xd8, 0xe9, 0x7f, 0xeb, 0x44, + 0x94, 0xf2, 0xed, 0x30, 0x2a, 0x4c, 0xe2, 0xbc, + 0x7f, 0x5f, 0x40, 0x25, 0x4c, 0x70, 0x20, 0xd5, + 0x7c, 0x00, 0x41, 0x18, 0x89, 0x46, 0x2d, 0x77, + 0xa5, 0x43, 0x8b, 0xb4, 0xe9, 0x7d, 0x17, 0x77, + 0x00, 0xbf, 0x72, 0x43, 0xa0, 0x7f, 0x16, 0x80, + }, + }, + { + {0x7f, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xf8, 0xff, 0xff, + 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, + 0x00, 0x0f, 0xff, 0xff, }, + {0xd5, 0x8b, 0x61, 0xaa, 0x41, 0xc3, 0x2d, 0xd5, + 0xeb, 0xa4, 0x62, 0x64, 0x7d, 0xba, 0x75, 0xc5, + 0xd6, 0x7c, 0x83, 0x60, 0x6c, 0x0a, 0xf2, 0xbd, + 0x92, 0x84, 0x46, 0xa9, 0xd2, 0x4b, 0xa6, 0xa8, + 0x37, 0xbe, 0x04, 0x60, 0xdd, 0x10, 0x7a, 0xe7, + 0x77, 0x25, 0x69, 0x6d, 0x21, 0x14, 0x46, 0xc5, + 0x60, 0x9b, 0x45, 0x95, 0x97, 0x6b, 0x16, 0xbd, + }, + }, + { + {0x7f, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xfe, 0x3f, + 0xff, 0xfc, 0x10, 0x00, 0x00, 0x20, 0x00, 0x3f, + 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x3f, 0xff, 0xff, 0xff, }, + {0xdc, 0x9f, 0xa7, 0x79, 0x78, 0xa0, 0x05, 0x51, + 0x09, 0x80, 0xe9, 0x29, 0xa1, 0x48, 0x5f, 0x63, + 0x71, 0x6d, 0xf6, 0x95, 0xd7, 0xa0, 0xc1, 0x8b, + 0xb5, 0x18, 0xdf, 0x03, 0xed, 0xe2, 0xb0, 0x16, + 0xf2, 0xdd, 0xff, 0xc2, 0xa8, 0xc0, 0x15, 0xb1, + 0x34, 0x92, 0x82, 0x75, 0xce, 0x09, 0xe5, 0x66, + 0x1b, 0x7a, 0xb1, 0x4c, 0xe0, 0xd1, 0xd4, 0x03, + }, + }, + { + {0x70, 0x01, 0xf0, 0x00, 0x1c, 0x00, 0x01, 0xc0, + 0x00, 0x00, 0x1f, 0xff, 0xff, 0xfc, 0x00, 0x00, + 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x0f, 0xc0, 0x00, + 0x00, 0x01, 0xfc, 0x00, }, + {0x49, 0x9d, 0x8b, 0x28, 0x29, 0xcf, 0xb8, 0x79, + 0xc9, 0x01, 0xf7, 0xd8, 0x5d, 0x35, 0x70, 0x45, + 0xed, 0xab, 0x55, 0x02, 0x88, 0x24, 0xd0, 0xf0, + 0x5b, 0xa2, 0x79, 0xba, 0xbf, 0x92, 0x95, 0x37, + 0xb0, 0x6e, 0x40, 0x15, 0x91, 0x96, 0x39, 0xd9, + 0x4f, 0x57, 0x83, 0x8f, 0xa3, 0x3f, 0xc3, 0xd9, + 0x52, 0x59, 0x8d, 0xcd, 0xbb, 0x44, 0xd6, 0x38, + }, + }, + { + {0x00, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, + 0x00, 0xff, 0xf0, 0x30, 0x00, 0x1f, 0x00, 0x00, + 0xff, 0xff, 0xf0, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, }, + {0x82, 0x46, 0xc9, 0x99, 0x13, 0x71, 0x86, 0x63, + 0x2c, 0x5f, 0x9e, 0xdd, 0xf3, 0xb1, 0xb0, 0xe1, + 0x76, 0x4c, 0x5e, 0x8b, 0xd0, 0xe0, 0xd8, 0xa5, + 0x54, 0xb9, 0xcb, 0x77, 0xe8, 0x0e, 0xd8, 0x66, + 0x0b, 0xc1, 0xcb, 0x17, 0xac, 0x7d, 0x84, 0x5b, + 0xe4, 0x0a, 0x7a, 0x02, 0x2d, 0x33, 0x06, 0xf1, + 0x16, 0xae, 0x9f, 0x81, 0xfe, 0xa6, 0x59, 0x47, + }, + }, + { + {0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xfe, 0x08, 0x00, 0x00, 0x1f, + 0xf0, 0x00, 0x1f, 0xff, }, + {0x66, 0x70, 0xc2, 0x0a, 0xfc, 0xce, 0xae, 0xa6, + 0x72, 0xc9, 0x7f, 0x75, 0xe2, 0xe9, 0xdd, 0x5c, + 0x84, 0x60, 0xe5, 0x4b, 0xb3, 0x85, 0x38, 0xeb, + 0xb4, 0xbd, 0x30, 0xeb, 0xf2, 0x80, 0xd8, 0x00, + 0x8d, 0x07, 0xa4, 0xca, 0xf5, 0x42, 0x71, 0xf9, + 0x93, 0x52, 0x7d, 0x46, 0xff, 0x3f, 0xf4, 0x6f, + 0xd1, 0x19, 0x0a, 0x3f, 0x1f, 0xaa, 0x4f, 0x74, + }, + }, + { + {0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc0, 0x00, 0x07, 0xff, 0xff, 0xe0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, }, + {0x00, 0x0e, 0xca, 0x93, 0x42, 0x47, 0x42, 0x5c, + 0xfd, 0x94, 0x9b, 0x79, 0x5c, 0xb5, 0xce, 0x1e, + 0xff, 0x40, 0x15, 0x50, 0x38, 0x6e, 0x28, 0xd1, + 0xa4, 0xc5, 0xa8, 0xeb, 0xd4, 0xc0, 0x10, 0x40, + 0xdb, 0xa1, 0x96, 0x28, 0x93, 0x1b, 0xc8, 0x85, + 0x53, 0x70, 0x31, 0x7c, 0x72, 0x2c, 0xbd, 0x9c, + 0xa6, 0x15, 0x69, 0x85, 0xf1, 0xc2, 0xe9, 0xce, + }, + }, + { + {0x7f, 0xff, 0xfc, 0x03, 0xff, 0x80, 0x7f, 0xff, + 0xe0, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x0f, 0xff, + 0x80, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, + 0xff, 0xfe, 0x00, 0x1f, }, + {0xef, 0x35, 0x3b, 0xf5, 0xc7, 0x3c, 0xd5, 0x51, + 0xb9, 0x6d, 0x59, 0x6f, 0xbc, 0x9a, 0x67, 0xf1, + 0x6d, 0x61, 0xdd, 0x9f, 0xe5, 0x6a, 0xf1, 0x9d, + 0xe1, 0xfb, 0xa9, 0xcd, 0x21, 0x77, 0x1b, 0x9c, + 0xdc, 0xe3, 0xe8, 0x43, 0x0c, 0x09, 0xb3, 0x83, + 0x8b, 0xe7, 0x0b, 0x48, 0xc2, 0x1e, 0x15, 0xbc, + 0x09, 0xee, 0x1f, 0x2d, 0x79, 0x45, 0xb9, 0x1f, + }, + }, + { + {0x00, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x7f, 0xff, + 0xff, 0xff, 0x01, 0xff, 0xfe, 0x03, 0xff, 0xfe, + 0x40, 0x00, 0x38, 0x00, 0x07, 0xe0, 0x00, 0x3f, + 0xfe, 0x00, 0x00, 0x00, }, + {0x40, 0x36, 0x05, 0x2a, 0x30, 0x91, 0xeb, 0x48, + 0x10, 0x46, 0xad, 0x32, 0x89, 0xc9, 0x5d, 0x3a, + 0xc9, 0x05, 0xca, 0x00, 0x23, 0xde, 0x2c, 0x03, + 0xec, 0xd4, 0x51, 0xcf, 0xd7, 0x68, 0x16, 0x5a, + 0x38, 0xa2, 0xb9, 0x6f, 0x81, 0x25, 0x86, 0xa9, + 0xd5, 0x9d, 0x41, 0x36, 0x03, 0x5d, 0x9c, 0x85, + 0x3a, 0x5b, 0xf2, 0xe1, 0xc8, 0x6a, 0x49, 0x93, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x29, }, + {0xfc, 0xc7, 0xf2, 0xb4, 0x5d, 0xf1, 0xcd, 0x5a, + 0x3c, 0x0c, 0x07, 0x31, 0xca, 0x47, 0xa8, 0xaf, + 0x75, 0xcf, 0xb0, 0x34, 0x7e, 0x83, 0x54, 0xee, + 0xfe, 0x78, 0x24, 0x55, 0xf2, 0xa2, 0x8e, 0xef, + 0xd8, 0xb3, 0x45, 0x83, 0x21, 0x16, 0xf1, 0xe5, + 0x74, 0xf2, 0xc6, 0xb2, 0xc8, 0x95, 0xaa, 0x8c, + 0x24, 0x94, 0x1f, 0x40, 0xd8, 0xb8, 0x0a, 0xd1, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2a, }, + {0xa1, 0xe8, 0x1c, 0x04, 0xf3, 0x0c, 0xe2, 0x01, + 0xc7, 0xc9, 0xac, 0xe7, 0x85, 0xed, 0x44, 0xcc, + 0x33, 0xb4, 0x55, 0xa0, 0x22, 0xf2, 0xac, 0xdb, + 0xc6, 0xca, 0xe8, 0x3c, 0x23, 0x0e, 0x09, 0x3c, + 0x24, 0xf6, 0x38, 0xf5, 0x33, 0xda, 0xc6, 0xe2, + 0xb6, 0xd0, 0x1d, 0xa3, 0xb5, 0xe7, 0xf4, 0x54, + 0x29, 0x31, 0x5c, 0xa9, 0x3f, 0xb8, 0xe6, 0x34, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2b, }, + {0xc9, 0xff, 0x61, 0xb0, 0x40, 0x87, 0x4c, 0x05, + 0x68, 0x47, 0x92, 0x16, 0x82, 0x4a, 0x15, 0xea, + 0xb1, 0xa8, 0x38, 0xa7, 0x97, 0xd1, 0x89, 0x74, + 0x62, 0x26, 0xe4, 0xcc, 0x15, 0x67, 0x29, 0xf1, + 0xa0, 0x03, 0x64, 0x70, 0x30, 0x66, 0x60, 0x54, + 0xe2, 0x08, 0x18, 0x0f, 0x8f, 0x7b, 0x0d, 0xf2, + 0x24, 0x9e, 0x44, 0xfb, 0xa5, 0x93, 0x1f, 0xff, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2c, }, + {0xb8, 0x35, 0x7c, 0x3a, 0x6c, 0xee, 0xf2, 0x88, + 0x31, 0x0e, 0x17, 0xb8, 0xbf, 0xef, 0xf9, 0x20, + 0x08, 0x46, 0xca, 0x8c, 0x19, 0x42, 0x49, 0x7c, + 0x48, 0x44, 0x03, 0xbc, 0x00, 0xeb, 0x61, 0x05, + 0x99, 0xf9, 0x59, 0x42, 0xdf, 0x10, 0x82, 0xe4, + 0xf9, 0x42, 0x6d, 0x08, 0x6f, 0xb9, 0xc6, 0x23, + 0x1a, 0xe8, 0xb2, 0x49, 0x33, 0xaa, 0xb5, 0xdb, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2d, }, + {0x0b, 0x6e, 0xc4, 0xfe, 0x17, 0x77, 0x38, 0x24, + 0x04, 0xef, 0x67, 0x99, 0x97, 0xba, 0x8d, 0x1c, + 0xc5, 0xcd, 0x8e, 0x85, 0x34, 0x92, 0x59, 0xf5, + 0x90, 0xc4, 0xc6, 0x6d, 0xcc, 0x66, 0x2b, 0x9b, + 0xcb, 0xa6, 0xf9, 0x4e, 0xe4, 0xff, 0x1c, 0x9c, + 0x10, 0xbd, 0x6d, 0xdd, 0x0d, 0x13, 0x8d, 0xf2, + 0xd0, 0x99, 0xa2, 0x82, 0x15, 0x2a, 0x4b, 0x7f, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2e, }, + {0xba, 0xa4, 0xd8, 0x63, 0x55, 0x11, 0xa7, 0xd2, + 0x88, 0xae, 0xbe, 0xed, 0xd1, 0x2c, 0xe5, 0x29, + 0xff, 0x10, 0x2c, 0x91, 0xf9, 0x7f, 0x86, 0x7e, + 0x21, 0x91, 0x6b, 0xf9, 0x68, 0x65, 0xa0, 0xb8, + 0xa6, 0x07, 0xf0, 0xb0, 0x4b, 0x13, 0xd1, 0xcb, + 0x0a, 0xa9, 0x92, 0xa5, 0xa9, 0x7f, 0x5e, 0xe8, + 0xca, 0x18, 0x49, 0xef, 0xb9, 0xed, 0x86, 0x78, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x2f, }, + {0xa5, 0x36, 0x40, 0xc8, 0x3d, 0xc2, 0x08, 0x60, + 0x3d, 0xed, 0x83, 0xe4, 0xec, 0xf7, 0x58, 0xf2, + 0x4c, 0x35, 0x7d, 0x7c, 0xf4, 0x80, 0x88, 0xb2, + 0xce, 0x01, 0xe9, 0xfa, 0x2a, 0x7e, 0xb3, 0x28, + 0xdb, 0xe6, 0x63, 0xb5, 0xa4, 0x68, 0xb5, 0xbc, + 0x97, 0xa0, 0x40, 0xa3, 0x74, 0x53, 0x96, 0xba, + 0x63, 0x6b, 0x96, 0x43, 0x70, 0xdc, 0x33, 0x52, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x30, }, + {0x34, 0xe8, 0xe1, 0x7a, 0x43, 0x0e, 0x43, 0x28, + 0x97, 0x93, 0xc3, 0x83, 0xfa, 0xc9, 0x77, 0x42, + 0x47, 0xb4, 0x0e, 0x9e, 0xbd, 0x33, 0x66, 0x98, + 0x1f, 0xcf, 0xae, 0xca, 0xda, 0xd7, 0xe6, 0x08, + 0xe3, 0x80, 0x48, 0x04, 0x34, 0xea, 0x64, 0x1c, + 0xc8, 0x2c, 0x82, 0xcb, 0xc9, 0x28, 0x01, 0x46, + 0x9c, 0x8d, 0xb0, 0x20, 0x4f, 0x13, 0x48, 0x9a, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x31, }, + {0x6e, 0x31, 0xee, 0x1d, 0xc1, 0x37, 0xf8, 0x1b, + 0x05, 0x67, 0x52, 0xe4, 0xde, 0xab, 0x14, 0x43, + 0xa4, 0x81, 0x03, 0x3e, 0x9b, 0x4c, 0x93, 0xa3, + 0x04, 0x4f, 0x4f, 0x7a, 0xdf, 0x82, 0x22, 0x0f, + 0xc7, 0xa4, 0x02, 0x15, 0x49, 0x16, 0x53, 0x25, + 0x72, 0x5f, 0x94, 0xc3, 0x41, 0x0d, 0xdb, 0x56, + 0xc5, 0x4e, 0x16, 0x1f, 0xc9, 0xef, 0x62, 0xee, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x32, }, + {0xef, 0x53, 0xb6, 0x29, 0x4a, 0xca, 0x43, 0x1f, + 0x0f, 0x3c, 0x22, 0xdc, 0x82, 0xeb, 0x90, 0x50, + 0x32, 0x4f, 0x1d, 0x88, 0xd3, 0x77, 0xe7, 0x16, + 0x44, 0x8e, 0x50, 0x7c, 0xdf, 0x4a, 0xef, 0xff, + 0xbf, 0x6d, 0x16, 0x99, 0xc9, 0x30, 0x48, 0x1c, + 0xd1, 0x02, 0x12, 0x7c, 0x9a, 0x3d, 0x99, 0x20, + 0x48, 0xab, 0x05, 0x92, 0x9b, 0x6e, 0x59, 0x27, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x33, }, + {0xae, 0xa9, 0xe1, 0x7a, 0x30, 0x65, 0x17, 0xeb, + 0x89, 0x15, 0x2a, 0xa7, 0x09, 0x6d, 0x2c, 0x38, + 0x1e, 0xc8, 0x13, 0xc5, 0x1a, 0xa8, 0x80, 0xe7, + 0xbe, 0xe2, 0xc0, 0xfd, 0xc6, 0x44, 0xcf, 0x15, + 0x4c, 0xc8, 0x1f, 0x5a, 0xde, 0x49, 0x34, 0x5e, + 0x54, 0x1b, 0x4d, 0x4b, 0x5c, 0x1a, 0xdb, 0x3e, + 0xb5, 0xc0, 0x1c, 0x14, 0xee, 0x94, 0x9a, 0xa2, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x34, }, + {0x2f, 0xdc, 0xcc, 0xfe, 0xe7, 0x20, 0xa7, 0x7e, + 0xf6, 0xcb, 0x3b, 0xfb, 0xb4, 0x47, 0xf9, 0x38, + 0x31, 0x17, 0xe3, 0xda, 0xa4, 0xa0, 0x7e, 0x36, + 0xed, 0x15, 0xf7, 0x8d, 0xc8, 0xe8, 0xcd, 0x1b, + 0x0b, 0xe4, 0x0b, 0x08, 0x77, 0xcf, 0xca, 0x19, + 0x58, 0x60, 0x31, 0x22, 0xf1, 0xe6, 0x91, 0x4f, + 0x84, 0xb7, 0xe8, 0xe9, 0x68, 0xae, 0x8b, 0x9e, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x35, }, + {0x85, 0x8e, 0x6f, 0x9c, 0xc6, 0xc1, 0x2c, 0x31, + 0xf5, 0xdf, 0x12, 0x4a, 0xa7, 0x77, 0x67, 0xb0, + 0x5c, 0x8b, 0xc0, 0x21, 0xbd, 0x68, 0x3d, 0x2b, + 0x55, 0x57, 0x15, 0x50, 0xfb, 0x92, 0x32, 0xc1, + 0x5a, 0x3b, 0xc7, 0x67, 0x3a, 0x3a, 0x03, 0xb0, + 0x25, 0x38, 0x24, 0xc5, 0x3d, 0x0f, 0xd1, 0x41, + 0x1b, 0x1c, 0xab, 0xe2, 0xe1, 0x87, 0xfb, 0x87, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x36, }, + {0xdb, 0x2f, 0x6b, 0xe6, 0x30, 0xe2, 0x46, 0xa5, + 0xcf, 0x7d, 0x99, 0xb8, 0x51, 0x94, 0xb1, 0x23, + 0xd4, 0x87, 0xe2, 0xd4, 0x66, 0xb9, 0x4b, 0x24, + 0xa0, 0x3c, 0x3e, 0x28, 0xf0, 0xc5, 0xcf, 0xf7, + 0xab, 0x68, 0x0d, 0x09, 0xee, 0x11, 0xda, 0xe8, + 0x4e, 0x9c, 0x10, 0x72, 0xac, 0x48, 0xea, 0x2e, + 0x74, 0x4b, 0x1b, 0x7f, 0x72, 0xfd, 0x46, 0x9e, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x37, }, + {0x1f, 0x24, 0x83, 0xf8, 0x25, 0x72, 0x25, 0x1f, + 0xca, 0x97, 0x5f, 0xea, 0x40, 0xdb, 0x82, 0x1d, + 0xf8, 0xad, 0x82, 0xa3, 0xc0, 0x02, 0xee, 0x6c, + 0x57, 0x11, 0x24, 0x08, 0x76, 0x05, 0x0f, 0x33, + 0x48, 0xaf, 0x26, 0x64, 0xaa, 0xc3, 0xa8, 0xb0, + 0x52, 0x81, 0x30, 0x4e, 0xbc, 0x7a, 0x79, 0x14, + 0xc6, 0xad, 0x50, 0xa4, 0xb4, 0xea, 0xc3, 0x83, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x38, }, + {0x31, 0xc4, 0x9a, 0xe7, 0x5b, 0xce, 0x78, 0x07, + 0xcd, 0xff, 0x22, 0x05, 0x5d, 0x94, 0xee, 0x90, + 0x21, 0xfe, 0xdb, 0xb5, 0xab, 0x51, 0xc5, 0x75, + 0x26, 0xf0, 0x11, 0xaa, 0xd8, 0x17, 0x40, 0x0e, + 0x8b, 0xa9, 0xca, 0x13, 0xa4, 0x5f, 0x36, 0x0e, + 0x3d, 0x12, 0x1e, 0xaa, 0xeb, 0x39, 0xaf, 0x82, + 0xd6, 0x00, 0x1c, 0x81, 0x86, 0xf5, 0xf8, 0x66, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x39, }, + {0xae, 0x99, 0xfe, 0xeb, 0xb5, 0xd2, 0x69, 0x45, + 0xb5, 0x48, 0x92, 0x09, 0x2a, 0x8a, 0xee, 0x02, + 0x91, 0x29, 0x30, 0xfa, 0x41, 0xcd, 0x11, 0x4e, + 0x40, 0x44, 0x73, 0x01, 0xfb, 0x7d, 0xa7, 0xf5, + 0xf1, 0x3a, 0x43, 0xb8, 0x17, 0x74, 0x37, 0x3c, + 0x87, 0x9c, 0xd3, 0x2d, 0x69, 0x34, 0xc0, 0x5f, + 0xa7, 0x58, 0xee, 0xb1, 0x4f, 0xcf, 0xab, 0x38, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x3a, }, + {0xdf, 0x1b, 0x1d, 0x66, 0xa5, 0x51, 0xd0, 0xd3, + 0x1e, 0xff, 0x82, 0x25, 0x58, 0xb9, 0xd2, 0xcc, + 0x75, 0xc2, 0x18, 0x02, 0x79, 0xfe, 0x0d, 0x08, + 0xfd, 0x89, 0x6d, 0x04, 0x5c, 0x08, 0x0f, 0xc3, + 0x52, 0x2f, 0x41, 0xbb, 0xb3, 0xf5, 0x5a, 0x97, + 0xcf, 0xec, 0xf2, 0x1f, 0x88, 0x2c, 0xe8, 0xcb, + 0xb1, 0xe5, 0x0c, 0xa6, 0xe6, 0x7e, 0x56, 0xdc, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x3b, }, + {0x70, 0x6a, 0x46, 0xdc, 0x76, 0xdc, 0xb7, 0x67, + 0x98, 0xe6, 0x0e, 0x6d, 0x89, 0x47, 0x47, 0x88, + 0xd1, 0x6d, 0xc1, 0x80, 0x32, 0xd2, 0x68, 0xfd, + 0x1a, 0x70, 0x4f, 0xa6, 0xe3, 0xd4, 0x89, 0x58, + 0x43, 0xda, 0x18, 0x8f, 0xd5, 0x8f, 0xb0, 0x56, + 0x79, 0x76, 0xd7, 0xb5, 0x03, 0x59, 0xd6, 0xb7, + 0x85, 0x30, 0xc8, 0xf6, 0x2d, 0x1b, 0x17, 0x46, + }, + }, + { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2, + 0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45, + 0x5c, 0x5c, 0x2a, 0x3c, }, + {0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f, + 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3, + 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6, + 0x11, 0x5c, 0x1d, 0x21, 0x42, 0xc8, 0x9c, 0x77, + 0x4a, 0x08, 0xdc, 0x04, 0xb3, 0xdd, 0x20, 0x19, + 0x32, 0xbc, 0x8a, 0x5e, 0xa5, 0xf8, 0xb8, 0x9b, + 0xbb, 0x2a, 0x7e, 0x66, 0x7a, 0xff, 0x81, 0xcd, + }, + }, +}; + +TEST(P224, ExternalToInternalAndBack) { + Point point; + + EXPECT_TRUE(point.SetFromString(base::StringPiece( + reinterpret_cast(kBasePointExternal), + sizeof(kBasePointExternal)))); + + const std::string external = point.ToString(); + + ASSERT_EQ(external.size(), 56u); + EXPECT_TRUE(memcmp(external.data(), kBasePointExternal, + sizeof(kBasePointExternal)) == 0); +} + +TEST(P224, ScalarBaseMult) { + Point point; + + for (size_t i = 0; i < arraysize(kNISTTestVectors); i++) { + p224::ScalarBaseMult(kNISTTestVectors[i].scalar, &point); + const std::string external = point.ToString(); + ASSERT_EQ(external.size(), 56u); + EXPECT_TRUE(memcmp(external.data(), kNISTTestVectors[i].affine, + external.size()) == 0); + } +} + +TEST(P224, Addition) { + Point a, b, minus_b, sum, a_again; + + ASSERT_TRUE(a.SetFromString(base::StringPiece( + reinterpret_cast(kNISTTestVectors[10].affine), 56))); + ASSERT_TRUE(b.SetFromString(base::StringPiece( + reinterpret_cast(kNISTTestVectors[11].affine), 56))); + + p224::Negate(b, &minus_b); + p224::Add(a, b, &sum); + EXPECT_TRUE(memcmp(&sum, &a, sizeof(sum) != 0)); + p224::Add(minus_b, sum, &a_again); + EXPECT_TRUE(a_again.ToString() == a.ToString()); +} + +TEST(P224, Infinity) { + char zeros[56]; + memset(zeros, 0, sizeof(zeros)); + + // Test that x^0 = ∞. + Point a; + p224::ScalarBaseMult(reinterpret_cast(zeros), &a); + EXPECT_TRUE(memcmp(zeros, a.ToString().data(), sizeof(zeros)) == 0); + + // We shouldn't allow ∞ to be imported. + EXPECT_FALSE(a.SetFromString(std::string(zeros, sizeof(zeros)))); +} + +} // namespace crypto diff --git a/chromium/crypto/random.cc b/chromium/crypto/random.cc new file mode 100644 index 00000000000..a19bb1a11c7 --- /dev/null +++ b/chromium/crypto/random.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/random.h" + +#include "base/rand_util.h" + +namespace crypto { + +void RandBytes(void *bytes, size_t length) { + // It's OK to call base::RandBytes(), because it's already strongly random. + // But _other_ code should go through this function to ensure that code which + // needs secure randomness is easily discoverable. + base::RandBytes(bytes, length); +} + +} // namespace crypto + diff --git a/chromium/crypto/random.h b/chromium/crypto/random.h new file mode 100644 index 00000000000..002616bd30e --- /dev/null +++ b/chromium/crypto/random.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_RANDOM_H_ +#define CRYPTO_RANDOM_H_ + +#include + +#include "crypto/crypto_export.h" + +namespace crypto { + +// Fills the given buffer with |length| random bytes of cryptographically +// secure random numbers. +// |length| must be positive. +CRYPTO_EXPORT void RandBytes(void *bytes, size_t length); + +} + +#endif diff --git a/chromium/crypto/random_unittest.cc b/chromium/crypto/random_unittest.cc new file mode 100644 index 00000000000..846d9b66f3d --- /dev/null +++ b/chromium/crypto/random_unittest.cc @@ -0,0 +1,27 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/random.h" + +#include "base/strings/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +// Basic functionality tests. Does NOT test the security of the random data. + +// Ensures we don't have all trivial data, i.e. that the data is indeed random. +// Currently, that means the bytes cannot be all the same (e.g. all zeros). +bool IsTrivial(const std::string& bytes) { + for (size_t i = 0; i < bytes.size(); i++) { + if (bytes[i] != bytes[0]) { + return false; + } + } + return true; +} + +TEST(RandBytes, RandBytes) { + std::string bytes(16, '\0'); + crypto::RandBytes(WriteInto(&bytes, bytes.size()), bytes.size()); + EXPECT_TRUE(!IsTrivial(bytes)); +} diff --git a/chromium/crypto/rsa_private_key.cc b/chromium/crypto/rsa_private_key.cc new file mode 100644 index 00000000000..812d9fa16e5 --- /dev/null +++ b/chromium/crypto/rsa_private_key.cc @@ -0,0 +1,384 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_util.h" + +// This file manually encodes and decodes RSA private keys using PrivateKeyInfo +// from PKCS #8 and RSAPrivateKey from PKCS #1. These structures are: +// +// PrivateKeyInfo ::= SEQUENCE { +// version Version, +// privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, +// privateKey PrivateKey, +// attributes [0] IMPLICIT Attributes OPTIONAL +// } +// +// RSAPrivateKey ::= SEQUENCE { +// version Version, +// modulus INTEGER, +// publicExponent INTEGER, +// privateExponent INTEGER, +// prime1 INTEGER, +// prime2 INTEGER, +// exponent1 INTEGER, +// exponent2 INTEGER, +// coefficient INTEGER +// } + +namespace { +// Helper for error handling during key import. +#define READ_ASSERT(truth) \ + if (!(truth)) { \ + NOTREACHED(); \ + return false; \ + } +} // namespace + +namespace crypto { + +const uint8 PrivateKeyInfoCodec::kRsaAlgorithmIdentifier[] = { + 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, + 0x05, 0x00 +}; + +PrivateKeyInfoCodec::PrivateKeyInfoCodec(bool big_endian) + : big_endian_(big_endian) {} + +PrivateKeyInfoCodec::~PrivateKeyInfoCodec() {} + +bool PrivateKeyInfoCodec::Export(std::vector* output) { + std::list content; + + // Version (always zero) + uint8 version = 0; + + PrependInteger(coefficient_, &content); + PrependInteger(exponent2_, &content); + PrependInteger(exponent1_, &content); + PrependInteger(prime2_, &content); + PrependInteger(prime1_, &content); + PrependInteger(private_exponent_, &content); + PrependInteger(public_exponent_, &content); + PrependInteger(modulus_, &content); + PrependInteger(&version, 1, &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + PrependTypeHeaderAndLength(kOctetStringTag, content.size(), &content); + + // RSA algorithm OID + for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i) + content.push_front(kRsaAlgorithmIdentifier[i - 1]); + + PrependInteger(&version, 1, &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everying into the output. + output->reserve(content.size()); + output->assign(content.begin(), content.end()); + + return true; +} + +bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::vector bit_string; + if (!ExportPublicKey(&bit_string)) + return false; + + // Add the sequence as the contents of a bit string. + std::list content; + PrependBitString(&bit_string[0], static_cast(bit_string.size()), + &content); + + // Add the RSA algorithm OID. + for (size_t i = sizeof(kRsaAlgorithmIdentifier); i > 0; --i) + content.push_front(kRsaAlgorithmIdentifier[i - 1]); + + // Finally, wrap everything in a sequence. + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + output->assign(content.begin(), content.end()); + + return true; +} + +bool PrivateKeyInfoCodec::ExportPublicKey(std::vector* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::list content; + PrependInteger(&public_exponent_[0], + static_cast(public_exponent_.size()), + &content); + PrependInteger(&modulus_[0], static_cast(modulus_.size()), &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + output->assign(content.begin(), content.end()); + + return true; +} + +bool PrivateKeyInfoCodec::Import(const std::vector& input) { + if (input.empty()) { + return false; + } + + // Parse the private key info up to the public key values, ignoring + // the subsequent private key values. + uint8* src = const_cast(&input.front()); + uint8* end = src + input.size(); + if (!ReadSequence(&src, end) || + !ReadVersion(&src, end) || + !ReadAlgorithmIdentifier(&src, end) || + !ReadTypeHeaderAndLength(&src, end, kOctetStringTag, NULL) || + !ReadSequence(&src, end) || + !ReadVersion(&src, end) || + !ReadInteger(&src, end, &modulus_)) + return false; + + int mod_size = modulus_.size(); + READ_ASSERT(mod_size % 2 == 0); + int primes_size = mod_size / 2; + + if (!ReadIntegerWithExpectedSize(&src, end, 4, &public_exponent_) || + !ReadIntegerWithExpectedSize(&src, end, mod_size, &private_exponent_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime1_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &prime2_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent1_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &exponent2_) || + !ReadIntegerWithExpectedSize(&src, end, primes_size, &coefficient_)) + return false; + + READ_ASSERT(src == end); + + + return true; +} + +void PrivateKeyInfoCodec::PrependInteger(const std::vector& in, + std::list* out) { + uint8* ptr = const_cast(&in.front()); + PrependIntegerImpl(ptr, in.size(), out, big_endian_); +} + +// Helper to prepend an ASN.1 integer. +void PrivateKeyInfoCodec::PrependInteger(uint8* val, + int num_bytes, + std::list* data) { + PrependIntegerImpl(val, num_bytes, data, big_endian_); +} + +void PrivateKeyInfoCodec::PrependIntegerImpl(uint8* val, + int num_bytes, + std::list* data, + bool big_endian) { + // Reverse input if little-endian. + std::vector tmp; + if (!big_endian) { + tmp.assign(val, val + num_bytes); + std::reverse(tmp.begin(), tmp.end()); + val = &tmp.front(); + } + + // ASN.1 integers are unpadded byte arrays, so skip any null padding bytes + // from the most-significant end of the integer. + int start = 0; + while (start < (num_bytes - 1) && val[start] == 0x00) { + start++; + num_bytes--; + } + PrependBytes(val, start, num_bytes, data); + + // ASN.1 integers are signed. To encode a positive integer whose sign bit + // (the most significant bit) would otherwise be set and make the number + // negative, ASN.1 requires a leading null byte to force the integer to be + // positive. + uint8 front = data->front(); + if ((front & 0x80) != 0) { + data->push_front(0x00); + num_bytes++; + } + + PrependTypeHeaderAndLength(kIntegerTag, num_bytes, data); +} + +bool PrivateKeyInfoCodec::ReadInteger(uint8** pos, + uint8* end, + std::vector* out) { + return ReadIntegerImpl(pos, end, out, big_endian_); +} + +bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(uint8** pos, + uint8* end, + size_t expected_size, + std::vector* out) { + std::vector temp; + if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian + return false; + + int pad = expected_size - temp.size(); + int index = 0; + if (out->size() == expected_size + 1) { + READ_ASSERT(out->front() == 0x00); + pad++; + index++; + } else { + READ_ASSERT(out->size() <= expected_size); + } + + out->insert(out->end(), pad, 0x00); + out->insert(out->end(), temp.begin(), temp.end()); + + // Reverse output if little-endian. + if (!big_endian_) + std::reverse(out->begin(), out->end()); + return true; +} + +bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8** pos, + uint8* end, + std::vector* out, + bool big_endian) { + uint32 length = 0; + if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length) || !length) + return false; + + // The first byte can be zero to force positiveness. We can ignore this. + if (**pos == 0x00) { + ++(*pos); + --length; + } + + if (length) + out->insert(out->end(), *pos, (*pos) + length); + + (*pos) += length; + + // Reverse output if little-endian. + if (!big_endian) + std::reverse(out->begin(), out->end()); + return true; +} + +void PrivateKeyInfoCodec::PrependBytes(uint8* val, + int start, + int num_bytes, + std::list* data) { + while (num_bytes > 0) { + --num_bytes; + data->push_front(val[start + num_bytes]); + } +} + +void PrivateKeyInfoCodec::PrependLength(size_t size, std::list* data) { + // The high bit is used to indicate whether additional octets are needed to + // represent the length. + if (size < 0x80) { + data->push_front(static_cast(size)); + } else { + uint8 num_bytes = 0; + while (size > 0) { + data->push_front(static_cast(size & 0xFF)); + size >>= 8; + num_bytes++; + } + CHECK_LE(num_bytes, 4); + data->push_front(0x80 | num_bytes); + } +} + +void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(uint8 type, + uint32 length, + std::list* output) { + PrependLength(length, output); + output->push_front(type); +} + +void PrivateKeyInfoCodec::PrependBitString(uint8* val, + int num_bytes, + std::list* output) { + // Start with the data. + PrependBytes(val, 0, num_bytes, output); + // Zero unused bits. + output->push_front(0); + // Add the length. + PrependLength(num_bytes + 1, output); + // Finally, add the bit string tag. + output->push_front((uint8) kBitStringTag); +} + +bool PrivateKeyInfoCodec::ReadLength(uint8** pos, uint8* end, uint32* result) { + READ_ASSERT(*pos < end); + int length = 0; + + // If the MSB is not set, the length is just the byte itself. + if (!(**pos & 0x80)) { + length = **pos; + (*pos)++; + } else { + // Otherwise, the lower 7 indicate the length of the length. + int length_of_length = **pos & 0x7F; + READ_ASSERT(length_of_length <= 4); + (*pos)++; + READ_ASSERT(*pos + length_of_length < end); + + length = 0; + for (int i = 0; i < length_of_length; ++i) { + length <<= 8; + length |= **pos; + (*pos)++; + } + } + + READ_ASSERT(*pos + length <= end); + if (result) *result = length; + return true; +} + +bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8** pos, + uint8* end, + uint8 expected_tag, + uint32* length) { + READ_ASSERT(*pos < end); + READ_ASSERT(**pos == expected_tag); + (*pos)++; + + return ReadLength(pos, end, length); +} + +bool PrivateKeyInfoCodec::ReadSequence(uint8** pos, uint8* end) { + return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL); +} + +bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8** pos, uint8* end) { + READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end); + READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier, + sizeof(kRsaAlgorithmIdentifier)) == 0); + (*pos) += sizeof(kRsaAlgorithmIdentifier); + return true; +} + +bool PrivateKeyInfoCodec::ReadVersion(uint8** pos, uint8* end) { + uint32 length = 0; + if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length)) + return false; + + // The version should be zero. + for (uint32 i = 0; i < length; ++i) { + READ_ASSERT(**pos == 0x00); + (*pos)++; + } + + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/rsa_private_key.h b/chromium/crypto/rsa_private_key.h new file mode 100644 index 00000000000..ad82148428a --- /dev/null +++ b/chromium/crypto/rsa_private_key.h @@ -0,0 +1,262 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_RSA_PRIVATE_KEY_H_ +#define CRYPTO_RSA_PRIVATE_KEY_H_ + +#include "build/build_config.h" + +#include +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +#if defined(USE_NSS) +#include "base/gtest_prod_util.h" +#endif + +#if defined(USE_OPENSSL) +// Forward declaration for openssl/*.h +typedef struct evp_pkey_st EVP_PKEY; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) +// Forward declaration. +typedef struct SECKEYPrivateKeyStr SECKEYPrivateKey; +typedef struct SECKEYPublicKeyStr SECKEYPublicKey; +#endif + + +namespace crypto { + +// Used internally by RSAPrivateKey for serializing and deserializing +// PKCS #8 PrivateKeyInfo and PublicKeyInfo. +class PrivateKeyInfoCodec { + public: + + // ASN.1 encoding of the AlgorithmIdentifier from PKCS #8. + static const uint8 kRsaAlgorithmIdentifier[]; + + // ASN.1 tags for some types we use. + static const uint8 kBitStringTag = 0x03; + static const uint8 kIntegerTag = 0x02; + static const uint8 kNullTag = 0x05; + static const uint8 kOctetStringTag = 0x04; + static const uint8 kSequenceTag = 0x30; + + // |big_endian| here specifies the byte-significance of the integer components + // that will be parsed & serialized (modulus(), etc...) during Import(), + // Export() and ExportPublicKeyInfo() -- not the ASN.1 DER encoding of the + // PrivateKeyInfo/PublicKeyInfo (which is always big-endian). + explicit PrivateKeyInfoCodec(bool big_endian); + + ~PrivateKeyInfoCodec(); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the PrivateKeyInfo structure to |output|. + bool Export(std::vector* output); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the PublicKeyInfo structure to |output|. + bool ExportPublicKeyInfo(std::vector* output); + + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the RSAPublicKey structure to |output|. + bool ExportPublicKey(std::vector* output); + + // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input| + // and populates the integer components with |big_endian_| byte-significance. + // IMPORTANT NOTE: This is currently *not* security-approved for importing + // keys from unstrusted sources. + bool Import(const std::vector& input); + + // Accessors to the contents of the integer components of the PrivateKeyInfo + // structure. + std::vector* modulus() { return &modulus_; }; + std::vector* public_exponent() { return &public_exponent_; }; + std::vector* private_exponent() { return &private_exponent_; }; + std::vector* prime1() { return &prime1_; }; + std::vector* prime2() { return &prime2_; }; + std::vector* exponent1() { return &exponent1_; }; + std::vector* exponent2() { return &exponent2_; }; + std::vector* coefficient() { return &coefficient_; }; + + private: + // Utility wrappers for PrependIntegerImpl that use the class's |big_endian_| + // value. + void PrependInteger(const std::vector& in, std::list* out); + void PrependInteger(uint8* val, int num_bytes, std::list* data); + + // Prepends the integer stored in |val| - |val + num_bytes| with |big_endian| + // byte-significance into |data| as an ASN.1 integer. + void PrependIntegerImpl(uint8* val, + int num_bytes, + std::list* data, + bool big_endian); + + // Utility wrappers for ReadIntegerImpl that use the class's |big_endian_| + // value. + bool ReadInteger(uint8** pos, uint8* end, std::vector* out); + bool ReadIntegerWithExpectedSize(uint8** pos, + uint8* end, + size_t expected_size, + std::vector* out); + + // Reads an ASN.1 integer from |pos|, and stores the result into |out| with + // |big_endian| byte-significance. + bool ReadIntegerImpl(uint8** pos, + uint8* end, + std::vector* out, + bool big_endian); + + // Prepends the integer stored in |val|, starting a index |start|, for + // |num_bytes| bytes onto |data|. + void PrependBytes(uint8* val, + int start, + int num_bytes, + std::list* data); + + // Helper to prepend an ASN.1 length field. + void PrependLength(size_t size, std::list* data); + + // Helper to prepend an ASN.1 type header. + void PrependTypeHeaderAndLength(uint8 type, + uint32 length, + std::list* output); + + // Helper to prepend an ASN.1 bit string + void PrependBitString(uint8* val, int num_bytes, std::list* output); + + // Read an ASN.1 length field. This also checks that the length does not + // extend beyond |end|. + bool ReadLength(uint8** pos, uint8* end, uint32* result); + + // Read an ASN.1 type header and its length. + bool ReadTypeHeaderAndLength(uint8** pos, + uint8* end, + uint8 expected_tag, + uint32* length); + + // Read an ASN.1 sequence declaration. This consumes the type header and + // length field, but not the contents of the sequence. + bool ReadSequence(uint8** pos, uint8* end); + + // Read the RSA AlgorithmIdentifier. + bool ReadAlgorithmIdentifier(uint8** pos, uint8* end); + + // Read one of the two version fields in PrivateKeyInfo. + bool ReadVersion(uint8** pos, uint8* end); + + // The byte-significance of the stored components (modulus, etc..). + bool big_endian_; + + // Component integers of the PrivateKeyInfo + std::vector modulus_; + std::vector public_exponent_; + std::vector private_exponent_; + std::vector prime1_; + std::vector prime2_; + std::vector exponent1_; + std::vector exponent2_; + std::vector coefficient_; + + DISALLOW_COPY_AND_ASSIGN(PrivateKeyInfoCodec); +}; + +// Encapsulates an RSA private key. Can be used to generate new keys, export +// keys to other formats, or to extract a public key. +// TODO(hclam): This class should be ref-counted so it can be reused easily. +class CRYPTO_EXPORT RSAPrivateKey { + public: + ~RSAPrivateKey(); + + // Create a new random instance. Can return NULL if initialization fails. + static RSAPrivateKey* Create(uint16 num_bits); + + // Create a new instance by importing an existing private key. The format is + // an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if + // initialization fails. + static RSAPrivateKey* CreateFromPrivateKeyInfo( + const std::vector& input); + +#if defined(USE_NSS) + // Create a new random instance. Can return NULL if initialization fails. + // The created key is permanent and is not exportable in plaintext form. + static RSAPrivateKey* CreateSensitive(uint16 num_bits); + + // Create a new instance by importing an existing private key. The format is + // an ASN.1-encoded PrivateKeyInfo block from PKCS #8. This can return NULL if + // initialization fails. + // The created key is permanent and is not exportable in plaintext form. + static RSAPrivateKey* CreateSensitiveFromPrivateKeyInfo( + const std::vector& input); + + // Create a new instance by referencing an existing private key + // structure. Does not import the key. + static RSAPrivateKey* CreateFromKey(SECKEYPrivateKey* key); + + // Import an existing public key, and then search for the private + // half in the key database. The format of the public key blob is is + // an X509 SubjectPublicKeyInfo block. This can return NULL if + // initialization fails or the private key cannot be found. The + // caller takes ownership of the returned object, but nothing new is + // created in the key database. + static RSAPrivateKey* FindFromPublicKeyInfo( + const std::vector& input); +#endif + +#if defined(USE_OPENSSL) + EVP_PKEY* key() { return key_; } +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + SECKEYPrivateKey* key() { return key_; } + SECKEYPublicKey* public_key() { return public_key_; } +#endif + + // Creates a copy of the object. + RSAPrivateKey* Copy() const; + + // Exports the private key to a PKCS #1 PrivateKey block. + bool ExportPrivateKey(std::vector* output) const; + + // Exports the public key to an X509 SubjectPublicKeyInfo block. + bool ExportPublicKey(std::vector* output) const; + + private: +#if defined(USE_NSS) + FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FindFromPublicKey); + FRIEND_TEST_ALL_PREFIXES(RSAPrivateKeyNSSTest, FailedFindFromPublicKey); +#endif + + // Constructor is private. Use one of the Create*() or Find*() + // methods above instead. + RSAPrivateKey(); + + // Shared helper for Create() and CreateSensitive(). + // TODO(cmasone): consider replacing |permanent| and |sensitive| with a + // flags arg created by ORing together some enumerated values. + // Note: |permanent| is only supported when USE_NSS is defined. + static RSAPrivateKey* CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive); + + // Shared helper for CreateFromPrivateKeyInfo() and + // CreateSensitiveFromPrivateKeyInfo(). + // Note: |permanent| is only supported when USE_NSS is defined. + static RSAPrivateKey* CreateFromPrivateKeyInfoWithParams( + const std::vector& input, + bool permanent, + bool sensitive); + +#if defined(USE_OPENSSL) + EVP_PKEY* key_; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + SECKEYPrivateKey* key_; + SECKEYPublicKey* public_key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey); +}; + +} // namespace crypto + +#endif // CRYPTO_RSA_PRIVATE_KEY_H_ diff --git a/chromium/crypto/rsa_private_key_nss.cc b/chromium/crypto/rsa_private_key_nss.cc new file mode 100644 index 00000000000..35697abb4e7 --- /dev/null +++ b/chromium/crypto/rsa_private_key_nss.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include +#include +#include +#include + +#include + +#include "base/debug/leak_annotations.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_util.h" +#include "crypto/nss_util.h" +#include "crypto/nss_util_internal.h" +#include "crypto/scoped_nss_types.h" + +// TODO(rafaelw): Consider using NSS's ASN.1 encoder. +namespace { + +static bool ReadAttribute(SECKEYPrivateKey* key, + CK_ATTRIBUTE_TYPE type, + std::vector* output) { + SECItem item; + SECStatus rv; + rv = PK11_ReadRawAttribute(PK11_TypePrivKey, key, type, &item); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + + output->assign(item.data, item.data + item.len); + SECITEM_FreeItem(&item, PR_FALSE); + return true; +} + +} // namespace + +namespace crypto { + +RSAPrivateKey::~RSAPrivateKey() { + if (key_) + SECKEY_DestroyPrivateKey(key_); + if (public_key_) + SECKEY_DestroyPublicKey(public_key_); +} + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + return CreateWithParams(num_bits, + false /* not permanent */, + false /* not sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + return CreateFromPrivateKeyInfoWithParams(input, + false /* not permanent */, + false /* not sensitive */); +} + +#if defined(USE_NSS) +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { + return CreateWithParams(num_bits, + true /* permanent */, + true /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( + const std::vector& input) { + return CreateFromPrivateKeyInfoWithParams(input, + true /* permanent */, + true /* sensitive */); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromKey(SECKEYPrivateKey* key) { + DCHECK(key); + if (SECKEY_GetPrivateKeyType(key) != rsaKey) + return NULL; + RSAPrivateKey* copy = new RSAPrivateKey(); + copy->key_ = SECKEY_CopyPrivateKey(key); + copy->public_key_ = SECKEY_ConvertToPublicKey(key); + if (!copy->key_ || !copy->public_key_) { + NOTREACHED(); + delete copy; + return NULL; + } + return copy; +} + +// static +RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( + const std::vector& input) { + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + // First, decode and save the public key. + SECItem key_der; + key_der.type = siBuffer; + key_der.data = const_cast(&input[0]); + key_der.len = input.size(); + + CERTSubjectPublicKeyInfo* spki = + SECKEY_DecodeDERSubjectPublicKeyInfo(&key_der); + if (!spki) { + NOTREACHED(); + return NULL; + } + + result->public_key_ = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + // Make sure the key is an RSA key. If not, that's an error + if (result->public_key_->keyType != rsaKey) { + NOTREACHED(); + return NULL; + } + + ScopedSECItem ck_id( + PK11_MakeIDFromPubKey(&(result->public_key_->u.rsa.modulus))); + if (!ck_id.get()) { + NOTREACHED(); + return NULL; + } + + // Search all slots in all modules for the key with the given ID. + AutoSECMODListReadLock auto_lock; + SECMODModuleList* head = SECMOD_GetDefaultModuleList(); + for (SECMODModuleList* item = head; item != NULL; item = item->next) { + int slot_count = item->module->loaded ? item->module->slotCount : 0; + for (int i = 0; i < slot_count; i++) { + // Finally...Look for the key! + result->key_ = PK11_FindKeyByKeyID(item->module->slots[i], + ck_id.get(), NULL); + if (result->key_) + return result.release(); + } + } + + // We didn't find the key. + return NULL; +} +#endif + +RSAPrivateKey* RSAPrivateKey::Copy() const { + RSAPrivateKey* copy = new RSAPrivateKey(); + copy->key_ = SECKEY_CopyPrivateKey(key_); + copy->public_key_ = SECKEY_CopyPublicKey(public_key_); + return copy; +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) const { + PrivateKeyInfoCodec private_key_info(true); + + // Manually read the component attributes of the private key and build up + // the PrivateKeyInfo. + if (!ReadAttribute(key_, CKA_MODULUS, private_key_info.modulus()) || + !ReadAttribute(key_, CKA_PUBLIC_EXPONENT, + private_key_info.public_exponent()) || + !ReadAttribute(key_, CKA_PRIVATE_EXPONENT, + private_key_info.private_exponent()) || + !ReadAttribute(key_, CKA_PRIME_1, private_key_info.prime1()) || + !ReadAttribute(key_, CKA_PRIME_2, private_key_info.prime2()) || + !ReadAttribute(key_, CKA_EXPONENT_1, private_key_info.exponent1()) || + !ReadAttribute(key_, CKA_EXPONENT_2, private_key_info.exponent2()) || + !ReadAttribute(key_, CKA_COEFFICIENT, private_key_info.coefficient())) { + NOTREACHED(); + return false; + } + + return private_key_info.Export(output); +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) const { + ScopedSECItem der_pubkey(SECKEY_EncodeDERSubjectPublicKeyInfo(public_key_)); + if (!der_pubkey.get()) { + NOTREACHED(); + return false; + } + + output->assign(der_pubkey->data, der_pubkey->data + der_pubkey->len); + return true; +} + +RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) { + EnsureNSSInit(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive) { +#if !defined(USE_NSS) + if (permanent) { + NOTIMPLEMENTED(); + return NULL; + } +#endif + + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + ScopedPK11Slot slot(permanent ? GetPrivateNSSKeySlot() : + PK11_GetInternalSlot()); + if (!slot.get()) + return NULL; + + PK11RSAGenParams param; + param.keySizeInBits = num_bits; + param.pe = 65537L; + result->key_ = PK11_GenerateKeyPair(slot.get(), + CKM_RSA_PKCS_KEY_PAIR_GEN, + ¶m, + &result->public_key_, + permanent, + sensitive, + NULL); + if (!result->key_) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( + const std::vector& input, bool permanent, bool sensitive) { +#if !defined(USE_NSS) + if (permanent) { + NOTIMPLEMENTED(); + return NULL; + } +#endif + + // This method currently leaks some memory. + // See http://crbug.com/34742. + ANNOTATE_SCOPED_MEMORY_LEAK; + EnsureNSSInit(); + + scoped_ptr result(new RSAPrivateKey); + + ScopedPK11Slot slot(permanent ? GetPrivateNSSKeySlot() : + PK11_GetInternalSlot()); + if (!slot.get()) + return NULL; + + SECItem der_private_key_info; + der_private_key_info.data = const_cast(&input.front()); + der_private_key_info.len = input.size(); + // Allow the private key to be used for key unwrapping, data decryption, + // and signature generation. + const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | + KU_DIGITAL_SIGNATURE; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &der_private_key_info, NULL, NULL, permanent, sensitive, + key_usage, &result->key_, NULL); + if (rv != SECSuccess) { + NOTREACHED(); + return NULL; + } + + result->public_key_ = SECKEY_ConvertToPublicKey(result->key_); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +} // namespace crypto diff --git a/chromium/crypto/rsa_private_key_nss_unittest.cc b/chromium/crypto/rsa_private_key_nss_unittest.cc new file mode 100644 index 00000000000..66d352a0bd2 --- /dev/null +++ b/chromium/crypto/rsa_private_key_nss_unittest.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace crypto { + +class RSAPrivateKeyNSSTest : public testing::Test { + public: + RSAPrivateKeyNSSTest() {} + virtual ~RSAPrivateKeyNSSTest() {} + + virtual void SetUp() { +#if defined(OS_CHROMEOS) + OpenPersistentNSSDB(); +#endif + } + + private: + ScopedTestNSSDB test_nssdb_; + + DISALLOW_COPY_AND_ASSIGN(RSAPrivateKeyNSSTest); +}; + +TEST_F(RSAPrivateKeyNSSTest, CreateFromKeyTest) { + scoped_ptr key_pair(RSAPrivateKey::Create(256)); + + scoped_ptr key_copy( + RSAPrivateKey::CreateFromKey(key_pair->key())); + ASSERT_TRUE(key_copy.get()); + + std::vector privkey; + std::vector pubkey; + ASSERT_TRUE(key_pair->ExportPrivateKey(&privkey)); + ASSERT_TRUE(key_pair->ExportPublicKey(&pubkey)); + + std::vector privkey_copy; + std::vector pubkey_copy; + ASSERT_TRUE(key_copy->ExportPrivateKey(&privkey_copy)); + ASSERT_TRUE(key_copy->ExportPublicKey(&pubkey_copy)); + + ASSERT_EQ(privkey, privkey_copy); + ASSERT_EQ(pubkey, pubkey_copy); +} + +TEST_F(RSAPrivateKeyNSSTest, FindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr key_pair(RSAPrivateKey::Create(256)); + + std::vector public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + scoped_ptr key_pair_2( + crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); + + EXPECT_EQ(key_pair->key_->pkcs11ID, key_pair_2->key_->pkcs11ID); +} + +TEST_F(RSAPrivateKeyNSSTest, FailedFindFromPublicKey) { + // Create a keypair, which will put the keys in the user's NSSDB. + scoped_ptr key_pair(RSAPrivateKey::Create(256)); + + std::vector public_key; + ASSERT_TRUE(key_pair->ExportPublicKey(&public_key)); + + // Remove the keys from the DB, and make sure we can't find them again. + if (key_pair->key_) { + PK11_DestroyTokenObject(key_pair->key_->pkcs11Slot, + key_pair->key_->pkcs11ID); + } + if (key_pair->public_key_) { + PK11_DestroyTokenObject(key_pair->public_key_->pkcs11Slot, + key_pair->public_key_->pkcs11ID); + } + + EXPECT_EQ(NULL, crypto::RSAPrivateKey::FindFromPublicKeyInfo(public_key)); +} + +} // namespace crypto diff --git a/chromium/crypto/rsa_private_key_openssl.cc b/chromium/crypto/rsa_private_key_openssl.cc new file mode 100644 index 00000000000..f191e393288 --- /dev/null +++ b/chromium/crypto/rsa_private_key_openssl.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +namespace { + +// Function pointer definition, for injecting the required key export function +// into ExportKey, below. The supplied function should export EVP_PKEY into +// the supplied BIO, returning 1 on success or 0 on failure. +typedef int (ExportFunction)(BIO*, EVP_PKEY*); + +// Helper to export |key| into |output| via the specified ExportFunction. +bool ExportKey(EVP_PKEY* key, + ExportFunction export_fn, + std::vector* output) { + if (!key) + return false; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + ScopedOpenSSL bio(BIO_new(BIO_s_mem())); + + int res = export_fn(bio.get(), key); + if (!res) + return false; + + char* data = NULL; + long len = BIO_get_mem_data(bio.get(), &data); + if (!data || len < 0) + return false; + + output->assign(data, data + len); + return true; +} + +} // namespace + +// static +RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + + ScopedOpenSSL rsa_key(RSA_new()); + ScopedOpenSSL bn(BN_new()); + if (!rsa_key.get() || !bn.get() || !BN_set_word(bn.get(), 65537L)) + return NULL; + + if (!RSA_generate_key_ex(rsa_key.get(), num_bits, bn.get(), NULL)) + return NULL; + + scoped_ptr result(new RSAPrivateKey); + result->key_ = EVP_PKEY_new(); + if (!result->key_ || !EVP_PKEY_set1_RSA(result->key_, rsa_key.get())) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( + const std::vector& input) { + if (input.empty()) + return NULL; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + // BIO_new_mem_buf is not const aware, but it does not modify the buffer. + char* data = reinterpret_cast(const_cast(&input[0])); + ScopedOpenSSL bio(BIO_new_mem_buf(data, input.size())); + if (!bio.get()) + return NULL; + + // Importing is a little more involved than exporting, as we must first + // PKCS#8 decode the input, and then import the EVP_PKEY from Private Key + // Info structure returned. + ScopedOpenSSL p8inf( + d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL)); + if (!p8inf.get()) + return NULL; + + scoped_ptr result(new RSAPrivateKey); + result->key_ = EVP_PKCS82PKEY(p8inf.get()); + if (!result->key_) + return NULL; + + return result.release(); +} + +RSAPrivateKey::RSAPrivateKey() + : key_(NULL) { +} + +RSAPrivateKey::~RSAPrivateKey() { + if (key_) + EVP_PKEY_free(key_); +} + +RSAPrivateKey* RSAPrivateKey::Copy() const { + scoped_ptr copy(new RSAPrivateKey()); + RSA* rsa = EVP_PKEY_get1_RSA(key_); + if (!rsa) + return NULL; + copy->key_ = EVP_PKEY_new(); + if (!EVP_PKEY_set1_RSA(copy->key_, rsa)) + return NULL; + return copy.release(); +} + +bool RSAPrivateKey::ExportPrivateKey(std::vector* output) const { + return ExportKey(key_, i2d_PKCS8PrivateKeyInfo_bio, output); +} + +bool RSAPrivateKey::ExportPublicKey(std::vector* output) const { + return ExportKey(key_, i2d_PUBKEY_bio, output); +} + +} // namespace crypto diff --git a/chromium/crypto/rsa_private_key_unittest.cc b/chromium/crypto/rsa_private_key_unittest.cc new file mode 100644 index 00000000000..b981bda07d4 --- /dev/null +++ b/chromium/crypto/rsa_private_key_unittest.cc @@ -0,0 +1,405 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/rsa_private_key.h" + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const uint8 kTestPrivateKeyInfo[] = { + 0x30, 0x82, 0x02, 0x78, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x62, 0x30, 0x82, 0x02, 0x5e, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, + 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61, + 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, + 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4, + 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, + 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f, + 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, + 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff, + 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, + 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5, + 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, + 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x81, 0x80, 0x03, 0x61, 0x89, + 0x37, 0xcb, 0xf2, 0x98, 0xa0, 0xce, 0xb4, 0xcb, + 0x16, 0x13, 0xf0, 0xe6, 0xaf, 0x5c, 0xc5, 0xa7, + 0x69, 0x71, 0xca, 0xba, 0x8d, 0xe0, 0x4d, 0xdd, + 0xed, 0xb8, 0x48, 0x8b, 0x16, 0x93, 0x36, 0x95, + 0xc2, 0x91, 0x40, 0x65, 0x17, 0xbd, 0x7f, 0xd6, + 0xad, 0x9e, 0x30, 0x28, 0x46, 0xe4, 0x3e, 0xcc, + 0x43, 0x78, 0xf9, 0xfe, 0x1f, 0x33, 0x23, 0x1e, + 0x31, 0x12, 0x9d, 0x3c, 0xa7, 0x08, 0x82, 0x7b, + 0x7d, 0x25, 0x4e, 0x5e, 0x19, 0xa8, 0x9b, 0xed, + 0x86, 0xb2, 0xcb, 0x3c, 0xfe, 0x4e, 0xa1, 0xfa, + 0x62, 0x87, 0x3a, 0x17, 0xf7, 0x60, 0xec, 0x38, + 0x29, 0xe8, 0x4f, 0x34, 0x9f, 0x76, 0x9d, 0xee, + 0xa3, 0xf6, 0x85, 0x6b, 0x84, 0x43, 0xc9, 0x1e, + 0x01, 0xff, 0xfd, 0xd0, 0x29, 0x4c, 0xfa, 0x8e, + 0x57, 0x0c, 0xc0, 0x71, 0xa5, 0xbb, 0x88, 0x46, + 0x29, 0x5c, 0xc0, 0x4f, 0x01, 0x02, 0x41, 0x00, + 0xf5, 0x83, 0xa4, 0x64, 0x4a, 0xf2, 0xdd, 0x8c, + 0x2c, 0xed, 0xa8, 0xd5, 0x60, 0x5a, 0xe4, 0xc7, + 0xcc, 0x61, 0xcd, 0x38, 0x42, 0x20, 0xd3, 0x82, + 0x18, 0xf2, 0x35, 0x00, 0x72, 0x2d, 0xf7, 0x89, + 0x80, 0x67, 0xb5, 0x93, 0x05, 0x5f, 0xdd, 0x42, + 0xba, 0x16, 0x1a, 0xea, 0x15, 0xc6, 0xf0, 0xb8, + 0x8c, 0xbc, 0xbf, 0x54, 0x9e, 0xf1, 0xc1, 0xb2, + 0xb3, 0x8b, 0xb6, 0x26, 0x02, 0x30, 0xc4, 0x81, + 0x02, 0x41, 0x00, 0xc0, 0x60, 0x62, 0x80, 0xe1, + 0x22, 0x78, 0xf6, 0x9d, 0x83, 0x18, 0xeb, 0x72, + 0x45, 0xd7, 0xc8, 0x01, 0x7f, 0xa9, 0xca, 0x8f, + 0x7d, 0xd6, 0xb8, 0x31, 0x2b, 0x84, 0x7f, 0x62, + 0xd9, 0xa9, 0x22, 0x17, 0x7d, 0x06, 0x35, 0x6c, + 0xf3, 0xc1, 0x94, 0x17, 0x85, 0x5a, 0xaf, 0x9c, + 0x5c, 0x09, 0x3c, 0xcf, 0x2f, 0x44, 0x9d, 0xb6, + 0x52, 0x68, 0x5f, 0xf9, 0x59, 0xc8, 0x84, 0x2b, + 0x39, 0x22, 0x8f, 0x02, 0x41, 0x00, 0xb2, 0x04, + 0xe2, 0x0e, 0x56, 0xca, 0x03, 0x1a, 0xc0, 0xf9, + 0x12, 0x92, 0xa5, 0x6b, 0x42, 0xb8, 0x1c, 0xda, + 0x4d, 0x93, 0x9d, 0x5f, 0x6f, 0xfd, 0xc5, 0x58, + 0xda, 0x55, 0x98, 0x74, 0xfc, 0x28, 0x17, 0x93, + 0x1b, 0x75, 0x9f, 0x50, 0x03, 0x7f, 0x7e, 0xae, + 0xc8, 0x95, 0x33, 0x75, 0x2c, 0xd6, 0xa4, 0x35, + 0xb8, 0x06, 0x03, 0xba, 0x08, 0x59, 0x2b, 0x17, + 0x02, 0xdc, 0x4c, 0x7a, 0x50, 0x01, 0x02, 0x41, + 0x00, 0x9d, 0xdb, 0x39, 0x59, 0x09, 0xe4, 0x30, + 0xa0, 0x24, 0xf5, 0xdb, 0x2f, 0xf0, 0x2f, 0xf1, + 0x75, 0x74, 0x0d, 0x5e, 0xb5, 0x11, 0x73, 0xb0, + 0x0a, 0xaa, 0x86, 0x4c, 0x0d, 0xff, 0x7e, 0x1d, + 0xb4, 0x14, 0xd4, 0x09, 0x91, 0x33, 0x5a, 0xfd, + 0xa0, 0x58, 0x80, 0x9b, 0xbe, 0x78, 0x2e, 0x69, + 0x82, 0x15, 0x7c, 0x72, 0xf0, 0x7b, 0x18, 0x39, + 0xff, 0x6e, 0xeb, 0xc6, 0x86, 0xf5, 0xb4, 0xc7, + 0x6f, 0x02, 0x41, 0x00, 0x8d, 0x1a, 0x37, 0x0f, + 0x76, 0xc4, 0x82, 0xfa, 0x5c, 0xc3, 0x79, 0x35, + 0x3e, 0x70, 0x8a, 0xbf, 0x27, 0x49, 0xb0, 0x99, + 0x63, 0xcb, 0x77, 0x5f, 0xa8, 0x82, 0x65, 0xf6, + 0x03, 0x52, 0x51, 0xf1, 0xae, 0x2e, 0x05, 0xb3, + 0xc6, 0xa4, 0x92, 0xd1, 0xce, 0x6c, 0x72, 0xfb, + 0x21, 0xb3, 0x02, 0x87, 0xe4, 0xfd, 0x61, 0xca, + 0x00, 0x42, 0x19, 0xf0, 0xda, 0x5a, 0x53, 0xe3, + 0xb1, 0xc5, 0x15, 0xf3 +}; + +} // namespace + +// Generate random private keys with two different sizes. Reimport, then +// export them again. We should get back the same exact bytes. +TEST(RSAPrivateKeyUnitTest, InitRandomTest) { + scoped_ptr keypair1( + crypto::RSAPrivateKey::Create(1024)); + scoped_ptr keypair2( + crypto::RSAPrivateKey::Create(2048)); + ASSERT_TRUE(keypair1.get()); + ASSERT_TRUE(keypair2.get()); + + std::vector privkey1; + std::vector privkey2; + std::vector pubkey1; + std::vector pubkey2; + + ASSERT_TRUE(keypair1->ExportPrivateKey(&privkey1)); + ASSERT_TRUE(keypair2->ExportPrivateKey(&privkey2)); + ASSERT_TRUE(keypair1->ExportPublicKey(&pubkey1)); + ASSERT_TRUE(keypair2->ExportPublicKey(&pubkey2)); + + scoped_ptr keypair3( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey1)); + scoped_ptr keypair4( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(privkey2)); + ASSERT_TRUE(keypair3.get()); + ASSERT_TRUE(keypair4.get()); + + std::vector privkey3; + std::vector privkey4; + ASSERT_TRUE(keypair3->ExportPrivateKey(&privkey3)); + ASSERT_TRUE(keypair4->ExportPrivateKey(&privkey4)); + + ASSERT_EQ(privkey1.size(), privkey3.size()); + ASSERT_EQ(privkey2.size(), privkey4.size()); + ASSERT_TRUE(0 == memcmp(&privkey1.front(), &privkey3.front(), + privkey1.size())); + ASSERT_TRUE(0 == memcmp(&privkey2.front(), &privkey4.front(), + privkey2.size())); +} + +// Test Copy() method. +TEST(RSAPrivateKeyUnitTest, CopyTest) { + std::vector input( + kTestPrivateKeyInfo, kTestPrivateKeyInfo + sizeof(kTestPrivateKeyInfo)); + + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input)); + + scoped_ptr key_copy(key->Copy()); + ASSERT_TRUE(key_copy.get()); + + std::vector privkey_copy; + ASSERT_TRUE(key_copy->ExportPrivateKey(&privkey_copy)); + ASSERT_EQ(input, privkey_copy); +} + + +// Verify that generated public keys look good. This test data was generated +// with the openssl command line tool. +TEST(RSAPrivateKeyUnitTest, PublicKeyTest) { + const uint8 expected_public_key_info[] = { + 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, + 0x89, 0x02, 0x81, 0x81, 0x00, 0xb8, 0x7f, 0x2b, + 0x20, 0xdc, 0x7c, 0x9b, 0x0c, 0xdc, 0x51, 0x61, + 0x99, 0x0d, 0x36, 0x0f, 0xd4, 0x66, 0x88, 0x08, + 0x55, 0x84, 0xd5, 0x3a, 0xbf, 0x2b, 0xa4, 0x64, + 0x85, 0x7b, 0x0c, 0x04, 0x13, 0x3f, 0x8d, 0xf4, + 0xbc, 0x38, 0x0d, 0x49, 0xfe, 0x6b, 0xc4, 0x5a, + 0xb0, 0x40, 0x53, 0x3a, 0xd7, 0x66, 0x09, 0x0f, + 0x9e, 0x36, 0x74, 0x30, 0xda, 0x8a, 0x31, 0x4f, + 0x1f, 0x14, 0x50, 0xd7, 0xc7, 0x20, 0x94, 0x17, + 0xde, 0x4e, 0xb9, 0x57, 0x5e, 0x7e, 0x0a, 0xe5, + 0xb2, 0x65, 0x7a, 0x89, 0x4e, 0xb6, 0x47, 0xff, + 0x1c, 0xbd, 0xb7, 0x38, 0x13, 0xaf, 0x47, 0x85, + 0x84, 0x32, 0x33, 0xf3, 0x17, 0x49, 0xbf, 0xe9, + 0x96, 0xd0, 0xd6, 0x14, 0x6f, 0x13, 0x8d, 0xc5, + 0xfc, 0x2c, 0x72, 0xba, 0xac, 0xea, 0x7e, 0x18, + 0x53, 0x56, 0xa6, 0x83, 0xa2, 0xce, 0x93, 0x93, + 0xe7, 0x1f, 0x0f, 0xe6, 0x0f, 0x02, 0x03, 0x01, + 0x00, 0x01 + }; + + std::vector input( + kTestPrivateKeyInfo, kTestPrivateKeyInfo + sizeof(kTestPrivateKeyInfo)); + + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input)); + ASSERT_TRUE(key.get()); + + std::vector output; + ASSERT_TRUE(key->ExportPublicKey(&output)); + + ASSERT_TRUE( + memcmp(expected_public_key_info, &output.front(), output.size()) == 0); +} + +// These two test keys each contain an integer that has 0x00 for its most +// significant byte. When encoded as ASN.1, this byte is dropped and there are +// two interesting sub-cases. When the sign bit of the integer is set, an extra +// null byte is added back to force the encoded value to be positive. When the +// sign bit is not set, the encoded integer is just left shorter than usual. +// See also: http://code.google.com/p/chromium/issues/detail?id=14877. +// +// Before we were handling this correctly, we would see one of two failures: +// * RSAPrivateKey::CreateFromPrivateKeyInfo would return null because the +// underlying windows API failed to import the key. +// * The import would succeed, but incorrectly interpret the data. On export, +// the key would contain different values. +// +// This test case verifies these two failures modes don't occur. +TEST(RSAPrivateKeyUnitTest, ShortIntegers) { + const uint8 short_integer_with_high_bit[] = { + 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x61, 0x30, 0x82, 0x02, 0x5d, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0x92, 0x59, 0x32, + 0x7d, 0x8e, 0xaf, 0x2e, 0xd5, 0xb2, 0x5c, 0x67, + 0xc8, 0x7d, 0x48, 0xb7, 0x84, 0x12, 0xd0, 0x76, + 0xda, 0xe1, 0xa3, 0x1e, 0x40, 0x01, 0x14, 0x5c, + 0xef, 0x26, 0x6e, 0x28, 0xa2, 0xf7, 0xa5, 0xb4, + 0x02, 0x37, 0xd0, 0x53, 0x10, 0xcb, 0x7c, 0x6a, + 0xf4, 0x53, 0x9f, 0xb8, 0xe0, 0x83, 0x93, 0xd1, + 0x19, 0xd8, 0x28, 0xd1, 0xd1, 0xd8, 0x87, 0x8f, + 0x92, 0xfd, 0x73, 0xc0, 0x4d, 0x3e, 0x07, 0x22, + 0x1f, 0xc1, 0x20, 0xb0, 0x70, 0xb2, 0x3b, 0xea, + 0xb1, 0xe5, 0x0a, 0xfd, 0x56, 0x49, 0x5e, 0x39, + 0x90, 0x91, 0xce, 0x04, 0x83, 0x29, 0xaa, 0xfd, + 0x12, 0xa4, 0x42, 0x26, 0x6c, 0x6e, 0x79, 0x70, + 0x77, 0x03, 0xb2, 0x07, 0x01, 0x3d, 0x85, 0x81, + 0x95, 0x9e, 0xda, 0x5a, 0xa3, 0xf4, 0x2d, 0x38, + 0x04, 0x58, 0xf5, 0x6b, 0xc9, 0xf1, 0xb5, 0x65, + 0xfe, 0x66, 0x0d, 0xa2, 0xd5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x81, 0x80, 0x5e, 0x01, 0x5f, + 0xb6, 0x59, 0x1d, 0xdc, 0x36, 0xb6, 0x60, 0x36, + 0xe6, 0x08, 0xdb, 0xd9, 0xcd, 0xc3, 0x8c, 0x16, + 0x9c, 0x98, 0x8d, 0x7f, 0xd3, 0xdb, 0x1d, 0xaa, + 0x68, 0x8f, 0xc5, 0xf8, 0xe2, 0x5d, 0xb3, 0x19, + 0xc2, 0xc6, 0xf9, 0x51, 0x32, 0x1b, 0x93, 0x6a, + 0xdc, 0x50, 0x8e, 0xeb, 0x61, 0x84, 0x03, 0x42, + 0x30, 0x98, 0xb1, 0xf7, 0xbd, 0x14, 0x9a, 0x57, + 0x36, 0x33, 0x09, 0xd4, 0x3e, 0x90, 0xda, 0xef, + 0x09, 0x6e, 0xef, 0x49, 0xb6, 0x60, 0x68, 0x5e, + 0x54, 0x17, 0x25, 0x5b, 0x37, 0xe3, 0x35, 0x63, + 0x5b, 0x60, 0x3c, 0xbd, 0x50, 0xdf, 0x46, 0x43, + 0x08, 0xa4, 0x71, 0x21, 0xf1, 0x30, 0x71, 0xdc, + 0xda, 0xd7, 0x6f, 0xd2, 0x18, 0xbd, 0x39, 0xf1, + 0xe1, 0xbe, 0xa8, 0x8d, 0x62, 0xdf, 0xa2, 0x3e, + 0xb6, 0x15, 0x26, 0xb6, 0x57, 0xbd, 0x63, 0xdb, + 0xc1, 0x91, 0xec, 0xb8, 0x01, 0x02, 0x41, 0x00, + 0xc6, 0x1a, 0x06, 0x48, 0xf2, 0x12, 0x1c, 0x9f, + 0x74, 0x20, 0x5c, 0x85, 0xa2, 0xda, 0xe5, 0x62, + 0x96, 0x8d, 0x22, 0x7b, 0x78, 0x73, 0xea, 0xbb, + 0x9f, 0x59, 0x42, 0x13, 0x15, 0xc8, 0x11, 0x50, + 0x6c, 0x55, 0xf6, 0xdf, 0x8b, 0xfe, 0xc7, 0xdd, + 0xa8, 0xca, 0x54, 0x41, 0xe8, 0xce, 0xbe, 0x7d, + 0xbd, 0xe2, 0x13, 0x4b, 0x5b, 0x61, 0xeb, 0x69, + 0x6c, 0xb1, 0x9b, 0x28, 0x68, 0x5b, 0xd6, 0x01, + 0x02, 0x41, 0x00, 0xbd, 0x1e, 0xfe, 0x51, 0x99, + 0xb6, 0xe3, 0x84, 0xfe, 0xf1, 0x9e, 0xfd, 0x9c, + 0xe7, 0x86, 0x43, 0x68, 0x7f, 0x2f, 0x6a, 0x2a, + 0x4c, 0xae, 0xa6, 0x41, 0x1c, 0xf0, 0x10, 0x37, + 0x54, 0x23, 0xba, 0x05, 0x0d, 0x18, 0x27, 0x8d, + 0xb8, 0xe4, 0x8f, 0xf2, 0x25, 0x73, 0x8a, 0xd7, + 0x05, 0x98, 0x6b, 0x3d, 0x55, 0xb7, 0x6f, 0x7c, + 0xec, 0x77, 0x61, 0x54, 0x7b, 0xb6, 0x6b, 0x31, + 0xec, 0x94, 0xd5, 0x02, 0x41, 0x00, 0x90, 0xa2, + 0xa5, 0x9e, 0x12, 0xa7, 0x68, 0xa0, 0x7e, 0xdf, + 0xb5, 0xcd, 0x98, 0x26, 0xab, 0xbd, 0xbc, 0x5f, + 0xd5, 0x22, 0x42, 0xc2, 0x97, 0x4a, 0x5f, 0x40, + 0x82, 0xfe, 0x7e, 0x33, 0xb1, 0x78, 0x7f, 0x70, + 0x90, 0x2b, 0x8d, 0x01, 0xfb, 0x18, 0xfa, 0x48, + 0xa7, 0x15, 0xec, 0x0d, 0x2e, 0x85, 0x8d, 0xe2, + 0x86, 0xe5, 0xc9, 0x15, 0x88, 0x14, 0x53, 0xd8, + 0xa4, 0x88, 0xef, 0x10, 0xc6, 0x01, 0x02, 0x41, + 0x00, 0xba, 0xe4, 0xaf, 0x14, 0xfa, 0xdf, 0xf6, + 0xd5, 0xce, 0x8f, 0xfe, 0xbb, 0xc8, 0x5c, 0x30, + 0x9d, 0xda, 0xdd, 0x9d, 0x80, 0xc0, 0x0e, 0x89, + 0xa5, 0xb8, 0xc1, 0x1d, 0x28, 0x19, 0x55, 0x67, + 0xfd, 0x03, 0xd2, 0xdd, 0xe4, 0xf0, 0xb4, 0x20, + 0x03, 0x74, 0x9b, 0xb8, 0x24, 0x23, 0xbb, 0xde, + 0xd5, 0x53, 0x86, 0xaa, 0xc1, 0x5d, 0x65, 0xdd, + 0xcf, 0xec, 0x8a, 0x59, 0x4a, 0x73, 0xca, 0xc5, + 0x85, 0x02, 0x40, 0x00, 0xc4, 0x5e, 0x8d, 0xa4, + 0xea, 0xbb, 0x6a, 0x9b, 0xe6, 0x3a, 0x4d, 0xc1, + 0xdb, 0xe5, 0x52, 0x38, 0xf9, 0x59, 0x91, 0x2d, + 0x90, 0x82, 0xe3, 0x31, 0x1b, 0x48, 0xb7, 0x42, + 0xfa, 0x1d, 0x83, 0xd5, 0x3d, 0x02, 0xc2, 0x12, + 0x71, 0x10, 0x3a, 0xbd, 0x92, 0x8f, 0x9b, 0xa2, + 0x6b, 0x2d, 0x21, 0xa4, 0x65, 0xe9, 0xfa, 0x8c, + 0x30, 0x2a, 0x89, 0xce, 0xd0, 0xa7, 0x67, 0xd8, + 0x45, 0x84, 0xb0 + }; + + const uint8 short_integer_without_high_bit[] = { + 0x30, 0x82, 0x02, 0x76, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x02, 0x60, 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, + 0x00, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x9e, 0x8d, + 0xc4, 0x6d, 0x38, 0xe8, 0x0e, 0x9f, 0x84, 0x03, + 0x40, 0x8e, 0x81, 0x2e, 0x56, 0x67, 0x78, 0x11, + 0x85, 0x27, 0x81, 0x52, 0xf2, 0x1b, 0x3e, 0x5b, + 0xf8, 0xab, 0xfc, 0xaf, 0xca, 0x5c, 0x26, 0xd5, + 0xfa, 0xd4, 0x55, 0x50, 0x38, 0xb9, 0x9d, 0x89, + 0x92, 0x7e, 0x34, 0xcf, 0x37, 0x82, 0x48, 0x2d, + 0xaa, 0xc4, 0x6a, 0x0e, 0x93, 0xea, 0xad, 0x8a, + 0x33, 0xf0, 0x42, 0x23, 0xe0, 0x4c, 0x98, 0xbf, + 0x01, 0x00, 0x1b, 0xfe, 0x06, 0x15, 0xc6, 0xe3, + 0x80, 0x79, 0x6d, 0xfe, 0x48, 0xcd, 0x40, 0xbb, + 0xf9, 0x58, 0xe6, 0xbf, 0xd5, 0x4c, 0x29, 0x48, + 0x53, 0x78, 0x06, 0x03, 0x0d, 0x59, 0xf5, 0x20, + 0xe0, 0xe6, 0x8c, 0xb2, 0xf5, 0xd8, 0x61, 0x52, + 0x7e, 0x40, 0x83, 0xd7, 0x69, 0xae, 0xd7, 0x75, + 0x02, 0x2d, 0x49, 0xd5, 0x15, 0x5b, 0xf1, 0xd9, + 0x4d, 0x60, 0x7d, 0x62, 0xa5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x02, 0x7f, 0x6d, 0x45, 0x23, 0xeb, + 0x95, 0x17, 0x34, 0x88, 0xf6, 0x91, 0xc7, 0x3f, + 0x48, 0x5a, 0xe0, 0x87, 0x63, 0x44, 0xae, 0x84, + 0xb2, 0x8c, 0x8a, 0xc8, 0xb2, 0x6f, 0x22, 0xf0, + 0xc5, 0x21, 0x61, 0x10, 0xa8, 0x69, 0x09, 0x1e, + 0x13, 0x7d, 0x94, 0x52, 0x1b, 0x5c, 0xe4, 0x7b, + 0xf0, 0x03, 0x8f, 0xbc, 0x72, 0x09, 0xdf, 0x78, + 0x84, 0x3e, 0xb9, 0xe5, 0xe6, 0x31, 0x0a, 0x01, + 0xf9, 0x32, 0xf8, 0xd6, 0x57, 0xa3, 0x87, 0xe6, + 0xf5, 0x98, 0xbc, 0x8e, 0x41, 0xb9, 0x50, 0x17, + 0x7b, 0xd3, 0x97, 0x5a, 0x44, 0x3a, 0xee, 0xff, + 0x6b, 0xb3, 0x3a, 0x52, 0xe7, 0xa4, 0x96, 0x9a, + 0xf6, 0x83, 0xc8, 0x97, 0x1c, 0x63, 0xa1, 0xd6, + 0xb3, 0xa8, 0xb2, 0xc7, 0x73, 0x25, 0x0f, 0x58, + 0x36, 0xb9, 0x7a, 0x47, 0xa7, 0x4d, 0x30, 0xfe, + 0x4d, 0x74, 0x56, 0xe8, 0xfb, 0xd6, 0x50, 0xe5, + 0xe0, 0x28, 0x15, 0x02, 0x41, 0x00, 0xeb, 0x15, + 0x62, 0xb6, 0x37, 0x41, 0x7c, 0xc5, 0x00, 0x22, + 0x2c, 0x5a, 0x5e, 0xe4, 0xb2, 0x11, 0x87, 0x89, + 0xad, 0xf4, 0x57, 0x68, 0x90, 0xb7, 0x9f, 0xe2, + 0x79, 0x20, 0x6b, 0x98, 0x00, 0x0d, 0x3a, 0x3b, + 0xc1, 0xcd, 0x36, 0xf9, 0x27, 0xda, 0x40, 0x36, + 0x1d, 0xb8, 0x5c, 0x96, 0xeb, 0x04, 0x08, 0xe1, + 0x3f, 0xfa, 0x94, 0x8b, 0x0f, 0xa0, 0xff, 0xc1, + 0x51, 0xea, 0x90, 0xad, 0x15, 0xc7, 0x02, 0x41, + 0x00, 0xd5, 0x06, 0x45, 0xd7, 0x55, 0x63, 0x1a, + 0xf0, 0x89, 0x81, 0xae, 0x87, 0x23, 0xa2, 0x39, + 0xfe, 0x3d, 0x82, 0xc7, 0xcb, 0x15, 0xb9, 0xe3, + 0xe2, 0x5b, 0xc6, 0xd2, 0x55, 0xdd, 0xab, 0x55, + 0x29, 0x7c, 0xda, 0x0e, 0x1c, 0x09, 0xfc, 0x73, + 0x0d, 0x01, 0xed, 0x6d, 0x2f, 0x05, 0xd0, 0xd5, + 0x1d, 0xce, 0x18, 0x7f, 0xb0, 0xc8, 0x47, 0x77, + 0xd2, 0xa9, 0x9e, 0xfc, 0x39, 0x4b, 0x3d, 0x94, + 0x33, 0x02, 0x41, 0x00, 0x8f, 0x94, 0x09, 0x2d, + 0x17, 0x44, 0x75, 0x0a, 0xf1, 0x10, 0xee, 0x1b, + 0xe7, 0xd7, 0x2f, 0xf6, 0xca, 0xdc, 0x49, 0x15, + 0x72, 0x09, 0x58, 0x51, 0xfe, 0x61, 0xd8, 0xee, + 0xf7, 0x27, 0xe7, 0xe8, 0x2c, 0x47, 0xf1, 0x0f, + 0x00, 0x63, 0x5e, 0x76, 0xcb, 0x3f, 0x02, 0x19, + 0xe6, 0xda, 0xfa, 0x01, 0x05, 0xd7, 0x65, 0x37, + 0x0b, 0x60, 0x7f, 0x94, 0x2a, 0x80, 0x8d, 0x22, + 0x81, 0x68, 0x65, 0x63, 0x02, 0x41, 0x00, 0xc2, + 0xd4, 0x18, 0xde, 0x47, 0x9e, 0xfb, 0x8d, 0x91, + 0x05, 0xc5, 0x3c, 0x9d, 0xcf, 0x8a, 0x60, 0xc7, + 0x9b, 0x2b, 0xe5, 0xc6, 0xba, 0x1b, 0xfc, 0xf3, + 0xd9, 0x54, 0x97, 0xe9, 0xc4, 0x00, 0x80, 0x90, + 0x4a, 0xd2, 0x6a, 0xbc, 0x8b, 0x62, 0x22, 0x3c, + 0x68, 0x0c, 0xda, 0xdb, 0xe3, 0xd2, 0x76, 0x8e, + 0xff, 0x03, 0x12, 0x09, 0x2a, 0xac, 0x21, 0x44, + 0xb7, 0x3e, 0x91, 0x9c, 0x09, 0xf6, 0xd7, 0x02, + 0x41, 0x00, 0xc0, 0xa1, 0xbb, 0x70, 0xdc, 0xf8, + 0xeb, 0x17, 0x61, 0xd4, 0x8c, 0x7c, 0x3b, 0x82, + 0x91, 0x58, 0xff, 0xf9, 0x19, 0xac, 0x3a, 0x73, + 0xa7, 0x20, 0xe5, 0x22, 0x02, 0xc4, 0xf6, 0xb9, + 0xb9, 0x43, 0x53, 0x35, 0x88, 0xe1, 0x05, 0xb6, + 0x43, 0x9b, 0x39, 0xc8, 0x04, 0x4d, 0x2b, 0x01, + 0xf7, 0xe6, 0x1b, 0x8d, 0x7e, 0x89, 0xe3, 0x43, + 0xd4, 0xf3, 0xab, 0x28, 0xd4, 0x5a, 0x1f, 0x20, + 0xea, 0xbe + }; + + std::vector input1; + std::vector input2; + + input1.resize(sizeof(short_integer_with_high_bit)); + input2.resize(sizeof(short_integer_without_high_bit)); + + memcpy(&input1.front(), short_integer_with_high_bit, + sizeof(short_integer_with_high_bit)); + memcpy(&input2.front(), short_integer_without_high_bit, + sizeof(short_integer_without_high_bit)); + + scoped_ptr keypair1( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input1)); + scoped_ptr keypair2( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(input2)); + ASSERT_TRUE(keypair1.get()); + ASSERT_TRUE(keypair2.get()); + + std::vector output1; + std::vector output2; + ASSERT_TRUE(keypair1->ExportPrivateKey(&output1)); + ASSERT_TRUE(keypair2->ExportPrivateKey(&output2)); + + ASSERT_EQ(input1.size(), output1.size()); + ASSERT_EQ(input2.size(), output2.size()); + ASSERT_TRUE(0 == memcmp(&output1.front(), &input1.front(), + input1.size())); + ASSERT_TRUE(0 == memcmp(&output2.front(), &input2.front(), + input2.size())); +} diff --git a/chromium/crypto/run_all_unittests.cc b/chromium/crypto/run_all_unittests.cc new file mode 100644 index 00000000000..0728a986228 --- /dev/null +++ b/chromium/crypto/run_all_unittests.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/test_suite.h" +#include "crypto/nss_util.h" + +int main(int argc, char** argv) { +#if defined(USE_NSS) + // This is most likely not needed, but it basically replaces a similar call + // that was performed on test_support_base. + // TODO(rvargas) Bug 79359: remove this. + crypto::EnsureNSSInit(); +#endif // defined(USE_NSS) + + return base::TestSuite(argc, argv).Run(); +} diff --git a/chromium/crypto/scoped_capi_types.h b/chromium/crypto/scoped_capi_types.h new file mode 100644 index 00000000000..16a51920010 --- /dev/null +++ b/chromium/crypto/scoped_capi_types.h @@ -0,0 +1,121 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SCOPED_CAPI_TYPES_H_ +#define CRYPTO_SCOPED_CAPI_TYPES_H_ + +#include +#include + +#include + +#include "base/logging.h" + +namespace crypto { + +// Simple destructor for the Free family of CryptoAPI functions, such as +// CryptDestroyHash, which take only a single argument to release. +template +struct CAPIDestroyer { + void operator()(CAPIHandle handle) const { + if (handle) { + BOOL ok = Destroyer(handle); + DCHECK(ok); + } + } +}; + +// Destructor for the Close/Release family of CryptoAPI functions, which take +// a second DWORD parameter indicating flags to use when closing or releasing. +// This includes functions like CertCloseStore or CryptReleaseContext. +template +struct CAPIDestroyerWithFlags { + void operator()(CAPIHandle handle) const { + if (handle) { + BOOL ok = Destroyer(handle, flags); + DCHECK(ok); + } + } +}; + +// scoped_ptr-like class for the CryptoAPI cryptography and certificate +// handles. Because these handles are defined as integer types, and not +// pointers, the existing scoped classes, such as scoped_ptr_malloc, are +// insufficient. The semantics are the same as scoped_ptr. +template +class ScopedCAPIHandle { + public: + explicit ScopedCAPIHandle(CAPIHandle handle = NULL) : handle_(handle) {} + + ~ScopedCAPIHandle() { + reset(); + } + + void reset(CAPIHandle handle = NULL) { + if (handle_ != handle) { + FreeProc free_proc; + free_proc(handle_); + handle_ = handle; + } + } + + operator CAPIHandle() const { return handle_; } + CAPIHandle get() const { return handle_; } + + CAPIHandle* receive() { + CHECK(handle_ == NULL); + return &handle_; + } + + bool operator==(CAPIHandle handle) const { + return handle_ == handle; + } + + bool operator!=(CAPIHandle handle) const { + return handle_ != handle; + } + + void swap(ScopedCAPIHandle& b) { + CAPIHandle tmp = b.handle_; + b.handle_ = handle_; + handle_ = tmp; + } + + CAPIHandle release() { + CAPIHandle tmp = handle_; + handle_ = NULL; + return tmp; + } + + private: + CAPIHandle handle_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCAPIHandle); +}; + +template inline +bool operator==(CH h, const ScopedCAPIHandle& b) { + return h == b.get(); +} + +template inline +bool operator!=(CH h, const ScopedCAPIHandle& b) { + return h != b.get(); +} + +typedef ScopedCAPIHandle< + HCRYPTPROV, + CAPIDestroyerWithFlags > ScopedHCRYPTPROV; + +typedef ScopedCAPIHandle< + HCRYPTKEY, CAPIDestroyer > ScopedHCRYPTKEY; + +typedef ScopedCAPIHandle< + HCRYPTHASH, CAPIDestroyer > ScopedHCRYPTHASH; + +} // namespace crypto + +#endif // CRYPTO_SCOPED_CAPI_TYPES_H_ diff --git a/chromium/crypto/scoped_nss_types.h b/chromium/crypto/scoped_nss_types.h new file mode 100644 index 00000000000..da90fea6748 --- /dev/null +++ b/chromium/crypto/scoped_nss_types.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SCOPED_NSS_TYPES_H_ +#define CRYPTO_SCOPED_NSS_TYPES_H_ + +#include +#include +#include +#include + +#include "base/memory/scoped_ptr.h" + +namespace crypto { + +template +struct NSSDestroyer { + void operator()(Type* ptr) const { + if (ptr) + Destroyer(ptr); + } +}; + +template +struct NSSDestroyer1 { + void operator()(Type* ptr) const { + if (ptr) + Destroyer(ptr, freeit); + } +}; + +// Define some convenient scopers around NSS pointers. +typedef scoped_ptr_malloc< + PK11Context, NSSDestroyer1 > ScopedPK11Context; +typedef scoped_ptr_malloc< + PK11SlotInfo, NSSDestroyer > ScopedPK11Slot; +typedef scoped_ptr_malloc< + PK11SymKey, NSSDestroyer > ScopedPK11SymKey; +typedef scoped_ptr_malloc< + SECKEYPublicKey, NSSDestroyer > + ScopedSECKEYPublicKey; +typedef scoped_ptr_malloc< + SECKEYPrivateKey, NSSDestroyer > + ScopedSECKEYPrivateKey; +typedef scoped_ptr_malloc< + SECAlgorithmID, NSSDestroyer1 > ScopedSECAlgorithmID; +typedef scoped_ptr_malloc< + SECItem, NSSDestroyer1 > ScopedSECItem; +typedef scoped_ptr_malloc< + PLArenaPool, NSSDestroyer1 > ScopedPLArenaPool; + +} // namespace crypto + +#endif // CRYPTO_SCOPED_NSS_TYPES_H_ diff --git a/chromium/crypto/secure_hash.h b/chromium/crypto/secure_hash.h new file mode 100644 index 00000000000..173fd0a8a3e --- /dev/null +++ b/chromium/crypto/secure_hash.h @@ -0,0 +1,50 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SECURE_HASH_H_ +#define CRYPTO_SECURE_HASH_H_ + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +class Pickle; +class PickleIterator; + +namespace crypto { + +// A wrapper to calculate secure hashes incrementally, allowing to +// be used when the full input is not known in advance. +class CRYPTO_EXPORT SecureHash { + public: + enum Algorithm { + SHA256, + }; + virtual ~SecureHash() {} + + static SecureHash* Create(Algorithm type); + + virtual void Update(const void* input, size_t len) = 0; + virtual void Finish(void* output, size_t len) = 0; + + // Serialize the context, so it can be restored at a later time. + // |pickle| will contain the serialized data. + // Returns whether or not |pickle| was filled. + virtual bool Serialize(Pickle* pickle) = 0; + + // Restore the context that was saved earlier. + // |data_iterator| allows this to be used as part of a larger pickle. + // |pickle| holds the saved data. + // Returns success or failure. + virtual bool Deserialize(PickleIterator* data_iterator) = 0; + + protected: + SecureHash() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SecureHash); +}; + +} // namespace crypto + +#endif // CRYPTO_SECURE_HASH_H_ diff --git a/chromium/crypto/secure_hash_default.cc b/chromium/crypto/secure_hash_default.cc new file mode 100644 index 00000000000..7b912e1af60 --- /dev/null +++ b/chromium/crypto/secure_hash_default.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/secure_hash.h" + +#include "base/logging.h" +#include "base/pickle.h" +#include "crypto/third_party/nss/chromium-blapi.h" +#include "crypto/third_party/nss/chromium-sha256.h" + +namespace crypto { + +namespace { + +const char kSHA256Descriptor[] = "NSS"; + +class SecureHashSHA256NSS : public SecureHash { + public: + static const int kSecureHashVersion = 1; + + SecureHashSHA256NSS() { + SHA256_Begin(&ctx_); + } + + virtual ~SecureHashSHA256NSS() { + memset(&ctx_, 0, sizeof(ctx_)); + } + + // SecureHash implementation: + virtual void Update(const void* input, size_t len) OVERRIDE { + SHA256_Update(&ctx_, static_cast(input), len); + } + + virtual void Finish(void* output, size_t len) OVERRIDE { + SHA256_End(&ctx_, static_cast(output), NULL, + static_cast(len)); + } + + virtual bool Serialize(Pickle* pickle) OVERRIDE; + virtual bool Deserialize(PickleIterator* data_iterator) OVERRIDE; + + private: + SHA256Context ctx_; +}; + +bool SecureHashSHA256NSS::Serialize(Pickle* pickle) { + if (!pickle) + return false; + + if (!pickle->WriteInt(kSecureHashVersion) || + !pickle->WriteString(kSHA256Descriptor) || + !pickle->WriteBytes(&ctx_, sizeof(ctx_))) { + return false; + } + + return true; +} + +bool SecureHashSHA256NSS::Deserialize(PickleIterator* data_iterator) { + int version; + if (!data_iterator->ReadInt(&version)) + return false; + + if (version > kSecureHashVersion) + return false; // We don't know how to deal with this. + + std::string type; + if (!data_iterator->ReadString(&type)) + return false; + + if (type != kSHA256Descriptor) + return false; // It's the wrong kind. + + const char* data = NULL; + if (!data_iterator->ReadBytes(&data, sizeof(ctx_))) + return false; + + memcpy(&ctx_, data, sizeof(ctx_)); + + return true; +} + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256NSS(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace crypto diff --git a/chromium/crypto/secure_hash_openssl.cc b/chromium/crypto/secure_hash_openssl.cc new file mode 100644 index 00000000000..84d28a52725 --- /dev/null +++ b/chromium/crypto/secure_hash_openssl.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/secure_hash.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/pickle.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +namespace { + +const char kSHA256Descriptor[] = "OpenSSL"; + +class SecureHashSHA256OpenSSL : public SecureHash { + public: + static const int kSecureHashVersion = 1; + + SecureHashSHA256OpenSSL() { + SHA256_Init(&ctx_); + } + + virtual ~SecureHashSHA256OpenSSL() { + OPENSSL_cleanse(&ctx_, sizeof(ctx_)); + } + + virtual void Update(const void* input, size_t len) OVERRIDE { + SHA256_Update(&ctx_, static_cast(input), len); + } + + virtual void Finish(void* output, size_t len) OVERRIDE { + ScopedOpenSSLSafeSizeBuffer result( + static_cast(output), len); + SHA256_Final(result.safe_buffer(), &ctx_); + } + + virtual bool Serialize(Pickle* pickle) OVERRIDE; + virtual bool Deserialize(PickleIterator* data_iterator) OVERRIDE; + + private: + SHA256_CTX ctx_; +}; + +bool SecureHashSHA256OpenSSL::Serialize(Pickle* pickle) { + if (!pickle) + return false; + + if (!pickle->WriteInt(kSecureHashVersion) || + !pickle->WriteString(kSHA256Descriptor) || + !pickle->WriteBytes(&ctx_, sizeof(ctx_))) { + return false; + } + + return true; +} + +bool SecureHashSHA256OpenSSL::Deserialize(PickleIterator* data_iterator) { + if (!data_iterator) + return false; + + int version; + if (!data_iterator->ReadInt(&version)) + return false; + + if (version > kSecureHashVersion) + return false; // We don't know how to deal with this. + + std::string type; + if (!data_iterator->ReadString(&type)) + return false; + + if (type != kSHA256Descriptor) + return false; // It's the wrong kind. + + const char* data = NULL; + if (!data_iterator->ReadBytes(&data, sizeof(ctx_))) + return false; + + memcpy(&ctx_, data, sizeof(ctx_)); + + return true; +} + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256OpenSSL(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace crypto diff --git a/chromium/crypto/secure_hash_unittest.cc b/chromium/crypto/secure_hash_unittest.cc new file mode 100644 index 00000000000..42b9ade3593 --- /dev/null +++ b/chromium/crypto/secure_hash_unittest.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/secure_hash.h" + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/pickle.h" +#include "crypto/sha2.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SecureHashTest, TestUpdate) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(500000, 'a'); // 'a' repeated half a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[crypto::kSHA256Length]; + + scoped_ptr ctx(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + ctx->Update(input3.data(), input3.size()); + ctx->Update(input3.data(), input3.size()); + + ctx->Finish(output3, sizeof(output3)); + for (size_t i = 0; i < crypto::kSHA256Length; i++) + EXPECT_EQ(expected3[i], static_cast(output3[i])); +} + +// Save the crypto state mid-stream, and create another instance with the +// saved state. Then feed the same data afterwards to both. +// When done, both should have the same hash value. +TEST(SecureHashTest, TestSerialization) { + std::string input1(10001, 'a'); // 'a' repeated 10001 times + std::string input2(10001, 'b'); // 'b' repeated 10001 times + std::string input3(10001, 'c'); // 'c' repeated 10001 times + std::string input4(10001, 'd'); // 'd' repeated 10001 times + std::string input5(10001, 'e'); // 'e' repeated 10001 times + + uint8 output1[crypto::kSHA256Length]; + uint8 output2[crypto::kSHA256Length]; + + scoped_ptr ctx1(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + scoped_ptr ctx2(crypto::SecureHash::Create( + crypto::SecureHash::SHA256)); + Pickle pickle; + ctx1->Update(input1.data(), input1.size()); + ctx1->Update(input2.data(), input2.size()); + ctx1->Update(input3.data(), input3.size()); + + EXPECT_TRUE(ctx1->Serialize(&pickle)); + ctx1->Update(input4.data(), input4.size()); + ctx1->Update(input5.data(), input5.size()); + + ctx1->Finish(output1, sizeof(output1)); + + PickleIterator data_iterator(pickle); + EXPECT_TRUE(ctx2->Deserialize(&data_iterator)); + ctx2->Update(input4.data(), input4.size()); + ctx2->Update(input5.data(), input5.size()); + + ctx2->Finish(output2, sizeof(output2)); + + EXPECT_EQ(0, memcmp(output1, output2, crypto::kSHA256Length)); +} diff --git a/chromium/crypto/secure_util.cc b/chromium/crypto/secure_util.cc new file mode 100644 index 00000000000..3fe8aa961a0 --- /dev/null +++ b/chromium/crypto/secure_util.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/secure_util.h" + +namespace crypto { + +bool SecureMemEqual(const void* s1, const void* s2, size_t n) { + const unsigned char* s1_ptr = reinterpret_cast(s1); + const unsigned char* s2_ptr = reinterpret_cast(s2); + unsigned char tmp = 0; + for (size_t i = 0; i < n; ++i, ++s1_ptr, ++s2_ptr) + tmp |= *s1_ptr ^ *s2_ptr; + return (tmp == 0); +} + +} // namespace crypto + diff --git a/chromium/crypto/secure_util.h b/chromium/crypto/secure_util.h new file mode 100644 index 00000000000..cfe05ca1554 --- /dev/null +++ b/chromium/crypto/secure_util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SECURE_UTIL_H_ +#define CRYPTO_SECURE_UTIL_H_ + +#include + +#include "crypto/crypto_export.h" + +namespace crypto { + +// Performs a constant-time comparison of two strings, returning true if the +// strings are equal. +// +// For cryptographic operations, comparison functions such as memcmp() may +// expose side-channel information about input, allowing an attacker to +// perform timing analysis to determine what the expected bits should be. In +// order to avoid such attacks, the comparison must execute in constant time, +// so as to not to reveal to the attacker where the difference(s) are. +// For an example attack, see +// http://groups.google.com/group/keyczar-discuss/browse_thread/thread/5571eca0948b2a13 +CRYPTO_EXPORT bool SecureMemEqual(const void* s1, const void* s2, size_t n); + +} // namespace crypto + +#endif // CRYPTO_SECURE_UTIL_H_ + diff --git a/chromium/crypto/sha2.cc b/chromium/crypto/sha2.cc new file mode 100644 index 00000000000..6f362379618 --- /dev/null +++ b/chromium/crypto/sha2.cc @@ -0,0 +1,25 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/sha2.h" + +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "crypto/secure_hash.h" + +namespace crypto { + +void SHA256HashString(const base::StringPiece& str, void* output, size_t len) { + scoped_ptr ctx(SecureHash::Create(SecureHash::SHA256)); + ctx->Update(str.data(), str.length()); + ctx->Finish(output, len); +} + +std::string SHA256HashString(const base::StringPiece& str) { + std::string output(kSHA256Length, 0); + SHA256HashString(str, string_as_array(&output), output.size()); + return output; +} + +} // namespace crypto diff --git a/chromium/crypto/sha2.h b/chromium/crypto/sha2.h new file mode 100644 index 00000000000..7e279d3e943 --- /dev/null +++ b/chromium/crypto/sha2.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SHA2_H_ +#define CRYPTO_SHA2_H_ + +#include + +#include "base/strings/string_piece.h" +#include "crypto/crypto_export.h" + +namespace crypto { + +// These functions perform SHA-256 operations. +// +// Functions for SHA-384 and SHA-512 can be added when the need arises. + +static const size_t kSHA256Length = 32; // Length in bytes of a SHA-256 hash. + +// Computes the SHA-256 hash of the input string 'str' and stores the first +// 'len' bytes of the hash in the output buffer 'output'. If 'len' > 32, +// only 32 bytes (the full hash) are stored in the 'output' buffer. +CRYPTO_EXPORT void SHA256HashString(const base::StringPiece& str, + void* output, size_t len); + +// Convenience version of the above that returns the result in a 32-byte +// string. +CRYPTO_EXPORT std::string SHA256HashString(const base::StringPiece& str); + +} // namespace crypto + +#endif // CRYPTO_SHA2_H_ diff --git a/chromium/crypto/sha2_unittest.cc b/chromium/crypto/sha2_unittest.cc new file mode 100644 index 00000000000..78da1360d4d --- /dev/null +++ b/chromium/crypto/sha2_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/sha2.h" + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(Sha256Test, Test1) { + // Example B.1 from FIPS 180-2: one-block message. + std::string input1 = "abc"; + int expected1[] = { 0xba, 0x78, 0x16, 0xbf, + 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, + 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad }; + + uint8 output1[crypto::kSHA256Length]; + crypto::SHA256HashString(input1, output1, sizeof(output1)); + for (size_t i = 0; i < crypto::kSHA256Length; i++) + EXPECT_EQ(expected1[i], static_cast(output1[i])); + + uint8 output_truncated1[4]; // 4 bytes == 32 bits + crypto::SHA256HashString(input1, + output_truncated1, sizeof(output_truncated1)); + for (size_t i = 0; i < sizeof(output_truncated1); i++) + EXPECT_EQ(expected1[i], static_cast(output_truncated1[i])); +} + +TEST(Sha256Test, Test1_String) { + // Same as the above, but using the wrapper that returns a std::string. + // Example B.1 from FIPS 180-2: one-block message. + std::string input1 = "abc"; + int expected1[] = { 0xba, 0x78, 0x16, 0xbf, + 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, + 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad }; + + std::string output1 = crypto::SHA256HashString(input1); + ASSERT_EQ(crypto::kSHA256Length, output1.size()); + for (size_t i = 0; i < crypto::kSHA256Length; i++) + EXPECT_EQ(expected1[i], static_cast(output1[i])); +} + +TEST(Sha256Test, Test2) { + // Example B.2 from FIPS 180-2: multi-block message. + std::string input2 = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + int expected2[] = { 0x24, 0x8d, 0x6a, 0x61, + 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, + 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, + 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, + 0x19, 0xdb, 0x06, 0xc1 }; + + uint8 output2[crypto::kSHA256Length]; + crypto::SHA256HashString(input2, output2, sizeof(output2)); + for (size_t i = 0; i < crypto::kSHA256Length; i++) + EXPECT_EQ(expected2[i], static_cast(output2[i])); + + uint8 output_truncated2[6]; + crypto::SHA256HashString(input2, + output_truncated2, sizeof(output_truncated2)); + for (size_t i = 0; i < sizeof(output_truncated2); i++) + EXPECT_EQ(expected2[i], static_cast(output_truncated2[i])); +} + +TEST(Sha256Test, Test3) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(1000000, 'a'); // 'a' repeated a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[crypto::kSHA256Length]; + crypto::SHA256HashString(input3, output3, sizeof(output3)); + for (size_t i = 0; i < crypto::kSHA256Length; i++) + EXPECT_EQ(expected3[i], static_cast(output3[i])); + + uint8 output_truncated3[12]; + crypto::SHA256HashString(input3, + output_truncated3, sizeof(output_truncated3)); + for (size_t i = 0; i < sizeof(output_truncated3); i++) + EXPECT_EQ(expected3[i], static_cast(output_truncated3[i])); +} diff --git a/chromium/crypto/signature_creator.h b/chromium/crypto/signature_creator.h new file mode 100644 index 00000000000..6074bcbe9cb --- /dev/null +++ b/chromium/crypto/signature_creator.h @@ -0,0 +1,67 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SIGNATURE_CREATOR_H_ +#define CRYPTO_SIGNATURE_CREATOR_H_ + +#include "build/build_config.h" + +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +#if defined(USE_OPENSSL) +// Forward declaration for openssl/*.h +typedef struct env_md_ctx_st EVP_MD_CTX; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) +// Forward declaration. +struct SGNContextStr; +#endif + +namespace crypto { + +class RSAPrivateKey; + +// Signs data using a bare private key (as opposed to a full certificate). +// Currently can only sign data using SHA-1 with RSA encryption. +class CRYPTO_EXPORT SignatureCreator { + public: + ~SignatureCreator(); + + // Create an instance. The caller must ensure that the provided PrivateKey + // instance outlives the created SignatureCreator. + static SignatureCreator* Create(RSAPrivateKey* key); + + // Signs the precomputed SHA-1 digest |data| using private |key| as + // specified in PKCS #1 v1.5. + static bool Sign(RSAPrivateKey* key, + const uint8* data, + int data_len, + std::vector* signature); + + // Update the signature with more data. + bool Update(const uint8* data_part, int data_part_len); + + // Finalize the signature. + bool Final(std::vector* signature); + + private: + // Private constructor. Use the Create() method instead. + SignatureCreator(); + + RSAPrivateKey* key_; + +#if defined(USE_OPENSSL) + EVP_MD_CTX* sign_context_; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + SGNContextStr* sign_context_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SignatureCreator); +}; + +} // namespace crypto + +#endif // CRYPTO_SIGNATURE_CREATOR_H_ diff --git a/chromium/crypto/signature_creator_nss.cc b/chromium/crypto/signature_creator_nss.cc new file mode 100644 index 00000000000..bc8dc449a43 --- /dev/null +++ b/chromium/crypto/signature_creator_nss.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_creator.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "crypto/nss_util.h" +#include "crypto/rsa_private_key.h" + +namespace crypto { + +SignatureCreator::~SignatureCreator() { + if (sign_context_) { + SGN_DestroyContext(sign_context_, PR_TRUE); + sign_context_ = NULL; + } +} + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + scoped_ptr result(new SignatureCreator); + result->key_ = key; + + result->sign_context_ = SGN_NewContext(SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, + key->key()); + if (!result->sign_context_) { + NOTREACHED(); + return NULL; + } + + SECStatus rv = SGN_Begin(result->sign_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + +// static +bool SignatureCreator::Sign(RSAPrivateKey* key, + const uint8* data, + int data_len, + std::vector* signature) { + SECItem data_item; + data_item.type = siBuffer; + data_item.data = const_cast(data); + data_item.len = data_len; + + SECItem signature_item; + SECStatus rv = SGN_Digest(key->key(), SEC_OID_SHA1, &signature_item, + &data_item); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + signature->assign(signature_item.data, + signature_item.data + signature_item.len); + SECITEM_FreeItem(&signature_item, PR_FALSE); + return true; +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + SECStatus rv = SGN_Update(sign_context_, data_part, data_part_len); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SignatureCreator::Final(std::vector* signature) { + SECItem signature_item; + SECStatus rv = SGN_End(sign_context_, &signature_item); + if (rv != SECSuccess) { + return false; + } + signature->assign(signature_item.data, + signature_item.data + signature_item.len); + SECITEM_FreeItem(&signature_item, PR_FALSE); + return true; +} + +SignatureCreator::SignatureCreator() + : key_(NULL), + sign_context_(NULL) { + EnsureNSSInit(); +} + +} // namespace crypto diff --git a/chromium/crypto/signature_creator_openssl.cc b/chromium/crypto/signature_creator_openssl.cc new file mode 100644 index 00000000000..e46d3d002c1 --- /dev/null +++ b/chromium/crypto/signature_creator_openssl.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_creator.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "crypto/openssl_util.h" +#include "crypto/rsa_private_key.h" + +namespace crypto { + +// static +SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr result(new SignatureCreator); + result->key_ = key; + if (!EVP_SignInit_ex(result->sign_context_, EVP_sha1(), NULL)) + return NULL; + return result.release(); +} + +// static +bool SignatureCreator::Sign(RSAPrivateKey* key, + const uint8* data, + int data_len, + std::vector* signature) { + RSA* rsa_key = EVP_PKEY_get1_RSA(key->key()); + if (!rsa_key) + return false; + signature->resize(RSA_size(rsa_key)); + + unsigned int len = 0; + bool success = RSA_sign(NID_sha1, data, data_len, vector_as_array(signature), + &len, rsa_key); + if (!success) { + signature->clear(); + return false; + } + signature->resize(len); + return true; +} + +SignatureCreator::SignatureCreator() + : sign_context_(EVP_MD_CTX_create()) { +} + +SignatureCreator::~SignatureCreator() { + EVP_MD_CTX_destroy(sign_context_); +} + +bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + return EVP_SignUpdate(sign_context_, data_part, data_part_len) == 1; +} + +bool SignatureCreator::Final(std::vector* signature) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + EVP_PKEY* key = key_->key(); + signature->resize(EVP_PKEY_size(key)); + + unsigned int len = 0; + int rv = EVP_SignFinal(sign_context_, vector_as_array(signature), &len, key); + if (!rv) { + signature->clear(); + return false; + } + signature->resize(len); + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/signature_creator_unittest.cc b/chromium/crypto/signature_creator_unittest.cc new file mode 100644 index 00000000000..f0a888816e6 --- /dev/null +++ b/chromium/crypto/signature_creator_unittest.cc @@ -0,0 +1,96 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/sha1.h" +#include "crypto/rsa_private_key.h" +#include "crypto/signature_creator.h" +#include "crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// This is the algorithm ID for SHA-1 with RSA encryption. +const uint8 kSHA1WithRSAAlgorithmID[] = { + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00 +}; + +} + +TEST(SignatureCreatorTest, BasicTest) { + // Do a verify round trip. + scoped_ptr key_original( + crypto::RSAPrivateKey::Create(1024)); + ASSERT_TRUE(key_original.get()); + + std::vector key_info; + key_original->ExportPrivateKey(&key_info); + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info)); + ASSERT_TRUE(key.get()); + + scoped_ptr signer( + crypto::SignatureCreator::Create(key.get())); + ASSERT_TRUE(signer.get()); + + std::string data("Hello, World!"); + ASSERT_TRUE(signer->Update(reinterpret_cast(data.c_str()), + data.size())); + + std::vector signature; + ASSERT_TRUE(signer->Final(&signature)); + + std::vector public_key_info; + ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); + + crypto::SignatureVerifier verifier; + ASSERT_TRUE(verifier.VerifyInit( + kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID), + &signature.front(), signature.size(), + &public_key_info.front(), public_key_info.size())); + + verifier.VerifyUpdate(reinterpret_cast(data.c_str()), + data.size()); + ASSERT_TRUE(verifier.VerifyFinal()); +} + +TEST(SignatureCreatorTest, SignDigestTest) { + // Do a verify round trip. + scoped_ptr key_original( + crypto::RSAPrivateKey::Create(1024)); + ASSERT_TRUE(key_original.get()); + + std::vector key_info; + key_original->ExportPrivateKey(&key_info); + scoped_ptr key( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_info)); + ASSERT_TRUE(key.get()); + + std::string data("Hello, World!"); + std::string sha1 = base::SHA1HashString(data); + // Sign sha1 of the input data. + std::vector signature; + ASSERT_TRUE(crypto::SignatureCreator::Sign( + key.get(), + reinterpret_cast(sha1.c_str()), + sha1.size(), + &signature)); + + std::vector public_key_info; + ASSERT_TRUE(key_original->ExportPublicKey(&public_key_info)); + + // Verify the input data. + crypto::SignatureVerifier verifier; + ASSERT_TRUE(verifier.VerifyInit( + kSHA1WithRSAAlgorithmID, sizeof(kSHA1WithRSAAlgorithmID), + &signature.front(), signature.size(), + &public_key_info.front(), public_key_info.size())); + + verifier.VerifyUpdate(reinterpret_cast(data.c_str()), + data.size()); + ASSERT_TRUE(verifier.VerifyFinal()); +} diff --git a/chromium/crypto/signature_verifier.h b/chromium/crypto/signature_verifier.h new file mode 100644 index 00000000000..93591d2a82d --- /dev/null +++ b/chromium/crypto/signature_verifier.h @@ -0,0 +1,146 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SIGNATURE_VERIFIER_H_ +#define CRYPTO_SIGNATURE_VERIFIER_H_ + +#include "build/build_config.h" + +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +#if defined(USE_OPENSSL) +typedef struct env_md_st EVP_MD; +typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; +#else +typedef struct HASHContextStr HASHContext; +typedef struct SECKEYPublicKeyStr SECKEYPublicKey; +typedef struct VFYContextStr VFYContext; +#endif + +namespace crypto { + +// The SignatureVerifier class verifies a signature using a bare public key +// (as opposed to a certificate). +class CRYPTO_EXPORT SignatureVerifier { + public: + // The set of supported hash functions. Extend as required. + enum HashAlgorithm { + SHA1, + SHA256, + }; + + SignatureVerifier(); + ~SignatureVerifier(); + + // Streaming interface: + + // Initiates a signature verification operation. This should be followed + // by one or more VerifyUpdate calls and a VerifyFinal call. + // NOTE: for RSA-PSS signatures, use VerifyInitRSAPSS instead. + // + // The signature algorithm is specified as a DER encoded ASN.1 + // AlgorithmIdentifier structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // The signature is encoded according to the signature algorithm, but it + // must not be further encoded in an ASN.1 BIT STRING. + // Note: An RSA signature is actually a big integer. It must be in + // big-endian byte order. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + bool VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len); + + // Initiates a RSA-PSS signature verification operation. This should be + // followed by one or more VerifyUpdate calls and a VerifyFinal call. + // + // The RSA-PSS signature algorithm parameters are specified with the + // |hash_alg|, |mask_hash_alg|, and |salt_len| arguments. + // + // An RSA-PSS signature is a nonnegative integer encoded as a byte string + // (of the same length as the RSA modulus) in big-endian byte order. It + // must not be further encoded in an ASN.1 BIT STRING. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + bool VerifyInitRSAPSS(HashAlgorithm hash_alg, + HashAlgorithm mask_hash_alg, + int salt_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len); + + // Feeds a piece of the data to the signature verifier. + void VerifyUpdate(const uint8* data_part, int data_part_len); + + // Concludes a signature verification operation. Returns true if the + // signature is valid. Returns false if the signature is invalid or an + // error occurred. + bool VerifyFinal(); + + // Note: we can provide a one-shot interface if there is interest: + // bool Verify(const uint8* data, + // int data_len, + // const uint8* signature_algorithm, + // int signature_algorithm_len, + // const uint8* signature, + // int signature_len, + // const uint8* public_key_info, + // int public_key_info_len); + + private: +#if defined(USE_OPENSSL) + bool CommonInit(const EVP_MD* digest, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len, + EVP_PKEY_CTX** pkey_ctx); +#else + static SECKEYPublicKey* DecodePublicKeyInfo(const uint8* public_key_info, + int public_key_info_len); +#endif + + void Reset(); + + std::vector signature_; + +#if defined(USE_OPENSSL) + struct VerifyContext; + VerifyContext* verify_context_; +#else + // Used for all signature types except RSA-PSS. + VFYContext* vfy_context_; + + // Used for RSA-PSS signatures. + HashAlgorithm hash_alg_; + HashAlgorithm mask_hash_alg_; + unsigned int salt_len_; + SECKEYPublicKey* public_key_; + HASHContext* hash_context_; +#endif +}; + +} // namespace crypto + +#endif // CRYPTO_SIGNATURE_VERIFIER_H_ diff --git a/chromium/crypto/signature_verifier_nss.cc b/chromium/crypto/signature_verifier_nss.cc new file mode 100644 index 00000000000..5be620da6bd --- /dev/null +++ b/chromium/crypto/signature_verifier_nss.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_verifier.h" + +#include +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" +#include "crypto/third_party/nss/chromium-nss.h" + +namespace crypto { + +namespace { + +HASH_HashType ToNSSHashType(SignatureVerifier::HashAlgorithm hash_alg) { + switch (hash_alg) { + case SignatureVerifier::SHA1: + return HASH_AlgSHA1; + case SignatureVerifier::SHA256: + return HASH_AlgSHA256; + } + return HASH_AlgNULL; +} + +SECStatus VerifyRSAPSS_End(SECKEYPublicKey* public_key, + HASHContext* hash_context, + HASH_HashType mask_hash_alg, + unsigned int salt_len, + const unsigned char* signature, + unsigned int signature_len) { + unsigned int hash_len = HASH_ResultLenContext(hash_context); + std::vector hash(hash_len); + HASH_End(hash_context, &hash[0], &hash_len, hash.size()); + + unsigned int modulus_len = SECKEY_PublicKeyStrength(public_key); + if (signature_len != modulus_len) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + std::vector enc(signature_len); + SECStatus rv = PK11_PubEncryptRaw(public_key, &enc[0], + const_cast(signature), + signature_len, NULL); + if (rv != SECSuccess) { + LOG(WARNING) << "PK11_PubEncryptRaw failed"; + return rv; + } + return emsa_pss_verify(&hash[0], &enc[0], enc.size(), + HASH_GetType(hash_context), mask_hash_alg, + salt_len); +} + +} // namespace + +SignatureVerifier::SignatureVerifier() + : vfy_context_(NULL), + hash_alg_(SHA1), + mask_hash_alg_(SHA1), + salt_len_(0), + public_key_(NULL), + hash_context_(NULL) { + EnsureNSSInit(); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + if (vfy_context_ || hash_context_) + return false; + + signature_.assign(signature, signature + signature_len); + + SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, + public_key_info_len); + if (!public_key) + return false; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECKEY_DestroyPublicKey(public_key); + return false; + } + + SECItem sig_alg_der; + sig_alg_der.type = siBuffer; + sig_alg_der.data = const_cast(signature_algorithm); + sig_alg_der.len = signature_algorithm_len; + SECAlgorithmID sig_alg_id; + SECStatus rv; + rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, + SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), + &sig_alg_der); + if (rv != SECSuccess) { + SECKEY_DestroyPublicKey(public_key); + PORT_FreeArena(arena, PR_TRUE); + return false; + } + + SECItem sig; + sig.type = siBuffer; + sig.data = const_cast(signature); + sig.len = signature_len; + SECOidTag hash_alg_tag; + vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig, + &sig_alg_id, &hash_alg_tag, + NULL); + SECKEY_DestroyPublicKey(public_key); // Done with public_key. + PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. + if (!vfy_context_) { + // A corrupted RSA signature could be detected without the data, so + // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE + // (-8182). + return false; + } + + rv = VFY_Begin(vfy_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + return true; +} + +bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg, + HashAlgorithm mask_hash_alg, + int salt_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + if (vfy_context_ || hash_context_) + return false; + + signature_.assign(signature, signature + signature_len); + + SECKEYPublicKey* public_key = DecodePublicKeyInfo(public_key_info, + public_key_info_len); + if (!public_key) + return false; + + public_key_ = public_key; + hash_alg_ = hash_alg; + mask_hash_alg_ = mask_hash_alg; + salt_len_ = salt_len; + hash_context_ = HASH_Create(ToNSSHashType(hash_alg_)); + if (!hash_context_) + return false; + HASH_Begin(hash_context_); + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + if (vfy_context_) { + SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); + DCHECK_EQ(SECSuccess, rv); + } else { + HASH_Update(hash_context_, data_part, data_part_len); + } +} + +bool SignatureVerifier::VerifyFinal() { + SECStatus rv; + if (vfy_context_) { + rv = VFY_End(vfy_context_); + } else { + rv = VerifyRSAPSS_End(public_key_, hash_context_, + ToNSSHashType(mask_hash_alg_), salt_len_, + signature_.data(), + signature_.size()); + } + Reset(); + + // If signature verification fails, the error code is + // SEC_ERROR_BAD_SIGNATURE (-8182). + return (rv == SECSuccess); +} + +// static +SECKEYPublicKey* SignatureVerifier::DecodePublicKeyInfo( + const uint8* public_key_info, + int public_key_info_len) { + CERTSubjectPublicKeyInfo* spki = NULL; + SECItem spki_der; + spki_der.type = siBuffer; + spki_der.data = const_cast(public_key_info); + spki_der.len = public_key_info_len; + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); + if (!spki) + return NULL; + SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. + return public_key; +} + +void SignatureVerifier::Reset() { + if (vfy_context_) { + VFY_DestroyContext(vfy_context_, PR_TRUE); + vfy_context_ = NULL; + } + if (hash_context_) { + HASH_Destroy(hash_context_); + hash_context_ = NULL; + } + if (public_key_) { + SECKEY_DestroyPublicKey(public_key_); + public_key_ = NULL; + } + signature_.clear(); +} + +} // namespace crypto diff --git a/chromium/crypto/signature_verifier_openssl.cc b/chromium/crypto/signature_verifier_openssl.cc new file mode 100644 index 00000000000..a85f00b491e --- /dev/null +++ b/chromium/crypto/signature_verifier_openssl.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_verifier.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +namespace { + +const EVP_MD* ToOpenSSLDigest(SignatureVerifier::HashAlgorithm hash_alg) { + switch (hash_alg) { + case SignatureVerifier::SHA1: + return EVP_sha1(); + case SignatureVerifier::SHA256: + return EVP_sha256(); + } + return EVP_md_null(); +} + +} // namespace + +struct SignatureVerifier::VerifyContext { + ScopedOpenSSL ctx; +}; + +SignatureVerifier::SignatureVerifier() + : verify_context_(NULL) { +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + ScopedOpenSSL algorithm( + d2i_X509_ALGOR(NULL, &signature_algorithm, signature_algorithm_len)); + if (!algorithm.get()) + return false; + int nid = OBJ_obj2nid(algorithm.get()->algorithm); + const EVP_MD* digest; + if (nid == NID_ecdsa_with_SHA1) { + digest = EVP_sha1(); + } else if (nid == NID_ecdsa_with_SHA256) { + digest = EVP_sha256(); + } else { + // This works for PKCS #1 v1.5 RSA signatures, but not for ECDSA + // signatures. + digest = EVP_get_digestbyobj(algorithm.get()->algorithm); + } + if (!digest) + return false; + + return CommonInit(digest, signature, signature_len, public_key_info, + public_key_info_len, NULL); +} + +bool SignatureVerifier::VerifyInitRSAPSS(HashAlgorithm hash_alg, + HashAlgorithm mask_hash_alg, + int salt_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + OpenSSLErrStackTracer err_tracer(FROM_HERE); + const EVP_MD* digest = ToOpenSSLDigest(hash_alg); + DCHECK(digest); + + EVP_PKEY_CTX* pkey_ctx; + if (!CommonInit(digest, signature, signature_len, public_key_info, + public_key_info_len, &pkey_ctx)) { + return false; + } + + int rv = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING); + if (rv != 1) + return false; + rv = EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, + ToOpenSSLDigest(mask_hash_alg)); + if (rv != 1) + return false; + rv = EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, salt_len); + return rv == 1; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + DCHECK(verify_context_); + OpenSSLErrStackTracer err_tracer(FROM_HERE); + int rv = EVP_DigestVerifyUpdate(verify_context_->ctx.get(), + data_part, data_part_len); + DCHECK_EQ(rv, 1); +} + +bool SignatureVerifier::VerifyFinal() { + DCHECK(verify_context_); + OpenSSLErrStackTracer err_tracer(FROM_HERE); + int rv = EVP_DigestVerifyFinal(verify_context_->ctx.get(), + vector_as_array(&signature_), + signature_.size()); + // rv is -1 if a DER-encoded ECDSA signature cannot be decoded correctly. + DCHECK_GE(rv, -1); + Reset(); + return rv == 1; +} + +bool SignatureVerifier::CommonInit(const EVP_MD* digest, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len, + EVP_PKEY_CTX** pkey_ctx) { + if (verify_context_) + return false; + + verify_context_ = new VerifyContext; + + signature_.assign(signature, signature + signature_len); + + // BIO_new_mem_buf is not const aware, but it does not modify the buffer. + char* data = reinterpret_cast(const_cast(public_key_info)); + ScopedOpenSSL bio(BIO_new_mem_buf(data, + public_key_info_len)); + if (!bio.get()) + return false; + + ScopedOpenSSL public_key( + d2i_PUBKEY_bio(bio.get(), NULL)); + if (!public_key.get()) + return false; + + verify_context_->ctx.reset(EVP_MD_CTX_create()); + int rv = EVP_DigestVerifyInit(verify_context_->ctx.get(), pkey_ctx, + digest, NULL, public_key.get()); + return rv == 1; +} + +void SignatureVerifier::Reset() { + delete verify_context_; + verify_context_ = NULL; + signature_.clear(); +} + +} // namespace crypto diff --git a/chromium/crypto/signature_verifier_unittest.cc b/chromium/crypto/signature_verifier_unittest.cc new file mode 100644 index 00000000000..332979971c6 --- /dev/null +++ b/chromium/crypto/signature_verifier_unittest.cc @@ -0,0 +1,1138 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SignatureVerifierTest, BasicTest) { + // The input data in this test comes from real certificates. + // + // tbs_certificate ("to-be-signed certificate", the part of a certificate + // that is signed), signature_algorithm, and algorithm come from the + // certificate of bugs.webkit.org. + // + // public_key_info comes from the certificate of the issuer, Go Daddy Secure + // Certification Authority. + // + // The bytes in the array initializers are formatted to expose the DER + // encoding of the ASN.1 structures. + + // The data that is signed is the following ASN.1 structure: + // TBSCertificate ::= SEQUENCE { + // ... -- omitted, not important + // } + const uint8 tbs_certificate[1017] = { + 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5) + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, + 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, + 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, + 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, + 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, + 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, + 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, + 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, + 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, + 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, + 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, + 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, + 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, + 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, + 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, + 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, + 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, + 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, + 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, + 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, + 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, + 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, + 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, + 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67 + }; + + // The signature algorithm is specified as the following ASN.1 structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + const uint8 signature_algorithm[15] = { + 0x30, 0x0d, // a SEQUENCE of length 13 (0xd) + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + // 1.2.840.113549.1.1.5 - sha1WithRSAEncryption + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, // a NULL of length 0 + }; + + // RSA signature, a big integer in the big-endian byte order. + const uint8 signature[256] = { + 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, + 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, + 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, + 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, + 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, + 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, + 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, + 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, + 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, + 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, + 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, + 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, + 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, + 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, + 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, + 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, + 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, + 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, + 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, + 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, + 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, + 0x3c, 0xd8, 0x48, 0x8a + }; + + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + const uint8 public_key_info[294] = { + 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122) + // algorithm + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // a NULL of length 0 + // subjectPublicKey + 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f) + 0x00, // number of unused bits + 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a) + // modulus + 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101) + 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec, + 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61, + 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7, + 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, + 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, + 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, + 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9, + 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95, + 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00, + 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, + 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, + 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, + 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48, + 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66, + 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8, + 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, + 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, + 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, + 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0, + 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5, + 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81, + 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, + 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, + 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, + 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe, + 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, + // public exponent + 0x02, 0x03, // an INTEGER of length 3 + 0x01, 0x00, 0x01 + }; + + // We use the signature verifier to perform four signature verification + // tests. + crypto::SignatureVerifier verifier; + bool ok; + + // Test 1: feed all of the data to the verifier at once (a single + // VerifyUpdate call). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 2: feed the data to the verifier in three parts (three VerifyUpdate + // calls). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, 256); + verifier.VerifyUpdate(tbs_certificate + 256, 256); + verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 3: verify the signature with incorrect data. + uint8 bad_tbs_certificate[sizeof(tbs_certificate)]; + memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate)); + bad_tbs_certificate[10] += 1; // Corrupt one byte of the data. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + + // Test 4: verify a bad signature. + uint8 bad_signature[sizeof(signature)]; + memcpy(bad_signature, signature, sizeof(signature)); + bad_signature[10] += 1; // Corrupt one byte of the signature. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + bad_signature, sizeof(bad_signature), + public_key_info, sizeof(public_key_info)); + + // A crypto library (e.g., NSS) may detect that the signature is corrupted + // and cause VerifyInit to return false, so it is fine for 'ok' to be false. + if (ok) { + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + } +} + +////////////////////////////////////////////////////////////////////// +// +// RSA-PSS signature verification known answer test +// +////////////////////////////////////////////////////////////////////// + +// The following RSA-PSS signature test vectors come from the pss-vect.txt +// file downloaded from +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip. +// +// For each key, 6 random messages of length between 1 and 256 octets have +// been RSASSA-PSS signed. +// +// Hash function: SHA-1 +// Mask generation function: MGF1 with SHA-1 +// Salt length: 20 octets + +// Example 1: A 1024-bit RSA Key Pair" + +// RSA modulus n: +static const char rsa_modulus_n_1[] = + "a5 6e 4a 0e 70 10 17 58 9a 51 87 dc 7e a8 41 d1 " + "56 f2 ec 0e 36 ad 52 a4 4d fe b1 e6 1f 7a d9 91 " + "d8 c5 10 56 ff ed b1 62 b4 c0 f2 83 a1 2a 88 a3 " + "94 df f5 26 ab 72 91 cb b3 07 ce ab fc e0 b1 df " + "d5 cd 95 08 09 6d 5b 2b 8b 6d f5 d6 71 ef 63 77 " + "c0 92 1c b2 3c 27 0a 70 e2 59 8e 6f f8 9d 19 f1 " + "05 ac c2 d3 f0 cb 35 f2 92 80 e1 38 6b 6f 64 c4 " + "ef 22 e1 e1 f2 0d 0c e8 cf fb 22 49 bd 9a 21 37 "; +// RSA public exponent e: " +static const char rsa_public_exponent_e_1[] = + "01 00 01 "; + +// RSASSA-PSS Signature Example 1.1 +// Message to be signed: +static const char message_1_1[] = + "cd c8 7d a2 23 d7 86 df 3b 45 e0 bb bc 72 13 26 " + "d1 ee 2a f8 06 cc 31 54 75 cc 6f 0d 9c 66 e1 b6 " + "23 71 d4 5c e2 39 2e 1a c9 28 44 c3 10 10 2f 15 " + "6a 0d 8d 52 c1 f4 c4 0b a3 aa 65 09 57 86 cb 76 " + "97 57 a6 56 3b a9 58 fe d0 bc c9 84 e8 b5 17 a3 " + "d5 f5 15 b2 3b 8a 41 e7 4a a8 67 69 3f 90 df b0 " + "61 a6 e8 6d fa ae e6 44 72 c0 0e 5f 20 94 57 29 " + "cb eb e7 7f 06 ce 78 e0 8f 40 98 fb a4 1f 9d 61 " + "93 c0 31 7e 8b 60 d4 b6 08 4a cb 42 d2 9e 38 08 " + "a3 bc 37 2d 85 e3 31 17 0f cb f7 cc 72 d0 b7 1c " + "29 66 48 b3 a4 d1 0f 41 62 95 d0 80 7a a6 25 ca " + "b2 74 4f d9 ea 8f d2 23 c4 25 37 02 98 28 bd 16 " + "be 02 54 6f 13 0f d2 e3 3b 93 6d 26 76 e0 8a ed " + "1b 73 31 8b 75 0a 01 67 d0 "; +// Salt: +static const char salt_1_1[] = + "de e9 59 c7 e0 64 11 36 14 20 ff 80 18 5e d5 7f " + "3e 67 76 af "; +// Signature: +static const char signature_1_1[] = + "90 74 30 8f b5 98 e9 70 1b 22 94 38 8e 52 f9 71 " + "fa ac 2b 60 a5 14 5a f1 85 df 52 87 b5 ed 28 87 " + "e5 7c e7 fd 44 dc 86 34 e4 07 c8 e0 e4 36 0b c2 " + "26 f3 ec 22 7f 9d 9e 54 63 8e 8d 31 f5 05 12 15 " + "df 6e bb 9c 2f 95 79 aa 77 59 8a 38 f9 14 b5 b9 " + "c1 bd 83 c4 e2 f9 f3 82 a0 d0 aa 35 42 ff ee 65 " + "98 4a 60 1b c6 9e b2 8d eb 27 dc a1 2c 82 c2 d4 " + "c3 f6 6c d5 00 f1 ff 2b 99 4d 8a 4e 30 cb b3 3c "; + +// RSASSA-PSS Signature Example 1.2 +// Message to be signed: +static const char message_1_2[] = + "85 13 84 cd fe 81 9c 22 ed 6c 4c cb 30 da eb 5c " + "f0 59 bc 8e 11 66 b7 e3 53 0c 4c 23 3e 2b 5f 8f " + "71 a1 cc a5 82 d4 3e cc 72 b1 bc a1 6d fc 70 13 " + "22 6b 9e "; +// Salt: +static const char salt_1_2[] = + "ef 28 69 fa 40 c3 46 cb 18 3d ab 3d 7b ff c9 8f " + "d5 6d f4 2d "; +// Signature: +static const char signature_1_2[] = + "3e f7 f4 6e 83 1b f9 2b 32 27 41 42 a5 85 ff ce " + "fb dc a7 b3 2a e9 0d 10 fb 0f 0c 72 99 84 f0 4e " + "f2 9a 9d f0 78 07 75 ce 43 73 9b 97 83 83 90 db " + "0a 55 05 e6 3d e9 27 02 8d 9d 29 b2 19 ca 2c 45 " + "17 83 25 58 a5 5d 69 4a 6d 25 b9 da b6 60 03 c4 " + "cc cd 90 78 02 19 3b e5 17 0d 26 14 7d 37 b9 35 " + "90 24 1b e5 1c 25 05 5f 47 ef 62 75 2c fb e2 14 " + "18 fa fe 98 c2 2c 4d 4d 47 72 4f db 56 69 e8 43 "; + +// RSASSA-PSS Signature Example 1.3 +// Message to be signed: +static const char message_1_3[] = + "a4 b1 59 94 17 61 c4 0c 6a 82 f2 b8 0d 1b 94 f5 " + "aa 26 54 fd 17 e1 2d 58 88 64 67 9b 54 cd 04 ef " + "8b d0 30 12 be 8d c3 7f 4b 83 af 79 63 fa ff 0d " + "fa 22 54 77 43 7c 48 01 7f f2 be 81 91 cf 39 55 " + "fc 07 35 6e ab 3f 32 2f 7f 62 0e 21 d2 54 e5 db " + "43 24 27 9f e0 67 e0 91 0e 2e 81 ca 2c ab 31 c7 " + "45 e6 7a 54 05 8e b5 0d 99 3c db 9e d0 b4 d0 29 " + "c0 6d 21 a9 4c a6 61 c3 ce 27 fa e1 d6 cb 20 f4 " + "56 4d 66 ce 47 67 58 3d 0e 5f 06 02 15 b5 90 17 " + "be 85 ea 84 89 39 12 7b d8 c9 c4 d4 7b 51 05 6c " + "03 1c f3 36 f1 7c 99 80 f3 b8 f5 b9 b6 87 8e 8b " + "79 7a a4 3b 88 26 84 33 3e 17 89 3f e9 ca a6 aa " + "29 9f 7e d1 a1 8e e2 c5 48 64 b7 b2 b9 9b 72 61 " + "8f b0 25 74 d1 39 ef 50 f0 19 c9 ee f4 16 97 13 " + "38 e7 d4 70 "; +// Salt: +static const char salt_1_3[] = + "71 0b 9c 47 47 d8 00 d4 de 87 f1 2a fd ce 6d f1 " + "81 07 cc 77 "; +// Signature: +static const char signature_1_3[] = + "66 60 26 fb a7 1b d3 e7 cf 13 15 7c c2 c5 1a 8e " + "4a a6 84 af 97 78 f9 18 49 f3 43 35 d1 41 c0 01 " + "54 c4 19 76 21 f9 62 4a 67 5b 5a bc 22 ee 7d 5b " + "aa ff aa e1 c9 ba ca 2c c3 73 b3 f3 3e 78 e6 14 " + "3c 39 5a 91 aa 7f ac a6 64 eb 73 3a fd 14 d8 82 " + "72 59 d9 9a 75 50 fa ca 50 1e f2 b0 4e 33 c2 3a " + "a5 1f 4b 9e 82 82 ef db 72 8c c0 ab 09 40 5a 91 " + "60 7c 63 69 96 1b c8 27 0d 2d 4f 39 fc e6 12 b1 "; + +// RSASSA-PSS Signature Example 1.4 +// Message to be signed: +static const char message_1_4[] = + "bc 65 67 47 fa 9e af b3 f0 "; +// Salt: +static const char salt_1_4[] = + "05 6f 00 98 5d e1 4d 8e f5 ce a9 e8 2f 8c 27 be " + "f7 20 33 5e "; +// Signature: +static const char signature_1_4[] = + "46 09 79 3b 23 e9 d0 93 62 dc 21 bb 47 da 0b 4f " + "3a 76 22 64 9a 47 d4 64 01 9b 9a ea fe 53 35 9c " + "17 8c 91 cd 58 ba 6b cb 78 be 03 46 a7 bc 63 7f " + "4b 87 3d 4b ab 38 ee 66 1f 19 96 34 c5 47 a1 ad " + "84 42 e0 3d a0 15 b1 36 e5 43 f7 ab 07 c0 c1 3e " + "42 25 b8 de 8c ce 25 d4 f6 eb 84 00 f8 1f 7e 18 " + "33 b7 ee 6e 33 4d 37 09 64 ca 79 fd b8 72 b4 d7 " + "52 23 b5 ee b0 81 01 59 1f b5 32 d1 55 a6 de 87 "; + +// RSASSA-PSS Signature Example 1.5 +// Message to be signed: +static const char message_1_5[] = + "b4 55 81 54 7e 54 27 77 0c 76 8e 8b 82 b7 55 64 " + "e0 ea 4e 9c 32 59 4d 6b ff 70 65 44 de 0a 87 76 " + "c7 a8 0b 45 76 55 0e ee 1b 2a ca bc 7e 8b 7d 3e " + "f7 bb 5b 03 e4 62 c1 10 47 ea dd 00 62 9a e5 75 " + "48 0a c1 47 0f e0 46 f1 3a 2b f5 af 17 92 1d c4 " + "b0 aa 8b 02 be e6 33 49 11 65 1d 7f 85 25 d1 0f " + "32 b5 1d 33 be 52 0d 3d df 5a 70 99 55 a3 df e7 " + "82 83 b9 e0 ab 54 04 6d 15 0c 17 7f 03 7f dc cc " + "5b e4 ea 5f 68 b5 e5 a3 8c 9d 7e dc cc c4 97 5f " + "45 5a 69 09 b4 "; +// Salt: +static const char salt_1_5[] = + "80 e7 0f f8 6a 08 de 3e c6 09 72 b3 9b 4f bf dc " + "ea 67 ae 8e "; +// Signature: +static const char signature_1_5[] = + "1d 2a ad 22 1c a4 d3 1d df 13 50 92 39 01 93 98 " + "e3 d1 4b 32 dc 34 dc 5a f4 ae ae a3 c0 95 af 73 " + "47 9c f0 a4 5e 56 29 63 5a 53 a0 18 37 76 15 b1 " + "6c b9 b1 3b 3e 09 d6 71 eb 71 e3 87 b8 54 5c 59 " + "60 da 5a 64 77 6e 76 8e 82 b2 c9 35 83 bf 10 4c " + "3f db 23 51 2b 7b 4e 89 f6 33 dd 00 63 a5 30 db " + "45 24 b0 1c 3f 38 4c 09 31 0e 31 5a 79 dc d3 d6 " + "84 02 2a 7f 31 c8 65 a6 64 e3 16 97 8b 75 9f ad "; + +// RSASSA-PSS Signature Example 1.6 +// Message to be signed: +static const char message_1_6[] = + "10 aa e9 a0 ab 0b 59 5d 08 41 20 7b 70 0d 48 d7 " + "5f ae dd e3 b7 75 cd 6b 4c c8 8a e0 6e 46 94 ec " + "74 ba 18 f8 52 0d 4f 5e a6 9c bb e7 cc 2b eb a4 " + "3e fd c1 02 15 ac 4e b3 2d c3 02 a1 f5 3d c6 c4 " + "35 22 67 e7 93 6c fe bf 7c 8d 67 03 57 84 a3 90 " + "9f a8 59 c7 b7 b5 9b 8e 39 c5 c2 34 9f 18 86 b7 " + "05 a3 02 67 d4 02 f7 48 6a b4 f5 8c ad 5d 69 ad " + "b1 7a b8 cd 0c e1 ca f5 02 5a f4 ae 24 b1 fb 87 " + "94 c6 07 0c c0 9a 51 e2 f9 91 13 11 e3 87 7d 00 " + "44 c7 1c 57 a9 93 39 50 08 80 6b 72 3a c3 83 73 " + "d3 95 48 18 18 52 8c 1e 70 53 73 92 82 05 35 29 " + "51 0e 93 5c d0 fa 77 b8 fa 53 cc 2d 47 4b d4 fb " + "3c c5 c6 72 d6 ff dc 90 a0 0f 98 48 71 2c 4b cf " + "e4 6c 60 57 36 59 b1 1e 64 57 e8 61 f0 f6 04 b6 " + "13 8d 14 4f 8c e4 e2 da 73 "; +// Salt: +static const char salt_1_6[] = + "a8 ab 69 dd 80 1f 00 74 c2 a1 fc 60 64 98 36 c6 " + "16 d9 96 81 "; +// Signature: +static const char signature_1_6[] = + "2a 34 f6 12 5e 1f 6b 0b f9 71 e8 4f bd 41 c6 32 " + "be 8f 2c 2a ce 7d e8 b6 92 6e 31 ff 93 e9 af 98 " + "7f bc 06 e5 1e 9b e1 4f 51 98 f9 1f 3f 95 3b d6 " + "7d a6 0a 9d f5 97 64 c3 dc 0f e0 8e 1c be f0 b7 " + "5f 86 8d 10 ad 3f ba 74 9f ef 59 fb 6d ac 46 a0 " + "d6 e5 04 36 93 31 58 6f 58 e4 62 8f 39 aa 27 89 " + "82 54 3b c0 ee b5 37 dc 61 95 80 19 b3 94 fb 27 " + "3f 21 58 58 a0 a0 1a c4 d6 50 b9 55 c6 7f 4c 58 "; + +// Example 9: A 1536-bit RSA Key Pair + +// RSA modulus n: +static const char rsa_modulus_n_9[] = + "e6 bd 69 2a c9 66 45 79 04 03 fd d0 f5 be b8 b9 " + "bf 92 ed 10 00 7f c3 65 04 64 19 dd 06 c0 5c 5b " + "5b 2f 48 ec f9 89 e4 ce 26 91 09 97 9c bb 40 b4 " + "a0 ad 24 d2 24 83 d1 ee 31 5a d4 cc b1 53 42 68 " + "35 26 91 c5 24 f6 dd 8e 6c 29 d2 24 cf 24 69 73 " + "ae c8 6c 5b f6 b1 40 1a 85 0d 1b 9a d1 bb 8c bc " + "ec 47 b0 6f 0f 8c 7f 45 d3 fc 8f 31 92 99 c5 43 " + "3d db c2 b3 05 3b 47 de d2 ec d4 a4 ca ef d6 14 " + "83 3d c8 bb 62 2f 31 7e d0 76 b8 05 7f e8 de 3f " + "84 48 0a d5 e8 3e 4a 61 90 4a 4f 24 8f b3 97 02 " + "73 57 e1 d3 0e 46 31 39 81 5c 6f d4 fd 5a c5 b8 " + "17 2a 45 23 0e cb 63 18 a0 4f 14 55 d8 4e 5a 8b "; +// RSA public exponent e: +static const char rsa_public_exponent_e_9[] = + "01 00 01 "; + +// RSASSA-PSS Signature Example 9.1 +// Message to be signed: +static const char message_9_1[] = + "a8 8e 26 58 55 e9 d7 ca 36 c6 87 95 f0 b3 1b 59 " + "1c d6 58 7c 71 d0 60 a0 b3 f7 f3 ea ef 43 79 59 " + "22 02 8b c2 b6 ad 46 7c fc 2d 7f 65 9c 53 85 aa " + "70 ba 36 72 cd de 4c fe 49 70 cc 79 04 60 1b 27 " + "88 72 bf 51 32 1c 4a 97 2f 3c 95 57 0f 34 45 d4 " + "f5 79 80 e0 f2 0d f5 48 46 e6 a5 2c 66 8f 12 88 " + "c0 3f 95 00 6e a3 2f 56 2d 40 d5 2a f9 fe b3 2f " + "0f a0 6d b6 5b 58 8a 23 7b 34 e5 92 d5 5c f9 79 " + "f9 03 a6 42 ef 64 d2 ed 54 2a a8 c7 7d c1 dd 76 " + "2f 45 a5 93 03 ed 75 e5 41 ca 27 1e 2b 60 ca 70 " + "9e 44 fa 06 61 13 1e 8d 5d 41 63 fd 8d 39 85 66 " + "ce 26 de 87 30 e7 2f 9c ca 73 76 41 c2 44 15 94 " + "20 63 70 28 df 0a 18 07 9d 62 08 ea 8b 47 11 a2 " + "c7 50 f5 "; +// Salt: +static const char salt_9_1[] = + "c0 a4 25 31 3d f8 d7 56 4b d2 43 4d 31 15 23 d5 " + "25 7e ed 80 "; +// Signature: +static const char signature_9_1[] = + "58 61 07 22 6c 3c e0 13 a7 c8 f0 4d 1a 6a 29 59 " + "bb 4b 8e 20 5b a4 3a 27 b5 0f 12 41 11 bc 35 ef " + "58 9b 03 9f 59 32 18 7c b6 96 d7 d9 a3 2c 0c 38 " + "30 0a 5c dd a4 83 4b 62 d2 eb 24 0a f3 3f 79 d1 " + "3d fb f0 95 bf 59 9e 0d 96 86 94 8c 19 64 74 7b " + "67 e8 9c 9a ba 5c d8 50 16 23 6f 56 6c c5 80 2c " + "b1 3e ad 51 bc 7c a6 be f3 b9 4d cb db b1 d5 70 " + "46 97 71 df 0e 00 b1 a8 a0 67 77 47 2d 23 16 27 " + "9e da e8 64 74 66 8d 4e 1e ff f9 5f 1d e6 1c 60 " + "20 da 32 ae 92 bb f1 65 20 fe f3 cf 4d 88 f6 11 " + "21 f2 4b bd 9f e9 1b 59 ca f1 23 5b 2a 93 ff 81 " + "fc 40 3a dd f4 eb de a8 49 34 a9 cd af 8e 1a 9e "; + +// RSASSA-PSS Signature Example 9.2 +// Message to be signed: +static const char message_9_2[] = + "c8 c9 c6 af 04 ac da 41 4d 22 7e f2 3e 08 20 c3 " + "73 2c 50 0d c8 72 75 e9 5b 0d 09 54 13 99 3c 26 " + "58 bc 1d 98 85 81 ba 87 9c 2d 20 1f 14 cb 88 ce " + "d1 53 a0 19 69 a7 bf 0a 7b e7 9c 84 c1 48 6b c1 " + "2b 3f a6 c5 98 71 b6 82 7c 8c e2 53 ca 5f ef a8 " + "a8 c6 90 bf 32 6e 8e 37 cd b9 6d 90 a8 2e ba b6 " + "9f 86 35 0e 18 22 e8 bd 53 6a 2e "; +// Salt: +static const char salt_9_2[] = + "b3 07 c4 3b 48 50 a8 da c2 f1 5f 32 e3 78 39 ef " + "8c 5c 0e 91 "; +// Signature: +static const char signature_9_2[] = + "80 b6 d6 43 25 52 09 f0 a4 56 76 38 97 ac 9e d2 " + "59 d4 59 b4 9c 28 87 e5 88 2e cb 44 34 cf d6 6d " + "d7 e1 69 93 75 38 1e 51 cd 7f 55 4f 2c 27 17 04 " + "b3 99 d4 2b 4b e2 54 0a 0e ca 61 95 1f 55 26 7f " + "7c 28 78 c1 22 84 2d ad b2 8b 01 bd 5f 8c 02 5f " + "7e 22 84 18 a6 73 c0 3d 6b c0 c7 36 d0 a2 95 46 " + "bd 67 f7 86 d9 d6 92 cc ea 77 8d 71 d9 8c 20 63 " + "b7 a7 10 92 18 7a 4d 35 af 10 81 11 d8 3e 83 ea " + "e4 6c 46 aa 34 27 7e 06 04 45 89 90 37 88 f1 d5 " + "e7 ce e2 5f b4 85 e9 29 49 11 88 14 d6 f2 c3 ee " + "36 14 89 01 6f 32 7f b5 bc 51 7e b5 04 70 bf fa " + "1a fa 5f 4c e9 aa 0c e5 b8 ee 19 bf 55 01 b9 58 "; + +// RSASSA-PSS Signature Example 9.3 +// Message to be signed: +static const char message_9_3[] = + "0a fa d4 2c cd 4f c6 06 54 a5 50 02 d2 28 f5 2a " + "4a 5f e0 3b 8b bb 08 ca 82 da ca 55 8b 44 db e1 " + "26 6e 50 c0 e7 45 a3 6d 9d 29 04 e3 40 8a bc d1 " + "fd 56 99 94 06 3f 4a 75 cc 72 f2 fe e2 a0 cd 89 " + "3a 43 af 1c 5b 8b 48 7d f0 a7 16 10 02 4e 4f 6d " + "df 9f 28 ad 08 13 c1 aa b9 1b cb 3c 90 64 d5 ff " + "74 2d ef fe a6 57 09 41 39 36 9e 5e a6 f4 a9 63 " + "19 a5 cc 82 24 14 5b 54 50 62 75 8f ef d1 fe 34 " + "09 ae 16 92 59 c6 cd fd 6b 5f 29 58 e3 14 fa ec " + "be 69 d2 ca ce 58 ee 55 17 9a b9 b3 e6 d1 ec c1 " + "4a 55 7c 5f eb e9 88 59 52 64 fc 5d a1 c5 71 46 " + "2e ca 79 8a 18 a1 a4 94 0c da b4 a3 e9 20 09 cc " + "d4 2e 1e 94 7b 13 14 e3 22 38 a2 de ce 7d 23 a8 " + "9b 5b 30 c7 51 fd 0a 4a 43 0d 2c 54 85 94 "; +// Salt: +static const char salt_9_3[] = + "9a 2b 00 7e 80 97 8b bb 19 2c 35 4e b7 da 9a ed " + "fc 74 db f5 "; +// Signature: +static const char signature_9_3[] = + "48 44 08 f3 89 8c d5 f5 34 83 f8 08 19 ef bf 27 " + "08 c3 4d 27 a8 b2 a6 fa e8 b3 22 f9 24 02 37 f9 " + "81 81 7a ca 18 46 f1 08 4d aa 6d 7c 07 95 f6 e5 " + "bf 1a f5 9c 38 e1 85 84 37 ce 1f 7e c4 19 b9 8c " + "87 36 ad f6 dd 9a 00 b1 80 6d 2b d3 ad 0a 73 77 " + "5e 05 f5 2d fe f3 a5 9a b4 b0 81 43 f0 df 05 cd " + "1a d9 d0 4b ec ec a6 da a4 a2 12 98 03 e2 00 cb " + "c7 77 87 ca f4 c1 d0 66 3a 6c 59 87 b6 05 95 20 " + "19 78 2c af 2e c1 42 6d 68 fb 94 ed 1d 4b e8 16 " + "a7 ed 08 1b 77 e6 ab 33 0b 3f fc 07 38 20 fe cd " + "e3 72 7f cb e2 95 ee 61 a0 50 a3 43 65 86 37 c3 " + "fd 65 9c fb 63 73 6d e3 2d 9f 90 d3 c2 f6 3e ca "; + +// RSASSA-PSS Signature Example 9.4 +// Message to be signed: +static const char message_9_4[] = + "1d fd 43 b4 6c 93 db 82 62 9b da e2 bd 0a 12 b8 " + "82 ea 04 c3 b4 65 f5 cf 93 02 3f 01 05 96 26 db " + "be 99 f2 6b b1 be 94 9d dd d1 6d c7 f3 de bb 19 " + "a1 94 62 7f 0b 22 44 34 df 7d 87 00 e9 e9 8b 06 " + "e3 60 c1 2f db e3 d1 9f 51 c9 68 4e b9 08 9e cb " + "b0 a2 f0 45 03 99 d3 f5 9e ac 72 94 08 5d 04 4f " + "53 93 c6 ce 73 74 23 d8 b8 6c 41 53 70 d3 89 e3 " + "0b 9f 0a 3c 02 d2 5d 00 82 e8 ad 6f 3f 1e f2 4a " + "45 c3 cf 82 b3 83 36 70 63 a4 d4 61 3e 42 64 f0 " + "1b 2d ac 2e 5a a4 20 43 f8 fb 5f 69 fa 87 1d 14 " + "fb 27 3e 76 7a 53 1c 40 f0 2f 34 3b c2 fb 45 a0 " + "c7 e0 f6 be 25 61 92 3a 77 21 1d 66 a6 e2 db b4 " + "3c 36 63 50 be ae 22 da 3a c2 c1 f5 07 70 96 fc " + "b5 c4 bf 25 5f 75 74 35 1a e0 b1 e1 f0 36 32 81 " + "7c 08 56 d4 a8 ba 97 af bd c8 b8 58 55 40 2b c5 " + "69 26 fc ec 20 9f 9e a8 "; +// Salt: +static const char salt_9_4[] = + "70 f3 82 bd df 4d 5d 2d d8 8b 3b c7 b7 30 8b e6 " + "32 b8 40 45 "; +// Signature: +static const char signature_9_4[] = + "84 eb eb 48 1b e5 98 45 b4 64 68 ba fb 47 1c 01 " + "12 e0 2b 23 5d 84 b5 d9 11 cb d1 92 6e e5 07 4a " + "e0 42 44 95 cb 20 e8 23 08 b8 eb b6 5f 41 9a 03 " + "fb 40 e7 2b 78 98 1d 88 aa d1 43 05 36 85 17 2c " + "97 b2 9c 8b 7b f0 ae 73 b5 b2 26 3c 40 3d a0 ed " + "2f 80 ff 74 50 af 78 28 eb 8b 86 f0 02 8b d2 a8 " + "b1 76 a4 d2 28 cc ce a1 83 94 f2 38 b0 9f f7 58 " + "cc 00 bc 04 30 11 52 35 57 42 f2 82 b5 4e 66 3a " + "91 9e 70 9d 8d a2 4a de 55 00 a7 b9 aa 50 22 6e " + "0c a5 29 23 e6 c2 d8 60 ec 50 ff 48 0f a5 74 77 " + "e8 2b 05 65 f4 37 9f 79 c7 72 d5 c2 da 80 af 9f " + "bf 32 5e ce 6f c2 0b 00 96 16 14 be e8 9a 18 3e "; + +// RSASSA-PSS Signature Example 9.5 +// Message to be signed: +static const char message_9_5[] = + "1b dc 6e 7c 98 fb 8c f5 4e 9b 09 7b 66 a8 31 e9 " + "cf e5 2d 9d 48 88 44 8e e4 b0 97 80 93 ba 1d 7d " + "73 ae 78 b3 a6 2b a4 ad 95 cd 28 9c cb 9e 00 52 " + "26 bb 3d 17 8b cc aa 82 1f b0 44 a4 e2 1e e9 76 " + "96 c1 4d 06 78 c9 4c 2d ae 93 b0 ad 73 92 22 18 " + "55 3d aa 7e 44 eb e5 77 25 a7 a4 5c c7 2b 9b 21 " + "38 a6 b1 7c 8d b4 11 ce 82 79 ee 12 41 af f0 a8 " + "be c6 f7 7f 87 ed b0 c6 9c b2 72 36 e3 43 5a 80 " + "0b 19 2e 4f 11 e5 19 e3 fe 30 fc 30 ea cc ca 4f " + "bb 41 76 90 29 bf 70 8e 81 7a 9e 68 38 05 be 67 " + "fa 10 09 84 68 3b 74 83 8e 3b cf fa 79 36 6e ed " + "1d 48 1c 76 72 91 18 83 8f 31 ba 8a 04 8a 93 c1 " + "be 44 24 59 8e 8d f6 32 8b 7a 77 88 0a 3f 9c 7e " + "2e 8d fc a8 eb 5a 26 fb 86 bd c5 56 d4 2b be 01 " + "d9 fa 6e d8 06 46 49 1c 93 41 "; +// Salt: +static const char salt_9_5[] = + "d6 89 25 7a 86 ef fa 68 21 2c 5e 0c 61 9e ca 29 " + "5f b9 1b 67 "; +// Signature: +static const char signature_9_5[] = + "82 10 2d f8 cb 91 e7 17 99 19 a0 4d 26 d3 35 d6 " + "4f bc 2f 87 2c 44 83 39 43 24 1d e8 45 48 10 27 " + "4c df 3d b5 f4 2d 42 3d b1 52 af 71 35 f7 01 42 " + "0e 39 b4 94 a6 7c bf d1 9f 91 19 da 23 3a 23 da " + "5c 64 39 b5 ba 0d 2b c3 73 ee e3 50 70 01 37 8d " + "4a 40 73 85 6b 7f e2 ab a0 b5 ee 93 b2 7f 4a fe " + "c7 d4 d1 20 92 1c 83 f6 06 76 5b 02 c1 9e 4d 6a " + "1a 3b 95 fa 4c 42 29 51 be 4f 52 13 10 77 ef 17 " + "17 97 29 cd df bd b5 69 50 db ac ee fe 78 cb 16 " + "64 0a 09 9e a5 6d 24 38 9e ef 10 f8 fe cb 31 ba " + "3e a3 b2 27 c0 a8 66 98 bb 89 e3 e9 36 39 05 bf " + "22 77 7b 2a 3a a5 21 b6 5b 4c ef 76 d8 3b de 4c "; + +// RSASSA-PSS Signature Example 9.6 +// Message to be signed: +static const char message_9_6[] = + "88 c7 a9 f1 36 04 01 d9 0e 53 b1 01 b6 1c 53 25 " + "c3 c7 5d b1 b4 11 fb eb 8e 83 0b 75 e9 6b 56 67 " + "0a d2 45 40 4e 16 79 35 44 ee 35 4b c6 13 a9 0c " + "c9 84 87 15 a7 3d b5 89 3e 7f 6d 27 98 15 c0 c1 " + "de 83 ef 8e 29 56 e3 a5 6e d2 6a 88 8d 7a 9c dc " + "d0 42 f4 b1 6b 7f a5 1e f1 a0 57 36 62 d1 6a 30 " + "2d 0e c5 b2 85 d2 e0 3a d9 65 29 c8 7b 3d 37 4d " + "b3 72 d9 5b 24 43 d0 61 b6 b1 a3 50 ba 87 80 7e " + "d0 83 af d1 eb 05 c3 f5 2f 4e ba 5e d2 22 77 14 " + "fd b5 0b 9d 9d 9d d6 81 4f 62 f6 27 2f cd 5c db " + "ce 7a 9e f7 97 "; +// Salt: +static const char salt_9_6[] = + "c2 5f 13 bf 67 d0 81 67 1a 04 81 a1 f1 82 0d 61 " + "3b ba 22 76 "; +// Signature: +static const char signature_9_6[] = + "a7 fd b0 d2 59 16 5c a2 c8 8d 00 bb f1 02 8a 86 " + "7d 33 76 99 d0 61 19 3b 17 a9 64 8e 14 cc bb aa " + "de ac aa cd ec 81 5e 75 71 29 4e bb 8a 11 7a f2 " + "05 fa 07 8b 47 b0 71 2c 19 9e 3a d0 51 35 c5 04 " + "c2 4b 81 70 51 15 74 08 02 48 79 92 ff d5 11 d4 " + "af c6 b8 54 49 1e b3 f0 dd 52 31 39 54 2f f1 5c " + "31 01 ee 85 54 35 17 c6 a3 c7 94 17 c6 7e 2d d9 " + "aa 74 1e 9a 29 b0 6d cb 59 3c 23 36 b3 67 0a e3 " + "af ba c7 c3 e7 6e 21 54 73 e8 66 e3 38 ca 24 4d " + "e0 0b 62 62 4d 6b 94 26 82 2c ea e9 f8 cc 46 08 " + "95 f4 12 50 07 3f d4 5c 5a 1e 7b 42 5c 20 4a 42 " + "3a 69 91 59 f6 90 3e 71 0b 37 a7 bb 2b c8 04 9f "; + +// Example 10: A 2048-bit RSA Key Pair + +// RSA modulus n: +static const char rsa_modulus_n_10[] = + "a5 dd 86 7a c4 cb 02 f9 0b 94 57 d4 8c 14 a7 70 " + "ef 99 1c 56 c3 9c 0e c6 5f d1 1a fa 89 37 ce a5 " + "7b 9b e7 ac 73 b4 5c 00 17 61 5b 82 d6 22 e3 18 " + "75 3b 60 27 c0 fd 15 7b e1 2f 80 90 fe e2 a7 ad " + "cd 0e ef 75 9f 88 ba 49 97 c7 a4 2d 58 c9 aa 12 " + "cb 99 ae 00 1f e5 21 c1 3b b5 43 14 45 a8 d5 ae " + "4f 5e 4c 7e 94 8a c2 27 d3 60 40 71 f2 0e 57 7e " + "90 5f be b1 5d fa f0 6d 1d e5 ae 62 53 d6 3a 6a " + "21 20 b3 1a 5d a5 da bc 95 50 60 0e 20 f2 7d 37 " + "39 e2 62 79 25 fe a3 cc 50 9f 21 df f0 4e 6e ea " + "45 49 c5 40 d6 80 9f f9 30 7e ed e9 1f ff 58 73 " + "3d 83 85 a2 37 d6 d3 70 5a 33 e3 91 90 09 92 07 " + "0d f7 ad f1 35 7c f7 e3 70 0c e3 66 7d e8 3f 17 " + "b8 df 17 78 db 38 1d ce 09 cb 4a d0 58 a5 11 00 " + "1a 73 81 98 ee 27 cf 55 a1 3b 75 45 39 90 65 82 " + "ec 8b 17 4b d5 8d 5d 1f 3d 76 7c 61 37 21 ae 05 "; +// RSA public exponent e: +static const char rsa_public_exponent_e_10[] = + "01 00 01 "; + +// RSASSA-PSS Signature Example 10.1 +// Message to be signed: +static const char message_10_1[] = + "88 31 77 e5 12 6b 9b e2 d9 a9 68 03 27 d5 37 0c " + "6f 26 86 1f 58 20 c4 3d a6 7a 3a d6 09 "; +// Salt: +static const char salt_10_1[] = + "04 e2 15 ee 6f f9 34 b9 da 70 d7 73 0c 87 34 ab " + "fc ec de 89 "; +// Signature: +static const char signature_10_1[] = + "82 c2 b1 60 09 3b 8a a3 c0 f7 52 2b 19 f8 73 54 " + "06 6c 77 84 7a bf 2a 9f ce 54 2d 0e 84 e9 20 c5 " + "af b4 9f fd fd ac e1 65 60 ee 94 a1 36 96 01 14 " + "8e ba d7 a0 e1 51 cf 16 33 17 91 a5 72 7d 05 f2 " + "1e 74 e7 eb 81 14 40 20 69 35 d7 44 76 5a 15 e7 " + "9f 01 5c b6 6c 53 2c 87 a6 a0 59 61 c8 bf ad 74 " + "1a 9a 66 57 02 28 94 39 3e 72 23 73 97 96 c0 2a " + "77 45 5d 0f 55 5b 0e c0 1d df 25 9b 62 07 fd 0f " + "d5 76 14 ce f1 a5 57 3b aa ff 4e c0 00 69 95 16 " + "59 b8 5f 24 30 0a 25 16 0c a8 52 2d c6 e6 72 7e " + "57 d0 19 d7 e6 36 29 b8 fe 5e 89 e2 5c c1 5b eb " + "3a 64 75 77 55 92 99 28 0b 9b 28 f7 9b 04 09 00 " + "0b e2 5b bd 96 40 8b a3 b4 3c c4 86 18 4d d1 c8 " + "e6 25 53 fa 1a f4 04 0f 60 66 3d e7 f5 e4 9c 04 " + "38 8e 25 7f 1c e8 9c 95 da b4 8a 31 5d 9b 66 b1 " + "b7 62 82 33 87 6f f2 38 52 30 d0 70 d0 7e 16 66 "; + +// RSASSA-PSS Signature Example 10.2 +// Message to be signed: +static const char message_10_2[] = + "dd 67 0a 01 46 58 68 ad c9 3f 26 13 19 57 a5 0c " + "52 fb 77 7c db aa 30 89 2c 9e 12 36 11 64 ec 13 " + "97 9d 43 04 81 18 e4 44 5d b8 7b ee 58 dd 98 7b " + "34 25 d0 20 71 d8 db ae 80 70 8b 03 9d bb 64 db " + "d1 de 56 57 d9 fe d0 c1 18 a5 41 43 74 2e 0f f3 " + "c8 7f 74 e4 58 57 64 7a f3 f7 9e b0 a1 4c 9d 75 " + "ea 9a 1a 04 b7 cf 47 8a 89 7a 70 8f d9 88 f4 8e " + "80 1e db 0b 70 39 df 8c 23 bb 3c 56 f4 e8 21 ac "; +// Salt: +static const char salt_10_2[] = + "8b 2b dd 4b 40 fa f5 45 c7 78 dd f9 bc 1a 49 cb " + "57 f9 b7 1b "; +// Signature: +static const char signature_10_2[] = + "14 ae 35 d9 dd 06 ba 92 f7 f3 b8 97 97 8a ed 7c " + "d4 bf 5f f0 b5 85 a4 0b d4 6c e1 b4 2c d2 70 30 " + "53 bb 90 44 d6 4e 81 3d 8f 96 db 2d d7 00 7d 10 " + "11 8f 6f 8f 84 96 09 7a d7 5e 1f f6 92 34 1b 28 " + "92 ad 55 a6 33 a1 c5 5e 7f 0a 0a d5 9a 0e 20 3a " + "5b 82 78 ae c5 4d d8 62 2e 28 31 d8 71 74 f8 ca " + "ff 43 ee 6c 46 44 53 45 d8 4a 59 65 9b fb 92 ec " + "d4 c8 18 66 86 95 f3 47 06 f6 68 28 a8 99 59 63 " + "7f 2b f3 e3 25 1c 24 bd ba 4d 4b 76 49 da 00 22 " + "21 8b 11 9c 84 e7 9a 65 27 ec 5b 8a 5f 86 1c 15 " + "99 52 e2 3e c0 5e 1e 71 73 46 fa ef e8 b1 68 68 " + "25 bd 2b 26 2f b2 53 10 66 c0 de 09 ac de 2e 42 " + "31 69 07 28 b5 d8 5e 11 5a 2f 6b 92 b7 9c 25 ab " + "c9 bd 93 99 ff 8b cf 82 5a 52 ea 1f 56 ea 76 dd " + "26 f4 3b aa fa 18 bf a9 2a 50 4c bd 35 69 9e 26 " + "d1 dc c5 a2 88 73 85 f3 c6 32 32 f0 6f 32 44 c3 "; + +// RSASSA-PSS Signature Example 10.3 +// Message to be signed: +static const char message_10_3[] = + "48 b2 b6 a5 7a 63 c8 4c ea 85 9d 65 c6 68 28 4b " + "08 d9 6b dc aa be 25 2d b0 e4 a9 6c b1 ba c6 01 " + "93 41 db 6f be fb 8d 10 6b 0e 90 ed a6 bc c6 c6 " + "26 2f 37 e7 ea 9c 7e 5d 22 6b d7 df 85 ec 5e 71 " + "ef ff 2f 54 c5 db 57 7f f7 29 ff 91 b8 42 49 1d " + "e2 74 1d 0c 63 16 07 df 58 6b 90 5b 23 b9 1a f1 " + "3d a1 23 04 bf 83 ec a8 a7 3e 87 1f f9 db "; +// Salt: +static const char salt_10_3[] = + "4e 96 fc 1b 39 8f 92 b4 46 71 01 0c 0d c3 ef d6 " + "e2 0c 2d 73 "; +// Signature: +static const char signature_10_3[] = + "6e 3e 4d 7b 6b 15 d2 fb 46 01 3b 89 00 aa 5b bb " + "39 39 cf 2c 09 57 17 98 70 42 02 6e e6 2c 74 c5 " + "4c ff d5 d7 d5 7e fb bf 95 0a 0f 5c 57 4f a0 9d " + "3f c1 c9 f5 13 b0 5b 4f f5 0d d8 df 7e df a2 01 " + "02 85 4c 35 e5 92 18 01 19 a7 0c e5 b0 85 18 2a " + "a0 2d 9e a2 aa 90 d1 df 03 f2 da ae 88 5b a2 f5 " + "d0 5a fd ac 97 47 6f 06 b9 3b 5b c9 4a 1a 80 aa " + "91 16 c4 d6 15 f3 33 b0 98 89 2b 25 ff ac e2 66 " + "f5 db 5a 5a 3b cc 10 a8 24 ed 55 aa d3 5b 72 78 " + "34 fb 8c 07 da 28 fc f4 16 a5 d9 b2 22 4f 1f 8b " + "44 2b 36 f9 1e 45 6f de a2 d7 cf e3 36 72 68 de " + "03 07 a4 c7 4e 92 41 59 ed 33 39 3d 5e 06 55 53 " + "1c 77 32 7b 89 82 1b de df 88 01 61 c7 8c d4 19 " + "6b 54 19 f7 ac c3 f1 3e 5e bf 16 1b 6e 7c 67 24 " + "71 6c a3 3b 85 c2 e2 56 40 19 2a c2 85 96 51 d5 " + "0b de 7e b9 76 e5 1c ec 82 8b 98 b6 56 3b 86 bb "; + +// RSASSA-PSS Signature Example 10.4 +// Message to be signed: +static const char message_10_4[] = + "0b 87 77 c7 f8 39 ba f0 a6 4b bb db c5 ce 79 75 " + "5c 57 a2 05 b8 45 c1 74 e2 d2 e9 05 46 a0 89 c4 " + "e6 ec 8a df fa 23 a7 ea 97 ba e6 b6 5d 78 2b 82 " + "db 5d 2b 5a 56 d2 2a 29 a0 5e 7c 44 33 e2 b8 2a " + "62 1a bb a9 0a dd 05 ce 39 3f c4 8a 84 05 42 45 " + "1a "; +// Salt: +static const char salt_10_4[] = + "c7 cd 69 8d 84 b6 51 28 d8 83 5e 3a 8b 1e b0 e0 " + "1c b5 41 ec "; +// Signature: +static const char signature_10_4[] = + "34 04 7f f9 6c 4d c0 dc 90 b2 d4 ff 59 a1 a3 61 " + "a4 75 4b 25 5d 2e e0 af 7d 8b f8 7c 9b c9 e7 dd " + "ee de 33 93 4c 63 ca 1c 0e 3d 26 2c b1 45 ef 93 " + "2a 1f 2c 0a 99 7a a6 a3 4f 8e ae e7 47 7d 82 cc " + "f0 90 95 a6 b8 ac ad 38 d4 ee c9 fb 7e ab 7a d0 " + "2d a1 d1 1d 8e 54 c1 82 5e 55 bf 58 c2 a2 32 34 " + "b9 02 be 12 4f 9e 90 38 a8 f6 8f a4 5d ab 72 f6 " + "6e 09 45 bf 1d 8b ac c9 04 4c 6f 07 09 8c 9f ce " + "c5 8a 3a ab 10 0c 80 51 78 15 5f 03 0a 12 4c 45 " + "0e 5a cb da 47 d0 e4 f1 0b 80 a2 3f 80 3e 77 4d " + "02 3b 00 15 c2 0b 9f 9b be 7c 91 29 63 38 d5 ec " + "b4 71 ca fb 03 20 07 b6 7a 60 be 5f 69 50 4a 9f " + "01 ab b3 cb 46 7b 26 0e 2b ce 86 0b e8 d9 5b f9 " + "2c 0c 8e 14 96 ed 1e 52 85 93 a4 ab b6 df 46 2d " + "de 8a 09 68 df fe 46 83 11 68 57 a2 32 f5 eb f6 " + "c8 5b e2 38 74 5a d0 f3 8f 76 7a 5f db f4 86 fb "; + +// RSASSA-PSS Signature Example 10.5 +// Message to be signed: +static const char message_10_5[] = + "f1 03 6e 00 8e 71 e9 64 da dc 92 19 ed 30 e1 7f " + "06 b4 b6 8a 95 5c 16 b3 12 b1 ed df 02 8b 74 97 " + "6b ed 6b 3f 6a 63 d4 e7 78 59 24 3c 9c cc dc 98 " + "01 65 23 ab b0 24 83 b3 55 91 c3 3a ad 81 21 3b " + "b7 c7 bb 1a 47 0a ab c1 0d 44 25 6c 4d 45 59 d9 " + "16 "; +// Salt: +static const char salt_10_5[] = + "ef a8 bf f9 62 12 b2 f4 a3 f3 71 a1 0d 57 41 52 " + "65 5f 5d fb "; +// Signature: +static const char signature_10_5[] = + "7e 09 35 ea 18 f4 d6 c1 d1 7c e8 2e b2 b3 83 6c " + "55 b3 84 58 9c e1 9d fe 74 33 63 ac 99 48 d1 f3 " + "46 b7 bf dd fe 92 ef d7 8a db 21 fa ef c8 9a de " + "42 b1 0f 37 40 03 fe 12 2e 67 42 9a 1c b8 cb d1 " + "f8 d9 01 45 64 c4 4d 12 01 16 f4 99 0f 1a 6e 38 " + "77 4c 19 4b d1 b8 21 32 86 b0 77 b0 49 9d 2e 7b " + "3f 43 4a b1 22 89 c5 56 68 4d ee d7 81 31 93 4b " + "b3 dd 65 37 23 6f 7c 6f 3d cb 09 d4 76 be 07 72 " + "1e 37 e1 ce ed 9b 2f 7b 40 68 87 bd 53 15 73 05 " + "e1 c8 b4 f8 4d 73 3b c1 e1 86 fe 06 cc 59 b6 ed " + "b8 f4 bd 7f fe fd f4 f7 ba 9c fb 9d 57 06 89 b5 " + "a1 a4 10 9a 74 6a 69 08 93 db 37 99 25 5a 0c b9 " + "21 5d 2d 1c d4 90 59 0e 95 2e 8c 87 86 aa 00 11 " + "26 52 52 47 0c 04 1d fb c3 ee c7 c3 cb f7 1c 24 " + "86 9d 11 5c 0c b4 a9 56 f5 6d 53 0b 80 ab 58 9a " + "cf ef c6 90 75 1d df 36 e8 d3 83 f8 3c ed d2 cc "; + +// RSASSA-PSS Signature Example 10.6 +// Message to be signed: +static const char message_10_6[] = + "25 f1 08 95 a8 77 16 c1 37 45 0b b9 51 9d fa a1 " + "f2 07 fa a9 42 ea 88 ab f7 1e 9c 17 98 00 85 b5 " + "55 ae ba b7 62 64 ae 2a 3a b9 3c 2d 12 98 11 91 " + "dd ac 6f b5 94 9e b3 6a ee 3c 5d a9 40 f0 07 52 " + "c9 16 d9 46 08 fa 7d 97 ba 6a 29 15 b6 88 f2 03 " + "23 d4 e9 d9 68 01 d8 9a 72 ab 58 92 dc 21 17 c0 " + "74 34 fc f9 72 e0 58 cf 8c 41 ca 4b 4f f5 54 f7 " + "d5 06 8a d3 15 5f ce d0 f3 12 5b c0 4f 91 93 37 " + "8a 8f 5c 4c 3b 8c b4 dd 6d 1c c6 9d 30 ec ca 6e " + "aa 51 e3 6a 05 73 0e 9e 34 2e 85 5b af 09 9d ef " + "b8 af d7 "; +// Salt: +static const char salt_10_6[] = + "ad 8b 15 23 70 36 46 22 4b 66 0b 55 08 85 91 7c " + "a2 d1 df 28 "; +// Signature: +static const char signature_10_6[] = + "6d 3b 5b 87 f6 7e a6 57 af 21 f7 54 41 97 7d 21 " + "80 f9 1b 2c 5f 69 2d e8 29 55 69 6a 68 67 30 d9 " + "b9 77 8d 97 07 58 cc b2 60 71 c2 20 9f fb d6 12 " + "5b e2 e9 6e a8 1b 67 cb 9b 93 08 23 9f da 17 f7 " + "b2 b6 4e cd a0 96 b6 b9 35 64 0a 5a 1c b4 2a 91 " + "55 b1 c9 ef 7a 63 3a 02 c5 9f 0d 6e e5 9b 85 2c " + "43 b3 50 29 e7 3c 94 0f f0 41 0e 8f 11 4e ed 46 " + "bb d0 fa e1 65 e4 2b e2 52 8a 40 1c 3b 28 fd 81 " + "8e f3 23 2d ca 9f 4d 2a 0f 51 66 ec 59 c4 23 96 " + "d6 c1 1d bc 12 15 a5 6f a1 71 69 db 95 75 34 3e " + "f3 4f 9d e3 2a 49 cd c3 17 49 22 f2 29 c2 3e 18 " + "e4 5d f9 35 31 19 ec 43 19 ce dc e7 a1 7c 64 08 " + "8c 1f 6f 52 be 29 63 41 00 b3 91 9d 38 f3 d1 ed " + "94 e6 89 1e 66 a7 3b 8f b8 49 f5 87 4d f5 94 59 " + "e2 98 c7 bb ce 2e ee 78 2a 19 5a a6 6f e2 d0 73 " + "2b 25 e5 95 f5 7d 3e 06 1b 1f c3 e4 06 3b f9 8f "; + +struct SignatureExample { + const char* message; + const char* salt; + const char* signature; +}; + +struct PSSTestVector { + const char* modulus_n; + const char* public_exponent_e; + SignatureExample example[6]; +}; + +static const PSSTestVector pss_test[] = { + { + rsa_modulus_n_1, + rsa_public_exponent_e_1, + { + { message_1_1, salt_1_1, signature_1_1 }, + { message_1_2, salt_1_2, signature_1_2 }, + { message_1_3, salt_1_3, signature_1_3 }, + { message_1_4, salt_1_4, signature_1_4 }, + { message_1_5, salt_1_5, signature_1_5 }, + { message_1_6, salt_1_6, signature_1_6 }, + } + }, + { + rsa_modulus_n_9, + rsa_public_exponent_e_9, + { + { message_9_1, salt_9_1, signature_9_1 }, + { message_9_2, salt_9_2, signature_9_2 }, + { message_9_3, salt_9_3, signature_9_3 }, + { message_9_4, salt_9_4, signature_9_4 }, + { message_9_5, salt_9_5, signature_9_5 }, + { message_9_6, salt_9_6, signature_9_6 }, + } + }, + { + rsa_modulus_n_10, + rsa_public_exponent_e_10, + { + { message_10_1, salt_10_1, signature_10_1 }, + { message_10_2, salt_10_2, signature_10_2 }, + { message_10_3, salt_10_3, signature_10_3 }, + { message_10_4, salt_10_4, signature_10_4 }, + { message_10_5, salt_10_5, signature_10_5 }, + { message_10_6, salt_10_6, signature_10_6 }, + } + }, +}; + +static uint8 HexDigitValue(char digit) { + if ('0' <= digit && digit <= '9') + return digit - '0'; + if ('a' <= digit && digit <= 'f') + return digit - 'a' + 10; + return digit - 'A' + 10; +} + +static bool DecodeTestInput(const char* in, std::vector* out) { + out->clear(); + while (in[0] != '\0') { + if (!isxdigit(in[0]) || !isxdigit(in[1]) || in[2] != ' ') + return false; + uint8 octet = HexDigitValue(in[0]) * 16 + HexDigitValue(in[1]); + out->push_back(octet); + in += 3; + } + return true; +} + +static bool EncodeRSAPublicKey(const std::vector& modulus_n, + const std::vector& public_exponent_e, + std::vector* public_key_info) { + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + // + // The signature algorithm is specified as the following ASN.1 structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // An RSA public key is specified as the following ASN.1 structure: + // RSAPublicKey ::= SEQUENCE { + // modulus INTEGER, -- n + // publicExponent INTEGER -- e + // } + static const uint8 kIntegerTag = 0x02; + static const uint8 kBitStringTag = 0x03; + static const uint8 kSequenceTag = 0x30; + public_key_info->clear(); + + // Encode the public exponent e as an INTEGER. + public_key_info->insert(public_key_info->begin(), + public_exponent_e.begin(), + public_exponent_e.end()); + uint8 length = public_exponent_e.size(); + public_key_info->insert(public_key_info->begin(), length); + public_key_info->insert(public_key_info->begin(), kIntegerTag); + + // Encode the modulus n as an INTEGER. + public_key_info->insert(public_key_info->begin(), + modulus_n.begin(), modulus_n.end()); + uint16 length16 = modulus_n.size(); + if (modulus_n[0] & 0x80) { + public_key_info->insert(public_key_info->begin(), 0x00); + length16++; + } + public_key_info->insert(public_key_info->begin(), length16 & 0xff); + public_key_info->insert(public_key_info->begin(), (length16 >> 8) & 0xff); + public_key_info->insert(public_key_info->begin(), 0x82); + public_key_info->insert(public_key_info->begin(), kIntegerTag); + + // Encode the RSAPublicKey SEQUENCE. + length16 = public_key_info->size(); + public_key_info->insert(public_key_info->begin(), length16 & 0xff); + public_key_info->insert(public_key_info->begin(), (length16 >> 8) & 0xff); + public_key_info->insert(public_key_info->begin(), 0x82); + public_key_info->insert(public_key_info->begin(), kSequenceTag); + + // Encode the BIT STRING. + // Number of unused bits. + public_key_info->insert(public_key_info->begin(), 0x00); + length16 = public_key_info->size(); + public_key_info->insert(public_key_info->begin(), length16 & 0xff); + public_key_info->insert(public_key_info->begin(), (length16 >> 8) & 0xff); + public_key_info->insert(public_key_info->begin(), 0x82); + public_key_info->insert(public_key_info->begin(), kBitStringTag); + + // Encode the AlgorithmIdentifier. + static const uint8 algorithm[] = { + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, + }; + public_key_info->insert(public_key_info->begin(), + algorithm, algorithm + sizeof(algorithm)); + + // Encode the outermost SEQUENCE. + length16 = public_key_info->size(); + public_key_info->insert(public_key_info->begin(), length16 & 0xff); + public_key_info->insert(public_key_info->begin(), (length16 >> 8) & 0xff); + public_key_info->insert(public_key_info->begin(), 0x82); + public_key_info->insert(public_key_info->begin(), kSequenceTag); + + return true; +} + +TEST(SignatureVerifierTest, VerifyRSAPSS) { + for (unsigned int i = 0; i < arraysize(pss_test); i++) { + std::vector modulus_n; + std::vector public_exponent_e; + ASSERT_TRUE(DecodeTestInput(pss_test[i].modulus_n, &modulus_n)); + ASSERT_TRUE(DecodeTestInput(pss_test[i].public_exponent_e, + &public_exponent_e)); + std::vector public_key_info; + ASSERT_TRUE(EncodeRSAPublicKey(modulus_n, public_exponent_e, + &public_key_info)); + + for (unsigned int j = 0; j < arraysize(pss_test[i].example); j++) { + std::vector message; + std::vector salt; + std::vector signature; + ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].message, &message)); + ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].salt, &salt)); + ASSERT_TRUE(DecodeTestInput(pss_test[i].example[j].signature, + &signature)); + + crypto::SignatureVerifier verifier; + bool ok; + + // Positive test. + ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1, + crypto::SignatureVerifier::SHA1, + salt.size(), + &signature[0], signature.size(), + &public_key_info[0], + public_key_info.size()); + ASSERT_TRUE(ok); + verifier.VerifyUpdate(&message[0], message.size()); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Modify the first byte of the message. + ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1, + crypto::SignatureVerifier::SHA1, + salt.size(), + &signature[0], signature.size(), + &public_key_info[0], + public_key_info.size()); + ASSERT_TRUE(ok); + message[0] += 1; + verifier.VerifyUpdate(&message[0], message.size()); + message[0] -= 1; + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + + // Truncate the message. + ASSERT_FALSE(message.empty()); + ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1, + crypto::SignatureVerifier::SHA1, + salt.size(), + &signature[0], signature.size(), + &public_key_info[0], + public_key_info.size()); + ASSERT_TRUE(ok); + verifier.VerifyUpdate(&message[0], message.size() - 1); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + + // Corrupt the signature. + signature[0] += 1; + ok = verifier.VerifyInitRSAPSS(crypto::SignatureVerifier::SHA1, + crypto::SignatureVerifier::SHA1, + salt.size(), + &signature[0], signature.size(), + &public_key_info[0], + public_key_info.size()); + signature[0] -= 1; + ASSERT_TRUE(ok); + verifier.VerifyUpdate(&message[0], message.size()); + ok = verifier.VerifyFinal(); + EXPECT_FALSE(ok); + } + } +} diff --git a/chromium/crypto/symmetric_key.h b/chromium/crypto/symmetric_key.h new file mode 100644 index 00000000000..cce2a9b4df1 --- /dev/null +++ b/chromium/crypto/symmetric_key.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CRYPTO_SYMMETRIC_KEY_H_ +#define CRYPTO_SYMMETRIC_KEY_H_ + +#include + +#include "base/basictypes.h" +#include "crypto/crypto_export.h" + +#if defined(NACL_WIN64) +// See comments for crypto_nacl_win64 in crypto.gyp. +// Must test for NACL_WIN64 before OS_WIN since former is a subset of latter. +#include "crypto/scoped_capi_types.h" +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) +#include "crypto/scoped_nss_types.h" +#endif + +namespace crypto { + +// Wraps a platform-specific symmetric key and allows it to be held in a +// scoped_ptr. +class CRYPTO_EXPORT SymmetricKey { + public: + // Defines the algorithm that a key will be used with. See also + // classs Encrptor. + enum Algorithm { + AES, + HMAC_SHA1, + }; + + virtual ~SymmetricKey(); + + // Generates a random key suitable to be used with |algorithm| and of + // |key_size_in_bits| bits. |key_size_in_bits| must be a multiple of 8. + // The caller is responsible for deleting the returned SymmetricKey. + static SymmetricKey* GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits); + + // Derives a key from the supplied password and salt using PBKDF2, suitable + // for use with specified |algorithm|. Note |algorithm| is not the algorithm + // used to derive the key from the password. |key_size_in_bits| must be a + // multiple of 8. The caller is responsible for deleting the returned + // SymmetricKey. + static SymmetricKey* DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits); + + // Imports an array of key bytes in |raw_key|. This key may have been + // generated by GenerateRandomKey or DeriveKeyFromPassword and exported with + // GetRawKey, or via another compatible method. The key must be of suitable + // size for use with |algorithm|. The caller owns the returned SymmetricKey. + static SymmetricKey* Import(Algorithm algorithm, const std::string& raw_key); + +#if defined(USE_OPENSSL) + const std::string& key() { return key_; } +#elif defined(NACL_WIN64) + HCRYPTKEY key() const { return key_.get(); } +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + PK11SymKey* key() const { return key_.get(); } +#endif + + // Extracts the raw key from the platform specific data. + // Warning: |raw_key| holds the raw key as bytes and thus must be handled + // carefully. + bool GetRawKey(std::string* raw_key); + +#if defined(OS_CHROMEOS) + // Creates symmetric key from NSS key. Takes over the ownership of |key|. + static SymmetricKey* CreateFromKey(PK11SymKey* key); +#endif + + private: +#if defined(USE_OPENSSL) + SymmetricKey() {} + std::string key_; +#elif defined(NACL_WIN64) + SymmetricKey(HCRYPTPROV provider, HCRYPTKEY key, + const void* key_data, size_t key_size_in_bytes); + + ScopedHCRYPTPROV provider_; + ScopedHCRYPTKEY key_; + + // Contains the raw key, if it is known during initialization and when it + // is likely that the associated |provider_| will be unable to export the + // |key_|. This is the case of HMAC keys when the key size exceeds 16 bytes + // when using the default RSA provider. + // TODO(rsleevi): See if KP_EFFECTIVE_KEYLEN is the reason why CryptExportKey + // fails with NTE_BAD_KEY/NTE_BAD_LEN + std::string raw_key_; +#elif defined(USE_NSS) || defined(OS_WIN) || defined(OS_MACOSX) + explicit SymmetricKey(PK11SymKey* key); + ScopedPK11SymKey key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SymmetricKey); +}; + +} // namespace crypto + +#endif // CRYPTO_SYMMETRIC_KEY_H_ diff --git a/chromium/crypto/symmetric_key_nss.cc b/chromium/crypto/symmetric_key_nss.cc new file mode 100644 index 00000000000..7fd0c320d88 --- /dev/null +++ b/chromium/crypto/symmetric_key_nss.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/symmetric_key.h" + +#include +#include + +#include "base/logging.h" +#include "crypto/nss_util.h" + +namespace crypto { + +SymmetricKey::~SymmetricKey() {} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_EQ(AES, algorithm); + + EnsureNSSInit(); + if (key_size_in_bits == 0) + return NULL; + + ScopedPK11Slot slot(PK11_GetInternalSlot()); + if (!slot.get()) + return NULL; + + PK11SymKey* sym_key = PK11_KeyGen(slot.get(), CKM_AES_KEY_GEN, NULL, + key_size_in_bits / 8, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + EnsureNSSInit(); + if (salt.empty() || iterations == 0 || key_size_in_bits == 0) + return NULL; + + SECItem password_item; + password_item.type = siBuffer; + password_item.data = reinterpret_cast( + const_cast(password.data())); + password_item.len = password.size(); + + SECItem salt_item; + salt_item.type = siBuffer; + salt_item.data = reinterpret_cast( + const_cast(salt.data())); + salt_item.len = salt.size(); + + SECOidTag cipher_algorithm = + algorithm == AES ? SEC_OID_AES_256_CBC : SEC_OID_HMAC_SHA1; + ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, + cipher_algorithm, + SEC_OID_HMAC_SHA1, + key_size_in_bits / 8, + iterations, + &salt_item)); + if (!alg_id.get()) + return NULL; + + ScopedPK11Slot slot(PK11_GetInternalSlot()); + if (!slot.get()) + return NULL; + + PK11SymKey* sym_key = PK11_PBEKeyGen(slot.get(), alg_id.get(), &password_item, + PR_FALSE, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + EnsureNSSInit(); + CK_MECHANISM_TYPE cipher = + algorithm == AES ? CKM_AES_CBC : CKM_SHA_1_HMAC; + + SECItem key_item; + key_item.type = siBuffer; + key_item.data = reinterpret_cast( + const_cast(raw_key.data())); + key_item.len = raw_key.size(); + + ScopedPK11Slot slot(PK11_GetInternalSlot()); + if (!slot.get()) + return NULL; + + // The exact value of the |origin| argument doesn't matter to NSS as long as + // it's not PK11_OriginFortezzaHack, so we pass PK11_OriginUnwrap as a + // placeholder. + PK11SymKey* sym_key = PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap, + CKA_ENCRYPT, &key_item, NULL); + if (!sym_key) + return NULL; + + return new SymmetricKey(sym_key); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + SECStatus rv = PK11_ExtractKeyValue(key_.get()); + if (SECSuccess != rv) + return false; + + SECItem* key_item = PK11_GetKeyData(key_.get()); + if (!key_item) + return false; + + raw_key->assign(reinterpret_cast(key_item->data), key_item->len); + return true; +} + +#if defined(OS_CHROMEOS) +// static +SymmetricKey* SymmetricKey::CreateFromKey(PK11SymKey* key) { + return new SymmetricKey(key); +} +#endif + +SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) { + DCHECK(key); +} + +} // namespace crypto diff --git a/chromium/crypto/symmetric_key_openssl.cc b/chromium/crypto/symmetric_key_openssl.cc new file mode 100644 index 00000000000..e1c6fb7eded --- /dev/null +++ b/chromium/crypto/symmetric_key_openssl.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/symmetric_key.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_util.h" +#include "crypto/openssl_util.h" + +namespace crypto { + +SymmetricKey::~SymmetricKey() { + std::fill(key_.begin(), key_.end(), '\0'); // Zero out the confidential key. +} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_EQ(AES, algorithm); + size_t key_size_in_bytes = key_size_in_bits / 8; + DCHECK_EQ(key_size_in_bits, key_size_in_bytes * 8); + + if (key_size_in_bytes == 0) + return NULL; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr key(new SymmetricKey); + uint8* key_data = + reinterpret_cast(WriteInto(&key->key_, key_size_in_bytes + 1)); + + int rv = RAND_bytes(key_data, static_cast(key_size_in_bytes)); + return rv == 1 ? key.release() : NULL; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + DCHECK(algorithm == AES || algorithm == HMAC_SHA1); + size_t key_size_in_bytes = key_size_in_bits / 8; + DCHECK_EQ(key_size_in_bits, key_size_in_bytes * 8); + + if (key_size_in_bytes == 0) + return NULL; + + OpenSSLErrStackTracer err_tracer(FROM_HERE); + scoped_ptr key(new SymmetricKey); + uint8* key_data = + reinterpret_cast(WriteInto(&key->key_, key_size_in_bytes + 1)); + int rv = PKCS5_PBKDF2_HMAC_SHA1(password.data(), password.length(), + reinterpret_cast(salt.data()), + salt.length(), iterations, + static_cast(key_size_in_bytes), + key_data); + return rv == 1 ? key.release() : NULL; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + scoped_ptr key(new SymmetricKey); + key->key_ = raw_key; + return key.release(); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + *raw_key = key_; + return true; +} + +} // namespace crypto diff --git a/chromium/crypto/symmetric_key_unittest.cc b/chromium/crypto/symmetric_key_unittest.cc new file mode 100644 index 00000000000..f0b4a60a4c5 --- /dev/null +++ b/chromium/crypto/symmetric_key_unittest.cc @@ -0,0 +1,225 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/symmetric_key.h" + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SymmetricKeyTest, GenerateRandomKey) { + scoped_ptr key( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key.get()); + std::string raw_key; + EXPECT_TRUE(key->GetRawKey(&raw_key)); + EXPECT_EQ(32U, raw_key.size()); + + // Do it again and check that the keys are different. + // (Note: this has a one-in-10^77 chance of failure!) + scoped_ptr key2( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key2.get()); + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + EXPECT_EQ(32U, raw_key2.size()); + EXPECT_NE(raw_key, raw_key2); +} + +TEST(SymmetricKeyTest, ImportGeneratedKey) { + scoped_ptr key1( + crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 256)); + ASSERT_TRUE(NULL != key1.get()); + std::string raw_key1; + EXPECT_TRUE(key1->GetRawKey(&raw_key1)); + + scoped_ptr key2( + crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key1)); + ASSERT_TRUE(NULL != key2.get()); + + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + + EXPECT_EQ(raw_key1, raw_key2); +} + +TEST(SymmetricKeyTest, ImportDerivedKey) { + scoped_ptr key1( + crypto::SymmetricKey::DeriveKeyFromPassword( + crypto::SymmetricKey::HMAC_SHA1, "password", "somesalt", 1024, 160)); + ASSERT_TRUE(NULL != key1.get()); + std::string raw_key1; + EXPECT_TRUE(key1->GetRawKey(&raw_key1)); + + scoped_ptr key2( + crypto::SymmetricKey::Import(crypto::SymmetricKey::HMAC_SHA1, raw_key1)); + ASSERT_TRUE(NULL != key2.get()); + + std::string raw_key2; + EXPECT_TRUE(key2->GetRawKey(&raw_key2)); + + EXPECT_EQ(raw_key1, raw_key2); +} + +struct PBKDF2TestVector { + crypto::SymmetricKey::Algorithm algorithm; + const char* password; + const char* salt; + unsigned int rounds; + unsigned int key_size_in_bits; + const char* expected; // ASCII encoded hex bytes +}; + +class SymmetricKeyDeriveKeyFromPasswordTest + : public testing::TestWithParam { +}; + +TEST_P(SymmetricKeyDeriveKeyFromPasswordTest, DeriveKeyFromPassword) { + PBKDF2TestVector test_data(GetParam()); +#if defined(OS_MACOSX) && !defined(OS_IOS) + // The OS X crypto libraries have minimum salt and iteration requirements + // so some of the tests below will cause them to barf. Skip these. + if (strlen(test_data.salt) < 8 || test_data.rounds < 1000) { + VLOG(1) << "Skipped test vector for " << test_data.expected; + return; + } +#endif // OS_MACOSX + + scoped_ptr key( + crypto::SymmetricKey::DeriveKeyFromPassword( + test_data.algorithm, + test_data.password, test_data.salt, + test_data.rounds, test_data.key_size_in_bits)); + ASSERT_TRUE(NULL != key.get()); + + std::string raw_key; + key->GetRawKey(&raw_key); + EXPECT_EQ(test_data.key_size_in_bits / 8, raw_key.size()); + EXPECT_EQ(test_data.expected, + StringToLowerASCII(base::HexEncode(raw_key.data(), + raw_key.size()))); +} + +static const PBKDF2TestVector kTestVectors[] = { + // These tests come from + // http://www.ietf.org/id/draft-josefsson-pbkdf2-test-vectors-00.txt + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 1, + 160, + "0c60c80f961f0e71f3a9b524af6012062fe037a6", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 2, + 160, + "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 4096, + 160, + "4b007901b765489abead49d926f721d065a429c1", + }, + // This test takes over 30s to run on the trybots. +#if 0 + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "salt", + 16777216, + 160, + "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984", + }, +#endif + + // These tests come from RFC 3962, via BSD source code at + // http://www.openbsd.org/cgi-bin/cvsweb/src/sbin/bioctl/pbkdf2.c?rev=HEAD&content-type=text/plain + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 1, + 160, + "cdedb5281bb2f801565a1122b25635150ad1f7a0", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 2, + 160, + "01dbee7f4a9e243e988b62c73cda935da05378b9", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "ATHENA.MIT.EDUraeburn", + 1200, + 160, + "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddb", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "password", + "\0224VxxV4\022", /* 0x1234567878563412 */ + 5, + 160, + "d1daa78615f287e6a1c8b120d7062a493f98d203", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase equals block size", + 1200, + 160, + "139c30c0966bc32ba55fdbf212530ac9c5ec59f1", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", + 1200, + 160, + "9ccad6d468770cd51b10e6a68721be611a8b4d28", + }, + { + crypto::SymmetricKey::HMAC_SHA1, + "\360\235\204\236", /* g-clef (0xf09d849e) */ + "EXAMPLE.COMpianist", + 50, + 160, + "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0", + }, + + // Regression tests for AES keys, derived from the Linux NSS implementation. + { + crypto::SymmetricKey::AES, + "A test password", + "saltsalt", + 1, + 256, + "44899a7777f0e6e8b752f875f02044b8ac593de146de896f2e8a816e315a36de", + }, + { + crypto::SymmetricKey::AES, + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", + 20, + 256, + "e0739745dc28b8721ba402e05214d2ac1eab54cf72bee1fba388297a09eb493c", + }, +}; + +INSTANTIATE_TEST_CASE_P(, SymmetricKeyDeriveKeyFromPasswordTest, + testing::ValuesIn(kTestVectors)); diff --git a/chromium/crypto/symmetric_key_win.cc b/chromium/crypto/symmetric_key_win.cc new file mode 100644 index 00000000000..b3d65f66137 --- /dev/null +++ b/chromium/crypto/symmetric_key_win.cc @@ -0,0 +1,536 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "crypto/symmetric_key.h" + +#include + +// TODO(wtc): replace scoped_array by std::vector. +#include "base/memory/scoped_ptr.h" +#include "base/sys_byteorder.h" + +namespace crypto { + +namespace { + +// The following is a non-public Microsoft header documented in MSDN under +// CryptImportKey / CryptExportKey. Following the header is the byte array of +// the actual plaintext key. +struct PlaintextBlobHeader { + BLOBHEADER hdr; + DWORD cbKeySize; +}; + +// CryptoAPI makes use of three distinct ALG_IDs for AES, rather than just +// CALG_AES (which exists, but depending on the functions you are calling, may +// result in function failure, whereas the subtype would succeed). +ALG_ID GetAESAlgIDForKeySize(size_t key_size_in_bits) { + // Only AES-128/-192/-256 is supported in CryptoAPI. + switch (key_size_in_bits) { + case 128: + return CALG_AES_128; + case 192: + return CALG_AES_192; + case 256: + return CALG_AES_256; + default: + NOTREACHED(); + return 0; + } +}; + +// Imports a raw/plaintext key of |key_size| stored in |*key_data| into a new +// key created for the specified |provider|. |alg| contains the algorithm of +// the key being imported. +// If |key_data| is intended to be used as an HMAC key, then |alg| should be +// CALG_HMAC. +// If successful, returns true and stores the imported key in |*key|. +// TODO(wtc): use this function in hmac_win.cc. +bool ImportRawKey(HCRYPTPROV provider, + ALG_ID alg, + const void* key_data, size_t key_size, + ScopedHCRYPTKEY* key) { + DCHECK_GT(key_size, 0); + + DWORD actual_size = + static_cast(sizeof(PlaintextBlobHeader) + key_size); + std::vector tmp_data(actual_size); + BYTE* actual_key = &tmp_data[0]; + memcpy(actual_key + sizeof(PlaintextBlobHeader), key_data, key_size); + PlaintextBlobHeader* key_header = + reinterpret_cast(actual_key); + memset(key_header, 0, sizeof(PlaintextBlobHeader)); + + key_header->hdr.bType = PLAINTEXTKEYBLOB; + key_header->hdr.bVersion = CUR_BLOB_VERSION; + key_header->hdr.aiKeyAlg = alg; + + key_header->cbKeySize = static_cast(key_size); + + HCRYPTKEY unsafe_key = NULL; + DWORD flags = CRYPT_EXPORTABLE; + if (alg == CALG_HMAC) { + // Though it may appear odd that IPSEC and RC2 are being used, this is + // done in accordance with Microsoft's FIPS 140-2 Security Policy for the + // RSA Enhanced Provider, as the approved means of using arbitrary HMAC + // key material. + key_header->hdr.aiKeyAlg = CALG_RC2; + flags |= CRYPT_IPSEC_HMAC_KEY; + } + + BOOL ok = + CryptImportKey(provider, actual_key, actual_size, 0, flags, &unsafe_key); + + // Clean up the temporary copy of key, regardless of whether it was imported + // sucessfully or not. + SecureZeroMemory(actual_key, actual_size); + + if (!ok) + return false; + + key->reset(unsafe_key); + return true; +} + +// Attempts to generate a random AES key of |key_size_in_bits|. Returns true +// if generation is successful, storing the generated key in |*key| and the +// key provider (CSP) in |*provider|. +bool GenerateAESKey(size_t key_size_in_bits, + ScopedHCRYPTPROV* provider, + ScopedHCRYPTKEY* key) { + DCHECK(provider); + DCHECK(key); + + ALG_ID alg = GetAESAlgIDForKeySize(key_size_in_bits); + if (alg == 0) + return false; + + ScopedHCRYPTPROV safe_provider; + // Note: The only time NULL is safe to be passed as pszContainer is when + // dwFlags contains CRYPT_VERIFYCONTEXT, as all keys generated and/or used + // will be treated as ephemeral keys and not persisted. + BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, + PROV_RSA_AES, CRYPT_VERIFYCONTEXT); + if (!ok) + return false; + + ScopedHCRYPTKEY safe_key; + // In the FIPS 140-2 Security Policy for CAPI on XP/Vista+, Microsoft notes + // that CryptGenKey makes use of the same functionality exposed via + // CryptGenRandom. The reason this is being used, as opposed to + // CryptGenRandom and CryptImportKey is for compliance with the security + // policy + ok = CryptGenKey(safe_provider.get(), alg, CRYPT_EXPORTABLE, + safe_key.receive()); + if (!ok) + return false; + + key->swap(safe_key); + provider->swap(safe_provider); + + return true; +} + +// Returns true if the HMAC key size meets the requirement of FIPS 198 +// Section 3. |alg| is the hash function used in the HMAC. +bool CheckHMACKeySize(size_t key_size_in_bits, ALG_ID alg) { + DWORD hash_size = 0; + switch (alg) { + case CALG_SHA1: + hash_size = 20; + break; + case CALG_SHA_256: + hash_size = 32; + break; + case CALG_SHA_384: + hash_size = 48; + break; + case CALG_SHA_512: + hash_size = 64; + break; + } + if (hash_size == 0) + return false; + + // An HMAC key must be >= L/2, where L is the output size of the hash + // function being used. + return (key_size_in_bits >= (hash_size / 2 * 8) && + (key_size_in_bits % 8) == 0); +} + +// Attempts to generate a random, |key_size_in_bits|-long HMAC key, for use +// with the hash function |alg|. +// |key_size_in_bits| must be >= 1/2 the hash size of |alg| for security. +// Returns true if generation is successful, storing the generated key in +// |*key| and the key provider (CSP) in |*provider|. +bool GenerateHMACKey(size_t key_size_in_bits, + ALG_ID alg, + ScopedHCRYPTPROV* provider, + ScopedHCRYPTKEY* key, + scoped_ptr* raw_key) { + DCHECK(provider); + DCHECK(key); + DCHECK(raw_key); + + if (!CheckHMACKeySize(key_size_in_bits, alg)) + return false; + + ScopedHCRYPTPROV safe_provider; + // See comment in GenerateAESKey as to why NULL is acceptable for the + // container name. + BOOL ok = CryptAcquireContext(safe_provider.receive(), NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); + if (!ok) + return false; + + DWORD key_size_in_bytes = static_cast(key_size_in_bits / 8); + scoped_ptr random(new BYTE[key_size_in_bytes]); + ok = CryptGenRandom(safe_provider, key_size_in_bytes, random.get()); + if (!ok) + return false; + + ScopedHCRYPTKEY safe_key; + bool rv = ImportRawKey(safe_provider, CALG_HMAC, random.get(), + key_size_in_bytes, &safe_key); + if (rv) { + key->swap(safe_key); + provider->swap(safe_provider); + raw_key->swap(random); + } + + SecureZeroMemory(random.get(), key_size_in_bytes); + return rv; +} + +// Attempts to create an HMAC hash instance using the specified |provider| +// and |key|. The inner hash function will be |hash_alg|. If successful, +// returns true and stores the hash in |*hash|. +// TODO(wtc): use this function in hmac_win.cc. +bool CreateHMACHash(HCRYPTPROV provider, + HCRYPTKEY key, + ALG_ID hash_alg, + ScopedHCRYPTHASH* hash) { + ScopedHCRYPTHASH safe_hash; + BOOL ok = CryptCreateHash(provider, CALG_HMAC, key, 0, safe_hash.receive()); + if (!ok) + return false; + + HMAC_INFO hmac_info; + memset(&hmac_info, 0, sizeof(hmac_info)); + hmac_info.HashAlgid = hash_alg; + + ok = CryptSetHashParam(safe_hash, HP_HMAC_INFO, + reinterpret_cast(&hmac_info), 0); + if (!ok) + return false; + + hash->swap(safe_hash); + return true; +} + +// Computes a block of the derived key using the PBKDF2 function F for the +// specified |block_index| using the PRF |hash|, writing the output to +// |output_buf|. +// |output_buf| must have enough space to accomodate the output of the PRF +// specified by |hash|. +// Returns true if the block was successfully computed. +bool ComputePBKDF2Block(HCRYPTHASH hash, + DWORD hash_size, + const std::string& salt, + size_t iterations, + uint32 block_index, + BYTE* output_buf) { + // From RFC 2898: + // 3. The 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}) + ScopedHCRYPTHASH safe_hash; + BOOL ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); + if (!ok) + return false; + + // Iteration U_1: Compute PRF for S. + ok = CryptHashData(safe_hash, reinterpret_cast(salt.data()), + static_cast(salt.size()), 0); + if (!ok) + return false; + + // Iteration U_1: and append (big-endian) INT (i). + uint32 big_endian_block_index = base::HostToNet32(block_index); + ok = CryptHashData(safe_hash, + reinterpret_cast(&big_endian_block_index), + sizeof(big_endian_block_index), 0); + + std::vector hash_value(hash_size); + + DWORD size = hash_size; + ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); + if (!ok || size != hash_size) + return false; + + memcpy(output_buf, &hash_value[0], hash_size); + + // Iteration 2 - c: Compute U_{iteration} by applying the PRF to + // U_{iteration - 1}, then xor the resultant hash with |output|, which + // contains U_1 ^ U_2 ^ ... ^ U_{iteration - 1}. + for (size_t iteration = 2; iteration <= iterations; ++iteration) { + safe_hash.reset(); + ok = CryptDuplicateHash(hash, NULL, 0, safe_hash.receive()); + if (!ok) + return false; + + ok = CryptHashData(safe_hash, &hash_value[0], hash_size, 0); + if (!ok) + return false; + + size = hash_size; + ok = CryptGetHashParam(safe_hash, HP_HASHVAL, &hash_value[0], &size, 0); + if (!ok || size != hash_size) + return false; + + for (int i = 0; i < hash_size; ++i) + output_buf[i] ^= hash_value[i]; + } + + return true; +} + +} // namespace + +SymmetricKey::~SymmetricKey() { + // TODO(wtc): create a "secure" string type that zeroes itself in the + // destructor. + if (!raw_key_.empty()) + SecureZeroMemory(const_cast(raw_key_.data()), raw_key_.size()); +} + +// static +SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, + size_t key_size_in_bits) { + DCHECK_GE(key_size_in_bits, 8); + + ScopedHCRYPTPROV provider; + ScopedHCRYPTKEY key; + + bool ok = false; + scoped_ptr raw_key; + + switch (algorithm) { + case AES: + ok = GenerateAESKey(key_size_in_bits, &provider, &key); + break; + case HMAC_SHA1: + ok = GenerateHMACKey(key_size_in_bits, CALG_SHA1, &provider, + &key, &raw_key); + break; + } + + if (!ok) { + NOTREACHED(); + return NULL; + } + + size_t key_size_in_bytes = key_size_in_bits / 8; + if (raw_key == NULL) + key_size_in_bytes = 0; + + SymmetricKey* result = new SymmetricKey(provider.release(), + key.release(), + raw_key.get(), + key_size_in_bytes); + if (raw_key != NULL) + SecureZeroMemory(raw_key.get(), key_size_in_bytes); + + return result; +} + +// static +SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, + const std::string& password, + const std::string& salt, + size_t iterations, + size_t key_size_in_bits) { + // CryptoAPI lacks routines to perform PBKDF2 derivation as specified + // in RFC 2898, so it must be manually implemented. Only HMAC-SHA1 is + // supported as the PRF. + + // While not used until the end, sanity-check the input before proceeding + // with the expensive computation. + DWORD provider_type = 0; + ALG_ID alg = 0; + switch (algorithm) { + case AES: + provider_type = PROV_RSA_AES; + alg = GetAESAlgIDForKeySize(key_size_in_bits); + break; + case HMAC_SHA1: + provider_type = PROV_RSA_FULL; + alg = CALG_HMAC; + break; + default: + NOTREACHED(); + break; + } + if (provider_type == 0 || alg == 0) + return NULL; + + ScopedHCRYPTPROV provider; + BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, + CRYPT_VERIFYCONTEXT); + if (!ok) + return NULL; + + // Convert the user password into a key suitable to be fed into the PRF + // function. + ScopedHCRYPTKEY password_as_key; + BYTE* password_as_bytes = + const_cast(reinterpret_cast(password.data())); + if (!ImportRawKey(provider, CALG_HMAC, password_as_bytes, + password.size(), &password_as_key)) + return NULL; + + // Configure the PRF function. Only HMAC variants are supported, with the + // only hash function supported being SHA1. + // TODO(rsleevi): Support SHA-256 on XP SP3+. + ScopedHCRYPTHASH prf; + if (!CreateHMACHash(provider, password_as_key, CALG_SHA1, &prf)) + return NULL; + + DWORD hLen = 0; + DWORD param_size = sizeof(hLen); + ok = CryptGetHashParam(prf, HP_HASHSIZE, + reinterpret_cast(&hLen), ¶m_size, 0); + if (!ok || hLen == 0) + return NULL; + + // 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long" and stop. + size_t dkLen = key_size_in_bits / 8; + DCHECK_GT(dkLen, 0); + + if ((dkLen / hLen) > 0xFFFFFFFF) { + DLOG(ERROR) << "Derived key too long."; + return NULL; + } + + // 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: + size_t L = (dkLen + hLen - 1) / hLen; + DCHECK_GT(L, 0); + + size_t total_generated_size = L * hLen; + std::vector generated_key(total_generated_size); + BYTE* block_offset = &generated_key[0]; + + // 3. For each block of the derived key apply the function F defined below + // to the password P, the salt S, the iteration count c, and the block + // index to compute the block: + // T_1 = F (P, S, c, 1) + // T_2 = F (P, S, c, 2) + // ... + // T_l = F (P, S, c, l) + // + // 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> + for (uint32 block_index = 1; block_index <= L; ++block_index) { + if (!ComputePBKDF2Block(prf, hLen, salt, iterations, block_index, + block_offset)) + return NULL; + block_offset += hLen; + } + + // Convert the derived key bytes into a key handle for the desired algorithm. + ScopedHCRYPTKEY key; + if (!ImportRawKey(provider, alg, &generated_key[0], dkLen, &key)) + return NULL; + + SymmetricKey* result = new SymmetricKey(provider.release(), key.release(), + &generated_key[0], dkLen); + + SecureZeroMemory(&generated_key[0], total_generated_size); + + return result; +} + +// static +SymmetricKey* SymmetricKey::Import(Algorithm algorithm, + const std::string& raw_key) { + DWORD provider_type = 0; + ALG_ID alg = 0; + switch (algorithm) { + case AES: + provider_type = PROV_RSA_AES; + alg = GetAESAlgIDForKeySize(raw_key.size() * 8); + break; + case HMAC_SHA1: + provider_type = PROV_RSA_FULL; + alg = CALG_HMAC; + break; + default: + NOTREACHED(); + break; + } + if (provider_type == 0 || alg == 0) + return NULL; + + ScopedHCRYPTPROV provider; + BOOL ok = CryptAcquireContext(provider.receive(), NULL, NULL, provider_type, + CRYPT_VERIFYCONTEXT); + if (!ok) + return NULL; + + ScopedHCRYPTKEY key; + if (!ImportRawKey(provider, alg, raw_key.data(), raw_key.size(), &key)) + return NULL; + + return new SymmetricKey(provider.release(), key.release(), + raw_key.data(), raw_key.size()); +} + +bool SymmetricKey::GetRawKey(std::string* raw_key) { + // Short circuit for when the key was supplied to the constructor. + if (!raw_key_.empty()) { + *raw_key = raw_key_; + return true; + } + + DWORD size = 0; + BOOL ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, NULL, &size); + if (!ok) + return false; + + std::vector result(size); + + ok = CryptExportKey(key_, 0, PLAINTEXTKEYBLOB, 0, &result[0], &size); + if (!ok) + return false; + + PlaintextBlobHeader* header = + reinterpret_cast(&result[0]); + raw_key->assign(reinterpret_cast(&result[sizeof(*header)]), + header->cbKeySize); + + SecureZeroMemory(&result[0], size); + + return true; +} + +SymmetricKey::SymmetricKey(HCRYPTPROV provider, + HCRYPTKEY key, + const void* key_data, size_t key_size_in_bytes) + : provider_(provider), key_(key) { + if (key_data) { + raw_key_.assign(reinterpret_cast(key_data), + key_size_in_bytes); + } +} + +} // namespace crypto diff --git a/chromium/crypto/third_party/nss/LICENSE b/chromium/crypto/third_party/nss/LICENSE new file mode 100644 index 00000000000..03671648c17 --- /dev/null +++ b/chromium/crypto/third_party/nss/LICENSE @@ -0,0 +1,35 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ diff --git a/chromium/crypto/third_party/nss/README.chromium b/chromium/crypto/third_party/nss/README.chromium new file mode 100644 index 00000000000..1a63665e6fa --- /dev/null +++ b/chromium/crypto/third_party/nss/README.chromium @@ -0,0 +1,18 @@ +Name: Network Security Services (NSS) +URL: http://www.mozilla.org/projects/security/pki/nss/ +License: MPL 1.1/GPL 2.0/LGPL 2.1 + +We extracted the SHA-256 source files, eliminated unneeded dependencies, +deleted or commented out unused code, and tweaked them for Chrome's source +tree. sha512.c is renamed sha512.cc so that it can include Chrome's C++ +header "base/basictypes.h". We define NOUNROLL256 to reduce the object code +size. + +In blapi.h and sha512.cc, replaced uint32 by unsigned int so that they can +be compiled with -DNO_NSPR_10_SUPPORT. NO_NSPR_10_SUPPORT turns off the +definition of the NSPR 1.0 types int8 - int64 and uint8 - uint64 to avoid +conflict with the same-named types defined in "base/basictypes.h". + +rsawrapr.c is copied from nss/lib/softoken/rsawrapr.c, with +HASH_GetRawHashObject changed to HASH_GetHashObject. It contains the +emsa_pss_verify function for verifying RSA-PSS signatures. diff --git a/chromium/crypto/third_party/nss/chromium-blapi.h b/chromium/crypto/third_party/nss/chromium-blapi.h new file mode 100644 index 00000000000..2ca772e4d32 --- /dev/null +++ b/chromium/crypto/third_party/nss/chromium-blapi.h @@ -0,0 +1,101 @@ +/* + * crypto.h - public data structures and prototypes for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapi.h,v 1.27 2007/11/09 18:49:32 wtc%google.com Exp $ */ + +#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_ +#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_ + +#include "crypto/third_party/nss/chromium-blapit.h" + +/******************************************/ + +extern SHA256Context *SHA256_NewContext(void); +extern void SHA256_DestroyContext(SHA256Context *cx, PRBool freeit); +extern void SHA256_Begin(SHA256Context *cx); +extern void SHA256_Update(SHA256Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA256_End(SHA256Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA256_Hash(unsigned char *dest, const char *src); +extern void SHA256_TraceState(SHA256Context *cx); +extern unsigned int SHA256_FlattenSize(SHA256Context *cx); +extern SECStatus SHA256_Flatten(SHA256Context *cx,unsigned char *space); +extern SHA256Context * SHA256_Resurrect(unsigned char *space, void *arg); +extern void SHA256_Clone(SHA256Context *dest, SHA256Context *src); + +/******************************************/ + +extern SHA512Context *SHA512_NewContext(void); +extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit); +extern void SHA512_Begin(SHA512Context *cx); +extern void SHA512_Update(SHA512Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA512_End(SHA512Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA512_Hash(unsigned char *dest, const char *src); +extern void SHA512_TraceState(SHA512Context *cx); +extern unsigned int SHA512_FlattenSize(SHA512Context *cx); +extern SECStatus SHA512_Flatten(SHA512Context *cx,unsigned char *space); +extern SHA512Context * SHA512_Resurrect(unsigned char *space, void *arg); +extern void SHA512_Clone(SHA512Context *dest, SHA512Context *src); + +/******************************************/ + +extern SHA384Context *SHA384_NewContext(void); +extern void SHA384_DestroyContext(SHA384Context *cx, PRBool freeit); +extern void SHA384_Begin(SHA384Context *cx); +extern void SHA384_Update(SHA384Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA384_End(SHA384Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length); +extern SECStatus SHA384_Hash(unsigned char *dest, const char *src); +extern void SHA384_TraceState(SHA384Context *cx); +extern unsigned int SHA384_FlattenSize(SHA384Context *cx); +extern SECStatus SHA384_Flatten(SHA384Context *cx,unsigned char *space); +extern SHA384Context * SHA384_Resurrect(unsigned char *space, void *arg); +extern void SHA384_Clone(SHA384Context *dest, SHA384Context *src); + +#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPI_H_ */ diff --git a/chromium/crypto/third_party/nss/chromium-blapit.h b/chromium/crypto/third_party/nss/chromium-blapit.h new file mode 100644 index 00000000000..669f64d805e --- /dev/null +++ b/chromium/crypto/third_party/nss/chromium-blapit.h @@ -0,0 +1,91 @@ +/* + * blapit.h - public data structures for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta and + * Douglas Stebila , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapit.h,v 1.20 2007/02/28 19:47:37 rrelyea%redhat.com Exp $ */ + +#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_ +#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_ + +#include "base/third_party/nspr/prtypes.h" + +/* +** A status code. Status's are used by procedures that return status +** values. Again the motivation is so that a compiler can generate +** warnings when return values are wrong. Correct testing of status codes: +** +** SECStatus rv; +** rv = some_function (some_argument); +** if (rv != SECSuccess) +** do_an_error_thing(); +** +*/ +typedef enum _SECStatus { + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +} SECStatus; + +#define SHA256_LENGTH 32 /* bytes */ +#define SHA384_LENGTH 48 /* bytes */ +#define SHA512_LENGTH 64 /* bytes */ +#define HASH_LENGTH_MAX SHA512_LENGTH + +/* + * Input block size for each hash algorithm. + */ + +#define SHA256_BLOCK_LENGTH 64 /* bytes */ +#define SHA384_BLOCK_LENGTH 128 /* bytes */ +#define SHA512_BLOCK_LENGTH 128 /* bytes */ +#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH + +/*************************************************************************** +** Opaque objects +*/ + +struct SHA256ContextStr ; +struct SHA512ContextStr ; + +typedef struct SHA256ContextStr SHA256Context; +typedef struct SHA512ContextStr SHA512Context; +/* SHA384Context is really a SHA512ContextStr. This is not a mistake. */ +typedef struct SHA512ContextStr SHA384Context; + +#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_BLAPIT_H_ */ diff --git a/chromium/crypto/third_party/nss/chromium-nss.h b/chromium/crypto/third_party/nss/chromium-nss.h new file mode 100644 index 00000000000..437e6bd5cdb --- /dev/null +++ b/chromium/crypto/third_party/nss/chromium-nss.h @@ -0,0 +1,79 @@ + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_ +#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_ + +// This file contains some functions we borrowed from NSS. + +#include +#include +#include +#include + +#include "crypto/crypto_export.h" + +extern "C" SECStatus emsa_pss_verify(const unsigned char *mHash, + const unsigned char *em, + unsigned int emLen, + HASH_HashType hashAlg, + HASH_HashType maskHashAlg, + unsigned int sLen); + +// Like PK11_ImportEncryptedPrivateKeyInfo, but hardcoded for EC, and returns +// the SECKEYPrivateKey. +// See https://bugzilla.mozilla.org/show_bug.cgi?id=211546 +// When we use NSS 3.13.2 or later, +// PK11_ImportEncryptedPrivateKeyInfoAndReturnKey can be used instead. +SECStatus ImportEncryptedECPrivateKeyInfoAndReturnKey( + PK11SlotInfo* slot, + SECKEYEncryptedPrivateKeyInfo* epki, + SECItem* password, + SECItem* nickname, + SECItem* public_value, + PRBool permanent, + PRBool sensitive, + SECKEYPrivateKey** private_key, + void* wincx); + +// Like SEC_DerSignData. +CRYPTO_EXPORT SECStatus DerSignData(PLArenaPool *arena, + SECItem *result, + SECItem *input, + SECKEYPrivateKey *key, + SECOidTag algo_id); + +#endif // CRYPTO_THIRD_PARTY_NSS_CHROMIUM_NSS_H_ diff --git a/chromium/crypto/third_party/nss/chromium-sha256.h b/chromium/crypto/third_party/nss/chromium-sha256.h new file mode 100644 index 00000000000..14661dc4c0f --- /dev/null +++ b/chromium/crypto/third_party/nss/chromium-sha256.h @@ -0,0 +1,51 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_ +#define CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_ + +#include "base/third_party/nspr/prtypes.h" + +struct SHA256ContextStr { + union { + PRUint32 w[64]; /* message schedule, input buffer, plus 48 words */ + PRUint8 b[256]; + } u; + PRUint32 h[8]; /* 8 state variables */ + PRUint32 sizeHi,sizeLo; /* 64-bit count of hashed bytes. */ +}; + +#endif /* CRYPTO_THIRD_PARTY_NSS_CHROMIUM_SHA_256_H_ */ diff --git a/chromium/crypto/third_party/nss/pk11akey.cc b/chromium/crypto/third_party/nss/pk11akey.cc new file mode 100644 index 00000000000..4db582fc6c4 --- /dev/null +++ b/chromium/crypto/third_party/nss/pk11akey.cc @@ -0,0 +1,98 @@ + /* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Stephen Henson + * Dr Vipul Gupta , and + * Douglas Stebila , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "crypto/third_party/nss/chromium-nss.h" + +#include + +#include "base/logging.h" + +// Based on PK11_ImportEncryptedPrivateKeyInfo function in +// mozilla/security/nss/lib/pk11wrap/pk11akey.c. +SECStatus ImportEncryptedECPrivateKeyInfoAndReturnKey( + PK11SlotInfo* slot, + SECKEYEncryptedPrivateKeyInfo* epki, + SECItem* password, + SECItem* nickname, + SECItem* public_value, + PRBool permanent, + PRBool sensitive, + SECKEYPrivateKey** private_key, + void* wincx) { + SECItem* crypto_param = NULL; + + CK_ATTRIBUTE_TYPE usage = CKA_SIGN; + + PK11SymKey* key = PK11_PBEKeyGen(slot, + &epki->algorithm, + password, + PR_FALSE, // faulty3DES + wincx); + if (key == NULL) { + DLOG(ERROR) << "PK11_PBEKeyGen: " << PORT_GetError(); + return SECFailure; + } + + CK_MECHANISM_TYPE crypto_mech_type = PK11_GetPBECryptoMechanism( + &epki->algorithm, &crypto_param, password); + if (crypto_mech_type == CKM_INVALID_MECHANISM) { + DLOG(ERROR) << "PK11_GetPBECryptoMechanism: " << PORT_GetError(); + PK11_FreeSymKey(key); + return SECFailure; + } + + crypto_mech_type = PK11_GetPadMechanism(crypto_mech_type); + + *private_key = PK11_UnwrapPrivKey(slot, key, crypto_mech_type, crypto_param, + &epki->encryptedData, nickname, + public_value, permanent, sensitive, CKK_EC, + &usage, 1, wincx); + + if (crypto_param != NULL) + SECITEM_ZfreeItem(crypto_param, PR_TRUE); + + PK11_FreeSymKey(key); + + if (!*private_key) { + DLOG(ERROR) << "PK11_UnwrapPrivKey: " << PORT_GetError(); + return SECFailure; + } + + return SECSuccess; +} diff --git a/chromium/crypto/third_party/nss/rsawrapr.c b/chromium/crypto/third_party/nss/rsawrapr.c new file mode 100644 index 00000000000..73e498f937d --- /dev/null +++ b/chromium/crypto/third_party/nss/rsawrapr.c @@ -0,0 +1,160 @@ +/* + * PKCS#1 encoding and decoding functions. + * This file is believed to contain no code licensed from other parties. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "seccomon.h" +#include "secerr.h" +#include "sechash.h" + +/* Needed for RSA-PSS functions */ +static const unsigned char eightZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* + * Mask generation function MGF1 as defined in PKCS #1 v2.1 / RFC 3447. + */ +static SECStatus +MGF1(HASH_HashType hashAlg, unsigned char *mask, unsigned int maskLen, + const unsigned char *mgfSeed, unsigned int mgfSeedLen) +{ + unsigned int digestLen; + PRUint32 counter, rounds; + unsigned char *tempHash, *temp; + const SECHashObject *hash; + void *hashContext; + unsigned char C[4]; + + hash = HASH_GetHashObject(hashAlg); + if (hash == NULL) + return SECFailure; + + hashContext = (*hash->create)(); + rounds = (maskLen + hash->length - 1) / hash->length; + for (counter = 0; counter < rounds; counter++) { + C[0] = (unsigned char)((counter >> 24) & 0xff); + C[1] = (unsigned char)((counter >> 16) & 0xff); + C[2] = (unsigned char)((counter >> 8) & 0xff); + C[3] = (unsigned char)(counter & 0xff); + + /* This could be optimized when the clone functions in + * rawhash.c are implemented. */ + (*hash->begin)(hashContext); + (*hash->update)(hashContext, mgfSeed, mgfSeedLen); + (*hash->update)(hashContext, C, sizeof C); + + tempHash = mask + counter * hash->length; + if (counter != (rounds-1)) { + (*hash->end)(hashContext, tempHash, &digestLen, hash->length); + } else { /* we're in the last round and need to cut the hash */ + temp = (unsigned char *)PORT_Alloc(hash->length); + (*hash->end)(hashContext, temp, &digestLen, hash->length); + PORT_Memcpy(tempHash, temp, maskLen - counter * hash->length); + PORT_Free(temp); + } + } + (*hash->destroy)(hashContext, PR_TRUE); + + return SECSuccess; +} + +/* + * Verify a RSA-PSS signature. + * Described in RFC 3447, section 9.1.2. + * We use mHash instead of M as input. + * emBits from the RFC is just modBits - 1, see section 8.1.2. + * We only support MGF1 as the MGF. + * + * NOTE: this code assumes modBits is a multiple of 8. + */ +SECStatus +emsa_pss_verify(const unsigned char *mHash, + const unsigned char *em, unsigned int emLen, + HASH_HashType hashAlg, HASH_HashType maskHashAlg, + unsigned int sLen) +{ + const SECHashObject *hash; + void *hash_context; + unsigned char *db; + unsigned char *H_; /* H' from the RFC */ + unsigned int i, dbMaskLen; + SECStatus rv; + + hash = HASH_GetHashObject(hashAlg); + dbMaskLen = emLen - hash->length - 1; + + /* Step 3 + 4 + 6 */ + if ((emLen < (hash->length + sLen + 2)) || + (em[emLen - 1] != 0xbc) || + ((em[0] & 0x80) != 0)) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + + /* Step 7 */ + db = (unsigned char *)PORT_Alloc(dbMaskLen); + if (db == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + /* &em[dbMaskLen] points to H, used as mgfSeed */ + MGF1(maskHashAlg, db, dbMaskLen, &em[dbMaskLen], hash->length); + + /* Step 8 */ + for (i = 0; i < dbMaskLen; i++) { + db[i] ^= em[i]; + } + + /* Step 9 */ + db[0] &= 0x7f; + + /* Step 10 */ + for (i = 0; i < (dbMaskLen - sLen - 1); i++) { + if (db[i] != 0) { + PORT_Free(db); + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + } + if (db[dbMaskLen - sLen - 1] != 0x01) { + PORT_Free(db); + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + return SECFailure; + } + + /* Step 12 + 13 */ + H_ = (unsigned char *)PORT_Alloc(hash->length); + if (H_ == NULL) { + PORT_Free(db); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + hash_context = (*hash->create)(); + if (hash_context == NULL) { + PORT_Free(db); + PORT_Free(H_); + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + (*hash->begin)(hash_context); + (*hash->update)(hash_context, eightZeros, 8); + (*hash->update)(hash_context, mHash, hash->length); + (*hash->update)(hash_context, &db[dbMaskLen - sLen], sLen); + (*hash->end)(hash_context, H_, &i, hash->length); + (*hash->destroy)(hash_context, PR_TRUE); + + PORT_Free(db); + + /* Step 14 */ + if (PORT_Memcmp(H_, &em[dbMaskLen], hash->length) != 0) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + } else { + rv = SECSuccess; + } + + PORT_Free(H_); + return rv; +} diff --git a/chromium/crypto/third_party/nss/secsign.cc b/chromium/crypto/third_party/nss/secsign.cc new file mode 100644 index 00000000000..a788defc704 --- /dev/null +++ b/chromium/crypto/third_party/nss/secsign.cc @@ -0,0 +1,132 @@ +/* + * Signature stuff. + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "crypto/third_party/nss/chromium-nss.h" + +#include + +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "build/build_config.h" + +SECStatus DerSignData(PLArenaPool *arena, + SECItem *result, + SECItem *input, + SECKEYPrivateKey *key, + SECOidTag algo_id) { + if (key->keyType != ecKey) { + return SEC_DerSignData(arena, result, input->data, input->len, key, + algo_id); + } + + // NSS has a private function sec_DecodeSigAlg it uses to figure out the + // correct hash from the algorithm id. + HASH_HashType hash_type; + switch (algo_id) { + case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE: + hash_type = HASH_AlgSHA1; + break; +#ifdef SHA224_LENGTH + case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE: + hash_type = HASH_AlgSHA224; + break; +#endif + case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE: + hash_type = HASH_AlgSHA256; + break; + case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE: + hash_type = HASH_AlgSHA384; + break; + case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE: + hash_type = HASH_AlgSHA512; + break; + default: + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + // Hash the input. + std::vector hash_data(HASH_ResultLen(hash_type)); + SECStatus rv = HASH_HashBuf( + hash_type, &hash_data[0], input->data, input->len); + if (rv != SECSuccess) + return rv; + SECItem hash = {siBuffer, &hash_data[0], + static_cast(hash_data.size())}; + + // Compute signature of hash. + int signature_len = PK11_SignatureLen(key); + std::vector signature_data(signature_len); + SECItem sig = {siBuffer, &signature_data[0], + static_cast(signature_len)}; + rv = PK11_Sign(key, &sig, &hash); + if (rv != SECSuccess) + return rv; + + CERTSignedData sd; + PORT_Memset(&sd, 0, sizeof(sd)); + // Fill in tbsCertificate. + sd.data.data = (unsigned char*) input->data; + sd.data.len = input->len; + + // Fill in signatureAlgorithm. + rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algo_id, 0); + if (rv != SECSuccess) + return rv; + + // Fill in signatureValue. + rv = DSAU_EncodeDerSigWithLen(&sd.signature, &sig, sig.len); + if (rv != SECSuccess) + return rv; + sd.signature.len <<= 3; // Convert to bit string. + + // DER encode the signed data object. + void* encode_result = SEC_ASN1EncodeItem( + arena, result, &sd, SEC_ASN1_GET(CERT_SignedDataTemplate)); + + PORT_Free(sd.signature.data); + + return encode_result ? SECSuccess : SECFailure; +} diff --git a/chromium/crypto/third_party/nss/sha512.cc b/chromium/crypto/third_party/nss/sha512.cc new file mode 100644 index 00000000000..976a604addf --- /dev/null +++ b/chromium/crypto/third_party/nss/sha512.cc @@ -0,0 +1,1391 @@ +/* + * sha512.c - implementation of SHA256, SHA384 and SHA512 + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */ + +// Prevent manual unrolling in the sha256 code, which reduces the binary code +// size from ~10k to ~1k. The performance should be reasonable for our use. +#define NOUNROLL256 1 + +#include "base/third_party/nspr/prtypes.h" /* for PRUintXX */ +#if defined(_X86_) || defined(SHA_NO_LONG_LONG) +#define NOUNROLL512 1 +#undef HAVE_LONG_LONG +#endif +#include "crypto/third_party/nss/chromium-blapi.h" +#include "crypto/third_party/nss/chromium-sha256.h" /* for struct SHA256ContextStr */ + +#include +#include +#define PORT_New(type) static_cast(malloc(sizeof(type))) +#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0) +#define PORT_Strlen(s) static_cast(strlen(s)) +#define PORT_Memcpy memcpy + +/* ============= Common constants and defines ======================= */ + +#define W ctx->u.w +#define B ctx->u.b +#define H ctx->h + +#define SHR(x,n) (x >> n) +#define SHL(x,n) (x << n) +#define Ch(x,y,z) ((x & y) ^ (~x & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) + +/* Padding used with all flavors of SHA */ +static const PRUint8 pad[240] = { +0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + /* compiler will fill the rest in with zeros */ +}; + +/* ============= SHA256 implemenmtation ================================== */ + +/* SHA-256 constants, K256. */ +static const PRUint32 K256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/* SHA-256 initial hash values */ +static const PRUint32 H256[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +#if defined(_MSC_VER) && defined(_X86_) +#ifndef FORCEINLINE +#if (_MSC_VER >= 1200) +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE __inline +#endif +#endif +#define FASTCALL __fastcall + +static FORCEINLINE PRUint32 FASTCALL +swap4b(PRUint32 dwd) +{ + __asm { + mov eax,dwd + bswap eax + } +} + +#define SHA_HTONL(x) swap4b(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#elif defined(LINUX) && defined(_X86_) +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#undef __pentium__ +#define __pentium__ 1 +#include +#define SHA_HTONL(x) bswap_32(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#else /* neither windows nor Linux PC */ +#define SWAP4MASK 0x00FF00FF +#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \ + ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK)) +#define BYTESWAP4(x) x = SHA_HTONL(x) +#endif + +#if defined(_MSC_VER) && defined(_X86_) +#pragma intrinsic (_lrotr, _lrotl) +#define ROTR32(x,n) _lrotr(x,n) +#define ROTL32(x,n) _lrotl(x,n) +#else +#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n))) +#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n))) +#endif + +/* Capitol Sigma and lower case sigma functions */ +#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22)) +#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25)) +#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3)) +#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10)) + +SHA256Context * +SHA256_NewContext(void) +{ + SHA256Context *ctx = PORT_New(SHA256Context); + return ctx; +} + +void +SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA256_Begin(SHA256Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H256, sizeof H256); +} + +static void +SHA256_Compress(SHA256Context *ctx) +{ + { + register PRUint32 t1, t2; + +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(W[0]); + BYTESWAP4(W[1]); + BYTESWAP4(W[2]); + BYTESWAP4(W[3]); + BYTESWAP4(W[4]); + BYTESWAP4(W[5]); + BYTESWAP4(W[6]); + BYTESWAP4(W[7]); + BYTESWAP4(W[8]); + BYTESWAP4(W[9]); + BYTESWAP4(W[10]); + BYTESWAP4(W[11]); + BYTESWAP4(W[12]); + BYTESWAP4(W[13]); + BYTESWAP4(W[14]); + BYTESWAP4(W[15]); +#endif + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + + /* prepare the "message schedule" */ +#ifdef NOUNROLL256 + { + int t; + for (t = 16; t < 64; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + +#endif +#undef INITW + } + { + PRUint32 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); + +#ifdef NOUNROLL256 + { + int t; + for (t = 0; t < 64; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) +#endif + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + } +#undef ROUND +} + +#undef s0 +#undef s1 +#undef S0 +#undef S1 + +void +SHA256_Update(SHA256Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + if (!inputLen) + return; + + /* Add inputLen into the count of bytes processed, before processing */ + if ((ctx->sizeLo += inputLen) < inputLen) + ctx->sizeHi++; + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA256_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA256_BLOCK_LENGTH) + SHA256_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA256_BLOCK_LENGTH) { + memcpy(B, input, SHA256_BLOCK_LENGTH); + input += SHA256_BLOCK_LENGTH; + inputLen -= SHA256_BLOCK_LENGTH; + SHA256_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA256_End(SHA256Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf); + PRUint32 hi, lo; +#ifdef SWAP4MASK + PRUint32 t1; +#endif + + hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29); + lo = (ctx->sizeLo << 3); + + SHA256_Update(ctx, pad, padLen); + +#if defined(IS_LITTLE_ENDIAN) + W[14] = SHA_HTONL(hi); + W[15] = SHA_HTONL(lo); +#else + W[14] = hi; + W[15] = lo; +#endif + SHA256_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(H[0]); + BYTESWAP4(H[1]); + BYTESWAP4(H[2]); + BYTESWAP4(H[3]); + BYTESWAP4(H[4]); + BYTESWAP4(H[5]); + BYTESWAP4(H[6]); + BYTESWAP4(H[7]); +#endif + padLen = PR_MIN(SHA256_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */ +#if 0 +SECStatus +SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA256Context ctx; + unsigned int outLen; + + SHA256_Begin(&ctx); + SHA256_Update(&ctx, src, src_length); + SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA256_Hash(unsigned char *dest, const char *src) +{ + return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA256_TraceState(SHA256Context *ctx) { } + +unsigned int +SHA256_FlattenSize(SHA256Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA256_Flatten(SHA256Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA256Context * +SHA256_Resurrect(unsigned char *space, void *arg) +{ + SHA256Context *ctx = SHA256_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA256_Clone(SHA256Context *dest, SHA256Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + + +/* ======= SHA512 and SHA384 common constants and defines ================= */ + +/* common #defines for SHA512 and SHA384 */ +#if defined(HAVE_LONG_LONG) +#define ROTR64(x,n) ((x >> n) | (x << (64 - n))) +#define ROTL64(x,n) ((x << n) | (x >> (64 - n))) + +#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39)) +#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41)) +#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7)) +#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6)) + +#if PR_BYTES_PER_LONG == 8 +#define ULLC(hi,lo) 0x ## hi ## lo ## UL +#elif defined(_MSC_VER) +#define ULLC(hi,lo) 0x ## hi ## lo ## ui64 +#else +#define ULLC(hi,lo) 0x ## hi ## lo ## ULL +#endif + +#define SHA_MASK16 ULLC(0000FFFF,0000FFFF) +#define SHA_MASK8 ULLC(00FF00FF,00FF00FF) +#define SHA_HTONLL(x) (t1 = x, \ + t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \ + t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \ + (t1 >> 32) | (t1 << 32)) +#define BYTESWAP8(x) x = SHA_HTONLL(x) + +#else /* no long long */ + +#if defined(IS_LITTLE_ENDIAN) +#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U } +#else +#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U } +#endif + +#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \ + x.hi ^= x.lo ^= x.hi ^= x.lo, x) +#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \ + tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0) +#endif + +/* SHA-384 and SHA-512 constants, K512. */ +static const PRUint64 K512[80] = { +#if PR_BYTES_PER_LONG == 8 + 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL , + 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL , + 0x3956c25bf348b538UL , 0x59f111f1b605d019UL , + 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL , + 0xd807aa98a3030242UL , 0x12835b0145706fbeUL , + 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL , + 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL , + 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL , + 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL , + 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL , + 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL , + 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL , + 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL , + 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL , + 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL , + 0x06ca6351e003826fUL , 0x142929670a0e6e70UL , + 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL , + 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL , + 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL , + 0x81c2c92e47edaee6UL , 0x92722c851482353bUL , + 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL , + 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL , + 0xd192e819d6ef5218UL , 0xd69906245565a910UL , + 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL , + 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL , + 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL , + 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL , + 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL , + 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL , + 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL , + 0x90befffa23631e28UL , 0xa4506cebde82bde9UL , + 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL , + 0xca273eceea26619cUL , 0xd186b8c721c0c207UL , + 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL , + 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL , + 0x113f9804bef90daeUL , 0x1b710b35131c471bUL , + 0x28db77f523047d84UL , 0x32caab7b40c72493UL , + 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL , + 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL , + 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL +#else + ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd), + ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc), + ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019), + ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118), + ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe), + ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2), + ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1), + ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694), + ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3), + ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65), + ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483), + ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5), + ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210), + ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4), + ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725), + ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70), + ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926), + ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df), + ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8), + ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b), + ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001), + ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30), + ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910), + ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8), + ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53), + ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8), + ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb), + ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3), + ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60), + ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec), + ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9), + ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b), + ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207), + ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178), + ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6), + ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b), + ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493), + ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c), + ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a), + ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817) +#endif +}; + +struct SHA512ContextStr { + union { + PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */ + PRUint32 l[160]; + PRUint8 b[640]; + } u; + PRUint64 h[8]; /* 8 state variables */ + PRUint64 sizeLo; /* 64-bit count of hashed bytes. */ +}; + +/* =========== SHA512 implementation ===================================== */ + +/* SHA-512 initial hash values */ +static const PRUint64 H512[8] = { +#if PR_BYTES_PER_LONG == 8 + 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL , + 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL , + 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL , + 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL +#else + ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b), + ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1), + ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f), + ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179) +#endif +}; + + +SHA512Context * +SHA512_NewContext(void) +{ + SHA512Context *ctx = PORT_New(SHA512Context); + return ctx; +} + +void +SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA512_Begin(SHA512Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H512, sizeof H512); +} + +#if defined(SHA512_TRACE) +#if defined(HAVE_LONG_LONG) +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \ + n, #e, d, #a, h); +#else +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \ + n, #e, d.hi, d.lo, #a, h.hi, h.lo); +#endif +#else +#define DUMP(n,a,d,e,h) +#endif + +#if defined(HAVE_LONG_LONG) + +#define ADDTO(x,y) y += x + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); \ + DUMP(n,a,d,e,h) + +#else /* use only 32-bit variables, and don't unroll loops */ + +#undef NOUNROLL512 +#define NOUNROLL512 1 + +#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo) + +#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) +#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32)) +#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) + +/* Capitol Sigma and lower case sigma functions */ +#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi)) +#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7)) + +#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi)) +#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6)) + +#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi)) +#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo)) + +#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi)) +#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo)) + +/* 32-bit versions of Ch and Maj */ +#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo)) +#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo)) + +#define INITW(t) \ + do { \ + PRUint32 lo, tm; \ + PRUint32 cy = 0; \ + lo = s1lo(W[t-2]); \ + lo += (tm = W[t-7].lo); if (lo < tm) cy++; \ + lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \ + lo += (tm = W[t-16].lo); if (lo < tm) cy++; \ + W[t].lo = lo; \ + W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \ + } while (0) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + { \ + PRUint32 lo, tm, cy; \ + lo = S1lo(e); \ + lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \ + lo += (tm = K512[n].lo); if (lo < tm) cy++; \ + lo += (tm = W[n].lo); if (lo < tm) cy++; \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \ + d.lo += h.lo; \ + d.hi += h.hi + (d.lo < h.lo); \ + lo = S0lo(a); \ + lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \ + DUMP(n,a,d,e,h) \ + } +#endif + +static void +SHA512_Compress(SHA512Context *ctx) +{ +#if defined(IS_LITTLE_ENDIAN) + { +#if defined(HAVE_LONG_LONG) + PRUint64 t1; +#else + PRUint32 t1; +#endif + BYTESWAP8(W[0]); + BYTESWAP8(W[1]); + BYTESWAP8(W[2]); + BYTESWAP8(W[3]); + BYTESWAP8(W[4]); + BYTESWAP8(W[5]); + BYTESWAP8(W[6]); + BYTESWAP8(W[7]); + BYTESWAP8(W[8]); + BYTESWAP8(W[9]); + BYTESWAP8(W[10]); + BYTESWAP8(W[11]); + BYTESWAP8(W[12]); + BYTESWAP8(W[13]); + BYTESWAP8(W[14]); + BYTESWAP8(W[15]); + } +#endif + + { + PRUint64 t1, t2; +#ifdef NOUNROLL512 + { + /* prepare the "message schedule" */ + int t; + for (t = 16; t < 80; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + INITW(64); + INITW(65); + INITW(66); + INITW(67); + INITW(68); + INITW(69); + + INITW(70); + INITW(71); + INITW(72); + INITW(73); + INITW(74); + INITW(75); + INITW(76); + INITW(77); + INITW(78); + INITW(79); +#endif + } +#ifdef SHA512_TRACE + { + int i; + for (i = 0; i < 80; ++i) { +#ifdef HAVE_LONG_LONG + printf("W[%2d] = %016lx\n", i, W[i]); +#else + printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo); +#endif + } + } +#endif + { + PRUint64 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#ifdef NOUNROLL512 + { + int t; + for (t = 0; t < 80; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) + + ROUND(64,a,b,c,d,e,f,g,h) + ROUND(65,h,a,b,c,d,e,f,g) + ROUND(66,g,h,a,b,c,d,e,f) + ROUND(67,f,g,h,a,b,c,d,e) + ROUND(68,e,f,g,h,a,b,c,d) + ROUND(69,d,e,f,g,h,a,b,c) + ROUND(70,c,d,e,f,g,h,a,b) + ROUND(71,b,c,d,e,f,g,h,a) + + ROUND(72,a,b,c,d,e,f,g,h) + ROUND(73,h,a,b,c,d,e,f,g) + ROUND(74,g,h,a,b,c,d,e,f) + ROUND(75,f,g,h,a,b,c,d,e) + ROUND(76,e,f,g,h,a,b,c,d) + ROUND(77,d,e,f,g,h,a,b,c) + ROUND(78,c,d,e,f,g,h,a,b) + ROUND(79,b,c,d,e,f,g,h,a) +#endif + + ADDTO(a,H[0]); + ADDTO(b,H[1]); + ADDTO(c,H[2]); + ADDTO(d,H[3]); + ADDTO(e,H[4]); + ADDTO(f,H[5]); + ADDTO(g,H[6]); + ADDTO(h,H[7]); + } +} + +void +SHA512_Update(SHA512Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf; + if (!inputLen) + return; + +#if defined(HAVE_LONG_LONG) + inBuf = (unsigned int)ctx->sizeLo & 0x7f; + /* Add inputLen into the count of bytes processed, before processing */ + ctx->sizeLo += inputLen; +#else + inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + ctx->sizeLo.lo += inputLen; + if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++; +#endif + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA512_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA512_BLOCK_LENGTH) + SHA512_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA512_BLOCK_LENGTH) { + memcpy(B, input, SHA512_BLOCK_LENGTH); + input += SHA512_BLOCK_LENGTH; + inputLen -= SHA512_BLOCK_LENGTH; + SHA512_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA512_End(SHA512Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#if defined(HAVE_LONG_LONG) + unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo, t1; + lo = (ctx->sizeLo << 3); +#else + unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo = ctx->sizeLo; + PRUint32 t1; + lo.lo <<= 3; +#endif + + SHA512_Update(ctx, pad, padLen); + +#if defined(HAVE_LONG_LONG) + W[14] = 0; +#else + W[14].lo = 0; + W[14].hi = 0; +#endif + + W[15] = lo; +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(W[15]); +#endif + SHA512_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(H[0]); + BYTESWAP8(H[1]); + BYTESWAP8(H[2]); + BYTESWAP8(H[3]); + BYTESWAP8(H[4]); + BYTESWAP8(H[5]); + BYTESWAP8(H[6]); + BYTESWAP8(H[7]); +#endif + padLen = PR_MIN(SHA512_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +SECStatus +SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA512_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA512_Hash(unsigned char *dest, const char *src) +{ + return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA512_TraceState(SHA512Context *ctx) { } + +unsigned int +SHA512_FlattenSize(SHA512Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA512_Flatten(SHA512Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA512Context * +SHA512_Resurrect(unsigned char *space, void *arg) +{ + SHA512Context *ctx = SHA512_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA512_Clone(SHA512Context *dest, SHA512Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + +/* ======================================================================= */ +/* SHA384 uses a SHA512Context as the real context. +** The only differences between SHA384 an SHA512 are: +** a) the intialization values for the context, and +** b) the number of bytes of data produced as output. +*/ + +/* SHA-384 initial hash values */ +static const PRUint64 H384[8] = { +#if PR_BYTES_PER_LONG == 8 + 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL , + 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL , + 0x67332667ffc00b31UL , 0x8eb44a8768581511UL , + 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL +#else + ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507), + ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939), + ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511), + ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4) +#endif +}; + +SHA384Context * +SHA384_NewContext(void) +{ + return SHA512_NewContext(); +} + +void +SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit) +{ + SHA512_DestroyContext(ctx, freeit); +} + +void +SHA384_Begin(SHA384Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H384, sizeof H384); +} + +void +SHA384_Update(SHA384Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + SHA512_Update(ctx, input, inputLen); +} + +void +SHA384_End(SHA384Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#define SHA_MIN(a,b) (a < b ? a : b) + unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH); + SHA512_End(ctx, digest, digestLen, maxLen); +} + +SECStatus +SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + unsigned int src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA384_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH); + + return SECSuccess; +} + +SECStatus +SHA384_Hash(unsigned char *dest, const char *src) +{ + return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + +void SHA384_TraceState(SHA384Context *ctx) { } + +unsigned int +SHA384_FlattenSize(SHA384Context *ctx) +{ + return sizeof(SHA384Context); +} + +SECStatus +SHA384_Flatten(SHA384Context *ctx,unsigned char *space) +{ + return SHA512_Flatten(ctx, space); +} + +SHA384Context * +SHA384_Resurrect(unsigned char *space, void *arg) +{ + return SHA512_Resurrect(space, arg); +} + +void SHA384_Clone(SHA384Context *dest, SHA384Context *src) +{ + memcpy(dest, src, sizeof *dest); +} +#endif /* Comment out unused code. */ + +/* ======================================================================= */ +#ifdef SELFTEST +#include + +static const char abc[] = { "abc" }; +static const char abcdbc[] = { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +}; +static const char abcdef[] = { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" +}; + +void +dumpHash32(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 4) { + printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]); + } + printf("\n"); +} + +void test256(void) +{ + unsigned char outBuf[SHA256_LENGTH]; + + printf("SHA256, input = %s\n", abc); + SHA256_Hash(outBuf, abc); + dumpHash32(outBuf, sizeof outBuf); + + printf("SHA256, input = %s\n", abcdbc); + SHA256_Hash(outBuf, abcdbc); + dumpHash32(outBuf, sizeof outBuf); +} + +void +dumpHash64(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 8) { + if (i % 32 == 0) + printf("\n"); + printf(" %02x%02x%02x%02x%02x%02x%02x%02x", + buf[i ], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7]); + } + printf("\n"); +} + +void test512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + printf("SHA512, input = %s\n", abc); + SHA512_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA512, input = %s\n", abcdef); + SHA512_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +void time512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + SHA512_Hash(outBuf, abc); + SHA512_Hash(outBuf, abcdef); +} + +void test384(void) +{ + unsigned char outBuf[SHA384_LENGTH]; + + printf("SHA384, input = %s\n", abc); + SHA384_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA384, input = %s\n", abcdef); + SHA384_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +int main (int argc, char *argv[], char *envp[]) +{ + int i = 1; + if (argc > 1) { + i = atoi(argv[1]); + } + if (i < 2) { + test256(); + test512(); + test384(); + } else { + while (i-- > 0) { + time512(); + } + printf("done\n"); + } + return 0; +} + +#endif -- cgit v1.2.1