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 <secerr.h> 6 7 #include "base/numerics/safe_math.h" 8 #include "content/child/webcrypto/crypto_data.h" 9 #include "content/child/webcrypto/nss/aes_key_nss.h" 10 #include "content/child/webcrypto/nss/key_nss.h" 11 #include "content/child/webcrypto/nss/sym_key_nss.h" 12 #include "content/child/webcrypto/nss/util_nss.h" 13 #include "content/child/webcrypto/status.h" 14 #include "content/child/webcrypto/webcrypto_util.h" 15 #include "crypto/scoped_nss_types.h" 16 #include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h" 17 #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" 18 19 namespace content { 20 21 namespace webcrypto { 22 23 namespace { 24 25 // The Default IV for AES-KW. See http://www.ietf.org/rfc/rfc3394.txt 26 // Section 2.2.3.1. 27 const unsigned char kAesIv[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}; 28 29 // The result of unwrapping is a SymKey rather than a buffer. This is a 30 // consequence of how NSS exposes AES-KW. Subsequent code can extract the value 31 // of the sym key to interpret it as key bytes in another format. 32 Status DoUnwrapSymKeyAesKw(const CryptoData& wrapped_key_data, 33 PK11SymKey* wrapping_key, 34 CK_MECHANISM_TYPE mechanism, 35 CK_FLAGS flags, 36 crypto::ScopedPK11SymKey* unwrapped_key) { 37 DCHECK_GE(wrapped_key_data.byte_length(), 24u); 38 DCHECK_EQ(wrapped_key_data.byte_length() % 8, 0u); 39 40 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); 41 crypto::ScopedSECItem param_item( 42 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); 43 if (!param_item) 44 return Status::ErrorUnexpected(); 45 46 SECItem cipher_text = MakeSECItemForBuffer(wrapped_key_data); 47 48 // The plaintext length is always 64 bits less than the data size. 49 const unsigned int plaintext_length = wrapped_key_data.byte_length() - 8; 50 51 #if defined(USE_NSS) 52 // Part of workaround for 53 // https://bugzilla.mozilla.org/show_bug.cgi?id=981170. See the explanation 54 // later in this function. 55 PORT_SetError(0); 56 #endif 57 58 crypto::ScopedPK11SymKey new_key( 59 PK11_UnwrapSymKeyWithFlags(wrapping_key, 60 CKM_NSS_AES_KEY_WRAP, 61 param_item.get(), 62 &cipher_text, 63 mechanism, 64 CKA_FLAGS_ONLY, 65 plaintext_length, 66 flags)); 67 68 // TODO(padolph): Use NSS PORT_GetError() and friends to report a more 69 // accurate error, providing if doesn't leak any information to web pages 70 // about other web crypto users, key details, etc. 71 if (!new_key) 72 return Status::OperationError(); 73 74 #if defined(USE_NSS) 75 // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=981170 76 // which was fixed in NSS 3.16.0. 77 // If unwrap fails, NSS nevertheless returns a valid-looking PK11SymKey, 78 // with a reasonable length but with key data pointing to uninitialized 79 // memory. 80 // To understand this workaround see the fix for 981170: 81 // https://hg.mozilla.org/projects/nss/rev/753bb69e543c 82 if (!NSS_VersionCheck("3.16") && PORT_GetError() == SEC_ERROR_BAD_DATA) 83 return Status::OperationError(); 84 #endif 85 86 *unwrapped_key = new_key.Pass(); 87 return Status::Success(); 88 } 89 90 Status WrapSymKeyAesKw(PK11SymKey* key, 91 PK11SymKey* wrapping_key, 92 std::vector<uint8_t>* buffer) { 93 // The data size must be at least 16 bytes and a multiple of 8 bytes. 94 // RFC 3394 does not specify a maximum allowed data length, but since only 95 // keys are being wrapped in this application (which are small), a reasonable 96 // max limit is whatever will fit into an unsigned. For the max size test, 97 // note that AES Key Wrap always adds 8 bytes to the input data size. 98 const unsigned int input_length = PK11_GetKeyLength(key); 99 DCHECK_GE(input_length, 16u); 100 DCHECK((input_length % 8) == 0); 101 102 SECItem iv_item = MakeSECItemForBuffer(CryptoData(kAesIv, sizeof(kAesIv))); 103 crypto::ScopedSECItem param_item( 104 PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP, &iv_item)); 105 if (!param_item) 106 return Status::ErrorUnexpected(); 107 108 base::CheckedNumeric<unsigned int> output_length = input_length; 109 output_length += 8; 110 if (!output_length.IsValid()) 111 return Status::ErrorDataTooLarge(); 112 113 buffer->resize(output_length.ValueOrDie()); 114 SECItem wrapped_key_item = MakeSECItemForBuffer(CryptoData(*buffer)); 115 116 if (SECSuccess != PK11_WrapSymKey(CKM_NSS_AES_KEY_WRAP, 117 param_item.get(), 118 wrapping_key, 119 key, 120 &wrapped_key_item)) { 121 return Status::OperationError(); 122 } 123 if (output_length.ValueOrDie() != wrapped_key_item.len) 124 return Status::ErrorUnexpected(); 125 126 return Status::Success(); 127 } 128 129 class AesKwCryptoAlgorithmNss : public AesAlgorithm { 130 public: 131 AesKwCryptoAlgorithmNss() 132 : AesAlgorithm( 133 CKM_NSS_AES_KEY_WRAP, 134 CKF_WRAP | CKF_WRAP, 135 blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey, 136 "KW") {} 137 138 virtual Status Encrypt(const blink::WebCryptoAlgorithm& algorithm, 139 const blink::WebCryptoKey& wrapping_key, 140 const CryptoData& data, 141 std::vector<uint8_t>* buffer) const OVERRIDE { 142 if (data.byte_length() < 16) 143 return Status::ErrorDataTooSmall(); 144 if (data.byte_length() % 8) 145 return Status::ErrorInvalidAesKwDataLength(); 146 147 // Due to limitations in the NSS API for the AES-KW algorithm, |data| must 148 // be temporarily viewed as a symmetric key to be wrapped (encrypted). 149 SECItem data_item = MakeSECItemForBuffer(data); 150 crypto::ScopedPK11Slot slot(PK11_GetInternalSlot()); 151 crypto::ScopedPK11SymKey data_as_sym_key( 152 PK11_ImportSymKey(slot.get(), 153 CKK_GENERIC_SECRET, 154 PK11_OriginUnwrap, 155 CKA_SIGN, 156 &data_item, 157 NULL)); 158 if (!data_as_sym_key) 159 return Status::OperationError(); 160 161 return WrapSymKeyAesKw( 162 data_as_sym_key.get(), SymKeyNss::Cast(wrapping_key)->key(), buffer); 163 } 164 165 virtual Status Decrypt(const blink::WebCryptoAlgorithm& algorithm, 166 const blink::WebCryptoKey& wrapping_key, 167 const CryptoData& data, 168 std::vector<uint8_t>* buffer) const OVERRIDE { 169 if (data.byte_length() < 24) 170 return Status::ErrorDataTooSmall(); 171 if (data.byte_length() % 8) 172 return Status::ErrorInvalidAesKwDataLength(); 173 174 // Due to limitations in the NSS API for the AES-KW algorithm, |data| must 175 // be temporarily viewed as a symmetric key to be unwrapped (decrypted). 176 crypto::ScopedPK11SymKey decrypted; 177 Status status = DoUnwrapSymKeyAesKw(data, 178 SymKeyNss::Cast(wrapping_key)->key(), 179 CKK_GENERIC_SECRET, 180 0, 181 &decrypted); 182 if (status.IsError()) 183 return status; 184 185 // Once the decrypt is complete, extract the resultant raw bytes from NSS 186 // and return them to the caller. 187 if (PK11_ExtractKeyValue(decrypted.get()) != SECSuccess) 188 return Status::OperationError(); 189 const SECItem* const key_data = PK11_GetKeyData(decrypted.get()); 190 if (!key_data) 191 return Status::OperationError(); 192 buffer->assign(key_data->data, key_data->data + key_data->len); 193 194 return Status::Success(); 195 } 196 }; 197 198 } // namespace 199 200 AlgorithmImplementation* CreatePlatformAesKwImplementation() { 201 return new AesKwCryptoAlgorithmNss; 202 } 203 204 } // namespace webcrypto 205 206 } // namespace content 207