1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crypto/symmetric_key.h"
6
7#include <CommonCrypto/CommonCryptor.h>
8#include <CoreFoundation/CFString.h>
9#include <Security/cssm.h>
10
11#include "base/logging.h"
12#include "crypto/cssm_init.h"
13
14namespace {
15
16CSSM_KEY_TYPE CheckKeyParams(crypto::SymmetricKey::Algorithm algorithm,
17                             size_t key_size_in_bits) {
18  if (algorithm == crypto::SymmetricKey::AES) {
19    CHECK(key_size_in_bits == 128 ||
20        key_size_in_bits == 192 ||
21        key_size_in_bits == 256)
22        << "Invalid key size " << key_size_in_bits << " bits";
23    return CSSM_ALGID_AES;
24  } else {
25    // FIPS 198 Section 3 requires a HMAC-SHA-1 derived keys to be at least
26    // (HMAC-SHA-1 output size / 2) to be compliant. Since the ouput size of
27    // HMAC-SHA-1 is 160 bits, we require at least 80 bits here.
28    CHECK(algorithm == crypto::SymmetricKey::HMAC_SHA1);
29    CHECK(key_size_in_bits >= 80 && (key_size_in_bits % 8) == 0)
30        << "Invalid key size " << key_size_in_bits << " bits";
31    return CSSM_ALGID_SHA1HMAC_LEGACY;
32  }
33}
34
35void* CreateRandomBytes(size_t size) {
36  CSSM_RETURN err;
37  CSSM_CC_HANDLE ctx;
38  err = CSSM_CSP_CreateRandomGenContext(crypto::GetSharedCSPHandle(),
39                                        CSSM_ALGID_APPLE_YARROW,
40                                        NULL,
41                                        size, &ctx);
42  if (err) {
43    crypto::LogCSSMError("CSSM_CSP_CreateRandomGenContext", err);
44    return NULL;
45  }
46  CSSM_DATA random_data = {};
47  err = CSSM_GenerateRandom(ctx, &random_data);
48  if (err) {
49    crypto::LogCSSMError("CSSM_GenerateRandom", err);
50    random_data.Data = NULL;
51  }
52  CSSM_DeleteContext(ctx);
53  return random_data.Data;  // Caller responsible for freeing this
54}
55
56inline CSSM_DATA StringToData(const std::string& str) {
57  CSSM_DATA data = {
58    str.size(),
59    reinterpret_cast<uint8_t*>(const_cast<char*>(str.data()))
60  };
61  return data;
62}
63
64}  // namespace
65
66namespace crypto {
67
68SymmetricKey::~SymmetricKey() {}
69
70// static
71SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
72                                              size_t key_size_in_bits) {
73  CheckKeyParams(algorithm, key_size_in_bits);
74  void* random_bytes = CreateRandomBytes((key_size_in_bits + 7) / 8);
75  if (!random_bytes)
76    return NULL;
77  SymmetricKey *key = new SymmetricKey(random_bytes, key_size_in_bits);
78  free(random_bytes);
79  return key;
80}
81
82// static
83SymmetricKey* SymmetricKey::DeriveKeyFromPassword(Algorithm algorithm,
84                                                  const std::string& password,
85                                                  const std::string& salt,
86                                                  size_t iterations,
87                                                  size_t key_size_in_bits) {
88  // Derived (haha) from cdsaDeriveKey() in Apple's CryptoSample.
89  CSSM_KEY_TYPE key_type = CheckKeyParams(algorithm, key_size_in_bits);
90  SymmetricKey* derived_key = NULL;
91  CSSM_KEY cssm_key = {};
92
93  CSSM_CC_HANDLE ctx = 0;
94  CSSM_ACCESS_CREDENTIALS credentials = {};
95  CSSM_RETURN err;
96  CSSM_DATA salt_data = StringToData(salt);
97  err = CSSM_CSP_CreateDeriveKeyContext(GetSharedCSPHandle(),
98                                        CSSM_ALGID_PKCS5_PBKDF2,
99                                        key_type, key_size_in_bits,
100                                        &credentials,
101                                        NULL,
102                                        iterations,
103                                        &salt_data,
104                                        NULL,
105                                        &ctx);
106  if (err) {
107    LogCSSMError("CSSM_CSP_CreateDeriveKeyContext", err);
108    return NULL;
109  }
110
111  CSSM_PKCS5_PBKDF2_PARAMS params = {};
112  params.Passphrase = StringToData(password);
113  params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
114  CSSM_DATA param_data = {sizeof(params), reinterpret_cast<uint8_t*>(&params)};
115  err = CSSM_DeriveKey(ctx,
116                       &param_data,
117                       CSSM_KEYUSE_ANY,
118                       CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
119                       NULL,
120                       NULL,
121                       &cssm_key);
122  if (err) {
123    LogCSSMError("CSSM_DeriveKey", err);
124    goto exit;
125  }
126
127  DCHECK_EQ(cssm_key.KeyData.Length, key_size_in_bits / 8);
128  derived_key = new SymmetricKey(cssm_key.KeyData.Data, key_size_in_bits);
129
130 exit:
131  CSSM_DeleteContext(ctx);
132  CSSM_FreeKey(GetSharedCSPHandle(), &credentials, &cssm_key, false);
133  return derived_key;
134}
135
136// static
137SymmetricKey* SymmetricKey::Import(Algorithm algorithm,
138                                   const std::string& raw_key) {
139  return new SymmetricKey(raw_key.data(), raw_key.size() * 8);
140}
141
142SymmetricKey::SymmetricKey(const void *key_data, size_t key_size_in_bits)
143  : key_(reinterpret_cast<const char*>(key_data),
144         key_size_in_bits / 8) {}
145
146bool SymmetricKey::GetRawKey(std::string* raw_key) {
147  *raw_key = key_;
148  return true;
149}
150
151CSSM_DATA SymmetricKey::cssm_data() const {
152  return StringToData(key_);
153}
154
155}  // namespace crypto
156