Changeset 218626 in webkit


Ignore:
Timestamp:
Jun 20, 2017 11:52:29 PM (7 years ago)
Author:
zandobersek@gmail.com
Message:

[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.

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.

Location:
trunk
Files:
5 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/ChangeLog

    r218553 r218626  
     12017-06-20  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement CryptoKeyEC SPKI imports
     4        https://bugs.webkit.org/show_bug.cgi?id=172927
     5
     6        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
     7
     8        * Source/cmake/FindLibtasn1.cmake: Added.
     9        * Source/cmake/OptionsGTK.cmake: Require libtasn1 when SUBTLE_CRYPTO is enabled.
     10        * Source/cmake/OptionsWPE.cmake: Ditto.
     11
    1122017-06-20  Carlos Garcia Campos  <cgarcia@igalia.com>
    213
  • trunk/LayoutTests/ChangeLog

    r218620 r218626  
     12017-06-20  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement CryptoKeyEC SPKI imports
     4        https://bugs.webkit.org/show_bug.cgi?id=172927
     5
     6        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
     7
     8        * platform/gtk/TestExpectations:
     9        Unskip or enable the EC-based SPKI import tests that are now passing.
     10
    1112017-06-20  Myles C. Maxfield  <mmaxfield@apple.com>
    212
  • trunk/LayoutTests/platform/gtk/TestExpectations

    r218558 r218626  
    759759webkit.org/b/133122 crypto/subtle/ecdh-import-pkcs8-key-p256.html [ Skip ]
    760760webkit.org/b/133122 crypto/subtle/ecdh-import-pkcs8-key-p384.html [ Skip ]
    761 webkit.org/b/133122 crypto/subtle/ecdh-import-spki-key-p256.html [ Skip ]
    762 webkit.org/b/133122 crypto/subtle/ecdh-import-spki-key-p384.html [ Skip ]
    763761webkit.org/b/133122 crypto/subtle/ecdsa-generate-export-key-pkcs8.html [ Skip ]
    764762webkit.org/b/133122 crypto/subtle/ecdsa-generate-export-key-spki.html [ Skip ]
    765763webkit.org/b/133122 crypto/subtle/ecdsa-import-pkcs8-key.html [ Skip ]
    766 webkit.org/b/133122 crypto/subtle/ecdsa-import-spki-key.html [ Skip ]
    767764webkit.org/b/133122 crypto/subtle/ec-import-jwk-key-export-pkcs8-key.html [ Skip ]
    768765webkit.org/b/133122 crypto/subtle/ec-import-jwk-key-export-spki-key.html [ Skip ]
     
    771768webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p256.html [ Skip ]
    772769webkit.org/b/133122 crypto/subtle/ec-import-pkcs8-key-export-pkcs8-key-p384.html [ Skip ]
    773 webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-jwk-key.html [ Skip ]
    774 webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-raw-key.html [ Skip ]
    775770webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-spki-key-p256.html [ Skip ]
    776771webkit.org/b/133122 crypto/subtle/ec-import-spki-key-export-spki-key-p384.html [ Skip ]
     
    812807webkit.org/b/133122 crypto/workers/subtle/ec-generate-export-spki-key.html [ Skip ]
    813808webkit.org/b/133122 crypto/workers/subtle/ec-import-pkcs8-key.html [ Skip ]
    814 webkit.org/b/133122 crypto/workers/subtle/ec-import-spki-key.html [ Skip ]
    815809webkit.org/b/133122 crypto/workers/subtle/ec-postMessage-worker.html [ Skip ]
    816810webkit.org/b/133122 crypto/workers/subtle/hmac-postMessage-worker.html [ Skip ]
     
    844838crypto/workers/subtle/rsa-pss-import-key-sign.html [ Pass ]
    845839crypto/workers/subtle/rsa-pss-import-key-verify.html [ Pass ]
     840
     841# libgcrypt-based implementation supports ecDH algorithm identifier for ECDH SPKI imports.
     842crypto/subtle/ecdh-import-spki-key-ecdh-identifier.html [ Pass ]
    846843
    847844# These are legacy APIs that we don't support, apart from a few digest algorithms.
  • trunk/Source/WebCore/ChangeLog

    r218624 r218626  
     12017-06-20  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement CryptoKeyEC SPKI imports
     4        https://bugs.webkit.org/show_bug.cgi?id=172927
     5
     6        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
     7
     8        No new tests -- affected tests are now passing and are unskipped.
     9
     10        Implement libgcrypt-based support for SPKI imports of EC keys.
     11
     12        Using libtasn1 through the utility functions and wrappers, the given key data
     13        is decoded against the SubjectPublicKeyInfo ASN.1 definition. The algorithm
     14        member is then properly validated, making sure that the key algorithm idenfitier
     15        is supported and that the algorithm parameters specify the correct EC curve.
     16
     17        The public key bit string is then retrieved and validated, ensuring it represents
     18        an uncompressed EC point that is of valid size for the specified EC curve. The
     19        point is then tested through an EC context to make sure it's positioned on the
     20        specified EC curve.
     21
     22        Finally, the curve name and uncompressed point data are embedded into a
     23        `public-key` s-expression that will be used through the libgcrypt API. This is
     24        then used, along with other information, to create a valid CryptoKeyEC object.
     25
     26        * PlatformGTK.cmake: Use LIBTASN1_INCLUDE_DIRECTORIES and LIBTASN1_LIBRARIES.
     27        * PlatformWPE.cmake: Ditto.
     28        * crypto/gcrypt/CryptoKeyECGCrypt.cpp:
     29        (WebCore::supportedAlgorithmIdentifier):
     30        (WebCore::curveForIdentifier):
     31        (WebCore::CryptoKeyEC::platformImportSpki):
     32
    1332017-06-20  Devin Rousso  <drousso@apple.com>
    234
  • trunk/Source/WebCore/PAL/ChangeLog

    r218620 r218626  
     12017-06-20  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement CryptoKeyEC SPKI imports
     4        https://bugs.webkit.org/show_bug.cgi?id=172927
     5
     6        Reviewed by Jiewen Tan, Michael Catanzaro and Carlos Garcia Campos.
     7
     8        Add a file that provides utility functions for operating with libtasn1 APIs.
     9
     10        The precomputed ASN.1 declarations, generated from the WebCrypto.asn file with
     11        the asn1Parser tool, are used to enable construction of ASN.1 structures that
     12        are then used to decode the SPKI or PKCS#8 data through the decodeStructure()
     13        function. Raw data of each element in that structure can be retrieved throug the
     14        elementData() function.
     15
     16        The Structure class is added as a wrapper for asn1_node objects that are used
     17        as decoding targets, simplifying lifetime management of these objects.
     18
     19        * pal/PlatformGTK.cmake:
     20        * pal/PlatformWPE.cmake:
     21        * pal/crypto/tasn1/Utilities.cpp: Added.
     22        (PAL::TASN1::asn1Definitions):
     23        (PAL::TASN1::decodeStructure):
     24        (PAL::TASN1::elementData):
     25        * pal/crypto/tasn1/Utilities.h: Added.
     26        (PAL::TASN1::Structure::~Structure):
     27        (PAL::TASN1::Structure::operator&):
     28        (PAL::TASN1::Structure::operator asn1_node):
     29        * pal/crypto/tasn1/WebCrypto.asn: Added.
     30
    1312017-06-20  Myles C. Maxfield  <mmaxfield@apple.com>
    232
  • trunk/Source/WebCore/PAL/pal/PlatformGTK.cmake

    r211027 r218626  
    11list(APPEND PAL_SOURCES
    22    crypto/gcrypt/CryptoDigestGCrypt.cpp
     3
     4    crypto/tasn1/Utilities.cpp
    35)
  • trunk/Source/WebCore/PAL/pal/PlatformWPE.cmake

    r216497 r218626  
    11list(APPEND PAL_SOURCES
    22    crypto/gcrypt/CryptoDigestGCrypt.cpp
     3
     4    crypto/tasn1/Utilities.cpp
    35)
  • trunk/Source/WebCore/PlatformGTK.cmake

    r217971 r218626  
    220220    ${LIBSECRET_LIBRARIES}
    221221    ${LIBSOUP_LIBRARIES}
     222    ${LIBTASN1_LIBRARIES}
    222223    ${LIBXML2_LIBRARIES}
    223224    ${LIBXSLT_LIBRARIES}
     
    246247    ${LIBSECRET_INCLUDE_DIRS}
    247248    ${LIBSOUP_INCLUDE_DIRS}
     249    ${LIBTASN1_INCLUDE_DIRS}
    248250    ${LIBXML2_INCLUDE_DIR}
    249251    ${LIBXSLT_INCLUDE_DIR}
  • trunk/Source/WebCore/PlatformWPE.cmake

    r217925 r218626  
    172172    ${LIBGCRYPT_LIBRARIES}
    173173    ${LIBSOUP_LIBRARIES}
     174    ${LIBTASN1_LIBRARIES}
    174175    ${LIBXML2_LIBRARIES}
    175176    ${LIBXSLT_LIBRARIES}
     
    186187    ${LIBGCRYPT_INCLUDE_DIRS}
    187188    ${LIBSOUP_INCLUDE_DIRS}
     189    ${LIBTASN1_INCLUDE_DIRS}
    188190    ${LIBXML2_INCLUDE_DIR}
    189191    ${LIBXSLT_INCLUDE_DIR}
  • trunk/Source/WebCore/crypto/gcrypt/CryptoKeyECGCrypt.cpp

    r218100 r218626  
    3535#include <pal/crypto/gcrypt/Handle.h>
    3636#include <pal/crypto/gcrypt/Utilities.h>
     37#include <pal/crypto/tasn1/Utilities.h>
    3738#include <wtf/text/Base64.h>
    3839
     
    194195}
    195196
    196 RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier, NamedCurve, Vector<uint8_t>&&, bool, CryptoKeyUsageBitmap)
    197 {
    198     notImplemented();
    199 
    200     return nullptr;
     197static bool supportedAlgorithmIdentifier(CryptoAlgorithmIdentifier keyIdentifier, const Vector<uint8_t>& identifier)
     198{
     199    static const std::array<uint8_t, 18> s_id_ecPublicKey { { "1.2.840.10045.2.1" } };
     200    static const std::array<uint8_t, 13> s_id_ecDH { { "1.3.132.1.12" } };
     201
     202    auto size = identifier.size();
     203    auto* data = identifier.data();
     204
     205    switch (keyIdentifier) {
     206    case CryptoAlgorithmIdentifier::ECDSA:
     207        // ECDSA only supports id-ecPublicKey algorithms for imported keys.
     208        if (size == s_id_ecPublicKey.size() && !std::memcmp(data, s_id_ecPublicKey.data(), size))
     209            return true;
     210        return false;
     211    case CryptoAlgorithmIdentifier::ECDH:
     212        // ECDH supports both id-ecPublicKey and ic-ecDH algorithms for imported keys.
     213        if (size == s_id_ecPublicKey.size() && !std::memcmp(data, s_id_ecPublicKey.data(), size))
     214            return true;
     215        if (size == s_id_ecDH.size() && !std::memcmp(data, s_id_ecDH.data(), size))
     216            return true;
     217        return false;
     218    default:
     219        ASSERT_NOT_REACHED();
     220        break;
     221    }
     222
     223    return false;
     224}
     225
     226static std::optional<CryptoKeyEC::NamedCurve> curveForIdentifier(const Vector<uint8_t>& identifier)
     227{
     228    static const std::array<uint8_t, 20> s_secp256r1 { { "1.2.840.10045.3.1.7" } };
     229    static const std::array<uint8_t, 13> s_secp384r1 { { "1.3.132.0.34" } };
     230    static const std::array<uint8_t, 13> s_secp521r1 { { "1.3.132.0.35" } };
     231
     232    auto size = identifier.size();
     233    auto* data = identifier.data();
     234
     235    if (size == s_secp256r1.size() && !std::memcmp(data, s_secp256r1.data(), size))
     236        return CryptoKeyEC::NamedCurve::P256;
     237    if (size == s_secp384r1.size() && !std::memcmp(data, s_secp384r1.data(), size))
     238        return CryptoKeyEC::NamedCurve::P384;
     239    if (size == s_secp521r1.size() && !std::memcmp(data, s_secp521r1.data(), size))
     240        return std::nullopt; // Not yet supported.
     241
     242    return std::nullopt;
     243}
     244
     245RefPtr<CryptoKeyEC> CryptoKeyEC::platformImportSpki(CryptoAlgorithmIdentifier identifier, NamedCurve curve, Vector<uint8_t>&& keyData, bool extractable, CryptoKeyUsageBitmap usages)
     246{
     247    // Decode the `SubjectPublicKeyInfo` structure using the provided key data.
     248    PAL::TASN1::Structure spki;
     249    if (!PAL::TASN1::decodeStructure(&spki, "WebCrypto.SubjectPublicKeyInfo", keyData))
     250        return nullptr;
     251
     252    // Validate `algorithm.algorithm`.
     253    {
     254        auto algorithm = PAL::TASN1::elementData(spki, "algorithm.algorithm");
     255        if (!algorithm)
     256            return nullptr;
     257
     258        if (!supportedAlgorithmIdentifier(identifier, *algorithm))
     259            return nullptr;
     260    }
     261
     262    // Validate `algorithm.parameters` and therein embedded `ECParameters`.
     263    {
     264        auto parameters = PAL::TASN1::elementData(spki, "algorithm.parameters");
     265        if (!parameters)
     266            return nullptr;
     267
     268        // Decode the `ECParameters` structure using the `algorithm.parameters` data.
     269        PAL::TASN1::Structure ecParameters;
     270        if (!PAL::TASN1::decodeStructure(&ecParameters, "WebCrypto.ECParameters", *parameters))
     271            return nullptr;
     272
     273        auto namedCurve = PAL::TASN1::elementData(ecParameters, "namedCurve");
     274        if (!namedCurve)
     275            return nullptr;
     276
     277        auto parameterCurve = curveForIdentifier(*namedCurve);
     278        if (!parameterCurve || *parameterCurve != curve)
     279            return nullptr;
     280    }
     281
     282    // Retrieve the `subjectPublicKey` data and embed it into the `public-key` s-expression.
     283    PAL::GCrypt::Handle<gcry_sexp_t> platformKey;
     284    {
     285        auto subjectPublicKey = PAL::TASN1::elementData(spki, "subjectPublicKey");
     286        if (!subjectPublicKey)
     287            return nullptr;
     288
     289        // Bail if the `subjectPublicKey` data size doesn't match the size of an uncompressed point
     290        // for this curve, or if the first byte in the `subjectPublicKey` data isn't 0x04, as required
     291        // for an uncompressed EC point encoded in an octet string.
     292        if (subjectPublicKey->size() != uncompressedPointSizeForCurve(curve) || subjectPublicKey->at(0) != 0x04)
     293            return nullptr;
     294
     295        // Convert X and Y coordinate data into MPIs.
     296        unsigned coordinateSize = uncompressedFieldElementSizeForCurve(curve);
     297        PAL::GCrypt::Handle<gcry_mpi_t> xMPI, yMPI;
     298        {
     299            gcry_error_t error = gcry_mpi_scan(&xMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1), coordinateSize, nullptr);
     300            if (error != GPG_ERR_NO_ERROR) {
     301                PAL::GCrypt::logError(error);
     302                return nullptr;
     303            }
     304
     305            error = gcry_mpi_scan(&yMPI, GCRYMPI_FMT_USG, &subjectPublicKey->at(1 + coordinateSize), coordinateSize, nullptr);
     306            if (error != GPG_ERR_NO_ERROR) {
     307                PAL::GCrypt::logError(error);
     308                return nullptr;
     309            }
     310        }
     311
     312        // Construct an MPI point from the X and Y coordinates and using 1 as the Z coordinate.
     313        // This always allocates the gcry_mpi_point_t object.
     314        PAL::GCrypt::Handle<gcry_mpi_point_t> point(gcry_mpi_point_set(nullptr, xMPI, yMPI, GCRYMPI_CONST_ONE));
     315
     316        // Create an EC context for the specified curve.
     317        PAL::GCrypt::Handle<gcry_ctx_t> context;
     318        gcry_error_t error = gcry_mpi_ec_new(&context, nullptr, curveName(curve));
     319        if (error != GPG_ERR_NO_ERROR) {
     320            PAL::GCrypt::logError(error);
     321            return nullptr;
     322        }
     323
     324        // Bail if the constructed MPI point is not on the specified EC curve.
     325        if (!gcry_mpi_ec_curve_point(point, context))
     326            return nullptr;
     327
     328        error = gcry_sexp_build(&platformKey, nullptr, "(public-key(ecc(curve %s)(q %b)))",
     329            curveName(curve), subjectPublicKey->size(), subjectPublicKey->data());
     330        if (error != GPG_ERR_NO_ERROR) {
     331            PAL::GCrypt::logError(error);
     332            return nullptr;
     333        }
     334    }
     335
     336    // Finally create a new CryptoKeyEC object, transferring to it ownership of the `public-key` s-expression.
     337    return create(identifier, curve, CryptoKeyType::Public, platformKey.release(), extractable, usages);
    201338}
    202339
  • trunk/Source/cmake/OptionsGTK.cmake

    r218478 r218626  
    272272
    273273if (ENABLE_SUBTLE_CRYPTO)
     274    find_package(Libtasn1 REQUIRED)
     275    if (NOT LIBTASN1_FOUND)
     276        message(FATAL_ERROR "libtasn1 is required to enable Web Crypto API support.")
     277    endif ()
    274278    if (LIBGCRYPT_VERSION VERSION_LESS 1.7.0)
    275279        message(FATAL_ERROR "libgcrypt 1.7.0 is required to enable Web Crypto API support.")
  • trunk/Source/cmake/OptionsWPE.cmake

    r218553 r218626  
    120120
    121121if (ENABLE_SUBTLE_CRYPTO)
     122    find_package(Libtasn1 REQUIRED)
     123    if (NOT LIBTASN1_FOUND)
     124        message(FATAL_ERROR "libtasn1 is required to enable Web Crypto API support.")
     125    endif ()
    122126    if (LIBGCRYPT_VERSION VERSION_LESS 1.7.0)
    123127        message(FATAL_ERROR "libgcrypt 1.7.0 is required to enable Web Crypto API support.")
Note: See TracChangeset for help on using the changeset viewer.