Home | History | Annotate | Download | only in openssl
      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 "content/child/webcrypto/openssl/rsa_key_openssl.h"
      6 
      7 #include <openssl/evp.h>
      8 #include <openssl/pkcs12.h>
      9 
     10 #include "base/logging.h"
     11 #include "base/stl_util.h"
     12 #include "content/child/webcrypto/crypto_data.h"
     13 #include "content/child/webcrypto/jwk.h"
     14 #include "content/child/webcrypto/openssl/key_openssl.h"
     15 #include "content/child/webcrypto/status.h"
     16 #include "content/child/webcrypto/webcrypto_util.h"
     17 #include "crypto/openssl_util.h"
     18 #include "crypto/scoped_openssl_types.h"
     19 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
     20 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
     21 
     22 namespace content {
     23 
     24 namespace webcrypto {
     25 
     26 namespace {
     27 
     28 Status ExportPKeySpki(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
     29   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
     30   crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
     31 
     32   // TODO(eroman): Use the OID specified by webcrypto spec.
     33   //               http://crbug.com/373545
     34   if (!i2d_PUBKEY_bio(bio.get(), key))
     35     return Status::ErrorUnexpected();
     36 
     37   char* data = NULL;
     38   long len = BIO_get_mem_data(bio.get(), &data);
     39   if (!data || len < 0)
     40     return Status::ErrorUnexpected();
     41 
     42   buffer->assign(data, data + len);
     43   return Status::Success();
     44 }
     45 
     46 Status ExportPKeyPkcs8(EVP_PKEY* key, std::vector<uint8_t>* buffer) {
     47   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
     48   crypto::ScopedBIO bio(BIO_new(BIO_s_mem()));
     49 
     50   // TODO(eroman): Use the OID specified by webcrypto spec.
     51   //               http://crbug.com/373545
     52   if (!i2d_PKCS8PrivateKeyInfo_bio(bio.get(), key))
     53     return Status::ErrorUnexpected();
     54 
     55   char* data = NULL;
     56   long len = BIO_get_mem_data(bio.get(), &data);
     57   if (!data || len < 0)
     58     return Status::ErrorUnexpected();
     59 
     60   buffer->assign(data, data + len);
     61   return Status::Success();
     62 }
     63 
     64 // Creates a blink::WebCryptoAlgorithm having the modulus length and public
     65 // exponent  of |key|.
     66 Status CreateRsaHashedKeyAlgorithm(
     67     blink::WebCryptoAlgorithmId rsa_algorithm,
     68     blink::WebCryptoAlgorithmId hash_algorithm,
     69     EVP_PKEY* key,
     70     blink::WebCryptoKeyAlgorithm* key_algorithm) {
     71   DCHECK(IsAlgorithmRsa(rsa_algorithm));
     72   DCHECK_EQ(EVP_PKEY_RSA, EVP_PKEY_id(key));
     73 
     74   crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(key));
     75   if (!rsa.get())
     76     return Status::ErrorUnexpected();
     77 
     78   unsigned int modulus_length_bits = BN_num_bits(rsa.get()->n);
     79 
     80   // Convert the public exponent to big-endian representation.
     81   std::vector<uint8_t> e(BN_num_bytes(rsa.get()->e));
     82   if (e.size() == 0)
     83     return Status::ErrorUnexpected();
     84   if (e.size() != BN_bn2bin(rsa.get()->e, &e[0]))
     85     return Status::ErrorUnexpected();
     86 
     87   *key_algorithm = blink::WebCryptoKeyAlgorithm::createRsaHashed(
     88       rsa_algorithm, modulus_length_bits, &e[0], e.size(), hash_algorithm);
     89 
     90   return Status::Success();
     91 }
     92 
     93 Status CreateWebCryptoPrivateKey(
     94     crypto::ScopedEVP_PKEY private_key,
     95     const blink::WebCryptoAlgorithmId rsa_algorithm_id,
     96     const blink::WebCryptoAlgorithm& hash,
     97     bool extractable,
     98     blink::WebCryptoKeyUsageMask usage_mask,
     99     blink::WebCryptoKey* key) {
    100   blink::WebCryptoKeyAlgorithm key_algorithm;
    101   Status status = CreateRsaHashedKeyAlgorithm(
    102       rsa_algorithm_id, hash.id(), private_key.get(), &key_algorithm);
    103   if (status.IsError())
    104     return status;
    105 
    106   // Serialize the key at creation time so that if structured cloning is
    107   // requested it can be done synchronously from the Blink thread.
    108   std::vector<uint8_t> pkcs8_data;
    109   status = ExportPKeyPkcs8(private_key.get(), &pkcs8_data);
    110   if (status.IsError())
    111     return status;
    112 
    113   *key = blink::WebCryptoKey::create(
    114       new AsymKeyOpenSsl(private_key.Pass(), CryptoData(pkcs8_data)),
    115       blink::WebCryptoKeyTypePrivate,
    116       extractable,
    117       key_algorithm,
    118       usage_mask);
    119   return Status::Success();
    120 }
    121 
    122 Status CreateWebCryptoPublicKey(
    123     crypto::ScopedEVP_PKEY public_key,
    124     const blink::WebCryptoAlgorithmId rsa_algorithm_id,
    125     const blink::WebCryptoAlgorithm& hash,
    126     bool extractable,
    127     blink::WebCryptoKeyUsageMask usage_mask,
    128     blink::WebCryptoKey* key) {
    129   blink::WebCryptoKeyAlgorithm key_algorithm;
    130   Status status = CreateRsaHashedKeyAlgorithm(
    131       rsa_algorithm_id, hash.id(), public_key.get(), &key_algorithm);
    132   if (status.IsError())
    133     return status;
    134 
    135   // Serialize the key at creation time so that if structured cloning is
    136   // requested it can be done synchronously from the Blink thread.
    137   std::vector<uint8_t> spki_data;
    138   status = ExportPKeySpki(public_key.get(), &spki_data);
    139   if (status.IsError())
    140     return status;
    141 
    142   *key = blink::WebCryptoKey::create(
    143       new AsymKeyOpenSsl(public_key.Pass(), CryptoData(spki_data)),
    144       blink::WebCryptoKeyTypePublic,
    145       extractable,
    146       key_algorithm,
    147       usage_mask);
    148   return Status::Success();
    149 }
    150 
    151 // Converts a BIGNUM to a big endian byte array.
    152 std::vector<uint8_t> BIGNUMToVector(BIGNUM* n) {
    153   std::vector<uint8_t> v(BN_num_bytes(n));
    154   BN_bn2bin(n, vector_as_array(&v));
    155   return v;
    156 }
    157 
    158 // Allocates a new BIGNUM given a std::string big-endian representation.
    159 BIGNUM* CreateBIGNUM(const std::string& n) {
    160   return BN_bin2bn(reinterpret_cast<const uint8_t*>(n.data()), n.size(), NULL);
    161 }
    162 
    163 Status ImportRsaPrivateKey(const blink::WebCryptoAlgorithm& algorithm,
    164                            bool extractable,
    165                            blink::WebCryptoKeyUsageMask usage_mask,
    166                            const JwkRsaInfo& params,
    167                            blink::WebCryptoKey* key) {
    168   crypto::ScopedRSA rsa(RSA_new());
    169 
    170   rsa->n = CreateBIGNUM(params.n);
    171   rsa->e = CreateBIGNUM(params.e);
    172   rsa->d = CreateBIGNUM(params.d);
    173   rsa->p = CreateBIGNUM(params.p);
    174   rsa->q = CreateBIGNUM(params.q);
    175   rsa->dmp1 = CreateBIGNUM(params.dp);
    176   rsa->dmq1 = CreateBIGNUM(params.dq);
    177   rsa->iqmp = CreateBIGNUM(params.qi);
    178 
    179   if (!rsa->n || !rsa->e || !rsa->d || !rsa->p || !rsa->q || !rsa->dmp1 ||
    180       !rsa->dmq1 || !rsa->iqmp) {
    181     return Status::OperationError();
    182   }
    183 
    184   // TODO(eroman): This should really be a DataError, however for compatibility
    185   //               with NSS it is an OperationError.
    186   if (!RSA_check_key(rsa.get()))
    187     return Status::OperationError();
    188 
    189   // Create a corresponding EVP_PKEY.
    190   crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
    191   if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
    192     return Status::OperationError();
    193 
    194   return CreateWebCryptoPrivateKey(pkey.Pass(),
    195                                    algorithm.id(),
    196                                    algorithm.rsaHashedImportParams()->hash(),
    197                                    extractable,
    198                                    usage_mask,
    199                                    key);
    200 }
    201 
    202 Status ImportRsaPublicKey(const blink::WebCryptoAlgorithm& algorithm,
    203                           bool extractable,
    204                           blink::WebCryptoKeyUsageMask usage_mask,
    205                           const CryptoData& n,
    206                           const CryptoData& e,
    207                           blink::WebCryptoKey* key) {
    208   crypto::ScopedRSA rsa(RSA_new());
    209 
    210   rsa->n = BN_bin2bn(n.bytes(), n.byte_length(), NULL);
    211   rsa->e = BN_bin2bn(e.bytes(), e.byte_length(), NULL);
    212 
    213   if (!rsa->n || !rsa->e)
    214     return Status::OperationError();
    215 
    216   // Create a corresponding EVP_PKEY.
    217   crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
    218   if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
    219     return Status::OperationError();
    220 
    221   return CreateWebCryptoPublicKey(pkey.Pass(),
    222                                   algorithm.id(),
    223                                   algorithm.rsaHashedImportParams()->hash(),
    224                                   extractable,
    225                                   usage_mask,
    226                                   key);
    227 }
    228 
    229 }  // namespace
    230 
    231 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeGenerateKeyPair(
    232     blink::WebCryptoKeyUsageMask combined_usage_mask,
    233     blink::WebCryptoKeyUsageMask* public_usage_mask,
    234     blink::WebCryptoKeyUsageMask* private_usage_mask) const {
    235   Status status = CheckKeyCreationUsages(
    236       all_public_key_usages_ | all_private_key_usages_, combined_usage_mask);
    237   if (status.IsError())
    238     return status;
    239 
    240   *public_usage_mask = combined_usage_mask & all_public_key_usages_;
    241   *private_usage_mask = combined_usage_mask & all_private_key_usages_;
    242 
    243   return Status::Success();
    244 }
    245 
    246 Status RsaHashedAlgorithm::GenerateKeyPair(
    247     const blink::WebCryptoAlgorithm& algorithm,
    248     bool extractable,
    249     blink::WebCryptoKeyUsageMask public_usage_mask,
    250     blink::WebCryptoKeyUsageMask private_usage_mask,
    251     blink::WebCryptoKey* public_key,
    252     blink::WebCryptoKey* private_key) const {
    253   const blink::WebCryptoRsaHashedKeyGenParams* params =
    254       algorithm.rsaHashedKeyGenParams();
    255 
    256   unsigned int public_exponent = 0;
    257   unsigned int modulus_length_bits = 0;
    258   Status status =
    259       GetRsaKeyGenParameters(params, &public_exponent, &modulus_length_bits);
    260   if (status.IsError())
    261     return status;
    262 
    263   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
    264 
    265   // Generate an RSA key pair.
    266   crypto::ScopedRSA rsa_private_key(RSA_new());
    267   crypto::ScopedBIGNUM bn(BN_new());
    268   if (!rsa_private_key.get() || !bn.get() ||
    269       !BN_set_word(bn.get(), public_exponent)) {
    270     return Status::OperationError();
    271   }
    272 
    273   if (!RSA_generate_key_ex(
    274           rsa_private_key.get(), modulus_length_bits, bn.get(), NULL)) {
    275     return Status::OperationError();
    276   }
    277 
    278   // Construct an EVP_PKEY for the private key.
    279   crypto::ScopedEVP_PKEY private_pkey(EVP_PKEY_new());
    280   if (!private_pkey ||
    281       !EVP_PKEY_set1_RSA(private_pkey.get(), rsa_private_key.get())) {
    282     return Status::OperationError();
    283   }
    284 
    285   // Construct an EVP_PKEY for the public key.
    286   crypto::ScopedRSA rsa_public_key(RSAPublicKey_dup(rsa_private_key.get()));
    287   crypto::ScopedEVP_PKEY public_pkey(EVP_PKEY_new());
    288   if (!public_pkey ||
    289       !EVP_PKEY_set1_RSA(public_pkey.get(), rsa_public_key.get())) {
    290     return Status::OperationError();
    291   }
    292 
    293   // Note that extractable is unconditionally set to true. This is because per
    294   // the WebCrypto spec generated public keys are always public.
    295   status = CreateWebCryptoPublicKey(public_pkey.Pass(),
    296                                     algorithm.id(),
    297                                     params->hash(),
    298                                     true,
    299                                     public_usage_mask,
    300                                     public_key);
    301   if (status.IsError())
    302     return status;
    303 
    304   return CreateWebCryptoPrivateKey(private_pkey.Pass(),
    305                                    algorithm.id(),
    306                                    params->hash(),
    307                                    extractable,
    308                                    private_usage_mask,
    309                                    private_key);
    310 }
    311 
    312 Status RsaHashedAlgorithm::VerifyKeyUsagesBeforeImportKey(
    313     blink::WebCryptoKeyFormat format,
    314     blink::WebCryptoKeyUsageMask usage_mask) const {
    315   switch (format) {
    316     case blink::WebCryptoKeyFormatSpki:
    317       return CheckKeyCreationUsages(all_public_key_usages_, usage_mask);
    318     case blink::WebCryptoKeyFormatPkcs8:
    319       return CheckKeyCreationUsages(all_private_key_usages_, usage_mask);
    320     case blink::WebCryptoKeyFormatJwk:
    321       // TODO(eroman): http://crbug.com/395904
    322       return CheckKeyCreationUsages(
    323           all_public_key_usages_ | all_private_key_usages_, usage_mask);
    324     default:
    325       return Status::ErrorUnsupportedImportKeyFormat();
    326   }
    327 }
    328 
    329 Status RsaHashedAlgorithm::ImportKeyPkcs8(
    330     const CryptoData& key_data,
    331     const blink::WebCryptoAlgorithm& algorithm,
    332     bool extractable,
    333     blink::WebCryptoKeyUsageMask usage_mask,
    334     blink::WebCryptoKey* key) const {
    335   if (!key_data.byte_length())
    336     return Status::ErrorImportEmptyKeyData();
    337 
    338   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
    339 
    340   crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()),
    341                                         key_data.byte_length()));
    342   if (!bio.get())
    343     return Status::ErrorUnexpected();
    344 
    345   crypto::ScopedOpenSSL<PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free>::Type
    346       p8inf(d2i_PKCS8_PRIV_KEY_INFO_bio(bio.get(), NULL));
    347   if (!p8inf.get())
    348     return Status::DataError();
    349 
    350   crypto::ScopedEVP_PKEY private_key(EVP_PKCS82PKEY(p8inf.get()));
    351   if (!private_key.get())
    352     return Status::DataError();
    353 
    354   if (EVP_PKEY_id(private_key.get()) != EVP_PKEY_RSA)
    355     return Status::DataError();  // Data did not define an RSA key.
    356 
    357   // Verify the parameters of the key (because EVP_PKCS82PKEY() happily imports
    358   // invalid keys).
    359   crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(private_key.get()));
    360   if (!rsa.get())
    361     return Status::ErrorUnexpected();
    362   if (!RSA_check_key(rsa.get()))
    363     return Status::DataError();
    364 
    365   // TODO(eroman): Validate the algorithm OID against the webcrypto provided
    366   // hash. http://crbug.com/389400
    367 
    368   return CreateWebCryptoPrivateKey(private_key.Pass(),
    369                                    algorithm.id(),
    370                                    algorithm.rsaHashedImportParams()->hash(),
    371                                    extractable,
    372                                    usage_mask,
    373                                    key);
    374 }
    375 
    376 Status RsaHashedAlgorithm::ImportKeySpki(
    377     const CryptoData& key_data,
    378     const blink::WebCryptoAlgorithm& algorithm,
    379     bool extractable,
    380     blink::WebCryptoKeyUsageMask usage_mask,
    381     blink::WebCryptoKey* key) const {
    382   if (!key_data.byte_length())
    383     return Status::ErrorImportEmptyKeyData();
    384 
    385   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
    386 
    387   crypto::ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(key_data.bytes()),
    388                                         key_data.byte_length()));
    389   if (!bio.get())
    390     return Status::ErrorUnexpected();
    391 
    392   crypto::ScopedEVP_PKEY public_key(d2i_PUBKEY_bio(bio.get(), NULL));
    393   if (!public_key.get())
    394     return Status::DataError();
    395 
    396   if (EVP_PKEY_id(public_key.get()) != EVP_PKEY_RSA)
    397     return Status::DataError();  // Data did not define an RSA key.
    398 
    399   // TODO(eroman): Validate the algorithm OID against the webcrypto provided
    400   // hash. http://crbug.com/389400
    401 
    402   return CreateWebCryptoPublicKey(public_key.Pass(),
    403                                   algorithm.id(),
    404                                   algorithm.rsaHashedImportParams()->hash(),
    405                                   extractable,
    406                                   usage_mask,
    407                                   key);
    408 }
    409 
    410 Status RsaHashedAlgorithm::ImportKeyJwk(
    411     const CryptoData& key_data,
    412     const blink::WebCryptoAlgorithm& algorithm,
    413     bool extractable,
    414     blink::WebCryptoKeyUsageMask usage_mask,
    415     blink::WebCryptoKey* key) const {
    416   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
    417 
    418   const char* jwk_algorithm =
    419       GetJwkAlgorithm(algorithm.rsaHashedImportParams()->hash().id());
    420 
    421   if (!jwk_algorithm)
    422     return Status::ErrorUnexpected();
    423 
    424   JwkRsaInfo jwk;
    425   Status status =
    426       ReadRsaKeyJwk(key_data, jwk_algorithm, extractable, usage_mask, &jwk);
    427   if (status.IsError())
    428     return status;
    429 
    430   // Once the key type is known, verify the usages.
    431   status = CheckKeyCreationUsages(
    432       jwk.is_private_key ? all_private_key_usages_ : all_public_key_usages_,
    433       usage_mask);
    434   if (status.IsError())
    435     return status;
    436 
    437   return jwk.is_private_key
    438              ? ImportRsaPrivateKey(algorithm, extractable, usage_mask, jwk, key)
    439              : ImportRsaPublicKey(algorithm,
    440                                   extractable,
    441                                   usage_mask,
    442                                   CryptoData(jwk.n),
    443                                   CryptoData(jwk.e),
    444                                   key);
    445 }
    446 
    447 Status RsaHashedAlgorithm::ExportKeyPkcs8(const blink::WebCryptoKey& key,
    448                                           std::vector<uint8_t>* buffer) const {
    449   if (key.type() != blink::WebCryptoKeyTypePrivate)
    450     return Status::ErrorUnexpectedKeyType();
    451   *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
    452   return Status::Success();
    453 }
    454 
    455 Status RsaHashedAlgorithm::ExportKeySpki(const blink::WebCryptoKey& key,
    456                                          std::vector<uint8_t>* buffer) const {
    457   if (key.type() != blink::WebCryptoKeyTypePublic)
    458     return Status::ErrorUnexpectedKeyType();
    459   *buffer = AsymKeyOpenSsl::Cast(key)->serialized_key_data();
    460   return Status::Success();
    461 }
    462 
    463 Status RsaHashedAlgorithm::ExportKeyJwk(const blink::WebCryptoKey& key,
    464                                         std::vector<uint8_t>* buffer) const {
    465   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
    466 
    467   EVP_PKEY* public_key = AsymKeyOpenSsl::Cast(key)->key();
    468   crypto::ScopedRSA rsa(EVP_PKEY_get1_RSA(public_key));
    469   if (!rsa.get())
    470     return Status::ErrorUnexpected();
    471 
    472   const char* jwk_algorithm =
    473       GetJwkAlgorithm(key.algorithm().rsaHashedParams()->hash().id());
    474   if (!jwk_algorithm)
    475     return Status::ErrorUnexpected();
    476 
    477   switch (key.type()) {
    478     case blink::WebCryptoKeyTypePublic:
    479       WriteRsaPublicKeyJwk(CryptoData(BIGNUMToVector(rsa->n)),
    480                            CryptoData(BIGNUMToVector(rsa->e)),
    481                            jwk_algorithm,
    482                            key.extractable(),
    483                            key.usages(),
    484                            buffer);
    485       return Status::Success();
    486     case blink::WebCryptoKeyTypePrivate:
    487       WriteRsaPrivateKeyJwk(CryptoData(BIGNUMToVector(rsa->n)),
    488                             CryptoData(BIGNUMToVector(rsa->e)),
    489                             CryptoData(BIGNUMToVector(rsa->d)),
    490                             CryptoData(BIGNUMToVector(rsa->p)),
    491                             CryptoData(BIGNUMToVector(rsa->q)),
    492                             CryptoData(BIGNUMToVector(rsa->dmp1)),
    493                             CryptoData(BIGNUMToVector(rsa->dmq1)),
    494                             CryptoData(BIGNUMToVector(rsa->iqmp)),
    495                             jwk_algorithm,
    496                             key.extractable(),
    497                             key.usages(),
    498                             buffer);
    499       return Status::Success();
    500 
    501     default:
    502       return Status::ErrorUnexpected();
    503   }
    504 }
    505 
    506 }  // namespace webcrypto
    507 
    508 }  // namespace content
    509