[WebCrypto] Crypto operations should copy their parameters before hoping to another...
[WebKit-https.git] / Source / WebCore / crypto / mac / CryptoAlgorithmAES_CTRMac.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CryptoAlgorithmAES_CTR.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithmAesCtrParams.h"
32 #include "CryptoKeyAES.h"
33 #include <CommonCrypto/CommonCrypto.h>
34
35 namespace WebCore {
36
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
48 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)
49 {
50     // FIXME: We should remove the following hack once <rdar://problem/31361050> is fixed.
51     // counter = nonce + counter
52     // CommonCrypto currently can neither reset the counter nor detect overflow once the counter reaches its max value restricted
53     // by the counterLength. It then increments the nonce which should stay same for the whole operation. To remedy this issue,
54     // we detect the overflow ahead and divide the operation into two parts.
55     // Ignore the case: counterLength > __WORDSIZE.
56     size_t numberOfBlocks = data.size() % kCCBlockSizeAES128 ? data.size() / kCCBlockSizeAES128 + 1 : data.size() / kCCBlockSizeAES128;
57     // Detect loop
58     if (counterLength < __WORDSIZE && numberOfBlocks > (1 << counterLength))
59         return Exception { OperationError };
60     // 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;
69
70     // Divide data into two parts if necessary.
71     size_t headSize = data.size();
72     if (capacity < numberOfBlocks)
73         headSize = capacity * kCCBlockSizeAES128;
74
75     // first part: compute the first n=capacity blocks of data if capacity is insufficient. Otherwise, return the result.
76     CCCryptorRef cryptor;
77     CCCryptorStatus status = CCCryptorCreateWithMode(operation, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding, counter.data(), key.data(), key.size(), 0, 0, 0, kCCModeOptionCTR_BE, &cryptor);
78     if (status)
79         return Exception { OperationError };
80
81     Vector<uint8_t> head(CCCryptorGetOutputLength(cryptor, headSize, true));
82
83     size_t bytesWritten;
84     status = CCCryptorUpdate(cryptor, data.data(), headSize, head.data(), head.size(), &bytesWritten);
85     if (status)
86         return Exception { OperationError };
87
88     uint8_t* p = head.data() + bytesWritten;
89     status = CCCryptorFinal(cryptor, p, head.end() - p, &bytesWritten);
90     p += bytesWritten;
91     if (status)
92         return Exception { OperationError };
93
94     ASSERT_WITH_SECURITY_IMPLICATION(p <= head.end());
95     head.shrink(p - head.begin());
96
97     CCCryptorRelease(cryptor);
98
99     if (capacity >= numberOfBlocks)
100         return WTFMove(head);
101
102     // second part: compute the remaining data and append them to the head.
103     // 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
115     status = CCCryptorCreateWithMode(operation, kCCModeCTR, kCCAlgorithmAES128, ccNoPadding, remainingCounter.data(), key.data(), key.size(), 0, 0, 0, kCCModeOptionCTR_BE, &cryptor);
116     if (status)
117         return Exception { OperationError };
118
119     size_t tailSize = data.size() - headSize;
120     Vector<uint8_t> tail(CCCryptorGetOutputLength(cryptor, tailSize, true));
121
122     status = CCCryptorUpdate(cryptor, data.data() + headSize, tailSize, tail.data(), tail.size(), &bytesWritten);
123     if (status)
124         return Exception { OperationError };
125
126     p = tail.data() + bytesWritten;
127     status = CCCryptorFinal(cryptor, p, tail.end() - p, &bytesWritten);
128     p += bytesWritten;
129     if (status)
130         return Exception { OperationError };
131
132     ASSERT_WITH_SECURITY_IMPLICATION(p <= tail.end());
133     tail.shrink(p - tail.begin());
134
135     CCCryptorRelease(cryptor);
136
137     head.appendVector(tail);
138     return WTFMove(head);
139 }
140
141 ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CTR::platformEncrypt(const CryptoAlgorithmAesCtrParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& plainText)
142 {
143     ASSERT(parameters.counterVector().size() == kCCBlockSizeAES128);
144     return transformAES_CTR(kCCEncrypt, parameters.counterVector(), parameters.length, key.key(), plainText);
145 }
146
147 ExceptionOr<Vector<uint8_t>> CryptoAlgorithmAES_CTR::platformDecrypt(const CryptoAlgorithmAesCtrParams& parameters, const CryptoKeyAES& key, const Vector<uint8_t>& cipherText)
148 {
149       ASSERT(parameters.counterVector().size() == kCCBlockSizeAES128);
150     return transformAES_CTR(kCCDecrypt, parameters.counterVector(), parameters.length, key.key(), cipherText);
151 }
152
153 } // namespace WebCore
154
155 #endif // ENABLE(SUBTLE_CRYPTO)