[GCrypt] Implement CryptoKeyRSA PKCS#8 imports
authorzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 15 Jul 2017 05:20:32 +0000 (05:20 +0000)
committerzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 15 Jul 2017 05:20:32 +0000 (05:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=173696

Reviewed by Jiewen Tan.

Source/WebCore:

Implement the PKCS#8 import operation for RSA keys for platforms that use
libgcrypt.

In CryptoKeyRSA::importPkcs8(), the provided key data is decoded against the
'PrivateKeyInfo` ASN.1 structure. We then validate the `version` element and
check that the `privateKeyAlgorithm.algorithm` element contains a supported
object identifier. This check is for now mostly superficial, only ensuring
that the object identifier is either id-rsaEncryption, id-RSAES-OAEP or
id-RSASSA-PSS. This has to be further extended to also check the
id-sha{1,256,384,512}WithRSAEncryption identifiers as well as decoding the
`privateKeyAlgorithm.parameters` element against a specific ASN.1 structure, if
necessary (RSASSA-PSS-params or RSAES-OAEP-params), and cross-checking the
specified digest algorithm with the algorithm that's specified through the main
object identifier or the structure contained in `privateKeyAlgorithm.parameters`.
This is avoided for now because no test in WebKit or the web-platform-tests
suite covers this detail of the specification.

Data under the `privateKey` element is decoded against the `RSAPrivateKey` ASN.1
structure, and the `version` element of that structure is validated. We then
retrieve data from that structure for the modulus, public exponent, private
exponent, both primes, both exponents and the coefficient parameters, bailing if
any of them is missing. Because libgcrypt switches the use of p and q parameters,
deviating from the standard use, we have to recompute the u parameter (the
coefficient). With that calculated, we're then able to construct the `private-key`
s-expression, embedding into it all the necessary parameters, and transferring
the ownership of this object to the new CryptoKeyRSA object that's then returned
from the importPkcs8() method.

No new tests -- related tests are now passing and are unskipped.

* crypto/gcrypt/CryptoKeyRSAGCrypt.cpp:
(WebCore::CryptoKeyRSA::importPkcs8):

LayoutTests:

* platform/gtk/TestExpectations: Unskip the RSA PKCS#8 import tests
that are now passing.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@219535 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/platform/gtk/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/crypto/gcrypt/CryptoKeyRSAGCrypt.cpp

index 7dd8862..4800b23 100644 (file)
@@ -1,3 +1,13 @@
+2017-07-14  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyRSA PKCS#8 imports
+        https://bugs.webkit.org/show_bug.cgi?id=173696
+
+        Reviewed by Jiewen Tan.
+
+        * platform/gtk/TestExpectations: Unskip the RSA PKCS#8 import tests
+        that are now passing.
+
 2017-07-14  Chris Dumez  <cdumez@apple.com>
 
         Possible crash in ~UserGestureIndicator() when on non-main thread
index 3feb1b3..19066fd 100644 (file)
@@ -767,7 +767,6 @@ webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p256.html
 webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p384.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-export-key-malformed-parameters.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-import-jwk-key-export-pkcs8-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/rsa-import-pkcs8-key-export-jwk-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-import-pkcs8-key-export-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-import-spki-key-export-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-indexeddb-non-exportable-private.html [ Skip ]
@@ -775,12 +774,9 @@ webkit.org/b/133122 crypto/subtle/rsa-indexeddb-non-exportable.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-indexeddb-private.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-indexeddb.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-oaep-generate-export-key-pkcs8.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/rsa-oaep-import-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-pss-generate-export-key-pkcs8.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsaes-pkcs1-v1_5-generate-export-key-pkcs8.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/rsaes-pkcs1-v1_5-import-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsassa-pkcs1-v1_5-generate-export-key-pkcs8.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/rsassa-pkcs1-v1_5-import-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-decrypt.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-encrypt.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/aes-cfb-import-key-unwrap-key.html [ Skip ]
@@ -793,7 +789,6 @@ webkit.org/b/133122 crypto/workers/subtle/hrsa-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/multiple-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/raw-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/rsa-export-pkcs8-key.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/rsa-import-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/rsa-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/rsa-pss-import-key-sign.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/rsa-pss-import-key-verify.html [ Skip ]
index 0be17de..743f8b4 100644 (file)
@@ -1,3 +1,43 @@
+2017-07-14  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyRSA PKCS#8 imports
+        https://bugs.webkit.org/show_bug.cgi?id=173696
+
+        Reviewed by Jiewen Tan.
+
+        Implement the PKCS#8 import operation for RSA keys for platforms that use
+        libgcrypt.
+
+        In CryptoKeyRSA::importPkcs8(), the provided key data is decoded against the
+        'PrivateKeyInfo` ASN.1 structure. We then validate the `version` element and
+        check that the `privateKeyAlgorithm.algorithm` element contains a supported
+        object identifier. This check is for now mostly superficial, only ensuring
+        that the object identifier is either id-rsaEncryption, id-RSAES-OAEP or
+        id-RSASSA-PSS. This has to be further extended to also check the
+        id-sha{1,256,384,512}WithRSAEncryption identifiers as well as decoding the
+        `privateKeyAlgorithm.parameters` element against a specific ASN.1 structure, if
+        necessary (RSASSA-PSS-params or RSAES-OAEP-params), and cross-checking the
+        specified digest algorithm with the algorithm that's specified through the main
+        object identifier or the structure contained in `privateKeyAlgorithm.parameters`.
+        This is avoided for now because no test in WebKit or the web-platform-tests
+        suite covers this detail of the specification.
+
+        Data under the `privateKey` element is decoded against the `RSAPrivateKey` ASN.1
+        structure, and the `version` element of that structure is validated. We then
+        retrieve data from that structure for the modulus, public exponent, private
+        exponent, both primes, both exponents and the coefficient parameters, bailing if
+        any of them is missing. Because libgcrypt switches the use of p and q parameters,
+        deviating from the standard use, we have to recompute the u parameter (the
+        coefficient). With that calculated, we're then able to construct the `private-key`
+        s-expression, embedding into it all the necessary parameters, and transferring
+        the ownership of this object to the new CryptoKeyRSA object that's then returned
+        from the importPkcs8() method.
+
+        No new tests -- related tests are now passing and are unskipped.
+
+        * crypto/gcrypt/CryptoKeyRSAGCrypt.cpp:
+        (WebCore::CryptoKeyRSA::importPkcs8):
+
 2017-07-14  Chris Dumez  <cdumez@apple.com>
 
         Possible crash in ~UserGestureIndicator() when on non-main thread
index 6f65ac3..0a38ae6 100644 (file)
@@ -321,11 +321,99 @@ RefPtr<CryptoKeyRSA> CryptoKeyRSA::importSpki(CryptoAlgorithmIdentifier identifi
     return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Public, platformKey.release(), extractable, usages));
 }
 
-RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier, std::optional<CryptoAlgorithmIdentifier>, Vector<uint8_t>&&, bool, CryptoKeyUsageBitmap)
+RefPtr<CryptoKeyRSA> CryptoKeyRSA::importPkcs8(CryptoAlgorithmIdentifier identifier, std::optional<CryptoAlgorithmIdentifier> hash, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
 {
-    notImplemented();
+    // Decode the `PrivateKeyInfo` structure using the provided key data.
+    PAL::TASN1::Structure pkcs8;
+    if (!PAL::TASN1::decodeStructure(&pkcs8, "WebCrypto.PrivateKeyInfo", keyData))
+        return nullptr;
+
+    // Validate `version`.
+    {
+        auto version = PAL::TASN1::elementData(pkcs8, "version");
+        if (!version)
+            return nullptr;
+
+        if (version->size() != 1 || version->at(0) != 0x00)
+            return nullptr;
+    }
+
+    // Validate `privateKeyAlgorithm.algorithm`.
+    {
+        auto algorithm = PAL::TASN1::elementData(pkcs8, "privateKeyAlgorithm.algorithm");
+        if (!algorithm)
+            return nullptr;
+
+        if (!supportedAlgorithmIdentifier(algorithm->data(), algorithm->size()))
+            return nullptr;
+    }
+
+    // Decode the `RSAPrivateKey` structure using the `privateKey` data.
+    PAL::TASN1::Structure rsaPrivateKey;
+    {
+        auto privateKey = PAL::TASN1::elementData(pkcs8, "privateKey");
+        if (!privateKey)
+            return nullptr;
+
+        if (!PAL::TASN1::decodeStructure(&rsaPrivateKey, "WebCrypto.RSAPrivateKey", *privateKey))
+            return nullptr;
+    }
+
+    // Validate `privateKey.version`.
+    {
+        auto version = PAL::TASN1::elementData(rsaPrivateKey, "version");
+        if (!version)
+            return nullptr;
+
+        if (version->size() != 1 || version->at(0) != 0x00)
+            return nullptr;
+    }
+
+    // Retrieve the `modulus`, `publicExponent`, `privateExponent`, `prime1`, `prime2`,
+    // `exponent1`, `exponent2` and `coefficient` data and embed it into the `public-key` s-expression.
+    PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
+    {
+        auto modulus = PAL::TASN1::elementData(rsaPrivateKey, "modulus");
+        auto publicExponent = PAL::TASN1::elementData(rsaPrivateKey, "publicExponent");
+        auto privateExponent = PAL::TASN1::elementData(rsaPrivateKey, "privateExponent");
+        auto prime1 = PAL::TASN1::elementData(rsaPrivateKey, "prime1");
+        auto prime2 = PAL::TASN1::elementData(rsaPrivateKey, "prime2");
+        auto exponent1 = PAL::TASN1::elementData(rsaPrivateKey, "exponent1");
+        auto exponent2 = PAL::TASN1::elementData(rsaPrivateKey, "exponent2");
+        auto coefficient = PAL::TASN1::elementData(rsaPrivateKey, "coefficient");
+
+        if (!modulus || !publicExponent || !privateExponent
+            || !prime1 || !prime2 || !exponent1 || !exponent2 || !coefficient)
+            return nullptr;
+
+        // libgcrypt inverts the use of p and q parameters, so we have to recalculate the `coefficient` value.
+        PAL::GCrypt::Handle<gcry_mpi_t> uMPI(gcry_mpi_new(0));
+        {
+            PAL::GCrypt::Handle<gcry_mpi_t> pMPI;
+            gcry_error_t error = gcry_mpi_scan(&pMPI, GCRYMPI_FMT_USG, prime1->data(), prime1->size(), nullptr);
+            if (error != GPG_ERR_NO_ERROR)
+                return nullptr;
+
+            PAL::GCrypt::Handle<gcry_mpi_t> qMPI;
+            error = gcry_mpi_scan(&qMPI, GCRYMPI_FMT_USG, prime2->data(), prime2->size(), nullptr);
+            if (error != GPG_ERR_NO_ERROR)
+                return nullptr;
+
+            gcry_mpi_invm(uMPI, qMPI, pMPI);
+        }
+
+        gcry_error_t error = gcry_sexp_build(&platformKey, nullptr, "(private-key(rsa(n %b)(e %b)(d %b)(p %b)(q %b)(u %M)))",
+            modulus->size(), modulus->data(),
+            publicExponent->size(), publicExponent->data(),
+            privateExponent->size(), privateExponent->data(),
+            prime2->size(), prime2->data(), prime1->size(), prime1->data(), uMPI.handle());
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return nullptr;
+        }
+    }
 
-    return nullptr;
+    return adoptRef(new CryptoKeyRSA(identifier, hash.value_or(CryptoAlgorithmIdentifier::SHA_1), !!hash, CryptoKeyType::Private, platformKey.release(), extractable, usages));
 }
 
 ExceptionOr<Vector<uint8_t>> CryptoKeyRSA::exportSpki() const