Diff
Modified: trunk/LayoutTests/ChangeLog (256502 => 256503)
--- trunk/LayoutTests/ChangeLog 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/LayoutTests/ChangeLog 2020-02-13 07:20:17 UTC (rev 256503)
@@ -1,3 +1,15 @@
+2020-02-12 Yoshiaki Jitsukawa <[email protected]>
+
+ [WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
+ https://bugs.webkit.org/show_bug.cgi?id=207238
+
+ Reviewed by Jiewen Tan.
+
+ 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:
+
2020-02-12 Lauro Moura <[email protected]>
[GTK][WPE] More layout tests gardening
Modified: trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt-expected.txt (256502 => 256503)
--- trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt-expected.txt 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt-expected.txt 2020-02-13 07:20:17 UTC (rev 256503)
@@ -11,8 +11,24 @@
PASS bytesToHexString(cipherText) is expectedCipherText
Length = 2, overflow
PASS bytesToHexString(cipherText) is expectedCipherText4
+Length = 2, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText5
Length = 8, overflow
-PASS bytesToHexString(cipherText) is expectedCipherText5
+PASS bytesToHexString(cipherText) is expectedCipherText6
+Length = 8, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText7
+Length = 64, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText8
+Length = 64, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText9
+Length = 66, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText10
+Length = 66, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText11
+Length = 128, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText12
+Length = 128, overflow
+PASS bytesToHexString(cipherText) is expectedCipherText13
PASS successfullyParsed is true
TEST COMPLETE
Modified: trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt.html (256502 => 256503)
--- trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt.html 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt.html 2020-02-13 07:20:17 UTC (rev 256503)
@@ -32,18 +32,66 @@
}
var aesCtrParams4= {
name: "aes-ctr",
- counter: asciiToUint8Array("jnOw99oOZFLIEPMr"),
+ counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dff"),
length: 2,
}
var aesCtrParams5= {
name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dfe"),
+ length: 2,
+}
+var aesCtrParams6= {
+ name: "aes-ctr",
counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dff"),
length: 8,
}
+var aesCtrParams7= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f4f5a464c4945504dfe"),
+ length: 8,
+}
+var aesCtrParams8= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f4fffffffffffffffff"),
+ length: 64,
+}
+var aesCtrParams9= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f4ffffffffffffffffe"),
+ length: 64,
+}
+var aesCtrParams10= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f7fffffffffffffffff"),
+ length: 66,
+}
+var aesCtrParams11= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("6a6e4f7739396f7ffffffffffffffffe"),
+ length: 66,
+}
+var aesCtrParams12= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("ffffffffffffffffffffffffffffffff"),
+ length: 128,
+}
+var aesCtrParams13= {
+ name: "aes-ctr",
+ counter: hexStringToUint8Array("fffffffffffffffffffffffffffffffe"),
+ length: 128,
+}
var rawKey = asciiToUint8Array("jnOw99oOZFLIEPMr");
var expectedCipherText = "a5f940e93406d4bd9b7318e653d4cb9d1af497f52fcbb659a038e711e8bd61fb4863931d25911e2e9ff30cf37ec27dd813a62830";
-var expectedCipherText4 = "a5f940e93406d4bd9b7318e653d4cb9d1af497f52fcbb659a038e711e8bd61fb96fed7fa5bf75d282a5477583b970b171740a2fa";
-var expectedCipherText5 = "6a461eb3f64ef4c466597ba877a9512bf224051c88ae885c565a7ada56843f3b84ec7596df67cbfdcfbeb275768f4d7270ce7ddf";
+var expectedCipherText4 = "6a461eb3f64ef4c466597ba877a9512b5ab41b4338edc2822d1f0dfac0cec07149766e189fa426d5ea30fe541018362088db2117";
+var expectedCipherText5 = "b2d2295a2fa06ef570752c7d1bc08fc64e4c5effce0da6ff6d0a5fa93a8d5b6b168c581103e691a62c5229f08082f8321b4d654b";
+var expectedCipherText6 = "6a461eb3f64ef4c466597ba877a9512bf224051c88ae885c565a7ada56843f3b84ec7596df67cbfdcfbeb275768f4d7270ce7ddf";
+var expectedCipherText7 = "b2d2295a2fa06ef570752c7d1bc08fc64e4c5effce0da6ff6d0a5fa93a8d5b6bbe1c464eb3a5db7857175ed016c80778d6d77ec5";
+var expectedCipherText8 = "3c37c5ea017d201bf608f86b0225c0d616d0e4f0ddd7aba96d4bb4ee3b829832b5ab2c2963d1d6b32ef3db59956bc15245b101c9";
+var expectedCipherText9 = "86253252027d2b6fd6c95d7849f51abc183d85a6393e7220fd5bdc6a4f01ca965ae8a7a2e6dcf88d6c0690e47bcea071e790277a";
+var expectedCipherText10 = "be0615c8485e2e7adc4e547b6aea98f59095547d42f9b1471edfe464152b1294f565b32c9ed385042291bec3e4ae312c3f32080b";
+var expectedCipherText11 = "48eef2dfe7c2d41d93747a2c9f5a50ad9a0c5584701d7c41d71d707a27ce92b5dcad172f79f2e2631f92c06e55672ad7a75eb87f";
+var expectedCipherText12 = "1065c7107fd33a3f1e05627238d30955055274f507c82716ccfb77f609446c07b1f07b80fc989b97be49007133953ad173a11cd7";
+var expectedCipherText13 = "b936426686f9d79bf53cf9bb6a810997346f875c479068041556467375f70315496a37a73cc37432cdb653fc49085444e3cb70d3";
crypto.subtle.importKey("raw", rawKey, "aes-ctr", extractable, ["encrypt"]).then(function(result) {
key = result;
@@ -76,7 +124,7 @@
shouldBe("bytesToHexString(cipherText)", "expectedCipherText4");
- debug("Length = 8, overflow");
+ debug("Length = 2, overflow");
return crypto.subtle.encrypt(aesCtrParams5, key, plainText);
}).then(function(result) {
cipherText = result;
@@ -83,6 +131,62 @@
shouldBe("bytesToHexString(cipherText)", "expectedCipherText5");
+ debug("Length = 8, overflow");
+ return crypto.subtle.encrypt(aesCtrParams6, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText6");
+
+ debug("Length = 8, overflow");
+ return crypto.subtle.encrypt(aesCtrParams7, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText7");
+
+ debug("Length = 64, overflow");
+ return crypto.subtle.encrypt(aesCtrParams8, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText8");
+
+ debug("Length = 64, overflow");
+ return crypto.subtle.encrypt(aesCtrParams9, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText9");
+
+ debug("Length = 66, overflow");
+ return crypto.subtle.encrypt(aesCtrParams10, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText10");
+
+ debug("Length = 66, overflow");
+ return crypto.subtle.encrypt(aesCtrParams11, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText11");
+
+ debug("Length = 128, overflow");
+ return crypto.subtle.encrypt(aesCtrParams12, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText12");
+
+ debug("Length = 128, overflow");
+ return crypto.subtle.encrypt(aesCtrParams13, key, plainText);
+}).then(function(result) {
+ cipherText = result;
+
+ shouldBe("bytesToHexString(cipherText)", "expectedCipherText13");
+
finishJSTest();
});
Modified: trunk/Source/WebCore/ChangeLog (256502 => 256503)
--- trunk/Source/WebCore/ChangeLog 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/Source/WebCore/ChangeLog 2020-02-13 07:20:17 UTC (rev 256503)
@@ -1,3 +1,24 @@
+2020-02-12 Yoshiaki Jitsukawa <[email protected]>
+
+ [WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
+ https://bugs.webkit.org/show_bug.cgi?id=207238
+
+ Reviewed by Jiewen Tan.
+
+ 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.
+
2020-02-12 Said Abou-Hallawa <[email protected]>
WebP image format is not supported
Modified: trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.cpp (256502 => 256503)
--- trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.cpp 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.cpp 2020-02-13 07:20:17 UTC (rev 256503)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2020 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,6 +33,7 @@
#include "CryptoAlgorithmAesKeyParams.h"
#include "CryptoKeyAES.h"
#include <wtf/CrossThreadCopier.h>
+#include <wtf/FlipBytes.h>
namespace WebCore {
@@ -40,6 +42,7 @@
static const char* const ALG192 = "A192CTR";
static const char* const ALG256 = "A256CTR";
static const size_t CounterSize = 16;
+static const uint64_t AllBitsSet = ~(uint64_t)0;
}
static inline bool usagesAreInvalidForCryptoAlgorithmAES_CTR(CryptoKeyUsageBitmap usages)
@@ -200,6 +203,118 @@
return CryptoKeyAES::getKeyLength(parameters);
}
+CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockHelper(const Vector<uint8_t>& counterVector, size_t counterLength)
+ : m_counterLength(counterLength)
+{
+ using namespace CryptoAlgorithmAES_CTRInternal;
+
+ ASSERT(counterVector.size() == CounterSize);
+ ASSERT(counterLength <= CounterSize * 8);
+ bool littleEndian = false; // counterVector is stored in big-endian.
+ memcpy(&m_bits.m_hi, counterVector.data(), 8);
+ m_bits.m_hi = flipBytesIfLittleEndian(m_bits.m_hi, littleEndian);
+ memcpy(&m_bits.m_lo, counterVector.data() + 8, 8);
+ m_bits.m_lo = flipBytesIfLittleEndian(m_bits.m_lo, littleEndian);
}
+size_t CryptoAlgorithmAES_CTR::CounterBlockHelper::countToOverflowSaturating() const
+{
+ CounterBlockBits counterMask;
+ counterMask.set();
+ counterMask <<= m_counterLength;
+ counterMask = ~counterMask;
+
+ auto countMinusOne = ~m_bits & counterMask;
+
+ CounterBlockBits sizeTypeMask;
+ sizeTypeMask.set();
+ sizeTypeMask <<= sizeof(size_t) * 8;
+ if ((sizeTypeMask & countMinusOne).any()) {
+ // Saturating to the size_t max since the count is greater than that.
+ return std::numeric_limits<size_t>::max();
+ }
+
+ countMinusOne &= ~sizeTypeMask;
+ if (countMinusOne.all()) {
+ // As all bits are set, adding one would result in an overflow.
+ // Return size_t max instead.
+ return std::numeric_limits<size_t>::max();
+ }
+
+ static_assert(sizeof(size_t) <= sizeof(uint64_t));
+ return countMinusOne.m_lo + 1;
+}
+
+Vector<uint8_t> CryptoAlgorithmAES_CTR::CounterBlockHelper::counterVectorAfterOverflow() const
+{
+ using namespace CryptoAlgorithmAES_CTRInternal;
+
+ CounterBlockBits nonceMask;
+ nonceMask.set();
+ nonceMask <<= m_counterLength;
+ auto bits = m_bits & nonceMask;
+
+ bool littleEndian = false; // counterVector is stored in big-endian.
+ Vector<uint8_t> counterVector(CounterSize);
+ uint64_t hi = flipBytesIfLittleEndian(bits.m_hi, littleEndian);
+ memcpy(counterVector.data(), &hi, 8);
+ uint64_t lo = flipBytesIfLittleEndian(bits.m_lo, littleEndian);
+ memcpy(counterVector.data() + 8, &lo, 8);
+
+ return counterVector;
+}
+
+void CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::set()
+{
+ using namespace CryptoAlgorithmAES_CTRInternal;
+ m_hi = AllBitsSet;
+ m_lo = AllBitsSet;
+}
+
+bool CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::all() const
+{
+ using namespace CryptoAlgorithmAES_CTRInternal;
+ return m_hi == AllBitsSet && m_lo == AllBitsSet;
+}
+
+bool CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::any() const
+{
+ return m_hi || m_lo;
+}
+
+auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator&(const CounterBlockBits& rhs) const -> CounterBlockBits
+{
+ return { m_hi & rhs.m_hi, m_lo & rhs.m_lo };
+}
+
+auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator~() const -> CounterBlockBits
+{
+ return { ~m_hi, ~m_lo };
+}
+
+auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator <<=(unsigned shift) -> CounterBlockBits&
+{
+ if (shift < 64) {
+ m_hi = (m_hi << shift) | m_lo >> (64 - shift);
+ m_lo <<= shift;
+ } else if (shift < 128) {
+ shift -= 64;
+ m_hi = m_lo << shift;
+ m_lo = 0;
+ } else {
+ m_hi = 0;
+ m_lo = 0;
+ }
+ return *this;
+}
+
+auto CryptoAlgorithmAES_CTR::CounterBlockHelper::CounterBlockBits::operator &=(const CounterBlockBits& rhs) -> CounterBlockBits&
+{
+ m_hi &= rhs.m_hi;
+ m_lo &= rhs.m_lo;
+ return *this;
+}
+
+}
+
#endif // ENABLE(WEB_CRYPTO)
Modified: trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.h (256502 => 256503)
--- trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.h 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.h 2020-02-13 07:20:17 UTC (rev 256503)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2020 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,6 +37,33 @@
class CryptoAlgorithmAES_CTR final : public CryptoAlgorithm {
public:
+ class CounterBlockHelper {
+ public:
+ CounterBlockHelper(const Vector<uint8_t>& counterVector, size_t counterLength);
+
+ size_t countToOverflowSaturating() const;
+ Vector<uint8_t> counterVectorAfterOverflow() const;
+
+ private:
+ // 128 bits integer with miminum required operators.
+ struct CounterBlockBits {
+ void set();
+ bool all() const;
+ bool any() const;
+
+ CounterBlockBits operator&(const CounterBlockBits&) const;
+ CounterBlockBits operator~() const;
+ CounterBlockBits& operator <<=(unsigned);
+ CounterBlockBits& operator &=(const CounterBlockBits&);
+
+ uint64_t m_hi { 0 };
+ uint64_t m_lo { 0 };
+ };
+
+ CounterBlockBits m_bits;
+ const size_t m_counterLength;
+ };
+
static constexpr const char* s_name = "AES-CTR";
static constexpr CryptoAlgorithmIdentifier s_identifier = CryptoAlgorithmIdentifier::AES_CTR;
static Ref<CryptoAlgorithm> create();
Modified: trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp (256502 => 256503)
--- trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp 2020-02-13 05:54:38 UTC (rev 256502)
+++ trunk/Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp 2020-02-13 07:20:17 UTC (rev 256503)
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2020 Sony Interactive Entertainment Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,17 +35,6 @@
namespace WebCore {
-// It takes the last WORDSIZE/8 bytes of the bigInteger and then convert them into a size_t value
-static size_t bigIntegerToSizeT(const Vector<uint8_t>& bigInteger)
-{
- size_t result = 0;
- for (size_t i = bigInteger.size() - (__WORDSIZE / 8); i < bigInteger.size(); ++i) {
- result <<= 8;
- result += bigInteger[i];
- }
- return result;
-}
-
static 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)
{
// FIXME: We should remove the following hack once <rdar://problem/31361050> is fixed.
@@ -52,20 +42,15 @@
// CommonCrypto currently can neither reset the counter nor detect overflow once the counter reaches its max value restricted
// by the counterLength. It then increments the nonce which should stay same for the whole operation. To remedy this issue,
// we detect the overflow ahead and divide the operation into two parts.
- // Ignore the case: counterLength > __WORDSIZE.
size_t numberOfBlocks = data.size() % kCCBlockSizeAES128 ? data.size() / kCCBlockSizeAES128 + 1 : data.size() / kCCBlockSizeAES128;
+
// Detect loop
- if (counterLength < __WORDSIZE && numberOfBlocks > (1 << counterLength))
+ if (counterLength < sizeof(size_t) * 8 && numberOfBlocks > (1 << counterLength))
return Exception { OperationError };
+
// Calculate capacity before overflow
- size_t rightMost = bigIntegerToSizeT(counter); // convert right most __WORDSIZE bits into a size_t value, which is the longest counter we could possibly use.
- size_t capacity = numberOfBlocks; // SIZE_MAX - counter
- if (counterLength < __WORDSIZE) {
- size_t mask = SIZE_MAX << counterLength; // Used to set nonce in rightMost(nonce + counter) to 1s
- capacity = SIZE_MAX - (rightMost| mask) + 1;
- }
- if (counterLength == __WORDSIZE)
- capacity = SIZE_MAX - rightMost + 1;
+ CryptoAlgorithmAES_CTR::CounterBlockHelper counterBlockHelper(counter, counterLength);
+ size_t capacity = counterBlockHelper.countToOverflowSaturating();
// Divide data into two parts if necessary.
size_t headSize = data.size();
@@ -101,17 +86,7 @@
// second part: compute the remaining data and append them to the head.
// reset counter
- Vector<uint8_t> remainingCounter(counter.size());
- size_t counterOffset = counterLength % 8;
- size_t nonceOffset = counter.size() - counterLength / 8 - !!counterOffset;
- memcpy(remainingCounter.data(), counter.data(), nonceOffset); // copy the nonce
- // set the middle byte
- if (!!counterOffset) {
- size_t mask = SIZE_MAX << counterOffset;
- remainingCounter[nonceOffset] = counter[nonceOffset] & mask;
- }
- memset(remainingCounter.data() + nonceOffset + !!counterOffset, 0, counterLength / 8); // reset the counter
-
+ Vector<uint8_t> remainingCounter = counterBlockHelper.counterVectorAfterOverflow();
status = CCCryptorCreateWithMode(operation, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding, remainingCounter.data(), key.data(), key.size(), 0, 0, 0, kCCModeOptionCTR_BE, &cryptor);
if (status)
return Exception { OperationError };