Changeset 256503 in webkit


Ignore:
Timestamp:
Feb 12, 2020 11:20:17 PM (4 years ago)
Author:
yoshiaki.jitsukawa@sony.com
Message:

[WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
https://bugs.webkit.org/show_bug.cgi?id=207238

Reviewed by Jiewen Tan.

Source/WebCore:

Add CounterBlockHelper to calculate the remaining count for counter bits to overflow
and the whole counter block bits (nonce + zero counter bits) after overflow.

With this helper, simplify the AES-CTR implementation on CommonCrypto.

  • crypto/algorithms/CryptoAlgorithmAES_CTR.cpp:

(WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockHelper):
(WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::countToOverflowSaturating const):
(WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::counterVectorAfterOverflow const):

  • crypto/algorithms/CryptoAlgorithmAES_CTR.h:
  • crypto/mac/CryptoAlgorithmAES_CTRMac.cpp:

(WebCore::transformAES_CTR):
(WebCore::bigIntegerToSizeT): Deleted.

LayoutTests:

Add test cases where the counter length is 66 and 128, and the counter capacity
to overflow is 1 and 2.

  • crypto/subtle/aes-ctr-import-key-encrypt-expected.txt:
  • crypto/subtle/aes-ctr-import-key-encrypt.html:
Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r256502 r256503  
     12020-02-12  Yoshiaki Jitsukawa  <yoshiaki.jitsukawa@sony.com>
     2
     3        [WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
     4        https://bugs.webkit.org/show_bug.cgi?id=207238
     5
     6        Reviewed by Jiewen Tan.
     7
     8        Add test cases where the counter length is 66 and 128, and the counter capacity
     9        to overflow is 1 and 2.
     10        * crypto/subtle/aes-ctr-import-key-encrypt-expected.txt:
     11        * crypto/subtle/aes-ctr-import-key-encrypt.html:
     12
    1132020-02-12  Lauro Moura  <lmoura@igalia.com>
    214
  • trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt-expected.txt

    r215051 r256503  
    1212Length = 2, overflow
    1313PASS bytesToHexString(cipherText) is expectedCipherText4
     14Length = 2, overflow
     15PASS bytesToHexString(cipherText) is expectedCipherText5
    1416Length = 8, overflow
    15 PASS bytesToHexString(cipherText) is expectedCipherText5
     17PASS bytesToHexString(cipherText) is expectedCipherText6
     18Length = 8, overflow
     19PASS bytesToHexString(cipherText) is expectedCipherText7
     20Length = 64, overflow
     21PASS bytesToHexString(cipherText) is expectedCipherText8
     22Length = 64, overflow
     23PASS bytesToHexString(cipherText) is expectedCipherText9
     24Length = 66, overflow
     25PASS bytesToHexString(cipherText) is expectedCipherText10
     26Length = 66, overflow
     27PASS bytesToHexString(cipherText) is expectedCipherText11
     28Length = 128, overflow
     29PASS bytesToHexString(cipherText) is expectedCipherText12
     30Length = 128, overflow
     31PASS bytesToHexString(cipherText) is expectedCipherText13
    1632PASS successfullyParsed is true
    1733
  • trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt.html

    r215051 r256503  
    3333var aesCtrParams4= {
    3434    name: "aes-ctr",
    35     counter: asciiToUint8Array("jnOw99oOZFLIEPMr"),
     35    counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dff"),
    3636    length: 2,
    3737}
    3838var aesCtrParams5= {
    3939    name: "aes-ctr",
     40    counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dfe"),
     41    length: 2,
     42}
     43var aesCtrParams6= {
     44    name: "aes-ctr",
    4045    counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dff"),
    4146    length: 8,
    4247}
     48var aesCtrParams7= {
     49    name: "aes-ctr",
     50    counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dfe"),
     51    length: 8,
     52}
     53var aesCtrParams8= {
     54    name: "aes-ctr",
     55    counter: hexStringToUint8Array("6a6e4f7739396f4fffffffffffffffff"),
     56    length: 64,
     57}
     58var aesCtrParams9= {
     59    name: "aes-ctr",
     60    counter: hexStringToUint8Array("6a6e4f7739396f4ffffffffffffffffe"),
     61    length: 64,
     62}
     63var aesCtrParams10= {
     64    name: "aes-ctr",
     65    counter: hexStringToUint8Array("6a6e4f7739396f7fffffffffffffffff"),
     66    length: 66,
     67}
     68var aesCtrParams11= {
     69    name: "aes-ctr",
     70    counter: hexStringToUint8Array("6a6e4f7739396f7ffffffffffffffffe"),
     71    length: 66,
     72}
     73var aesCtrParams12= {
     74    name: "aes-ctr",
     75    counter: hexStringToUint8Array("ffffffffffffffffffffffffffffffff"),
     76    length: 128,
     77}
     78var aesCtrParams13= {
     79    name: "aes-ctr",
     80    counter: hexStringToUint8Array("fffffffffffffffffffffffffffffffe"),
     81    length: 128,
     82}
    4383var rawKey = asciiToUint8Array("jnOw99oOZFLIEPMr");
    4484var expectedCipherText = "a5f940e93406d4bd9b7318e653d4cb9d1af497f52fcbb659a038e711e8bd61fb4863931d25911e2e9ff30cf37ec27dd813a62830";
    45 var expectedCipherText4 = "a5f940e93406d4bd9b7318e653d4cb9d1af497f52fcbb659a038e711e8bd61fb96fed7fa5bf75d282a5477583b970b171740a2fa";
    46 var expectedCipherText5 = "6a461eb3f64ef4c466597ba877a9512bf224051c88ae885c565a7ada56843f3b84ec7596df67cbfdcfbeb275768f4d7270ce7ddf";
     85var expectedCipherText4 = "6a461eb3f64ef4c466597ba877a9512b5ab41b4338edc2822d1f0dfac0cec07149766e189fa426d5ea30fe541018362088db2117";
     86var expectedCipherText5 = "b2d2295a2fa06ef570752c7d1bc08fc64e4c5effce0da6ff6d0a5fa93a8d5b6b168c581103e691a62c5229f08082f8321b4d654b";
     87var expectedCipherText6 = "6a461eb3f64ef4c466597ba877a9512bf224051c88ae885c565a7ada56843f3b84ec7596df67cbfdcfbeb275768f4d7270ce7ddf";
     88var expectedCipherText7 = "b2d2295a2fa06ef570752c7d1bc08fc64e4c5effce0da6ff6d0a5fa93a8d5b6bbe1c464eb3a5db7857175ed016c80778d6d77ec5";
     89var expectedCipherText8 = "3c37c5ea017d201bf608f86b0225c0d616d0e4f0ddd7aba96d4bb4ee3b829832b5ab2c2963d1d6b32ef3db59956bc15245b101c9";
     90var expectedCipherText9 = "86253252027d2b6fd6c95d7849f51abc183d85a6393e7220fd5bdc6a4f01ca965ae8a7a2e6dcf88d6c0690e47bcea071e790277a";
     91var expectedCipherText10 = "be0615c8485e2e7adc4e547b6aea98f59095547d42f9b1471edfe464152b1294f565b32c9ed385042291bec3e4ae312c3f32080b";
     92var expectedCipherText11 = "48eef2dfe7c2d41d93747a2c9f5a50ad9a0c5584701d7c41d71d707a27ce92b5dcad172f79f2e2631f92c06e55672ad7a75eb87f";
     93var expectedCipherText12 = "1065c7107fd33a3f1e05627238d30955055274f507c82716ccfb77f609446c07b1f07b80fc989b97be49007133953ad173a11cd7";
     94var expectedCipherText13 = "b936426686f9d79bf53cf9bb6a810997346f875c479068041556467375f70315496a37a73cc37432cdb653fc49085444e3cb70d3";
    4795
    4896crypto.subtle.importKey("raw", rawKey, "aes-ctr", extractable, ["encrypt"]).then(function(result) {
     
    77125    shouldBe("bytesToHexString(cipherText)", "expectedCipherText4");
    78126
    79     debug("Length = 8, overflow");
     127    debug("Length = 2, overflow");
    80128    return crypto.subtle.encrypt(aesCtrParams5, key, plainText);
    81129}).then(function(result) {
     
    83131
    84132    shouldBe("bytesToHexString(cipherText)", "expectedCipherText5");
     133
     134    debug("Length = 8, overflow");
     135    return crypto.subtle.encrypt(aesCtrParams6, key, plainText);
     136}).then(function(result) {
     137    cipherText = result;
     138
     139    shouldBe("bytesToHexString(cipherText)", "expectedCipherText6");
     140
     141    debug("Length = 8, overflow");
     142    return crypto.subtle.encrypt(aesCtrParams7, key, plainText);
     143}).then(function(result) {
     144    cipherText = result;
     145
     146    shouldBe("bytesToHexString(cipherText)", "expectedCipherText7");
     147
     148    debug("Length = 64, overflow");
     149    return crypto.subtle.encrypt(aesCtrParams8, key, plainText);
     150}).then(function(result) {
     151    cipherText = result;
     152
     153    shouldBe("bytesToHexString(cipherText)", "expectedCipherText8");
     154
     155    debug("Length = 64, overflow");
     156    return crypto.subtle.encrypt(aesCtrParams9, key, plainText);
     157}).then(function(result) {
     158    cipherText = result;
     159
     160    shouldBe("bytesToHexString(cipherText)", "expectedCipherText9");
     161
     162    debug("Length = 66, overflow");
     163    return crypto.subtle.encrypt(aesCtrParams10, key, plainText);
     164}).then(function(result) {
     165    cipherText = result;
     166
     167    shouldBe("bytesToHexString(cipherText)", "expectedCipherText10");
     168
     169    debug("Length = 66, overflow");
     170    return crypto.subtle.encrypt(aesCtrParams11, key, plainText);
     171}).then(function(result) {
     172    cipherText = result;
     173
     174    shouldBe("bytesToHexString(cipherText)", "expectedCipherText11");
     175
     176    debug("Length = 128, overflow");
     177    return crypto.subtle.encrypt(aesCtrParams12, key, plainText);
     178}).then(function(result) {
     179    cipherText = result;
     180
     181    shouldBe("bytesToHexString(cipherText)", "expectedCipherText12");
     182
     183    debug("Length = 128, overflow");
     184    return crypto.subtle.encrypt(aesCtrParams13, key, plainText);
     185}).then(function(result) {
     186    cipherText = result;
     187
     188    shouldBe("bytesToHexString(cipherText)", "expectedCipherText13");
    85189
    86190    finishJSTest();
  • trunk/Source/WebCore/ChangeLog

    r256501 r256503  
     12020-02-12  Yoshiaki Jitsukawa  <yoshiaki.jitsukawa@sony.com>
     2
     3        [WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
     4        https://bugs.webkit.org/show_bug.cgi?id=207238
     5
     6        Reviewed by Jiewen Tan.
     7
     8        Add CounterBlockHelper to calculate the remaining count for counter bits to overflow
     9        and the whole counter block bits (nonce + zero counter bits) after overflow.
     10
     11        With this helper, simplify the AES-CTR implementation on CommonCrypto.
     12
     13        * crypto/algorithms/CryptoAlgorithmAES_CTR.cpp:
     14        (WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockHelper):
     15        (WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::countToOverflowSaturating const):
     16        (WebCore::CryptoAlgorithmAES_CTR::CounterBlockHelper::counterVectorAfterOverflow const):
     17        * crypto/algorithms/CryptoAlgorithmAES_CTR.h:
     18        * crypto/mac/CryptoAlgorithmAES_CTRMac.cpp:
     19        (WebCore::transformAES_CTR):
     20        (WebCore::bigIntegerToSizeT): Deleted.
     21
    1222020-02-12  Said Abou-Hallawa  <sabouhallawa@apple.com>
    223
  • trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.cpp

    r238754 r256503  
    11/*
    22 * Copyright (C) 2017 Apple Inc. All rights reserved.
     3 * Copyright (C) 2020 Sony Interactive Entertainment Inc.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    3334#include "CryptoKeyAES.h"
    3435#include <wtf/CrossThreadCopier.h>
     36#include <wtf/FlipBytes.h>
    3537
    3638namespace WebCore {
     
    4143static const char* const ALG256 = "A256CTR";
    4244static const size_t CounterSize = 16;
     45static const uint64_t AllBitsSet = ~(uint64_t)0;
    4346}
    4447
     
    201204}
    202205
     206CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockHelper(const Vector<uint8_t>& counterVector, size_t counterLength)
     207    : m_counterLength(counterLength)
     208{
     209    using namespace CryptoAlgorithmAES_CTRInternal;
     210
     211    ASSERT(counterVector.size() == CounterSize);
     212    ASSERT(counterLength <= CounterSize * 8);
     213    bool littleEndian = false; // counterVector is stored in big-endian.
     214    memcpy(&m_bits.m_hi, counterVector.data(), 8);
     215    m_bits.m_hi = flipBytesIfLittleEndian(m_bits.m_hi, littleEndian);
     216    memcpy(&m_bits.m_lo, counterVector.data() + 8, 8);
     217    m_bits.m_lo = flipBytesIfLittleEndian(m_bits.m_lo, littleEndian);
     218}
     219
     220size_t CryptoAlgorithmAES_CTR::CounterBlockHelper::countToOverflowSaturating() const
     221{
     222    CounterBlockBits counterMask;
     223    counterMask.set();
     224    counterMask <<= m_counterLength;
     225    counterMask = ~counterMask;
     226
     227    auto countMinusOne = ~m_bits & counterMask;
     228
     229    CounterBlockBits sizeTypeMask;
     230    sizeTypeMask.set();
     231    sizeTypeMask <<= sizeof(size_t) * 8;
     232    if ((sizeTypeMask & countMinusOne).any()) {
     233        // Saturating to the size_t max since the count is greater than that.
     234        return std::numeric_limits<size_t>::max();
     235    }
     236
     237    countMinusOne &= ~sizeTypeMask;
     238    if (countMinusOne.all()) {
     239        // As all bits are set, adding one would result in an overflow.
     240        // Return size_t max instead.
     241        return std::numeric_limits<size_t>::max();
     242    }
     243
     244    static_assert(sizeof(size_t) <= sizeof(uint64_t));
     245    return countMinusOne.m_lo + 1;
     246}
     247
     248Vector<uint8_t> CryptoAlgorithmAES_CTR::CounterBlockHelper::counterVectorAfterOverflow() const
     249{
     250    using namespace CryptoAlgorithmAES_CTRInternal;
     251
     252    CounterBlockBits nonceMask;
     253    nonceMask.set();
     254    nonceMask <<= m_counterLength;
     255    auto bits = m_bits & nonceMask;
     256
     257    bool littleEndian = false; // counterVector is stored in big-endian.
     258    Vector<uint8_t> counterVector(CounterSize);
     259    uint64_t hi = flipBytesIfLittleEndian(bits.m_hi, littleEndian);
     260    memcpy(counterVector.data(), &hi, 8);
     261    uint64_t lo = flipBytesIfLittleEndian(bits.m_lo, littleEndian);
     262    memcpy(counterVector.data() + 8, &lo, 8);
     263
     264    return counterVector;
     265}
     266
     267void CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::set()
     268{
     269    using namespace CryptoAlgorithmAES_CTRInternal;
     270    m_hi = AllBitsSet;
     271    m_lo = AllBitsSet;
     272}
     273
     274bool CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::all() const
     275{
     276    using namespace CryptoAlgorithmAES_CTRInternal;
     277    return m_hi == AllBitsSet && m_lo == AllBitsSet;
     278}
     279
     280bool CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::any() const
     281{
     282    return m_hi || m_lo;
     283}
     284
     285auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator&(const CounterBlockBits& rhs) const -> CounterBlockBits
     286{
     287    return { m_hi & rhs.m_hi, m_lo & rhs.m_lo };
     288}
     289
     290auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator~() const -> CounterBlockBits
     291{
     292    return { ~m_hi, ~m_lo };
     293}
     294
     295auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator <<=(unsigned shift) -> CounterBlockBits&
     296{
     297    if (shift < 64) {
     298        m_hi = (m_hi << shift) | m_lo >> (64 - shift);
     299        m_lo <<= shift;
     300    } else if (shift < 128) {
     301        shift -= 64;
     302        m_hi = m_lo << shift;
     303        m_lo = 0;
     304    } else {
     305        m_hi = 0;
     306        m_lo = 0;
     307    }
     308    return *this;
     309}
     310
     311auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator &=(const CounterBlockBits& rhs) -> CounterBlockBits&
     312{
     313    m_hi &= rhs.m_hi;
     314    m_lo &= rhs.m_lo;
     315    return *this;
     316}
     317
    203318}
    204319
  • trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.h

    r238754 r256503  
    11/*
    22 * Copyright (C) 2017 Apple Inc. All rights reserved.
     3 * Copyright (C) 2020 Sony Interactive Entertainment Inc.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    3738class CryptoAlgorithmAES_CTR final : public CryptoAlgorithm {
    3839public:
     40    class CounterBlockHelper {
     41    public:
     42        CounterBlockHelper(const Vector<uint8_t>& counterVector, size_t counterLength);
     43
     44        size_t countToOverflowSaturating() const;
     45        Vector<uint8_t> counterVectorAfterOverflow() const;
     46
     47    private:
     48        // 128 bits integer with miminum required operators.
     49        struct CounterBlockBits {
     50            void set();
     51            bool all() const;
     52            bool any() const;
     53
     54            CounterBlockBits operator&(const CounterBlockBits&) const;
     55            CounterBlockBits operator~() const;
     56            CounterBlockBits& operator <<=(unsigned);
     57            CounterBlockBits& operator &=(const CounterBlockBits&);
     58
     59            uint64_t m_hi { 0 };
     60            uint64_t m_lo { 0 };
     61        };
     62
     63        CounterBlockBits m_bits;
     64        const size_t m_counterLength;
     65    };
     66
    3967    static constexpr const char* s_name = "AES-CTR";
    4068    static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::AES_CTR;
  • trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp

    r238754 r256503  
    11/*
    22 * Copyright (C) 2017 Apple Inc. All rights reserved.
     3 * Copyright (C) 2020 Sony Interactive Entertainment Inc.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    3536namespace WebCore {
    3637
    37 // It takes the last WORDSIZE/8 bytes of the bigInteger and then convert them into a size_t value
    38 static size_t bigIntegerToSizeT(const Vector<uint8_t>& bigInteger)
    39 {
    40     size_t result = 0;
    41     for (size_t i = bigInteger.size() - (__WORDSIZE / 8); i < bigInteger.size(); ++i) {
    42         result <<= 8;
    43         result += bigInteger[i];
    44     }
    45     return result;
    46 }
    47 
    4838static ExceptionOr<Vector<uint8_t>> transformAES_CTR(CCOperation operation, const Vector<uint8_t>& counter, size_t counterLength, const Vector<uint8_t>& key, const Vector<uint8_t>& data)
    4939{
     
    5343    // by the counterLength. It then increments the nonce which should stay same for the whole operation. To remedy this issue,
    5444    // we detect the overflow ahead and divide the operation into two parts.
    55     // Ignore the case: counterLength > __WORDSIZE.
    5645    size_t numberOfBlocks = data.size() % kCCBlockSizeAES128 ? data.size() / kCCBlockSizeAES128 + 1 : data.size() / kCCBlockSizeAES128;
     46
    5747    // Detect loop
    58     if (counterLength < __WORDSIZE && numberOfBlocks > (1 << counterLength))
     48    if (counterLength < sizeof(size_t) * 8 && numberOfBlocks > (1 << counterLength))
    5949        return Exception { OperationError };
     50
    6051    // Calculate capacity before overflow
    61     size_t rightMost = bigIntegerToSizeT(counter); // convert right most __WORDSIZE bits into a size_t value, which is the longest counter we could possibly use.
    62     size_t capacity = numberOfBlocks; // SIZE_MAX - counter
    63     if (counterLength < __WORDSIZE) {
    64         size_t mask = SIZE_MAX << counterLength; // Used to set nonce in rightMost(nonce + counter) to 1s
    65         capacity = SIZE_MAX - (rightMost| mask) + 1;
    66     }
    67     if (counterLength == __WORDSIZE)
    68         capacity = SIZE_MAX - rightMost + 1;
     52    CryptoAlgorithmAES_CTR::CounterBlockHelper counterBlockHelper(counter, counterLength);
     53    size_t capacity = counterBlockHelper.countToOverflowSaturating();
    6954
    7055    // Divide data into two parts if necessary.
     
    10287    // second part: compute the remaining data and append them to the head.
    10388    // reset counter
    104     Vector<uint8_t> remainingCounter(counter.size());
    105     size_t counterOffset = counterLength % 8;
    106     size_t nonceOffset = counter.size() - counterLength / 8 - !!counterOffset;
    107     memcpy(remainingCounter.data(), counter.data(), nonceOffset); // copy the nonce
    108     // set the middle byte
    109     if (!!counterOffset) {
    110         size_t mask = SIZE_MAX << counterOffset;
    111         remainingCounter[nonceOffset] = counter[nonceOffset] & mask;
    112     }
    113     memset(remainingCounter.data() + nonceOffset + !!counterOffset, 0, counterLength / 8); // reset the counter
    114 
     89    Vector<uint8_t> remainingCounter = counterBlockHelper.counterVectorAfterOverflow();
    11590    status = CCCryptorCreateWithMode(operation, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding, remainingCounter.data(), key.data(), key.size(), 0, 0, 0, kCCModeOptionCTR_BE, &cryptor);
    11691    if (status)
Note: See TracChangeset for help on using the changeset viewer.