Diff
Modified: trunk/LayoutTests/ChangeLog (159402 => 159403)
--- trunk/LayoutTests/ChangeLog 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/LayoutTests/ChangeLog 2013-11-18 08:42:41 UTC (rev 159403)
@@ -1,3 +1,13 @@
+2013-11-17 Alexey Proskuryakov <[email protected]>
+
+ 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 <[email protected]>
Unreviewed GTK gardening. Rebaselining CSS tests' expectations.
Added: trunk/LayoutTests/crypto/subtle/rsa-export-key-expected.txt (0 => 159403)
--- trunk/LayoutTests/crypto/subtle/rsa-export-key-expected.txt (rev 0)
+++ trunk/LayoutTests/crypto/subtle/rsa-export-key-expected.txt 2013-11-18 08:42:41 UTC (rev 159403)
@@ -0,0 +1,24 @@
+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
+
Property changes on: trunk/LayoutTests/crypto/subtle/rsa-export-key-expected.txt
___________________________________________________________________
Added: svn:mime-type
Added: svn:eol-style
Added: trunk/LayoutTests/crypto/subtle/rsa-export-key.html (0 => 159403)
--- trunk/LayoutTests/crypto/subtle/rsa-export-key.html (rev 0)
+++ trunk/LayoutTests/crypto/subtle/rsa-export-key.html 2013-11-18 08:42:41 UTC (rev 159403)
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+</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=""
+</body>
+</html>
Property changes on: trunk/LayoutTests/crypto/subtle/rsa-export-key.html
___________________________________________________________________
Added: svn:mime-type
Modified: trunk/Source/WebCore/ChangeLog (159402 => 159403)
--- trunk/Source/WebCore/ChangeLog 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/Source/WebCore/ChangeLog 2013-11-18 08:42:41 UTC (rev 159403)
@@ -1,5 +1,30 @@
2013-11-17 Alexey Proskuryakov <[email protected]>
+ 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 <[email protected]>
+
RSASSA-PKCS1-v1_5 JWK import doesn't check key size
https://bugs.webkit.org/show_bug.cgi?id=124472
Modified: trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.cpp (159402 => 159403)
--- trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.cpp 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.cpp 2013-11-18 08:42:41 UTC (rev 159403)
@@ -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 @@
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 @@
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 @@
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);
}
Modified: trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.h (159402 => 159403)
--- trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.h 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/Source/WebCore/bindings/js/JSCryptoKeySerializationJWK.h 2013-11-18 08:42:41 UTC (rev 159403)
@@ -42,6 +42,7 @@
class CryptoAlgorithmParameters;
class CryptoKey;
+class CryptoKeyDataRSAComponents;
class JSCryptoKeySerializationJWK FINAL : public CryptoKeySerialization {
WTF_MAKE_NONCOPYABLE(JSCryptoKeySerializationJWK);
@@ -66,6 +67,7 @@
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);
Modified: trunk/Source/WebCore/crypto/keys/CryptoKeyRSA.h (159402 => 159403)
--- trunk/Source/WebCore/crypto/keys/CryptoKeyRSA.h 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/Source/WebCore/crypto/keys/CryptoKeyRSA.h 2013-11-18 08:42:41 UTC (rev 159403)
@@ -51,16 +51,19 @@
virtual ~CryptoKeyRSA();
void restrictToHash(CryptoAlgorithmIdentifier);
+ bool isRestrictedToHash(CryptoAlgorithmIdentifier&) const;
+ size_t keySizeInBits() const;
+
static void generatePair(CryptoAlgorithmIdentifier, unsigned modulusLength, const Vector<uint8_t>& publicExponent, bool extractable, CryptoKeyUsage, std::unique_ptr<PromiseWrapper>);
- virtual CryptoKeyClass keyClass() const OVERRIDE { return CryptoKeyClass::RSA; }
-
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;
Modified: trunk/Source/WebCore/crypto/mac/CryptoKeyRSAMac.cpp (159402 => 159403)
--- trunk/Source/WebCore/crypto/mac/CryptoKeyRSAMac.cpp 2013-11-18 08:27:52 UTC (rev 159402)
+++ trunk/Source/WebCore/crypto/mac/CryptoKeyRSAMac.cpp 2013-11-18 08:42:41 UTC (rev 159403)
@@ -60,13 +60,19 @@
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 @@
m_hash = identifier;
}
+bool CryptoKeyRSA::isRestrictedToHash(CryptoAlgorithmIdentifier& identifier) const
+{
+ if (!m_restrictedToSpecificHash)
+ return false;
+
+ identifier = m_hash;
+ return true;
+}
+
+size_t CryptoKeyRSA::keySizeInBits() const
+{
+ 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 0;
+ }
+
+ return modulus.size() * 8;
+}
+
void CryptoKeyRSA::buildAlgorithmDescription(CryptoAlgorithmDescriptionBuilder& builder) const
{
CryptoKey::buildAlgorithmDescription(builder);
- ASSERT(CCRSAGetKeyType(m_platformKey) == ccRSAKeyPublic || CCRSAGetKeyType(m_platformKey) == ccRSAKeyPrivate);
- bool platformKeyIsPublic = CCRSAGetKeyType(m_platformKey) == ccRSAKeyPublic;
- CCRSACryptorRef publicKey = platformKeyIsPublic ? m_platformKey : CCRSACryptorGetPublicKeyFromPrivateKey(m_platformKey);
-
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;
@@ -154,9 +174,24 @@
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)