Commit a7a1d44a authored by ap@apple.com's avatar ap@apple.com

Support exporting public RSASSA-PKCS1-v1_5 keys

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

Reviewed by Sam Weinig.

Source/WebCore: 

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

* bindings/js/JSCryptoKeySerializationJWK.h:
* bindings/js/JSCryptoKeySerializationJWK.cpp:
(WebCore::JSCryptoKeySerializationJWK::buildJSONForRSAComponents):
(WebCore::JSCryptoKeySerializationJWK::addJWKAlgorithmToJSON):
(WebCore::JSCryptoKeySerializationJWK::serialize):
Added said support (this part works with private keys too).

* crypto/keys/CryptoKeyRSA.h:
* crypto/mac/CryptoKeyRSAMac.cpp:
(WebCore::CryptoKeyRSA::getPublicKeyComponents): Moved the logic for getting a
public key from private one here for reuse in keySizeInBits().
(WebCore::CryptoKeyRSA::isRestrictedToHash):
(WebCore::CryptoKeyRSA::keySizeInBits):
(WebCore::CryptoKeyRSA::exportData):
Exposed information necessary for JWK serialization.

LayoutTests: 

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



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@159403 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 0fbb373e
2013-11-17 Alexey Proskuryakov <ap@apple.com>
Support exporting public RSASSA-PKCS1-v1_5 keys
https://bugs.webkit.org/show_bug.cgi?id=124475
Reviewed by Sam Weinig.
* crypto/subtle/rsa-export-key-expected.txt: Added.
* crypto/subtle/rsa-export-key.html: Added.
2013-11-18 Zan Dobersek <zdobersek@igalia.com>
Unreviewed GTK gardening. Rebaselining CSS tests' expectations.
......
Test exporting an RSA key.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
Importing a JWK key...
PASS crypto.subtle.exportKey(null, key) threw exception TypeError: Unknown key format.
PASS crypto.subtle.exportKey(undefined, key) threw exception TypeError: Unknown key format.
PASS crypto.subtle.exportKey({}, key) threw exception TypeError: Unknown key format.
PASS crypto.subtle.exportKey("", key) threw exception TypeError: Unknown key format.
PASS crypto.subtle.exportKey("foobar", key) threw exception TypeError: Unknown key format.
Exporting the key as JWK...
PASS exportedJWK.kty is 'RSA'
PASS exportedJWK.n is publicKeyJSON.n
PASS exportedJWK.e is publicKeyJSON.e
PASS exportedJWK.alg is 'RS256'
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 an RSA key.");
jsTestIsAsync = true;
var extractable = true;
var nonExtractable = false;
var publicKeyJSON = {
kty: "RSA",
alg: "RS256",
n: "rcCUCv7Oc1HVam1DIhCzqknThWawOp8QLk8Ziy2p10ByjQFCajoFiyuAWl-R1WXZaf4xitLRracT9agpzIzc-MbLSHIGgWQGO21lGiImy5ftZ-D8bHAqRz2y15pzD4c4CEou7XSSLDoRnR0QG5MsDhD6s2gV9mwHkrtkCxtMWdBi-77as8wGmlNRldcOSgZDLK8UnCSgA1OguZ989bFyc8tOOEIb0xUSfPSz3LPSCnyYz68aDjmKVeNH-ig857OScyWbGyEy3Biw64qun3juUlNWsJ3zngkOdteYWytx5Qr4XKNs6R-Myyq72KUp02mJDZiiyiglxML_i3-_CeecCw",
e: "AQAB"
};
var jwkKeyAsArrayBuffer = asciiToUint8Array(JSON.stringify(publicKeyJSON));
debug("\nImporting a JWK key...");
crypto.subtle.importKey("jwk", jwkKeyAsArrayBuffer, "RSASSA-PKCS1-v1_5", extractable, ['sign', 'verify']).then(function(result) {
key = result;
shouldThrow('crypto.subtle.exportKey(null, key)');
shouldThrow('crypto.subtle.exportKey(undefined, key)');
shouldThrow('crypto.subtle.exportKey({}, key)');
shouldThrow('crypto.subtle.exportKey("", key)');
shouldThrow('crypto.subtle.exportKey("foobar", key)');
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", "publicKeyJSON.n");
shouldBe("exportedJWK.e", "publicKeyJSON.e");
shouldBe("exportedJWK.alg", "'RS256'");
shouldBe("exportedJWK.extractable", "true");
shouldBe("exportedJWK.use", "'sig'");
finishJSTest();
});
</script>
<script src="../../resources/js-test-post.js"></script>
</body>
</html>
2013-11-17 Alexey Proskuryakov <ap@apple.com>
Support exporting public RSASSA-PKCS1-v1_5 keys
https://bugs.webkit.org/show_bug.cgi?id=124475
Reviewed by Sam Weinig.
Test: crypto/subtle/rsa-export-key.html
* bindings/js/JSCryptoKeySerializationJWK.h:
* bindings/js/JSCryptoKeySerializationJWK.cpp:
(WebCore::JSCryptoKeySerializationJWK::buildJSONForRSAComponents):
(WebCore::JSCryptoKeySerializationJWK::addJWKAlgorithmToJSON):
(WebCore::JSCryptoKeySerializationJWK::serialize):
Added said support (this part works with private keys too).
* crypto/keys/CryptoKeyRSA.h:
* crypto/mac/CryptoKeyRSAMac.cpp:
(WebCore::CryptoKeyRSA::getPublicKeyComponents): Moved the logic for getting a
public key from private one here for reuse in keySizeInBits().
(WebCore::CryptoKeyRSA::isRestrictedToHash):
(WebCore::CryptoKeyRSA::keySizeInBits):
(WebCore::CryptoKeyRSA::exportData):
Exposed information necessary for JWK serialization.
2013-11-17 Alexey Proskuryakov <ap@apple.com>
RSASSA-PKCS1-v1_5 JWK import doesn't check key size
......
......@@ -37,6 +37,7 @@
#include "CryptoKeyDataOctetSequence.h"
#include "CryptoKeyDataRSAComponents.h"
#include "CryptoKeyHMAC.h"
#include "CryptoKeyRSA.h"
#include "ExceptionCode.h"
#include "JSDOMBinding.h"
#include <heap/StrongInlines.h>
......@@ -433,6 +434,40 @@ void JSCryptoKeySerializationJWK::buildJSONForOctetSequence(ExecState* exec, con
addToJSON(exec, result, "k", base64URLEncode(keyData));
}
void JSCryptoKeySerializationJWK::buildJSONForRSAComponents(JSC::ExecState* exec, const CryptoKeyDataRSAComponents& data, JSC::JSObject* result)
{
addToJSON(exec, result, "kty", "RSA");
addToJSON(exec, result, "n", base64URLEncode(data.modulus()));
addToJSON(exec, result, "e", base64URLEncode(data.exponent()));
if (data.type() == CryptoKeyDataRSAComponents::Type::Public)
return;
addToJSON(exec, result, "d", base64URLEncode(data.privateExponent()));
if (!data.hasAdditionalPrivateKeyParameters())
return;
addToJSON(exec, result, "p", base64URLEncode(data.firstPrimeInfo().primeFactor));
addToJSON(exec, result, "q", base64URLEncode(data.secondPrimeInfo().primeFactor));
addToJSON(exec, result, "dp", base64URLEncode(data.firstPrimeInfo().factorCRTExponent));
addToJSON(exec, result, "dq", base64URLEncode(data.secondPrimeInfo().factorCRTExponent));
addToJSON(exec, result, "qi", base64URLEncode(data.secondPrimeInfo().factorCRTCoefficient));
if (data.otherPrimeInfos().isEmpty())
return;
JSArray* oth = constructEmptyArray(exec, 0, exec->lexicalGlobalObject(), data.otherPrimeInfos().size());
for (size_t i = 0, size = data.otherPrimeInfos().size(); i < size; ++i) {
JSObject* jsPrimeInfo = constructEmptyObject(exec);
addToJSON(exec, jsPrimeInfo, "r", base64URLEncode(data.otherPrimeInfos()[i].primeFactor));
addToJSON(exec, jsPrimeInfo, "d", base64URLEncode(data.otherPrimeInfos()[i].factorCRTExponent));
addToJSON(exec, jsPrimeInfo, "t", base64URLEncode(data.otherPrimeInfos()[i].factorCRTCoefficient));
oth->putDirectIndex(exec, i, jsPrimeInfo);
}
result->putDirect(exec->vm(), Identifier(exec, "oth"), oth);
}
void JSCryptoKeySerializationJWK::addToJSON(ExecState* exec, JSObject* json, const char* key, const String& value)
{
VM& vm = exec->vm();
......@@ -482,6 +517,28 @@ void JSCryptoKeySerializationJWK::addJWKAlgorithmToJSON(ExecState* exec, JSObjec
break;
}
break;
case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: {
const CryptoKeyRSA& rsaKey = toCryptoKeyRSA(key);
CryptoAlgorithmIdentifier hash;
if (!rsaKey.isRestrictedToHash(hash))
break;
if (rsaKey.keySizeInBits() < 2048)
break;
switch (hash) {
case CryptoAlgorithmIdentifier::SHA_256:
jwkAlgorithm = "RS256";
break;
case CryptoAlgorithmIdentifier::SHA_384:
jwkAlgorithm = "RS384";
break;
case CryptoAlgorithmIdentifier::SHA_512:
jwkAlgorithm = "RS512";
break;
default:
break;
}
break;
}
default:
break;
}
......@@ -534,11 +591,14 @@ String JSCryptoKeySerializationJWK::serialize(ExecState* exec, const CryptoKey&
if (isCryptoKeyDataOctetSequence(*keyData))
buildJSONForOctetSequence(exec, toCryptoKeyDataOctetSequence(*keyData).octetSequence(), result);
else if (isCryptoKeyDataRSAComponents(*keyData))
buildJSONForRSAComponents(exec, toCryptoKeyDataRSAComponents(*keyData), result);
else {
throwTypeError(exec, "Key doesn't support exportKey");
return String();
}
ASSERT(!exec->hadException());
if (exec->hadException())
return String();
return JSONStringify(exec, result, 4);
}
......
......@@ -42,6 +42,7 @@ namespace WebCore {
class CryptoAlgorithmParameters;
class CryptoKey;
class CryptoKeyDataRSAComponents;
class JSCryptoKeySerializationJWK FINAL : public CryptoKeySerialization {
WTF_MAKE_NONCOPYABLE(JSCryptoKeySerializationJWK);
......@@ -66,6 +67,7 @@ private:
virtual std::unique_ptr<CryptoKeyData> keyData() const OVERRIDE;
static void buildJSONForOctetSequence(JSC::ExecState*, const Vector<uint8_t>&, JSC::JSObject* result);
static void buildJSONForRSAComponents(JSC::ExecState*, const CryptoKeyDataRSAComponents&, JSC::JSObject* result);
static void addJWKAlgorithmToJSON(JSC::ExecState*, JSC::JSObject*, const CryptoKey& key);
static void addJWKUseToJSON(JSC::ExecState*, JSC::JSObject*, CryptoKeyUsage);
static void addToJSON(JSC::ExecState*, JSC::JSObject*, const char* key, const String& value);
......
......@@ -51,16 +51,19 @@ public:
virtual ~CryptoKeyRSA();
void restrictToHash(CryptoAlgorithmIdentifier);
bool isRestrictedToHash(CryptoAlgorithmIdentifier&) const;
static void generatePair(CryptoAlgorithmIdentifier, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool extractable, CryptoKeyUsage, std::unique_ptr<PromiseWrapper>);
size_t keySizeInBits() const;
virtual CryptoKeyClass keyClass() const OVERRIDE { return CryptoKeyClass::RSA; }
static void generatePair(CryptoAlgorithmIdentifier, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool extractable, CryptoKeyUsage, std::unique_ptr<PromiseWrapper>);
PlatformRSAKey platformKey() const { return m_platformKey; }
private:
CryptoKeyRSA(CryptoAlgorithmIdentifier, CryptoKeyType, PlatformRSAKey, bool extractable, CryptoKeyUsage);
virtual CryptoKeyClass keyClass() const OVERRIDE { return CryptoKeyClass::RSA; }
virtual void buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder&) const OVERRIDE;
virtual std::unique_ptr<CryptoKeyData> exportData() const OVERRIDE;
......
......@@ -60,13 +60,19 @@ namespace WebCore {
static CCCryptorStatus getPublicKeyComponents(CCRSACryptorRef rsaKey, Vector<uint8_t>& modulus, Vector<uint8_t>& publicExponent)
{
ASSERT(CCRSAGetKeyType(rsaKey) == ccRSAKeyPublic);
ASSERT(CCRSAGetKeyType(rsaKey) == ccRSAKeyPublic || CCRSAGetKeyType(rsaKey) == ccRSAKeyPrivate);
bool keyIsPublic = CCRSAGetKeyType(rsaKey) == ccRSAKeyPublic;
CCRSACryptorRef publicKey = keyIsPublic ? rsaKey : CCRSACryptorGetPublicKeyFromPrivateKey(rsaKey);
modulus.resize(16384);
size_t modulusLength = modulus.size();
publicExponent.resize(16384);
size_t exponentLength = publicExponent.size();
CCCryptorStatus status = CCRSAGetKeyComponents(rsaKey, modulus.data(), &modulusLength, publicExponent.data(), &exponentLength, 0, 0, 0, 0);
CCCryptorStatus status = CCRSAGetKeyComponents(publicKey, modulus.data(), &modulusLength, publicExponent.data(), &exponentLength, 0, 0, 0, 0);
if (!keyIsPublic) {
// CCRSACryptorGetPublicKeyFromPrivateKey has "Get" in the name, but its result needs to be released (see <rdar://problem/15449697>).
CCRSACryptorRelease(publicKey);
}
if (status)
return status;
......@@ -122,21 +128,35 @@ void CryptoKeyRSA::restrictToHash(CryptoAlgorithmIdentifier identifier)
m_hash = identifier;
}
void CryptoKeyRSA::buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder& builder) const
bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
{
CryptoKey::buildAlgorithmDescription(builder);
if (!m_restrictedToSpecificHash)
return false;
ASSERT(CCRSAGetKeyType(m_platformKey) == ccRSAKeyPublic || CCRSAGetKeyType(m_platformKey) == ccRSAKeyPrivate);
bool platformKeyIsPublic = CCRSAGetKeyType(m_platformKey) == ccRSAKeyPublic;
CCRSACryptorRef publicKey = platformKeyIsPublic ? m_platformKey : CCRSACryptorGetPublicKeyFromPrivateKey(m_platformKey);
identifier = m_hash;
return true;
}
size_t CryptoKeyRSA::keySizeInBits() const
{
Vector<uint8_t> modulus;
Vector<uint8_t> publicExponent;
CCCryptorStatus status = getPublicKeyComponents(publicKey, modulus, publicExponent);
if (!platformKeyIsPublic) {
// CCRSACryptorGetPublicKeyFromPrivateKey has "Get" in the name, but its result needs to be released (see <rdar://problem/15449697>).
CCRSACryptorRelease(publicKey);
CCCryptorStatus status = getPublicKeyComponents(m_platformKey, modulus, publicExponent);
if (status) {
WTFLogAlways("Couldn't get RSA key components, status %d", status);
return 0;
}
return modulus.size() * 8;
}
void CryptoKeyRSA::buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder& builder) const
{
CryptoKey::buildAlgorithmDescription(builder);
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;
......@@ -154,9 +174,24 @@ void CryptoKeyRSA::buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder&
std::unique_ptr<CryptoKeyData> CryptoKeyRSA::exportData() const
{
// Not implemented yet.
ASSERT(extractable());
return nullptr;
switch (CCRSAGetKeyType(m_platformKey)) {
case ccRSAKeyPublic: {
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;
}
return CryptoKeyDataRSAComponents::createPublic(modulus, publicExponent);
}
case ccRSAKeyPrivate:
// Not supported yet.
default:
return nullptr;
}
}
static bool bigIntegerToUInt32(const Vector<uint8_t>& bigInteger, uint32_t& result)
......
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