Commit 902a179d authored by ap@apple.com's avatar ap@apple.com

Support exporting private WebCrypto RSA keys

https://bugs.webkit.org/show_bug.cgi?id=124483

Reviewed by Anders Carlsson.

Source/WebCore: 

Test: crypto/subtle/rsa-export-private-key.html

It might be better to have our own bignum implementation in WTF, but we currently
don't, and the need for this computation is Common Crypto specific anyway.

* crypto/CommonCryptoUtilities.h:
* crypto/CommonCryptoUtilities.cpp:
(WebCore::CCBigNum::CCBigNum):
(WebCore::CCBigNum::~CCBigNum):
(WebCore::CCBigNum::operator=):
(WebCore::CCBigNum::data):
(WebCore::CCBigNum::operator-):
(WebCore::CCBigNum::operator%):
(WebCore::CCBigNum::inverse):
Added a minimal wrapper around CommonCrypto BigNum.

* crypto/mac/CryptoKeyRSAMac.cpp:
(WebCore::getPrivateKeyComponents): Compute missing parts using CCBigNum.
(WebCore::CryptoKeyRSA::exportData): Implemented private key case.

LayoutTests: 

* crypto/subtle/rsa-export-private-key-expected.txt: Added.
* crypto/subtle/rsa-export-private-key.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@160029 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 87fd04f3
2013-12-03 Alexey Proskuryakov <ap@apple.com>
Support exporting private WebCrypto RSA keys
https://bugs.webkit.org/show_bug.cgi?id=124483
Reviewed by Anders Carlsson.
* crypto/subtle/rsa-export-private-key-expected.txt: Added.
* crypto/subtle/rsa-export-private-key.html: Added.
2013-12-03 Alexey Proskuryakov <ap@apple.com>
WebCrypto HMAC doesn't check key algorithm's hash
......
Test exporting a private RSA key.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Importing a JWK key...
Exporting the key as JWK...
PASS exportedJWK.kty is 'RSA'
PASS exportedJWK.n is privateKeyJSON.n
PASS exportedJWK.e is privateKeyJSON.e
PASS exportedJWK.d is privateKeyJSON.d
PASS exportedJWK.p is privateKeyJSON.p
PASS exportedJWK.q is privateKeyJSON.q
PASS exportedJWK.dp is privateKeyJSON.dp
PASS exportedJWK.dq is privateKeyJSON.dq
PASS exportedJWK.qi is privateKeyJSON.qi
PASS exportedJWK.oth is privateKeyJSON.oth
PASS exportedJWK.alg is privateKeyJSON.alg
PASS exportedJWK.extractable is true
PASS exportedJWK.use is 'sig'
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../resources/js-test-pre.js"></script>
<script src="resources/common.js"></script>
</head>
<body>
<p id="description"></p>
<div id="console"></div>
<script>
description("Test exporting a private RSA key.");
jsTestIsAsync = true;
var extractable = true;
var nonExtractable = false;
// Example from JWK specification.
var privateKeyJSON = {
"kty":"RSA",
"n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
"e":"AQAB",
"d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q",
"p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs",
"q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk",
"dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0",
"dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk",
"qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU",
"alg":"RS256",
"kid":"2011-04-29"
}
var jwkKeyAsArrayBuffer = asciiToUint8Array(JSON.stringify(privateKeyJSON));
debug("\nImporting a JWK key...");
crypto.subtle.importKey("jwk", jwkKeyAsArrayBuffer, "RSASSA-PKCS1-v1_5", extractable, ['sign', 'verify']).then(function(result) {
key = result;
debug("\nExporting the key as JWK...");
return crypto.subtle.exportKey("jwk", key);
}).then(function(result) {
exportedJWK = JSON.parse(bytesToASCIIString(result));
shouldBe("exportedJWK.kty", "'RSA'");
shouldBe("exportedJWK.n", "privateKeyJSON.n");
shouldBe("exportedJWK.e", "privateKeyJSON.e");
shouldBe("exportedJWK.d", "privateKeyJSON.d");
shouldBe("exportedJWK.p", "privateKeyJSON.p");
shouldBe("exportedJWK.q", "privateKeyJSON.q");
shouldBe("exportedJWK.dp", "privateKeyJSON.dp");
shouldBe("exportedJWK.dq", "privateKeyJSON.dq");
shouldBe("exportedJWK.qi", "privateKeyJSON.qi");
shouldBe("exportedJWK.oth", "privateKeyJSON.oth");
shouldBe("exportedJWK.alg", "privateKeyJSON.alg");
shouldBe("exportedJWK.extractable", "true");
shouldBe("exportedJWK.use", "'sig'");
finishJSTest();
});
</script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
2013-12-03 Alexey Proskuryakov <ap@apple.com>
Support exporting private WebCrypto RSA keys
https://bugs.webkit.org/show_bug.cgi?id=124483
Reviewed by Anders Carlsson.
Test: crypto/subtle/rsa-export-private-key.html
It might be better to have our own bignum implementation in WTF, but we currently
don't, and the need for this computation is Common Crypto specific anyway.
* crypto/CommonCryptoUtilities.h:
* crypto/CommonCryptoUtilities.cpp:
(WebCore::CCBigNum::CCBigNum):
(WebCore::CCBigNum::~CCBigNum):
(WebCore::CCBigNum::operator=):
(WebCore::CCBigNum::data):
(WebCore::CCBigNum::operator-):
(WebCore::CCBigNum::operator%):
(WebCore::CCBigNum::inverse):
Added a minimal wrapper around CommonCrypto BigNum.
* crypto/mac/CryptoKeyRSAMac.cpp:
(WebCore::getPrivateKeyComponents): Compute missing parts using CCBigNum.
(WebCore::CryptoKeyRSA::exportData): Implemented private key case.
2013-12-03 Ryosuke Niwa <rniwa@webkit.org>
XML fragment parsing algorithm doesn't use the context element's default namespace URI
......@@ -28,6 +28,23 @@
#if ENABLE(SUBTLE_CRYPTO)
#if defined(__has_include)
#if __has_include(<CommonCrypto/CommonBigNum.h>)
#include <CommonCrypto/CommonBigNum.h>
#endif
#endif
typedef CCCryptorStatus CCStatus;
extern "C" CCBigNumRef CCBigNumFromData(CCStatus *status, const void *s, size_t len);
extern "C" size_t CCBigNumToData(CCStatus *status, const CCBigNumRef bn, void *to);
extern "C" uint32_t CCBigNumByteCount(const CCBigNumRef bn);
extern "C" CCBigNumRef CCCreateBigNum(CCStatus *status);
extern "C" void CCBigNumFree(CCBigNumRef bn);
extern "C" CCBigNumRef CCBigNumCopy(CCStatus *status, const CCBigNumRef bn);
extern "C" CCStatus CCBigNumSubI(CCBigNumRef result, const CCBigNumRef a, const uint32_t b);
extern "C" CCStatus CCBigNumMod(CCBigNumRef result, CCBigNumRef dividend, CCBigNumRef modulus);
extern "C" CCStatus CCBigNumInverseMod(CCBigNumRef result, const CCBigNumRef a, const CCBigNumRef modulus);
namespace WebCore {
bool getCommonCryptoDigestAlgorithm(CryptoAlgorithmIdentifier hashFunction, CCDigestAlgorithm& algorithm)
......@@ -53,6 +70,106 @@ bool getCommonCryptoDigestAlgorithm(CryptoAlgorithmIdentifier hashFunction, CCDi
}
}
CCBigNum::CCBigNum(CCBigNumRef number)
: m_number(number)
{
}
CCBigNum::CCBigNum(const uint8_t* data, size_t size)
{
CCStatus status = kCCSuccess;
m_number = CCBigNumFromData(&status, data, size);
RELEASE_ASSERT(!status);
}
CCBigNum::~CCBigNum()
{
CCBigNumFree(m_number);
}
CCBigNum::CCBigNum(const CCBigNum& other)
{
CCStatus status = kCCSuccess;
m_number = CCBigNumCopy(&status, other.m_number);
RELEASE_ASSERT(!status);
}
CCBigNum::CCBigNum(CCBigNum&& other)
{
m_number = other.m_number;
other.m_number = nullptr;
}
CCBigNum& CCBigNum::operator=(const CCBigNum& other)
{
if (this == &other)
return *this;
CCBigNumFree(m_number);
CCStatus status = kCCSuccess;
m_number = CCBigNumCopy(&status, other.m_number);
RELEASE_ASSERT(!status);
return *this;
}
CCBigNum& CCBigNum::operator=(CCBigNum&& other)
{
if (this == &other)
return *this;
m_number = other.m_number;
other.m_number = nullptr;
return *this;
}
Vector<uint8_t> CCBigNum::data() const
{
Vector<uint8_t> result(CCBigNumByteCount(m_number));
CCStatus status = kCCSuccess;
CCBigNumToData(&status, m_number, result.data());
RELEASE_ASSERT(!status);
return result;
}
CCBigNum CCBigNum::operator-(uint32_t b) const
{
CCStatus status = kCCSuccess;
CCBigNumRef result = CCCreateBigNum(&status);
RELEASE_ASSERT(!status);
status = CCBigNumSubI(result, m_number, b);
RELEASE_ASSERT(!status);
return result;
}
CCBigNum CCBigNum::operator%(const CCBigNum& modulus) const
{
CCStatus status = kCCSuccess;
CCBigNumRef result = CCCreateBigNum(&status);
RELEASE_ASSERT(!status);
status = CCBigNumMod(result, m_number, modulus.m_number);
RELEASE_ASSERT(!status);
return result;
}
CCBigNum CCBigNum::inverse(const CCBigNum& modulus) const
{
CCStatus status = kCCSuccess;
CCBigNumRef result = CCCreateBigNum(&status);
RELEASE_ASSERT(!status);
status = CCBigNumInverseMod(result, m_number, modulus.m_number);
RELEASE_ASSERT(!status);
return result;
}
} // namespace WebCore
#endif // ENABLE(SUBTLE_CRYPTO)
......@@ -29,8 +29,8 @@
#if ENABLE(SUBTLE_CRYPTO)
#include "CryptoAlgorithmIdentifier.h"
#include <CommonCrypto/CommonCryptor.h>
#include <wtf/Vector.h>
#if defined(__has_include)
#if __has_include(<CommonCrypto/CommonRSACryptor.h>)
......@@ -71,6 +71,8 @@ enum {
};
#endif
typedef struct _CCBigNumRef *CCBigNumRef;
typedef struct __CCRandom *CCRandomRef;
extern const CCRandomRef kCCRandomDefault;
extern "C" int CCRandomCopyBytes(CCRandomRef rnd, void *bytes, size_t count);
......@@ -89,6 +91,28 @@ extern "C" CCRSAKeyType CCRSAGetKeyType(CCRSACryptorRef key);
namespace WebCore {
class CCBigNum {
public:
CCBigNum(const uint8_t*, size_t);
~CCBigNum();
CCBigNum(const CCBigNum&);
CCBigNum(CCBigNum&&);
CCBigNum& operator=(const CCBigNum&);
CCBigNum& operator=(CCBigNum&&);
Vector<uint8_t> data() const;
CCBigNum operator-(uint32_t) const;
CCBigNum operator%(const CCBigNum&) const;
CCBigNum inverse(const CCBigNum& modulus) const;
private:
CCBigNum(CCBigNumRef);
CCBigNumRef m_number;
};
bool getCommonCryptoDigestAlgorithm(CryptoAlgorithmIdentifier, CCDigestAlgorithm&);
} // namespace WebCore
......
......@@ -59,6 +59,42 @@ static CCCryptorStatus getPublicKeyComponents(CCRSACryptorRef rsaKey, Vector<uin
return status;
}
static CCCryptorStatus getPrivateKeyComponents(CCRSACryptorRef rsaKey, Vector<uint8_t>& privateExponent, CryptoKeyDataRSAComponents::PrimeInfo& firstPrimeInfo, CryptoKeyDataRSAComponents::PrimeInfo& secondPrimeInfo)
{
ASSERT(CCRSAGetKeyType(rsaKey) == ccRSAKeyPrivate);
Vector<uint8_t> unusedModulus(16384);
size_t modulusLength = unusedModulus.size();
privateExponent.resize(16384);
size_t exponentLength = privateExponent.size();
firstPrimeInfo.primeFactor.resize(16384);
size_t pLength = firstPrimeInfo.primeFactor.size();
secondPrimeInfo.primeFactor.resize(16384);
size_t qLength = secondPrimeInfo.primeFactor.size();
CCCryptorStatus status = CCRSAGetKeyComponents(rsaKey, unusedModulus.data(), &modulusLength, privateExponent.data(), &exponentLength, firstPrimeInfo.primeFactor.data(), &pLength, secondPrimeInfo.primeFactor.data(), &qLength);
if (status)
return status;
privateExponent.shrink(exponentLength);
firstPrimeInfo.primeFactor.shrink(pLength);
secondPrimeInfo.primeFactor.shrink(qLength);
CCBigNum d(privateExponent.data(), privateExponent.size());
CCBigNum p(firstPrimeInfo.primeFactor.data(), firstPrimeInfo.primeFactor.size());
CCBigNum q(secondPrimeInfo.primeFactor.data(), secondPrimeInfo.primeFactor.size());
CCBigNum dp = d % (p - 1);
CCBigNum dq = d % (q - 1);
CCBigNum qi = q.inverse(p);
firstPrimeInfo.factorCRTExponent = dp.data();
secondPrimeInfo.factorCRTExponent = dq.data();
secondPrimeInfo.factorCRTCoefficient = qi.data();
return status;
}
CryptoKeyRSA::CryptoKeyRSA(CryptoAlgorithmIdentifier identifier, CryptoKeyType type, PlatformRSAKey platformKey, bool extractable, CryptoKeyUsage usage)
: CryptoKey(identifier, type, extractable, usage)
, m_platformKey(platformKey)
......@@ -165,8 +201,25 @@ std::unique_ptr<CryptoKeyData> CryptoKeyRSA::exportData() const
}
return CryptoKeyDataRSAComponents::createPublic(modulus, publicExponent);
}
case ccRSAKeyPrivate:
// Not supported yet.
case ccRSAKeyPrivate: {
Vector<uint8_t> modulus;
Vector<uint8_t> publicExponent;
CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent);
if (status) {
WTFLogAlways("Couldn't get RSA key components, status %d", status);
return nullptr;
}
Vector<uint8_t> privateExponent;
CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos; // Always empty, CommonCrypto only supports two primes (cf. <rdar://problem/15444074>).
status = getPrivateKeyComponents(m_platformKey, privateExponent, firstPrimeInfo, secondPrimeInfo);
if (status) {
WTFLogAlways("Couldn't get RSA key components, status %d", status);
return nullptr;
}
return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, publicExponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
}
default:
return nullptr;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment