[GCrypt] Implement CryptoKeyEC SPKI imports
authorzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Jun 2017 06:52:29 +0000 (06:52 +0000)
committerzandobersek@gmail.com <zandobersek@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Jun 2017 06:52:29 +0000 (06:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=172927

Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.

.:

* Source/cmake/FindLibtasn1.cmake: Added.
* Source/cmake/OptionsGTK.cmake: Require libtasn1 when SUBTLE_CRYPTO is enabled.
* Source/cmake/OptionsWPE.cmake: Ditto.

Source/WebCore:

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

Implement libgcrypt-based support for SPKI imports of EC keys.

Using libtasn1 through the utility functions and wrappers, the given key data
is decoded against the SubjectPublicKeyInfo ASN.1 definition. The algorithm
member is then properly validated, making sure that the key algorithm idenfitier
is supported and that the algorithm parameters specify the correct EC curve.

The public key bit string is then retrieved and validated, ensuring it represents
an uncompressed EC point that is of valid size for the specified EC curve. The
point is then tested through an EC context to make sure it's positioned on the
specified EC curve.

Finally, the curve name and uncompressed point data are embedded into a
`public-key` s-expression that will be used through the libgcrypt API. This is
then used, along with other information, to create a valid CryptoKeyEC object.

* PlatformGTK.cmake: Use LIBTASN1_INCLUDE_DIRECTORIES and LIBTASN1_LIBRARIES.
* PlatformWPE.cmake: Ditto.
* crypto/gcrypt/CryptoKeyECGCrypt.cpp:
(WebCore::supportedAlgorithmIdentifier):
(WebCore::curveForIdentifier):
(WebCore::CryptoKeyEC::platformImportSpki):

Source/WebCore/PAL:

Add a file that provides utility functions for operating with libtasn1 APIs.

The precomputed ASN.1 declarations, generated from the WebCrypto.asn file with
the asn1Parser tool, are used to enable construction of ASN.1 structures that
are then used to decode the SPKI or PKCS#8 data through the decodeStructure()
function. Raw data of each element in that structure can be retrieved throug the
elementData() function.

The Structure class is added as a wrapper for asn1_node objects that are used
as decoding targets, simplifying lifetime management of these objects.

* pal/PlatformGTK.cmake:
* pal/PlatformWPE.cmake:
* pal/crypto/tasn1/Utilities.cpp: Added.
(PAL::TASN1::asn1Definitions):
(PAL::TASN1::decodeStructure):
(PAL::TASN1::elementData):
* pal/crypto/tasn1/Utilities.h: Added.
(PAL::TASN1::Structure::~Structure):
(PAL::TASN1::Structure::operator&):
(PAL::TASN1::Structure::operator asn1_node):
* pal/crypto/tasn1/WebCrypto.asn: Added.

LayoutTests:

* platform/gtk/TestExpectations:
Unskip or enable the EC-based SPKI import tests that are now passing.

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

16 files changed:
ChangeLog
LayoutTests/ChangeLog
LayoutTests/platform/gtk/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/pal/PlatformGTK.cmake
Source/WebCore/PAL/pal/PlatformWPE.cmake
Source/WebCore/PAL/pal/crypto/tasn1/Utilities.cpp [new file with mode: 0644]
Source/WebCore/PAL/pal/crypto/tasn1/Utilities.h [new file with mode: 0644]
Source/WebCore/PAL/pal/crypto/tasn1/WebCrypto.asn [new file with mode: 0644]
Source/WebCore/PlatformGTK.cmake
Source/WebCore/PlatformWPE.cmake
Source/WebCore/crypto/gcrypt/CryptoKeyECGCrypt.cpp
Source/cmake/FindLibtasn1.cmake [new file with mode: 0644]
Source/cmake/OptionsGTK.cmake
Source/cmake/OptionsWPE.cmake

index e23075e..0886b89 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2017-06-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyEC SPKI imports
+        https://bugs.webkit.org/show_bug.cgi?id=172927
+
+        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
+
+        * Source/cmake/FindLibtasn1.cmake: Added.
+        * Source/cmake/OptionsGTK.cmake: Require libtasn1 when SUBTLE_CRYPTO is enabled.
+        * Source/cmake/OptionsWPE.cmake: Ditto.
+
 2017-06-20  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [WPE] Add initial implementation of glib API
index 87a5fd2..ef0ea5c 100644 (file)
@@ -1,3 +1,13 @@
+2017-06-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyEC SPKI imports
+        https://bugs.webkit.org/show_bug.cgi?id=172927
+
+        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
+
+        * platform/gtk/TestExpectations:
+        Unskip or enable the EC-based SPKI import tests that are now passing.
+
 2017-06-20  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Disable font variations on macOS Sierra and iOS 10
index 0418845..0ec7221 100644 (file)
@@ -758,20 +758,15 @@ webkit.org/b/133122 crypto/subtle/ecdh-generate-export-key-spki-p256.html [ Skip
 webkit.org/b/133122 crypto/subtle/ecdh-generate-export-key-spki-p384.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdh-import-pkcs8-key-p256.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdh-import-pkcs8-key-p384.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/ecdh-import-spki-key-p256.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/ecdh-import-spki-key-p384.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdsa-generate-export-key-pkcs8.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdsa-generate-export-key-spki.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ecdsa-import-pkcs8-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/ecdsa-import-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-jwk-key-export-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-jwk-key-export-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-raw-key-export-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-jwk-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p256.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p384.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-jwk-key.html [ Skip ]
-webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-raw-key.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-spki-key-p256.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-spki-key-p384.html [ Skip ]
 webkit.org/b/133122 crypto/subtle/rsa-export-key-malformed-parameters.html [ Skip ]
@@ -811,7 +806,6 @@ webkit.org/b/133122 crypto/workers/subtle/aes-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-generate-export-pkcs8-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-generate-export-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-import-pkcs8-key.html [ Skip ]
-webkit.org/b/133122 crypto/workers/subtle/ec-import-spki-key.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/ec-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/hmac-postMessage-worker.html [ Skip ]
 webkit.org/b/133122 crypto/workers/subtle/hrsa-postMessage-worker.html [ Skip ]
@@ -844,6 +838,9 @@ crypto/subtle/rsa-pss-import-key-verify.html [ Pass ]
 crypto/workers/subtle/rsa-pss-import-key-sign.html [ Pass ]
 crypto/workers/subtle/rsa-pss-import-key-verify.html [ Pass ]
 
+# libgcrypt-based implementation supports ecDH algorithm identifier for ECDH SPKI imports.
+crypto/subtle/ecdh-import-spki-key-ecdh-identifier.html [ Pass ]
+
 # These are legacy APIs that we don't support, apart from a few digest algorithms.
 webkit.org/b/133122 crypto/webkitSubtle [ Skip ]
 webkit.org/b/133319 crypto/webkitSubtle/sha-1.html [ Pass ]
index 8e299a2..705ee52 100644 (file)
@@ -1,3 +1,35 @@
+2017-06-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyEC SPKI imports
+        https://bugs.webkit.org/show_bug.cgi?id=172927
+
+        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
+
+        No new tests -- affected tests are now passing and are unskipped.
+
+        Implement libgcrypt-based support for SPKI imports of EC keys.
+
+        Using libtasn1 through the utility functions and wrappers, the given key data
+        is decoded against the SubjectPublicKeyInfo ASN.1 definition. The algorithm
+        member is then properly validated, making sure that the key algorithm idenfitier
+        is supported and that the algorithm parameters specify the correct EC curve.
+
+        The public key bit string is then retrieved and validated, ensuring it represents
+        an uncompressed EC point that is of valid size for the specified EC curve. The
+        point is then tested through an EC context to make sure it's positioned on the
+        specified EC curve.
+
+        Finally, the curve name and uncompressed point data are embedded into a
+        `public-key` s-expression that will be used through the libgcrypt API. This is
+        then used, along with other information, to create a valid CryptoKeyEC object.
+
+        * PlatformGTK.cmake: Use LIBTASN1_INCLUDE_DIRECTORIES and LIBTASN1_LIBRARIES.
+        * PlatformWPE.cmake: Ditto.
+        * crypto/gcrypt/CryptoKeyECGCrypt.cpp:
+        (WebCore::supportedAlgorithmIdentifier):
+        (WebCore::curveForIdentifier):
+        (WebCore::CryptoKeyEC::platformImportSpki):
+
 2017-06-20  Devin Rousso  <drousso@apple.com>
 
         WebGPU contexts should have a back reference to the canvas element
index 7148154..8ddbe0e 100644 (file)
@@ -1,3 +1,33 @@
+2017-06-20  Zan Dobersek  <zdobersek@igalia.com>
+
+        [GCrypt] Implement CryptoKeyEC SPKI imports
+        https://bugs.webkit.org/show_bug.cgi?id=172927
+
+        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
+
+        Add a file that provides utility functions for operating with libtasn1 APIs.
+
+        The precomputed ASN.1 declarations, generated from the WebCrypto.asn file with
+        the asn1Parser tool, are used to enable construction of ASN.1 structures that
+        are then used to decode the SPKI or PKCS#8 data through the decodeStructure()
+        function. Raw data of each element in that structure can be retrieved throug the
+        elementData() function.
+
+        The Structure class is added as a wrapper for asn1_node objects that are used
+        as decoding targets, simplifying lifetime management of these objects.
+
+        * pal/PlatformGTK.cmake:
+        * pal/PlatformWPE.cmake:
+        * pal/crypto/tasn1/Utilities.cpp: Added.
+        (PAL::TASN1::asn1Definitions):
+        (PAL::TASN1::decodeStructure):
+        (PAL::TASN1::elementData):
+        * pal/crypto/tasn1/Utilities.h: Added.
+        (PAL::TASN1::Structure::~Structure):
+        (PAL::TASN1::Structure::operator&):
+        (PAL::TASN1::Structure::operator asn1_node):
+        * pal/crypto/tasn1/WebCrypto.asn: Added.
+
 2017-06-20  Myles C. Maxfield  <mmaxfield@apple.com>
 
         Disable font variations on macOS Sierra and iOS 10
index 775c189..afa5a9a 100644 (file)
@@ -1,3 +1,5 @@
 list(APPEND PAL_SOURCES
     crypto/gcrypt/CryptoDigestGCrypt.cpp
+
+    crypto/tasn1/Utilities.cpp
 )
index 775c189..afa5a9a 100644 (file)
@@ -1,3 +1,5 @@
 list(APPEND PAL_SOURCES
     crypto/gcrypt/CryptoDigestGCrypt.cpp
+
+    crypto/tasn1/Utilities.cpp
 )
diff --git a/Source/WebCore/PAL/pal/crypto/tasn1/Utilities.cpp b/Source/WebCore/PAL/pal/crypto/tasn1/Utilities.cpp
new file mode 100644 (file)
index 0000000..4fdf055
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 Metrological Group B.V.
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Utilities.h"
+
+#include <mutex>
+
+namespace PAL {
+namespace TASN1 {
+
+static asn1_node asn1Definitions()
+{
+    // Generated with `asn1Parser WebCrypto.asn`.
+
+    static asn1_node s_definitions;
+    static const asn1_static_node s_WebCryptoASN1[] = {
+        { "WebCrypto", 536872976, nullptr },
+        { nullptr, 1073741836, nullptr },
+        { "SubjectPublicKeyInfo", 1610612741, nullptr },
+        { "algorithm", 1073741826, "AlgorithmIdentifier"},
+        { "subjectPublicKey", 6, nullptr },
+        { "AlgorithmIdentifier", 1610612741, nullptr },
+        { "algorithm", 1073741836, nullptr },
+        { "parameters", 541081613, nullptr },
+        { "algorithm", 1, nullptr },
+        { "PrivateKeyInfo", 1610612741, nullptr },
+        { "version", 1073741826, "Version"},
+        { "privateKeyAlgorithm", 1073741826, "PrivateKeyAlgorithmIdentifier"},
+        { "privateKey", 1073741826, "PrivateKey"},
+        { "attributes", 536895490, "Attributes"},
+        { nullptr, 4104, "0"},
+        { "Version", 1073741827, nullptr },
+        { "PrivateKeyAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+        { "PrivateKey", 1073741831, nullptr },
+        { "Attributes", 1610612751, nullptr },
+        { nullptr, 2, "Attribute"},
+        { "Attribute", 1610612741, nullptr },
+        { "type", 1073741836, nullptr },
+        { "values", 2, "AttributeSetValue"},
+        { "AttributeSetValue", 1610612751, nullptr },
+        { nullptr, 13, nullptr },
+        { "ECParameters", 1610612754, nullptr },
+        { "namedCurve", 12, nullptr },
+        { "ECPrivateKey", 1610612741, nullptr },
+        { "version", 1073741827, nullptr },
+        { "privateKey", 1073741831, nullptr },
+        { "parameters", 1610637314, "ECParameters"},
+        { nullptr, 2056, "0"},
+        { "publicKey", 536895494, nullptr },
+        { nullptr, 2056, "1"},
+        { "RSAPublicKey", 1610612741, nullptr },
+        { "modulus", 1073741827, nullptr },
+        { "publicExponent", 3, nullptr },
+        { "RSAPrivateKey", 1610612741, nullptr },
+        { "version", 1073741826, "Version"},
+        { "modulus", 1073741827, nullptr },
+        { "publicExponent", 1073741827, nullptr },
+        { "privateExponent", 1073741827, nullptr },
+        { "prime1", 1073741827, nullptr },
+        { "prime2", 1073741827, nullptr },
+        { "exponent1", 1073741827, nullptr },
+        { "exponent2", 1073741827, nullptr },
+        { "coefficient", 1073741827, nullptr },
+        { "otherPrimeInfos", 16386, "OtherPrimeInfos"},
+        { "OtherPrimeInfos", 1612709899, nullptr },
+        { "MAX", 1074266122, "1"},
+        { nullptr, 2, "OtherPrimeInfo"},
+        { "OtherPrimeInfo", 536870917, nullptr },
+        { "prime", 1073741827, nullptr },
+        { "exponent", 1073741827, nullptr },
+        { "coefficient", 3, nullptr },
+        { nullptr, 0, nullptr }
+    };
+
+    static std::once_flag s_onceFlag;
+    std::call_once(s_onceFlag, [] { asn1_array2tree(s_WebCryptoASN1, &s_definitions, nullptr); });
+
+    return s_definitions;
+}
+
+bool decodeStructure(asn1_node* root, const char* elementName, const Vector<uint8_t>& data)
+{
+    int ret = asn1_create_element(asn1Definitions(), elementName, root);
+    if (ret != ASN1_SUCCESS)
+        return false;
+
+    int dataSize = data.size();
+    ret = asn1_der_decoding2(root, data.data(), &dataSize, ASN1_DECODE_FLAG_STRICT_DER, nullptr);
+    return ret == ASN1_SUCCESS;
+}
+
+std::optional<Vector<uint8_t>> elementData(asn1_node root, const char* elementName)
+{
+    int length = 0;
+    unsigned type = 0;
+    int ret = asn1_read_value_type(root, elementName, nullptr, &length, &type);
+    if (ret != ASN1_MEM_ERROR)
+        return std::nullopt;
+
+    if (type == ASN1_ETYPE_BIT_STRING) {
+        if (length % 8)
+            return std::nullopt;
+        length /= 8;
+    }
+
+    Vector<uint8_t> data(length);
+    ret = asn1_read_value(root, elementName, data.data(), &length);
+    if (ret != ASN1_SUCCESS)
+        return std::nullopt;
+
+    return data;
+}
+
+} // namespace TASN1
+} // namespace PAL
diff --git a/Source/WebCore/PAL/pal/crypto/tasn1/Utilities.h b/Source/WebCore/PAL/pal/crypto/tasn1/Utilities.h
new file mode 100644 (file)
index 0000000..963f465
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 Metrological Group B.V.
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <libtasn1.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace PAL {
+namespace TASN1 {
+
+class Structure {
+public:
+    Structure() = default;
+
+    ~Structure()
+    {
+        asn1_delete_structure(&m_structure);
+    }
+
+    Structure(const Structure&) = delete;
+    Structure& operator=(const Structure&) = delete;
+
+    Structure(Structure&&) = delete;
+    Structure& operator=(Structure&&) = delete;
+
+    asn1_node* operator&() { return &m_structure; }
+    operator asn1_node() const { return m_structure; }
+
+private:
+    asn1_node m_structure { nullptr };
+};
+
+bool decodeStructure(asn1_node* root, const char* elementName, const Vector<uint8_t>& data);
+std::optional<Vector<uint8_t>> elementData(asn1_node root, const char* elementName);
+
+} // namespace TASN1
+} // namespace PAL
diff --git a/Source/WebCore/PAL/pal/crypto/tasn1/WebCrypto.asn b/Source/WebCore/PAL/pal/crypto/tasn1/WebCrypto.asn
new file mode 100644 (file)
index 0000000..c813b89
--- /dev/null
@@ -0,0 +1,90 @@
+WebCrypto { }
+
+DEFINITIONS EXPLICIT TAGS ::=
+
+BEGIN
+
+-- http://www.ietf.org/rfc/rfc5280.txt Appendix A.1
+
+SubjectPublicKeyInfo ::=  SEQUENCE  {
+  algorithm         AlgorithmIdentifier,
+  subjectPublicKey  BIT STRING
+}
+
+AlgorithmIdentifier ::=  SEQUENCE  {
+  algorithm   OBJECT IDENTIFIER,
+  parameters  ANY DEFINED BY algorithm OPTIONAL
+}
+
+-- http://www.ietf.org/rfc/rfc5208.txt Appendix A
+
+PrivateKeyInfo ::= SEQUENCE {
+  version                   Version,
+  privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
+  privateKey                PrivateKey,
+  attributes           [0]  IMPLICIT Attributes OPTIONAL
+}
+
+Version ::= INTEGER
+
+PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+
+PrivateKey ::= OCTET STRING
+
+Attributes ::= SET OF Attribute
+
+Attribute ::= SEQUENCE {
+  type    OBJECT IDENTIFIER,
+  values  AttributeSetValue
+}
+
+AttributeSetValue ::= SET OF ANY
+
+-- http://www.ietf.org/rfc/rfc5480.txt Appendix A
+
+ECParameters ::= CHOICE {
+  namedCurve         OBJECT IDENTIFIER -- MUST be supported.
+  -- implicitCurve   NULL - MUST NOT be used.
+  -- specifiedCurve  SpecifiedECDomain - MUST NOT be used.
+}
+
+-- http://www.ietf.org/rfc/rfc5915.txt Appendix A
+
+ECPrivateKey ::= SEQUENCE {
+  version        INTEGER,
+  privateKey     OCTET STRING,
+  parameters [0] ECParameters OPTIONAL,
+  publicKey  [1] BIT STRING OPTIONAL
+}
+
+-- http://www.ietf.org/rfc/rfc3447.txt Appendix A.1.1
+
+RSAPublicKey ::= SEQUENCE {
+  modulus           INTEGER,  -- n
+  publicExponent    INTEGER   -- e
+}
+
+-- http://www.ietf.org/rfc/rfc3447.txt Appendix A.1.2
+
+RSAPrivateKey ::= SEQUENCE {
+  version           Version,
+  modulus           INTEGER,  -- n
+  publicExponent    INTEGER,  -- e
+  privateExponent   INTEGER,  -- d
+  prime1            INTEGER,  -- p
+  prime2            INTEGER,  -- q
+  exponent1         INTEGER,  -- d mod (p-1)
+  exponent2         INTEGER,  -- d mod (q-1)
+  coefficient       INTEGER,  -- (inverse of q) mod p
+  otherPrimeInfos   OtherPrimeInfos OPTIONAL
+}
+
+OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
+
+OtherPrimeInfo ::= SEQUENCE {
+  prime INTEGER,  -- ri
+  exponent INTEGER, -- di
+  coefficient INTEGER -- ti
+}
+
+END
index 90e1fe1..4086228 100644 (file)
@@ -219,6 +219,7 @@ list(APPEND WebCore_LIBRARIES
     ${LIBGCRYPT_LIBRARIES}
     ${LIBSECRET_LIBRARIES}
     ${LIBSOUP_LIBRARIES}
+    ${LIBTASN1_LIBRARIES}
     ${LIBXML2_LIBRARIES}
     ${LIBXSLT_LIBRARIES}
     ${HYPHEN_LIBRARIES}
@@ -245,6 +246,7 @@ list(APPEND WebCore_SYSTEM_INCLUDE_DIRECTORIES
     ${LIBGCRYPT_INCLUDE_DIRS}
     ${LIBSECRET_INCLUDE_DIRS}
     ${LIBSOUP_INCLUDE_DIRS}
+    ${LIBTASN1_INCLUDE_DIRS}
     ${LIBXML2_INCLUDE_DIR}
     ${LIBXSLT_INCLUDE_DIR}
     ${SQLITE_INCLUDE_DIR}
index 4157275..db1cd32 100644 (file)
@@ -171,6 +171,7 @@ list(APPEND WebCore_LIBRARIES
     ${ICU_LIBRARIES}
     ${LIBGCRYPT_LIBRARIES}
     ${LIBSOUP_LIBRARIES}
+    ${LIBTASN1_LIBRARIES}
     ${LIBXML2_LIBRARIES}
     ${LIBXSLT_LIBRARIES}
     ${SQLITE_LIBRARIES}
@@ -185,6 +186,7 @@ list(APPEND WebCore_INCLUDE_DIRECTORIES
     ${ICU_INCLUDE_DIRS}
     ${LIBGCRYPT_INCLUDE_DIRS}
     ${LIBSOUP_INCLUDE_DIRS}
+    ${LIBTASN1_INCLUDE_DIRS}
     ${LIBXML2_INCLUDE_DIR}
     ${LIBXSLT_INCLUDE_DIR}
     ${SQLITE_INCLUDE_DIR}
index 5effd6c..c22a9dc 100644 (file)
@@ -34,6 +34,7 @@
 #include "NotImplemented.h"
 #include <pal/crypto/gcrypt/Handle.h>
 #include <pal/crypto/gcrypt/Utilities.h>
+#include <pal/crypto/tasn1/Utilities.h>
 #include <wtf/text/Base64.h>
 
 namespace WebCore {
@@ -193,11 +194,147 @@ RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportJWKPrivate(CryptoAlgorithmIdentif
     return create(identifier, curve, CryptoKeyType::Private, platformKey.release(), extractable, usages);
 }
 
-RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&&, bool, CryptoKeyUsageBitmap)
+static bool supportedAlgorithmIdentifier(CryptoAlgorithmIdentifier keyIdentifier, const Vector<uint8_t>& identifier)
 {
-    notImplemented();
+    static const std::array<uint8_t, 18> s_id_ecPublicKey { { "1.2.840.10045.2.1" } };
+    static const std::array<uint8_t, 13> s_id_ecDH { { "1.3.132.1.12" } };
+
+    auto size = identifier.size();
+    auto* data = identifier.data();
+
+    switch (keyIdentifier) {
+    case CryptoAlgorithmIdentifier::ECDSA:
+        // ECDSA only supports id-ecPublicKey algorithms for imported keys.
+        if (size == s_id_ecPublicKey.size() && !std::memcmp(data, s_id_ecPublicKey.data(), size))
+            return true;
+        return false;
+    case CryptoAlgorithmIdentifier::ECDH:
+        // ECDH supports both id-ecPublicKey and ic-ecDH algorithms for imported keys.
+        if (size == s_id_ecPublicKey.size() && !std::memcmp(data, s_id_ecPublicKey.data(), size))
+            return true;
+        if (size == s_id_ecDH.size() && !std::memcmp(data, s_id_ecDH.data(), size))
+            return true;
+        return false;
+    default:
+        ASSERT_NOT_REACHED();
+        break;
+    }
 
-    return nullptr;
+    return false;
+}
+
+static std::optional<CryptoKeyEC::NamedCurve> curveForIdentifier(const Vector<uint8_t>& identifier)
+{
+    static const std::array<uint8_t, 20> s_secp256r1 { { "1.2.840.10045.3.1.7" } };
+    static const std::array<uint8_t, 13> s_secp384r1 { { "1.3.132.0.34" } };
+    static const std::array<uint8_t, 13> s_secp521r1 { { "1.3.132.0.35" } };
+
+    auto size = identifier.size();
+    auto* data = identifier.data();
+
+    if (size == s_secp256r1.size() && !std::memcmp(data, s_secp256r1.data(), size))
+        return CryptoKeyEC::NamedCurve::P256;
+    if (size == s_secp384r1.size() && !std::memcmp(data, s_secp384r1.data(), size))
+        return CryptoKeyEC::NamedCurve::P384;
+    if (size == s_secp521r1.size() && !std::memcmp(data, s_secp521r1.data(), size))
+        return std::nullopt; // Not yet supported.
+
+    return std::nullopt;
+}
+
+RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
+{
+    // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
+    PAL::TASN1::Structure spki;
+    if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
+        return nullptr;
+
+    // Validate `algorithm.algorithm`.
+    {
+        auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
+        if (!algorithm)
+            return nullptr;
+
+        if (!supportedAlgorithmIdentifier(identifier, *algorithm))
+            return nullptr;
+    }
+
+    // Validate `algorithm.parameters` and therein embedded `ECParameters`.
+    {
+        auto parameters = PAL::TASN1::elementData(spki, "algorithm.parameters");
+        if (!parameters)
+            return nullptr;
+
+        // Decode the `ECParameters` structure using the `algorithm.parameters` data.
+        PAL::TASN1::Structure ecParameters;
+        if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
+            return nullptr;
+
+        auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
+        if (!namedCurve)
+            return nullptr;
+
+        auto parameterCurve = curveForIdentifier(*namedCurve);
+        if (!parameterCurve || *parameterCurve != curve)
+            return nullptr;
+    }
+
+    // Retrieve the `subjectPublicKey` data and embed it into the `public-key` s-expression.
+    PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
+    {
+        auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
+        if (!subjectPublicKey)
+            return nullptr;
+
+        // Bail if the `subjectPublicKey` data size doesn't match the size of an uncompressed point
+        // for this curve, or if the first byte in the `subjectPublicKey` data isn't 0x04, as required
+        // for an uncompressed EC point encoded in an octet string.
+        if (subjectPublicKey->size() != uncompressedPointSizeForCurve(curve) || subjectPublicKey->at(0) != 0x04)
+            return nullptr;
+
+        // Convert X and Y coordinate data into MPIs.
+        unsigned coordinateSize = uncompressedFieldElementSizeForCurve(curve);
+        PAL::GCrypt::Handle<gcry_mpi_t> xMPI, yMPI;
+        {
+            gcry_error_t error = gcry_mpi_scan(&xMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1), coordinateSize, nullptr);
+            if (error != GPG_ERR_NO_ERROR) {
+                PAL::GCrypt::logError(error);
+                return nullptr;
+            }
+
+            error = gcry_mpi_scan(&yMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1 + coordinateSize), coordinateSize, nullptr);
+            if (error != GPG_ERR_NO_ERROR) {
+                PAL::GCrypt::logError(error);
+                return nullptr;
+            }
+        }
+
+        // Construct an MPI point from the X and Y coordinates and using 1 as the Z coordinate.
+        // This always allocates the gcry_mpi_point_t object.
+        PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_point_set(nullptr, xMPI, yMPI, GCRYMPI_CONST_ONE));
+
+        // Create an EC context for the specified curve.
+        PAL::GCrypt::Handle<gcry_ctx_t> context;
+        gcry_error_t error = gcry_mpi_ec_new(&context, nullptr, curveName(curve));
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return nullptr;
+        }
+
+        // Bail if the constructed MPI point is not on the specified EC curve.
+        if (!gcry_mpi_ec_curve_point(point, context))
+            return nullptr;
+
+        error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
+            curveName(curve), subjectPublicKey->size(), subjectPublicKey->data());
+        if (error != GPG_ERR_NO_ERROR) {
+            PAL::GCrypt::logError(error);
+            return nullptr;
+        }
+    }
+
+    // Finally create a new CryptoKeyEC object, transferring to it ownership of the `public-key` s-expression.
+    return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
 }
 
 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportPkcs8(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&&, bool, CryptoKeyUsageBitmap)
diff --git a/Source/cmake/FindLibtasn1.cmake b/Source/cmake/FindLibtasn1.cmake
new file mode 100644 (file)
index 0000000..09e6df3
--- /dev/null
@@ -0,0 +1,47 @@
+# - Try to find libtasn1.
+# Once done, this will define
+#
+#  LIBTASN1_INCLUDE_DIRS - the libtasn1 include directories
+#  LIBTASN1_LIBRARIES - the libtasn1 libraries.
+#
+# Copyright (C) 2017 Metrological Group B.V.
+# Copyright (C) 2017 Igalia S.L.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+find_package(PkgConfig)
+pkg_check_modules(PC_LIBTASN1 libtasn1)
+
+find_path(LIBTASN1_INCLUDE_DIRS
+    NAMES libtasn1.h
+    PATHS ${PC_LIBTASN1_INCLUDEDIR} ${PC_LIBTASN1_INCLUDE_DIRS}
+)
+
+find_library(LIBTASN1_LIBRARIES
+    NAMES tasn1
+    PATHS ${PC_LIBTASN1_LIBDIR} ${PC_LIBTASN1_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBTASN1 DEFAULT_MSG LIBTASN1_LIBRARIES)
+
+mark_as_advanced(LIBTASN1_INCLUDE_DIRS LIBTASN1_LIBRARIES)
index e8a9db6..02d83fd 100644 (file)
@@ -271,6 +271,10 @@ if (ENABLE_MEDIA_STREAM OR ENABLE_WEB_RTC)
 endif ()
 
 if (ENABLE_SUBTLE_CRYPTO)
+    find_package(Libtasn1 REQUIRED)
+    if (NOT LIBTASN1_FOUND)
+        message(FATAL_ERROR "libtasn1 is required to enable Web Crypto API support.")
+    endif ()
     if (LIBGCRYPT_VERSION VERSION_LESS 1.7.0)
         message(FATAL_ERROR "libgcrypt 1.7.0 is required to enable Web Crypto API support.")
     endif ()
index b044394..632a1d0 100644 (file)
@@ -119,6 +119,10 @@ if (ENABLE_ACCELERATED_2D_CANVAS)
 endif ()
 
 if (ENABLE_SUBTLE_CRYPTO)
+    find_package(Libtasn1 REQUIRED)
+    if (NOT LIBTASN1_FOUND)
+        message(FATAL_ERROR "libtasn1 is required to enable Web Crypto API support.")
+    endif ()
     if (LIBGCRYPT_VERSION VERSION_LESS 1.7.0)
         message(FATAL_ERROR "libgcrypt 1.7.0 is required to enable Web Crypto API support.")
     endif ()