Title: [159403] trunk
Revision
159403
Author
[email protected]
Date
2013-11-18 00:42:41 -0800 (Mon, 18 Nov 2013)

Log Message

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.

Modified Paths

Added Paths

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)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to