1/* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "CryptoKeyRSA.h" 28 29#if ENABLE(SUBTLE_CRYPTO) 30 31#include "CommonCryptoUtilities.h" 32#include "CryptoAlgorithmDescriptionBuilder.h" 33#include "CryptoAlgorithmRegistry.h" 34#include "CryptoKeyDataRSAComponents.h" 35#include "CryptoKeyPair.h" 36 37namespace WebCore { 38 39static CCCryptorStatus getPublicKeyComponents(CCRSACryptorRef rsaKey, Vector<uint8_t>& modulus, Vector<uint8_t>& publicExponent) 40{ 41 ASSERT(CCRSAGetKeyType(rsaKey) == ccRSAKeyPublic || CCRSAGetKeyType(rsaKey) == ccRSAKeyPrivate); 42 bool keyIsPublic = CCRSAGetKeyType(rsaKey) == ccRSAKeyPublic; 43 CCRSACryptorRef publicKey = keyIsPublic ? rsaKey : CCRSACryptorGetPublicKeyFromPrivateKey(rsaKey); 44 45 modulus.resize(16384); 46 size_t modulusLength = modulus.size(); 47 publicExponent.resize(16384); 48 size_t exponentLength = publicExponent.size(); 49 CCCryptorStatus status = CCRSAGetKeyComponents(publicKey, modulus.data(), &modulusLength, publicExponent.data(), &exponentLength, 0, 0, 0, 0); 50 if (!keyIsPublic) { 51 // CCRSACryptorGetPublicKeyFromPrivateKey has "Get" in the name, but its result needs to be released (see <rdar://problem/15449697>). 52 CCRSACryptorRelease(publicKey); 53 } 54 if (status) 55 return status; 56 57 modulus.shrink(modulusLength); 58 publicExponent.shrink(exponentLength); 59 return status; 60} 61 62static CCCryptorStatus getPrivateKeyComponents(CCRSACryptorRef rsaKey, Vector<uint8_t>& privateExponent, CryptoKeyDataRSAComponents::PrimeInfo& firstPrimeInfo, CryptoKeyDataRSAComponents::PrimeInfo& secondPrimeInfo) 63{ 64 ASSERT(CCRSAGetKeyType(rsaKey) == ccRSAKeyPrivate); 65 66 Vector<uint8_t> unusedModulus(16384); 67 size_t modulusLength = unusedModulus.size(); 68 privateExponent.resize(16384); 69 size_t exponentLength = privateExponent.size(); 70 firstPrimeInfo.primeFactor.resize(16384); 71 size_t pLength = firstPrimeInfo.primeFactor.size(); 72 secondPrimeInfo.primeFactor.resize(16384); 73 size_t qLength = secondPrimeInfo.primeFactor.size(); 74 75 CCCryptorStatus status = CCRSAGetKeyComponents(rsaKey, unusedModulus.data(), &modulusLength, privateExponent.data(), &exponentLength, firstPrimeInfo.primeFactor.data(), &pLength, secondPrimeInfo.primeFactor.data(), &qLength); 76 if (status) 77 return status; 78 79 privateExponent.shrink(exponentLength); 80 firstPrimeInfo.primeFactor.shrink(pLength); 81 secondPrimeInfo.primeFactor.shrink(qLength); 82 83 CCBigNum d(privateExponent.data(), privateExponent.size()); 84 CCBigNum p(firstPrimeInfo.primeFactor.data(), firstPrimeInfo.primeFactor.size()); 85 CCBigNum q(secondPrimeInfo.primeFactor.data(), secondPrimeInfo.primeFactor.size()); 86 87 CCBigNum dp = d % (p - 1); 88 CCBigNum dq = d % (q - 1); 89 CCBigNum qi = q.inverse(p); 90 91 firstPrimeInfo.factorCRTExponent = dp.data(); 92 secondPrimeInfo.factorCRTExponent = dq.data(); 93 secondPrimeInfo.factorCRTCoefficient = qi.data(); 94 95 return status; 96} 97 98CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoKeyType type, PlatformRSAKey platformKey, bool extractable, CryptoKeyUsage usage) 99 : CryptoKey(identifier, type, extractable, usage) 100 , m_platformKey(platformKey) 101 , m_restrictedToSpecificHash(false) 102{ 103} 104 105PassRefPtr<CryptoKeyRSA> CryptoKeyRSA::create(CryptoAlgorithmIdentifier identifier, const CryptoKeyDataRSAComponents& keyData, bool extractable, CryptoKeyUsage usage) 106{ 107 if (keyData.type() == CryptoKeyDataRSAComponents::Type::Private && !keyData.hasAdditionalPrivateKeyParameters()) { 108 // <rdar://problem/15452324> tracks adding support. 109 WTFLogAlways("Private keys without additional data are not supported"); 110 return nullptr; 111 } 112 if (keyData.otherPrimeInfos().size()) { 113 // <rdar://problem/15444074> tracks adding support. 114 WTFLogAlways("Keys with more than two primes are not supported"); 115 return nullptr; 116 } 117 CCRSACryptorRef cryptor; 118 CCCryptorStatus status = CCRSACryptorCreateFromData( 119 keyData.type() == CryptoKeyDataRSAComponents::Type::Public ? ccRSAKeyPublic : ccRSAKeyPrivate, 120 (uint8_t*)keyData.modulus().data(), keyData.modulus().size(), 121 (uint8_t*)keyData.exponent().data(), keyData.exponent().size(), 122 (uint8_t*)keyData.firstPrimeInfo().primeFactor.data(), keyData.firstPrimeInfo().primeFactor.size(), 123 (uint8_t*)keyData.secondPrimeInfo().primeFactor.data(), keyData.secondPrimeInfo().primeFactor.size(), 124 &cryptor); 125 126 if (status) { 127 LOG_ERROR("Couldn't create RSA key from data, error %d", status); 128 return nullptr; 129 } 130 131 return adoptRef(new CryptoKeyRSA(identifier, keyData.type() == CryptoKeyDataRSAComponents::Type::Public ? CryptoKeyType::Public : CryptoKeyType::Private, cryptor, extractable, usage)); 132} 133 134CryptoKeyRSA::~CryptoKeyRSA() 135{ 136 CCRSACryptorRelease(m_platformKey); 137} 138 139void CryptoKeyRSA::restrictToHash(CryptoAlgorithmIdentifier identifier) 140{ 141 m_restrictedToSpecificHash = true; 142 m_hash = identifier; 143} 144 145bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const 146{ 147 if (!m_restrictedToSpecificHash) 148 return false; 149 150 identifier = m_hash; 151 return true; 152} 153 154size_t CryptoKeyRSA::keySizeInBits() const 155{ 156 Vector<uint8_t> modulus; 157 Vector<uint8_t> publicExponent; 158 CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent); 159 if (status) { 160 WTFLogAlways("Couldn't get RSA key components, status %d", status); 161 return 0; 162 } 163 164 return modulus.size() * 8; 165} 166 167void CryptoKeyRSA::buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder& builder) const 168{ 169 CryptoKey::buildAlgorithmDescription(builder); 170 171 Vector<uint8_t> modulus; 172 Vector<uint8_t> publicExponent; 173 CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent); 174 if (status) { 175 WTFLogAlways("Couldn't get RSA key components, status %d", status); 176 return; 177 } 178 179 builder.add("modulusLength", modulus.size() * 8); 180 builder.add("publicExponent", publicExponent); 181 182 if (m_restrictedToSpecificHash) { 183 auto hashDescriptionBuilder = builder.createEmptyClone(); 184 hashDescriptionBuilder->add("name", CryptoAlgorithmRegistry::shared().nameForIdentifier(m_hash)); 185 builder.add("hash", *hashDescriptionBuilder); 186 } 187} 188 189std::unique_ptr<CryptoKeyData> CryptoKeyRSA::exportData() const 190{ 191 ASSERT(extractable()); 192 193 switch (CCRSAGetKeyType(m_platformKey)) { 194 case ccRSAKeyPublic: { 195 Vector<uint8_t> modulus; 196 Vector<uint8_t> publicExponent; 197 CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent); 198 if (status) { 199 WTFLogAlways("Couldn't get RSA key components, status %d", status); 200 return nullptr; 201 } 202 return CryptoKeyDataRSAComponents::createPublic(modulus, publicExponent); 203 } 204 case ccRSAKeyPrivate: { 205 Vector<uint8_t> modulus; 206 Vector<uint8_t> publicExponent; 207 CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent); 208 if (status) { 209 WTFLogAlways("Couldn't get RSA key components, status %d", status); 210 return nullptr; 211 } 212 Vector<uint8_t> privateExponent; 213 CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo; 214 CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo; 215 Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos; // Always empty, CommonCrypto only supports two primes (cf. <rdar://problem/15444074>). 216 status = getPrivateKeyComponents(m_platformKey, privateExponent, firstPrimeInfo, secondPrimeInfo); 217 if (status) { 218 WTFLogAlways("Couldn't get RSA key components, status %d", status); 219 return nullptr; 220 } 221 return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, publicExponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos); 222 } 223 default: 224 return nullptr; 225 } 226} 227 228static bool bigIntegerToUInt32(const Vector<uint8_t>& bigInteger, uint32_t& result) 229{ 230 result = 0; 231 for (size_t i = 0; i + 4 < bigInteger.size(); ++i) { 232 if (bigInteger[i]) 233 return false; // Too big to fit in 32 bits. 234 } 235 236 for (size_t i = bigInteger.size() > 4 ? bigInteger.size() - 4 : 0; i < bigInteger.size(); ++i) { 237 result <<= 8; 238 result += bigInteger[i]; 239 } 240 return true; 241} 242 243void CryptoKeyRSA::generatePair(CryptoAlgorithmIdentifier algorithm, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool extractable, CryptoKeyUsage usage, KeyPairCallback callback, VoidCallback failureCallback) 244{ 245 uint32_t e; 246 if (!bigIntegerToUInt32(publicExponent, e)) { 247 // Adding support is tracked as <rdar://problem/15444034>. 248 WTFLogAlways("Public exponent is too big, not supported"); 249 failureCallback(); 250 return; 251 } 252 253 // We only use the callback functions when back on the main thread, but captured variables are copied on a secondary thread too. 254 KeyPairCallback* localCallback = new KeyPairCallback(WTF::move(callback)); 255 VoidCallback* localFailureCallback = new VoidCallback(WTF::move(failureCallback)); 256 257 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 258 CCRSACryptorRef ccPublicKey; 259 CCRSACryptorRef ccPrivateKey; 260 CCCryptorStatus status = CCRSACryptorGeneratePair(modulusLength, e, &ccPublicKey, &ccPrivateKey); 261 if (status) { 262 WTFLogAlways("Could not generate a key pair, status %d", status); 263 dispatch_async(dispatch_get_main_queue(), ^{ 264 (*localFailureCallback)(); 265 delete localFailureCallback; 266 }); 267 return; 268 } 269 dispatch_async(dispatch_get_main_queue(), ^{ 270 RefPtr<CryptoKeyRSA> publicKey = CryptoKeyRSA::create(algorithm, CryptoKeyType::Public, ccPublicKey, true, usage); 271 RefPtr<CryptoKeyRSA> privateKey = CryptoKeyRSA::create(algorithm, CryptoKeyType::Private, ccPrivateKey, extractable, usage); 272 (*localCallback)(*CryptoKeyPair::create(publicKey.release(), privateKey.release())); 273 delete localCallback; 274 }); 275 }); 276} 277 278} // namespace WebCore 279 280#endif // ENABLE(SUBTLE_CRYPTO) 281