// 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 "crypto/cssm_init.h" namespace { CSSM_KEY_TYPE CheckKeyParams(crypto::SymmetricKey::Algorithm algorithm, size_t key_size_in_bits) { if (algorithm == crypto::SymmetricKey::AES) { CHECK(key_size_in_bits == 128 || key_size_in_bits == 192 || key_size_in_bits == 256) << "Invalid key size " << key_size_in_bits << " bits"; return CSSM_ALGID_AES; } else { // FIPS 198 Section 3 requires a HMAC-SHA-1 derived keys to be at least // (HMAC-SHA-1 output size / 2) to be compliant. Since the ouput size of // HMAC-SHA-1 is 160 bits, we require at least 80 bits here. CHECK(algorithm == crypto::SymmetricKey::HMAC_SHA1); CHECK(key_size_in_bits >= 80 && (key_size_in_bits % 8) == 0) << "Invalid key size " << key_size_in_bits << " bits"; return CSSM_ALGID_SHA1HMAC_LEGACY; } } uint8_t* CreateRandomBytes(size_t size) { CSSM_RETURN err; CSSM_CC_HANDLE ctx; err = CSSM_CSP_CreateRandomGenContext(crypto::GetSharedCSPHandle(), CSSM_ALGID_APPLE_YARROW, NULL, size, &ctx); if (err) { crypto::LogCSSMError("CSSM_CSP_CreateRandomGenContext", err); return NULL; } CSSM_DATA random_data = {}; err = CSSM_GenerateRandom(ctx, &random_data); if (err) { crypto::LogCSSMError("CSSM_GenerateRandom", err); random_data.Data = NULL; } CSSM_DeleteContext(ctx); return random_data.Data; // Caller responsible for freeing this. } inline CSSM_DATA StringToData(const std::string& str) { CSSM_DATA data = { str.size(), reinterpret_cast(const_cast(str.data())) }; return data; } } // namespace namespace crypto { SymmetricKey::~SymmetricKey() { std::fill(key_.begin(), key_.end(), 0); } // static SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm, size_t key_size_in_bits) { CheckKeyParams(algorithm, key_size_in_bits); size_t key_size_in_bytes = (key_size_in_bits + 7) / 8; uint8_t* random_bytes = CreateRandomBytes(key_size_in_bytes); if (!random_bytes) return NULL; SymmetricKey *key = new SymmetricKey(random_bytes, key_size_in_bits); std::fill(random_bytes, random_bytes + key_size_in_bytes, 0); free(random_bytes); return key; } // static SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm, const std::string& password, const std::string& salt, size_t iterations, size_t key_size_in_bits) { // Derived (haha) from cdsaDeriveKey() in Apple's CryptoSample. CSSM_KEY_TYPE key_type = CheckKeyParams(algorithm, key_size_in_bits); SymmetricKey* derived_key = NULL; CSSM_KEY cssm_key = {}; CSSM_CC_HANDLE ctx = 0; CSSM_ACCESS_CREDENTIALS credentials = {}; CSSM_RETURN err; CSSM_DATA salt_data = StringToData(salt); err = CSSM_CSP_CreateDeriveKeyContext(GetSharedCSPHandle(), CSSM_ALGID_PKCS5_PBKDF2, key_type, key_size_in_bits, &credentials, NULL, iterations, &salt_data, NULL, &ctx); if (err) { LogCSSMError("CSSM_CSP_CreateDeriveKeyContext", err); return NULL; } CSSM_PKCS5_PBKDF2_PARAMS params = {}; params.Passphrase = StringToData(password); params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1; CSSM_DATA param_data = {sizeof(params), reinterpret_cast(¶ms)}; err = CSSM_DeriveKey(ctx, ¶m_data, CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, NULL, NULL, &cssm_key); if (err) { LogCSSMError("CSSM_DeriveKey", err); goto exit; } DCHECK_EQ(cssm_key.KeyData.Length, key_size_in_bits / 8); derived_key = new SymmetricKey(cssm_key.KeyData.Data, key_size_in_bits); exit: CSSM_DeleteContext(ctx); CSSM_FreeKey(GetSharedCSPHandle(), &credentials, &cssm_key, false); return derived_key; } // static SymmetricKey* SymmetricKey::Import(Algorithm algorithm, const std::string& raw_key) { return new SymmetricKey(raw_key.data(), raw_key.size() * 8); } SymmetricKey::SymmetricKey(const void* key_data, size_t key_size_in_bits) : key_(reinterpret_cast(key_data), key_size_in_bits / 8) { } bool SymmetricKey::GetRawKey(std::string* raw_key) { *raw_key = key_; return true; } CSSM_DATA SymmetricKey::cssm_data() const { return StringToData(key_); } } // namespace crypto