summaryrefslogtreecommitdiffstats
path: root/crypto/symmetric_key_mac.cc
blob: 47193a0883311a9ee376721d8fee084734db8734 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// 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 <CommonCrypto/CommonCryptor.h>
#include <CoreFoundation/CFString.h>
#include <Security/cssm.h>

#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;
  }
}

void* 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<uint8_t*>(const_cast<char*>(str.data()))
  };
  return data;
}

}  // namespace

namespace crypto {

SymmetricKey::~SymmetricKey() {}

// static
SymmetricKey* SymmetricKey::GenerateRandomKey(Algorithm algorithm,
                                              size_t key_size_in_bits) {
  CheckKeyParams(algorithm, key_size_in_bits);
  void* random_bytes = CreateRandomBytes((key_size_in_bits + 7) / 8);
  if (!random_bytes)
    return NULL;
  SymmetricKey *key = new SymmetricKey(random_bytes, key_size_in_bits);
  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<uint8_t*>(&params)};
  err = CSSM_DeriveKey(ctx,
                       &param_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<const char*>(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