[WebCrypto][CommonCrypto] Incorrect AES-CTR with counterLength longer than 64
authoryoshiaki.jitsukawa@sony.com <yoshiaki.jitsukawa@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Feb 2020 07:20:17 +0000 (07:20 +0000)
committeryoshiaki.jitsukawa@sony.com <yoshiaki.jitsukawa@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 13 Feb 2020 07:20:17 +0000 (07:20 +0000)
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:

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

LayoutTests/ChangeLog
LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt-expected.txt
LayoutTests/crypto/subtle/aes-ctr-import-key-encrypt.html
Source/WebCore/ChangeLog
Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.cpp
Source/WebCore/crypto/algorithms/CryptoAlgorithmAES_CTR.h
Source/WebCore/crypto/mac/CryptoAlgorithmAES_CTRMac.cpp

index 4ba10d5..288a35a 100644 (file)
@@ -1,3 +1,15 @@
+2020-02-12  Yoshiaki Jitsukawa  <yoshiaki.jitsukawa@sony.com>
+
+        [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  <lmoura@igalia.com>
 
         [GTK][WPE] More layout tests gardening
index 6e42912..0b03cf6 100644 (file)
@@ -11,8 +11,24 @@ Length = 65
 PASS bytesToHexString(cipherText) is expectedCipherText
 Length = 2, overflow
 PASS bytesToHexString(cipherText) is expectedCipherText4
-Length = 8, overflow
+Length = 2, overflow
 PASS bytesToHexString(cipherText) is expectedCipherText5
+Length = 8, overflow
+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
index 3701458..9fbc7e0 100644 (file)
@@ -32,18 +32,66 @@ var aesCtrParams3 = {
 }
 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,13 +124,69 @@ crypto.subtle.importKey("raw", rawKey, "aes-ctr", extractable, ["encrypt"]).then
 
     shouldBe("bytesToHexString(cipherText)", "expectedCipherText4");
 
-    debug("Length = 8, overflow");
+    debug("Length = 2, overflow");
     return crypto.subtle.encrypt(aesCtrParams5, key, plainText);
 }).then(function(result) {
     cipherText = result;
 
     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();
 });
 
index 25c4a25..c9cc790 100644 (file)
@@ -1,3 +1,24 @@
+2020-02-12  Yoshiaki Jitsukawa  <yoshiaki.jitsukawa@sony.com>
+
+        [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  <sabouhallawa@apple.com>
 
         WebP image format is not supported
index a3a6b67..ed44328 100644 (file)
@@ -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 ALG128 = "A128CTR";
 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 @@ ExceptionOr<size_t> CryptoAlgorithmAES_CTR::getKeyLength(const CryptoAlgorithmPa
     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)
index 1e7ba0f..718f6e0 100644 (file)
@@ -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 CryptoKeyAES;
 
 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();
index b9970e7..cdb9db7 100644 (file)
@@ -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
 
 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 @@ static ExceptionOr<Vector<uint8_t>> transformAES_CTR(CCOperation operation, cons
     // 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 @@ static ExceptionOr<Vector<uint8_t>> transformAES_CTR(CCOperation operation, cons
 
     // 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 };