| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "components/webcrypto/openssl/util_openssl.h" | |
| 6 | |
| 7 #include <openssl/evp.h> | |
| 8 #include <openssl/pkcs12.h> | |
| 9 #include <openssl/rand.h> | |
| 10 | |
| 11 #include "base/stl_util.h" | |
| 12 #include "components/webcrypto/crypto_data.h" | |
| 13 #include "components/webcrypto/generate_key_result.h" | |
| 14 #include "components/webcrypto/openssl/key_openssl.h" | |
| 15 #include "components/webcrypto/platform_crypto.h" | |
| 16 #include "components/webcrypto/status.h" | |
| 17 #include "components/webcrypto/webcrypto_util.h" | |
| 18 #include "crypto/openssl_util.h" | |
| 19 | |
| 20 namespace webcrypto { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // Exports an EVP_PKEY public key to the SPKI format. | |
| 25 Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) { | |
| 26 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 27 crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); | |
| 28 | |
| 29 // TODO(eroman): Use the OID specified by webcrypto spec. | |
| 30 // http://crbug.com/373545 | |
| 31 if (!i2d_PUBKEY_bio(bio.get(), key)) | |
| 32 return Status::ErrorUnexpected(); | |
| 33 | |
| 34 char* data = NULL; | |
| 35 long len = BIO_get_mem_data(bio.get(), &data); | |
| 36 if (!data || len < 0) | |
| 37 return Status::ErrorUnexpected(); | |
| 38 | |
| 39 buffer->assign(data, data + len); | |
| 40 return Status::Success(); | |
| 41 } | |
| 42 | |
| 43 // Exports an EVP_PKEY private key to the PKCS8 format. | |
| 44 Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) { | |
| 45 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 46 crypto::ScopedBIO bio(BIO_new(BIO_s_mem())); | |
| 47 | |
| 48 // TODO(eroman): Use the OID specified by webcrypto spec. | |
| 49 // http://crbug.com/373545 | |
| 50 if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key)) | |
| 51 return Status::ErrorUnexpected(); | |
| 52 | |
| 53 char* data = NULL; | |
| 54 long len = BIO_get_mem_data(bio.get(), &data); | |
| 55 if (!data || len < 0) | |
| 56 return Status::ErrorUnexpected(); | |
| 57 | |
| 58 buffer->assign(data, data + len); | |
| 59 return Status::Success(); | |
| 60 } | |
| 61 | |
| 62 } // namespace | |
| 63 | |
| 64 void PlatformInit() { | |
| 65 crypto::EnsureOpenSSLInit(); | |
| 66 } | |
| 67 | |
| 68 const EVP_MD* GetDigest(blink::WebCryptoAlgorithmId id) { | |
| 69 switch (id) { | |
| 70 case blink::WebCryptoAlgorithmIdSha1: | |
| 71 return EVP_sha1(); | |
| 72 case blink::WebCryptoAlgorithmIdSha256: | |
| 73 return EVP_sha256(); | |
| 74 case blink::WebCryptoAlgorithmIdSha384: | |
| 75 return EVP_sha384(); | |
| 76 case blink::WebCryptoAlgorithmIdSha512: | |
| 77 return EVP_sha512(); | |
| 78 default: | |
| 79 return NULL; | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 Status AeadEncryptDecrypt(EncryptOrDecrypt mode, | |
| 84 const std::vector<uint8_t>& raw_key, | |
| 85 const CryptoData& data, | |
| 86 unsigned int tag_length_bytes, | |
| 87 const CryptoData& iv, | |
| 88 const CryptoData& additional_data, | |
| 89 const EVP_AEAD* aead_alg, | |
| 90 std::vector<uint8_t>* buffer) { | |
| 91 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 92 EVP_AEAD_CTX ctx; | |
| 93 | |
| 94 if (!aead_alg) | |
| 95 return Status::ErrorUnexpected(); | |
| 96 | |
| 97 if (!EVP_AEAD_CTX_init(&ctx, aead_alg, vector_as_array(&raw_key), | |
| 98 raw_key.size(), tag_length_bytes, NULL)) { | |
| 99 return Status::OperationError(); | |
| 100 } | |
| 101 | |
| 102 crypto::ScopedOpenSSL<EVP_AEAD_CTX, EVP_AEAD_CTX_cleanup> ctx_cleanup(&ctx); | |
| 103 | |
| 104 size_t len; | |
| 105 int ok; | |
| 106 | |
| 107 if (mode == DECRYPT) { | |
| 108 if (data.byte_length() < tag_length_bytes) | |
| 109 return Status::ErrorDataTooSmall(); | |
| 110 | |
| 111 buffer->resize(data.byte_length() - tag_length_bytes); | |
| 112 | |
| 113 ok = EVP_AEAD_CTX_open(&ctx, vector_as_array(buffer), &len, buffer->size(), | |
| 114 iv.bytes(), iv.byte_length(), data.bytes(), | |
| 115 data.byte_length(), additional_data.bytes(), | |
| 116 additional_data.byte_length()); | |
| 117 } else { | |
| 118 // No need to check for unsigned integer overflow here (seal fails if | |
| 119 // the output buffer is too small). | |
| 120 buffer->resize(data.byte_length() + EVP_AEAD_max_overhead(aead_alg)); | |
| 121 | |
| 122 ok = EVP_AEAD_CTX_seal(&ctx, vector_as_array(buffer), &len, buffer->size(), | |
| 123 iv.bytes(), iv.byte_length(), data.bytes(), | |
| 124 data.byte_length(), additional_data.bytes(), | |
| 125 additional_data.byte_length()); | |
| 126 } | |
| 127 | |
| 128 if (!ok) | |
| 129 return Status::OperationError(); | |
| 130 buffer->resize(len); | |
| 131 return Status::Success(); | |
| 132 } | |
| 133 | |
| 134 Status GenerateWebCryptoSecretKey(const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 135 bool extractable, | |
| 136 blink::WebCryptoKeyUsageMask usages, | |
| 137 unsigned int keylen_bits, | |
| 138 GenerateKeyResult* result) { | |
| 139 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 140 | |
| 141 unsigned int keylen_bytes = NumBitsToBytes(keylen_bits); | |
| 142 std::vector<unsigned char> random_bytes(keylen_bytes, 0); | |
| 143 | |
| 144 if (keylen_bytes > 0) { | |
| 145 if (!(RAND_bytes(&random_bytes[0], keylen_bytes))) | |
| 146 return Status::OperationError(); | |
| 147 TruncateToBitLength(keylen_bits, &random_bytes); | |
| 148 } | |
| 149 | |
| 150 result->AssignSecretKey(blink::WebCryptoKey::create( | |
| 151 new SymKeyOpenSsl(CryptoData(random_bytes)), | |
| 152 blink::WebCryptoKeyTypeSecret, extractable, algorithm, usages)); | |
| 153 | |
| 154 return Status::Success(); | |
| 155 } | |
| 156 | |
| 157 Status CreateWebCryptoSecretKey(const CryptoData& key_data, | |
| 158 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 159 bool extractable, | |
| 160 blink::WebCryptoKeyUsageMask usages, | |
| 161 blink::WebCryptoKey* key) { | |
| 162 *key = blink::WebCryptoKey::create(new SymKeyOpenSsl(key_data), | |
| 163 blink::WebCryptoKeyTypeSecret, extractable, | |
| 164 algorithm, usages); | |
| 165 return Status::Success(); | |
| 166 } | |
| 167 | |
| 168 Status CreateWebCryptoPublicKey(crypto::ScopedEVP_PKEY public_key, | |
| 169 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 170 bool extractable, | |
| 171 blink::WebCryptoKeyUsageMask usages, | |
| 172 blink::WebCryptoKey* key) { | |
| 173 // Serialize the key at creation time so that if structured cloning is | |
| 174 // requested it can be done synchronously from the Blink thread. | |
| 175 std::vector<uint8_t> spki_data; | |
| 176 Status status = ExportPKeySpki(public_key.get(), &spki_data); | |
| 177 if (status.IsError()) | |
| 178 return status; | |
| 179 | |
| 180 *key = blink::WebCryptoKey::create( | |
| 181 new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)), | |
| 182 blink::WebCryptoKeyTypePublic, extractable, algorithm, usages); | |
| 183 return Status::Success(); | |
| 184 } | |
| 185 | |
| 186 Status CreateWebCryptoPrivateKey(crypto::ScopedEVP_PKEY private_key, | |
| 187 const blink::WebCryptoKeyAlgorithm& algorithm, | |
| 188 bool extractable, | |
| 189 blink::WebCryptoKeyUsageMask usages, | |
| 190 blink::WebCryptoKey* key) { | |
| 191 // Serialize the key at creation time so that if structured cloning is | |
| 192 // requested it can be done synchronously from the Blink thread. | |
| 193 std::vector<uint8_t> pkcs8_data; | |
| 194 Status status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data); | |
| 195 if (status.IsError()) | |
| 196 return status; | |
| 197 | |
| 198 *key = blink::WebCryptoKey::create( | |
| 199 new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)), | |
| 200 blink::WebCryptoKeyTypePrivate, extractable, algorithm, usages); | |
| 201 return Status::Success(); | |
| 202 } | |
| 203 | |
| 204 Status ImportUnverifiedPkeyFromSpki(const CryptoData& key_data, | |
| 205 int expected_pkey_id, | |
| 206 crypto::ScopedEVP_PKEY* pkey) { | |
| 207 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 208 | |
| 209 const uint8_t* ptr = key_data.bytes(); | |
| 210 pkey->reset(d2i_PUBKEY(nullptr, &ptr, key_data.byte_length())); | |
| 211 if (!pkey->get() || ptr != key_data.bytes() + key_data.byte_length()) | |
| 212 return Status::DataError(); | |
| 213 | |
| 214 if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) | |
| 215 return Status::DataError(); // Data did not define expected key type. | |
| 216 | |
| 217 return Status::Success(); | |
| 218 } | |
| 219 | |
| 220 Status ImportUnverifiedPkeyFromPkcs8(const CryptoData& key_data, | |
| 221 int expected_pkey_id, | |
| 222 crypto::ScopedEVP_PKEY* pkey) { | |
| 223 crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); | |
| 224 | |
| 225 const uint8_t* ptr = key_data.bytes(); | |
| 226 crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free> p8inf( | |
| 227 d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, key_data.byte_length())); | |
| 228 if (!p8inf.get() || ptr != key_data.bytes() + key_data.byte_length()) | |
| 229 return Status::DataError(); | |
| 230 | |
| 231 pkey->reset(EVP_PKCS82PKEY(p8inf.get())); | |
| 232 if (!pkey->get()) | |
| 233 return Status::DataError(); | |
| 234 | |
| 235 if (EVP_PKEY_id(pkey->get()) != expected_pkey_id) | |
| 236 return Status::DataError(); // Data did not define expected key type. | |
| 237 | |
| 238 return Status::Success(); | |
| 239 } | |
| 240 | |
| 241 BIGNUM* CreateBIGNUM(const std::string& n) { | |
| 242 return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL); | |
| 243 } | |
| 244 | |
| 245 std::vector<uint8_t> BIGNUMToVector(const BIGNUM* n) { | |
| 246 std::vector<uint8_t> v(BN_num_bytes(n)); | |
| 247 BN_bn2bin(n, vector_as_array(&v)); | |
| 248 return v; | |
| 249 } | |
| 250 | |
| 251 } // namespace webcrypto | |
| OLD | NEW |