Changeset 214822 in webkit


Ignore:
Timestamp:
Apr 3, 2017 11:12:38 AM (7 years ago)
Author:
zandobersek@gmail.com
Message:

[GCrypt] Implement AES_GCM support
https://bugs.webkit.org/show_bug.cgi?id=170271

Reviewed by Michael Catanzaro.

Source/WebCore:

Implement the CryptoAlgorithmAES_GCM::platform{Encrypt,Decrypt}
functionality for configurations that use libgcrypt. This is done
by leveraging the gcry_cipher_* APIs for the AES algorithm that's
deducted appropriately from the key size and the GCM cipher mode.

No new tests -- current ones cover this sufficiently, but are not yet
enabled due to other missing platform-specific SUBTLE_CRYPTO
implementations.

  • crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp:

(WebCore::gcryptEncrypt):
(WebCore::gcryptDecrypt):
(WebCore::CryptoAlgorithmAES_GCM::platformEncrypt):
(WebCore::CryptoAlgorithmAES_GCM::platformDecrypt):

Source/WebCore/PAL:

  • pal/crypto/gcrypt/Handle.h:

(PAL::GCrypt::HandleDeleter<gcry_cipher_hd_t>::operator()): Specialize
the HandleDeleter<> template for the gcry_cipher_hd_t type.

Location:
trunk/Source/WebCore
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r214821 r214822  
     12017-04-03  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement AES_GCM support
     4        https://bugs.webkit.org/show_bug.cgi?id=170271
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        Implement the CryptoAlgorithmAES_GCM::platform{Encrypt,Decrypt}
     9        functionality for configurations that use libgcrypt. This is done
     10        by leveraging the gcry_cipher_* APIs for the AES algorithm that's
     11        deducted appropriately from the key size and the GCM cipher mode.
     12
     13        No new tests -- current ones cover this sufficiently, but are not yet
     14        enabled due to other missing platform-specific SUBTLE_CRYPTO
     15        implementations.
     16
     17        * crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp:
     18        (WebCore::gcryptEncrypt):
     19        (WebCore::gcryptDecrypt):
     20        (WebCore::CryptoAlgorithmAES_GCM::platformEncrypt):
     21        (WebCore::CryptoAlgorithmAES_GCM::platformDecrypt):
     22
    1232017-04-03  Zan Dobersek  <zdobersek@igalia.com>
    224
  • trunk/Source/WebCore/PAL/ChangeLog

    r214609 r214822  
     12017-04-03  Zan Dobersek  <zdobersek@igalia.com>
     2
     3        [GCrypt] Implement AES_GCM support
     4        https://bugs.webkit.org/show_bug.cgi?id=170271
     5
     6        Reviewed by Michael Catanzaro.
     7
     8        * pal/crypto/gcrypt/Handle.h:
     9        (PAL::GCrypt::HandleDeleter<gcry_cipher_hd_t>::operator()): Specialize
     10        the HandleDeleter<> template for the gcry_cipher_hd_t type.
     11
    1122017-03-30  Zan Dobersek  <zdobersek@igalia.com>
    213
  • trunk/Source/WebCore/PAL/pal/crypto/gcrypt/Handle.h

    r214550 r214822  
    8484
    8585template<>
     86struct HandleDeleter<gcry_cipher_hd_t> {
     87    void operator()(gcry_cipher_hd_t handle)
     88    {
     89        gcry_cipher_close(handle);
     90    }
     91};
     92
     93template<>
    8694struct HandleDeleter<gcry_mac_hd_t> {
    8795    void operator()(gcry_mac_hd_t handle)
  • trunk/Source/WebCore/crypto/gcrypt/CryptoAlgorithmAES_GCMGCrypt.cpp

    r214538 r214822  
    11/*
    22 * Copyright (C) 2017 Apple Inc. All rights reserved.
     3 * Copyright (C) 2017 Metrological Group B.V.
     4 * Copyright (C) 2017 Igalia S.L.
    35 *
    46 * Redistribution and use in source and binary forms, with or without
     
    2931#if ENABLE(SUBTLE_CRYPTO)
    3032
     33#include "CryptoAlgorithmAesGcmParams.h"
     34#include "CryptoKeyAES.h"
    3135#include "ExceptionCode.h"
    3236#include "NotImplemented.h"
     37#include "ScriptExecutionContext.h"
     38#include <pal/crypto/gcrypt/Handle.h>
     39#include <pal/crypto/gcrypt/Utilities.h>
     40#include <wtf/CryptographicUtilities.h>
    3341
    3442namespace WebCore {
    3543
    36 void CryptoAlgorithmAES_GCM::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
    37 {
    38     notImplemented();
    39 }
    40 
    41 void CryptoAlgorithmAES_GCM::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&&, Ref<CryptoKey>&&, Vector<uint8_t>&&, VectorCallback&&, ExceptionCallback&&, ScriptExecutionContext&, WorkQueue&)
    42 {
    43     notImplemented();
     44static std::optional<Vector<uint8_t>> gcryptEncrypt(const Vector<uint8_t>& key, const Vector<uint8_t>& iv, Vector<uint8_t>&& plainText, const Vector<uint8_t>& additionalData, uint8_t tagLength)
     45{
     46    auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
     47    if (!algorithm)
     48        return std::nullopt;
     49
     50    PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
     51    gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE);
     52    if (error != GPG_ERR_NO_ERROR) {
     53        PAL::GCrypt::logError(error);
     54        return std::nullopt;
     55    }
     56
     57    error = gcry_cipher_setkey(handle, key.data(), key.size());
     58    if (error != GPG_ERR_NO_ERROR) {
     59        PAL::GCrypt::logError(error);
     60        return std::nullopt;
     61    }
     62
     63    error = gcry_cipher_setiv(handle, iv.data(), iv.size());
     64    if (error != GPG_ERR_NO_ERROR) {
     65        PAL::GCrypt::logError(error);
     66        return std::nullopt;
     67    }
     68
     69    if (!additionalData.isEmpty()) {
     70        error = gcry_cipher_authenticate(handle, additionalData.data(), additionalData.size());
     71        if (error != GPG_ERR_NO_ERROR) {
     72            PAL::GCrypt::logError(error);
     73            return std::nullopt;
     74        }
     75    }
     76
     77    error = gcry_cipher_final(handle);
     78    if (error != GPG_ERR_NO_ERROR) {
     79        PAL::GCrypt::logError(error);
     80        return std::nullopt;
     81    }
     82
     83    Vector<uint8_t> output(plainText.size());
     84    error = gcry_cipher_encrypt(handle, output.data(), output.size(), plainText.data(), plainText.size());
     85    if (error != GPG_ERR_NO_ERROR) {
     86        PAL::GCrypt::logError(error);
     87        return std::nullopt;
     88    }
     89
     90    if (tagLength) {
     91        Vector<uint8_t> tag(tagLength);
     92        error = gcry_cipher_gettag(handle, tag.data(), tag.size());
     93        if (error != GPG_ERR_NO_ERROR) {
     94            PAL::GCrypt::logError(error);
     95            return std::nullopt;
     96        }
     97
     98        output.appendVector(tag);
     99    }
     100
     101    return output;
     102}
     103
     104static std::optional<Vector<uint8_t>> gcryptDecrypt(const Vector<uint8_t>& key, const Vector<uint8_t>& iv, Vector<uint8_t>&& cipherText, const Vector<uint8_t>& additionalData, uint8_t tagLength)
     105{
     106    auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
     107    if (!algorithm)
     108        return std::nullopt;
     109
     110    PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
     111    gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_GCM, 0);
     112    if (error != GPG_ERR_NO_ERROR) {
     113        PAL::GCrypt::logError(error);
     114        return std::nullopt;
     115    }
     116
     117    error = gcry_cipher_setkey(handle, key.data(), key.size());
     118    if (error != GPG_ERR_NO_ERROR) {
     119        PAL::GCrypt::logError(error);
     120        return std::nullopt;
     121    }
     122
     123    error = gcry_cipher_setiv(handle, iv.data(), iv.size());
     124    if (error != GPG_ERR_NO_ERROR) {
     125        PAL::GCrypt::logError(error);
     126        return std::nullopt;
     127    }
     128
     129    if (!additionalData.isEmpty()) {
     130        error = gcry_cipher_authenticate(handle, additionalData.data(), additionalData.size());
     131        if (error != GPG_ERR_NO_ERROR) {
     132            PAL::GCrypt::logError(error);
     133            return std::nullopt;
     134        }
     135    }
     136
     137    error = gcry_cipher_final(handle);
     138    if (error != GPG_ERR_NO_ERROR) {
     139        PAL::GCrypt::logError(error);
     140        return std::nullopt;
     141    }
     142
     143    size_t cipherLength = cipherText.size() - tagLength;
     144    Vector<uint8_t> output(cipherLength);
     145    error = gcry_cipher_decrypt(handle, output.data(), output.size(), cipherText.data(), cipherLength);
     146    if (error != GPG_ERR_NO_ERROR) {
     147        PAL::GCrypt::logError(error);
     148        return std::nullopt;
     149    }
     150
     151    if (tagLength) {
     152        Vector<uint8_t> tag(tagLength);
     153        error = gcry_cipher_gettag(handle, tag.data(), tagLength);
     154        if (error != GPG_ERR_NO_ERROR) {
     155            PAL::GCrypt::logError(error);
     156            return std::nullopt;
     157        }
     158
     159        if (constantTimeMemcmp(tag.data(), cipherText.data() + cipherLength, tagLength))
     160            return std::nullopt;
     161    }
     162
     163    return output;
     164}
     165
     166void CryptoAlgorithmAES_GCM::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
     167{
     168    context.ref();
     169    workQueue.dispatch(
     170        [parameters = WTFMove(parameters), key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
     171            auto& aesParameters = downcast<CryptoAlgorithmAesGcmParams>(*parameters);
     172            auto& aesKey = downcast<CryptoKeyAES>(key.get());
     173
     174            auto output = gcryptEncrypt(aesKey.key(), aesParameters.ivVector(), WTFMove(plainText), aesParameters.additionalDataVector(), aesParameters.tagLength.value_or(0) / 8);
     175            if (!output) {
     176                // We should only dereference callbacks after being back to the Document/Worker threads.
     177                context.postTask(
     178                    [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
     179                        exceptionCallback(OperationError);
     180                        context.deref();
     181                    });
     182                return;
     183            }
     184
     185            // We should only dereference callbacks after being back to the Document/Worker threads.
     186            context.postTask(
     187                [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
     188                    callback(WTFMove(output));
     189                    context.deref();
     190                });
     191        });
     192}
     193
     194void CryptoAlgorithmAES_GCM::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
     195{
     196    context.ref();
     197    workQueue.dispatch(
     198        [parameters = WTFMove(parameters), key = WTFMove(key), cipherText = WTFMove(cipherText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
     199            auto& aesParameters = downcast<CryptoAlgorithmAesGcmParams>(*parameters);
     200            auto& aesKey = downcast<CryptoKeyAES>(key.get());
     201
     202            auto output = gcryptDecrypt(aesKey.key(), aesParameters.ivVector(), WTFMove(cipherText), aesParameters.additionalDataVector(), aesParameters.tagLength.value_or(0) / 8);
     203            if (!output) {
     204                // We should only dereference callbacks after being back to the Document/Worker threads.
     205                context.postTask(
     206                    [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
     207                        exceptionCallback(OperationError);
     208                        context.deref();
     209                    });
     210                return;
     211            }
     212
     213            // We should only dereference callbacks after being back to the Document/Worker threads.
     214            context.postTask(
     215                [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
     216                    callback(WTFMove(output));
     217                    context.deref();
     218                });
     219        });
    44220}
    45221
Note: See TracChangeset for help on using the changeset viewer.