|
16 | 16 | #else
|
17 | 17 | @_implementationOnly import CCryptoBoringSSL
|
18 | 18 | @_implementationOnly import CCryptoBoringSSLShims
|
| 19 | +@_implementationOnly import CryptoBoringWrapper |
19 | 20 | import Foundation
|
20 | 21 |
|
| 22 | +extension BoringSSLAEAD { |
| 23 | + /// Seal a given message. |
| 24 | + func seal<Plaintext: DataProtocol, Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(message: Plaintext, key: SymmetricKey, nonce: Nonce, authenticatedData: AuthenticatedData) throws -> (ciphertext: Data, tag: Data) { |
| 25 | + do { |
| 26 | + let context = try AEADContext(cipher: self, key: key) |
| 27 | + return try context.seal(message: message, nonce: nonce, authenticatedData: authenticatedData) |
| 28 | + } catch CryptoBoringWrapperError.underlyingCoreCryptoError(let errorCode) { |
| 29 | + throw CryptoKitError.underlyingCoreCryptoError(error: errorCode) |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + /// Open a given message. |
| 34 | + func open<Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(ciphertext: Data, key: SymmetricKey, nonce: Nonce, tag: Data, authenticatedData: AuthenticatedData) throws -> Data { |
| 35 | + do { |
| 36 | + let context = try AEADContext(cipher: self, key: key) |
| 37 | + return try context.open(ciphertext: ciphertext, nonce: nonce, tag: tag, authenticatedData: authenticatedData) |
| 38 | + } catch CryptoBoringWrapperError.underlyingCoreCryptoError(let errorCode) { |
| 39 | + throw CryptoKitError.underlyingCoreCryptoError(error: errorCode) |
| 40 | + } |
| 41 | + } |
| 42 | +} |
| 43 | + |
21 | 44 | enum OpenSSLChaChaPolyImpl {
|
22 | 45 | static func encrypt<M: DataProtocol, AD: DataProtocol>(key: SymmetricKey, message: M, nonce: ChaChaPoly.Nonce?, authenticatedData: AD?) throws -> ChaChaPoly.SealedBox {
|
23 | 46 | guard key.bitCount == ChaChaPoly.keyBitsCount else {
|
@@ -48,208 +71,4 @@ enum OpenSSLChaChaPolyImpl {
|
48 | 71 | }
|
49 | 72 | }
|
50 | 73 | }
|
51 |
| - |
52 |
| -/// An abstraction over a BoringSSL AEAD |
53 |
| -@usableFromInline |
54 |
| -enum BoringSSLAEAD { |
55 |
| - /// The supported AEAD ciphers for BoringSSL. |
56 |
| - case aes128gcm |
57 |
| - case aes192gcm |
58 |
| - case aes256gcm |
59 |
| - case chacha20 |
60 |
| - |
61 |
| - /// Seal a given message. |
62 |
| - func seal<Plaintext: DataProtocol, Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(message: Plaintext, key: SymmetricKey, nonce: Nonce, authenticatedData: AuthenticatedData) throws -> (ciphertext: Data, tag: Data) { |
63 |
| - let context = try AEADContext(cipher: self, key: key) |
64 |
| - return try context.seal(message: message, nonce: nonce, authenticatedData: authenticatedData) |
65 |
| - } |
66 |
| - |
67 |
| - /// Open a given message. |
68 |
| - func open<Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(ciphertext: Data, key: SymmetricKey, nonce: Nonce, tag: Data, authenticatedData: AuthenticatedData) throws -> Data { |
69 |
| - let context = try AEADContext(cipher: self, key: key) |
70 |
| - return try context.open(ciphertext: ciphertext, nonce: nonce, tag: tag, authenticatedData: authenticatedData) |
71 |
| - } |
72 |
| -} |
73 |
| - |
74 |
| -extension BoringSSLAEAD { |
75 |
| - // Arguably this class is excessive, but it's probably better for this API to be as safe as possible |
76 |
| - // rather than rely on defer statements for our cleanup. |
77 |
| - class AEADContext { |
78 |
| - private var context: EVP_AEAD_CTX |
79 |
| - |
80 |
| - @usableFromInline |
81 |
| - init(cipher: BoringSSLAEAD, key: SymmetricKey) throws { |
82 |
| - self.context = EVP_AEAD_CTX() |
83 |
| - let rc: CInt = key.withUnsafeBytes { keyPointer in |
84 |
| - withUnsafeMutablePointer(to: &self.context) { contextPointer in |
85 |
| - // Create the AEAD context with a default tag length using the given key. |
86 |
| - CCryptoBoringSSLShims_EVP_AEAD_CTX_init(contextPointer, cipher.boringSSLCipher, keyPointer.baseAddress, keyPointer.count, 0, nil) |
87 |
| - } |
88 |
| - } |
89 |
| - guard rc == 1 else { |
90 |
| - throw CryptoKitError.internalBoringSSLError() |
91 |
| - } |
92 |
| - } |
93 |
| - |
94 |
| - deinit { |
95 |
| - withUnsafeMutablePointer(to: &self.context) { contextPointer in |
96 |
| - CCryptoBoringSSL_EVP_AEAD_CTX_cleanup(contextPointer) |
97 |
| - } |
98 |
| - } |
99 |
| - } |
100 |
| -} |
101 |
| - |
102 |
| -// MARK: - Sealing |
103 |
| - |
104 |
| -extension BoringSSLAEAD.AEADContext { |
105 |
| - /// The main entry point for sealing data. Covers the full gamut of types, including discontiguous data types. This must be inlinable. |
106 |
| - @inlinable |
107 |
| - func seal<Plaintext: DataProtocol, Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(message: Plaintext, nonce: Nonce, authenticatedData: AuthenticatedData) throws -> (ciphertext: Data, tag: Data) { |
108 |
| - // Seal is a somewhat awkward function. As it returns a Data, we are going to need to initialize a Data large enough to write into. Data does not provide us an |
109 |
| - // initializer that gives us access to its uninitialized memory, so the cost of creating this Data is the cost of allocating the data + the cost of initializing |
110 |
| - // it. For smaller plaintexts this isn't too big a deal, but for larger ones the initialization cost can really get hairy. |
111 |
| - // |
112 |
| - // We can avoid this by using Data(bytesNoCopy:deallocator:), so that's what we do. In principle we can do slightly better in the case where we have a discontiguous Plaintext |
113 |
| - // type, but it's honestly not worth it enough to justify the code complexity. |
114 |
| - switch (message.regions.count, authenticatedData.regions.count) { |
115 |
| - case (1, 1): |
116 |
| - // We can use a nice fast-path here. |
117 |
| - return try self._sealContiguous(message: message.regions.first!, nonce: nonce, authenticatedData: authenticatedData.regions.first!) |
118 |
| - case (1, _): |
119 |
| - let contiguousAD = Array(authenticatedData) |
120 |
| - return try self._sealContiguous(message: message.regions.first!, nonce: nonce, authenticatedData: contiguousAD) |
121 |
| - case (_, 1): |
122 |
| - let contiguousMessage = Array(message) |
123 |
| - return try self._sealContiguous(message: contiguousMessage, nonce: nonce, authenticatedData: authenticatedData.regions.first!) |
124 |
| - case (_, _): |
125 |
| - let contiguousMessage = Array(message) |
126 |
| - let contiguousAD = Array(authenticatedData) |
127 |
| - return try self._sealContiguous(message: contiguousMessage, nonce: nonce, authenticatedData: contiguousAD) |
128 |
| - } |
129 |
| - } |
130 |
| - |
131 |
| - /// A fast-path for sealing contiguous data. Also inlinable to gain specialization information. |
132 |
| - @inlinable |
133 |
| - func _sealContiguous<Plaintext: ContiguousBytes, Nonce: ContiguousBytes, AuthenticatedData: ContiguousBytes>(message: Plaintext, nonce: Nonce, authenticatedData: AuthenticatedData) throws -> (ciphertext: Data, tag: Data) { |
134 |
| - return try message.withUnsafeBytes { messagePointer in |
135 |
| - try nonce.withUnsafeBytes { noncePointer in |
136 |
| - try authenticatedData.withUnsafeBytes { authenticatedDataPointer in |
137 |
| - try self._sealContiguous(plaintext: messagePointer, noncePointer: noncePointer, authenticatedData: authenticatedDataPointer) |
138 |
| - } |
139 |
| - } |
140 |
| - } |
141 |
| - } |
142 |
| - |
143 |
| - /// The unsafe base call: not inlinable so that it can touch private variables. |
144 |
| - @usableFromInline |
145 |
| - func _sealContiguous(plaintext: UnsafeRawBufferPointer, noncePointer: UnsafeRawBufferPointer, authenticatedData: UnsafeRawBufferPointer) throws -> (ciphertext: Data, tag: Data) { |
146 |
| - let tagByteCount = CCryptoBoringSSL_EVP_AEAD_max_overhead(self.context.aead) |
147 |
| - |
148 |
| - // We use malloc here because we are going to call free later. We force unwrap to trigger crashes if the allocation |
149 |
| - // fails. |
150 |
| - let outputBuffer = UnsafeMutableRawBufferPointer(start: malloc(plaintext.count)!, count: plaintext.count) |
151 |
| - let tagBuffer = UnsafeMutableRawBufferPointer(start: malloc(tagByteCount)!, count: tagByteCount) |
152 |
| - var actualTagSize = tagBuffer.count |
153 |
| - |
154 |
| - let rc = withUnsafeMutablePointer(to: &self.context) { contextPointer in |
155 |
| - CCryptoBoringSSLShims_EVP_AEAD_CTX_seal_scatter(contextPointer, |
156 |
| - outputBuffer.baseAddress, |
157 |
| - tagBuffer.baseAddress, &actualTagSize, tagBuffer.count, |
158 |
| - noncePointer.baseAddress, noncePointer.count, |
159 |
| - plaintext.baseAddress, plaintext.count, |
160 |
| - nil, 0, |
161 |
| - authenticatedData.baseAddress, authenticatedData.count) |
162 |
| - } |
163 |
| - |
164 |
| - guard rc == 1 else { |
165 |
| - // Ooops, error. Free the memory we allocated before we throw. |
166 |
| - free(outputBuffer.baseAddress) |
167 |
| - free(tagBuffer.baseAddress) |
168 |
| - throw CryptoKitError.internalBoringSSLError() |
169 |
| - } |
170 |
| - |
171 |
| - let output = Data(bytesNoCopy: outputBuffer.baseAddress!, count: outputBuffer.count, deallocator: .free) |
172 |
| - let tag = Data(bytesNoCopy: tagBuffer.baseAddress!, count: actualTagSize, deallocator: .free) |
173 |
| - return (ciphertext: output, tag: tag) |
174 |
| - } |
175 |
| -} |
176 |
| - |
177 |
| -// MARK: - Opening |
178 |
| - |
179 |
| -extension BoringSSLAEAD.AEADContext { |
180 |
| - /// The main entry point for opening data. Covers the full gamut of types, including discontiguous data types. This must be inlinable. |
181 |
| - @inlinable |
182 |
| - func open<Nonce: ContiguousBytes, AuthenticatedData: DataProtocol>(ciphertext: Data, nonce: Nonce, tag: Data, authenticatedData: AuthenticatedData) throws -> Data { |
183 |
| - // Open is a somewhat awkward function. As it returns a Data, we are going to need to initialize a Data large enough to write into. Data does not provide us an |
184 |
| - // initializer that gives us access to its uninitialized memory, so the cost of creating this Data is the cost of allocating the data + the cost of initializing |
185 |
| - // it. For smaller plaintexts this isn't too big a deal, but for larger ones the initialization cost can really get hairy. |
186 |
| - // |
187 |
| - // We can avoid this by using Data(bytesNoCopy:deallocator:), so that's what we do. In principle we can do slightly better in the case where we have a discontiguous Plaintext |
188 |
| - // type, but it's honestly not worth it enough to justify the code complexity. |
189 |
| - if authenticatedData.regions.count == 1 { |
190 |
| - // We can use a nice fast-path here. |
191 |
| - return try self._openContiguous(ciphertext: ciphertext, nonce: nonce, tag: tag, authenticatedData: authenticatedData.regions.first!) |
192 |
| - } else { |
193 |
| - let contiguousAD = Array(authenticatedData) |
194 |
| - return try self._openContiguous(ciphertext: ciphertext, nonce: nonce, tag: tag, authenticatedData: contiguousAD) |
195 |
| - } |
196 |
| - } |
197 |
| - |
198 |
| - /// A fast-path for opening contiguous data. Also inlinable to gain specialization information. |
199 |
| - @inlinable |
200 |
| - func _openContiguous<Nonce: ContiguousBytes, AuthenticatedData: ContiguousBytes>(ciphertext: Data, nonce: Nonce, tag: Data, authenticatedData: AuthenticatedData) throws -> Data { |
201 |
| - try ciphertext.withUnsafeBytes { ciphertextPointer in |
202 |
| - try nonce.withUnsafeBytes { nonceBytes in |
203 |
| - try tag.withUnsafeBytes { tagBytes in |
204 |
| - try authenticatedData.withUnsafeBytes { authenticatedDataBytes in |
205 |
| - try self._openContiguous(ciphertext: ciphertextPointer, nonceBytes: nonceBytes, tagBytes: tagBytes, authenticatedData: authenticatedDataBytes) |
206 |
| - } |
207 |
| - } |
208 |
| - } |
209 |
| - } |
210 |
| - } |
211 |
| - |
212 |
| - /// The unsafe base call: not inlinable so that it can touch private variables. |
213 |
| - @usableFromInline |
214 |
| - func _openContiguous(ciphertext: UnsafeRawBufferPointer, nonceBytes: UnsafeRawBufferPointer, tagBytes: UnsafeRawBufferPointer, authenticatedData: UnsafeRawBufferPointer) throws -> Data { |
215 |
| - // We use malloc here because we are going to call free later. We force unwrap to trigger crashes if the allocation |
216 |
| - // fails. |
217 |
| - let outputBuffer = UnsafeMutableRawBufferPointer(start: malloc(ciphertext.count)!, count: ciphertext.count) |
218 |
| - |
219 |
| - let rc = withUnsafePointer(to: &self.context) { contextPointer in |
220 |
| - CCryptoBoringSSLShims_EVP_AEAD_CTX_open_gather(contextPointer, |
221 |
| - outputBuffer.baseAddress, |
222 |
| - nonceBytes.baseAddress, nonceBytes.count, |
223 |
| - ciphertext.baseAddress, ciphertext.count, |
224 |
| - tagBytes.baseAddress, tagBytes.count, |
225 |
| - authenticatedData.baseAddress, authenticatedData.count) |
226 |
| - } |
227 |
| - |
228 |
| - guard rc == 1 else { |
229 |
| - // Ooops, error. Free the memory we allocated before we throw. |
230 |
| - free(outputBuffer.baseAddress) |
231 |
| - throw CryptoKitError.internalBoringSSLError() |
232 |
| - } |
233 |
| - |
234 |
| - let output = Data(bytesNoCopy: outputBuffer.baseAddress!, count: outputBuffer.count, deallocator: .free) |
235 |
| - return output |
236 |
| - } |
237 |
| -} |
238 |
| - |
239 |
| -// MARK: - Supported ciphers |
240 |
| - |
241 |
| -extension BoringSSLAEAD { |
242 |
| - var boringSSLCipher: OpaquePointer { |
243 |
| - switch self { |
244 |
| - case .aes128gcm: |
245 |
| - return CCryptoBoringSSL_EVP_aead_aes_128_gcm() |
246 |
| - case .aes192gcm: |
247 |
| - return CCryptoBoringSSL_EVP_aead_aes_192_gcm() |
248 |
| - case .aes256gcm: |
249 |
| - return CCryptoBoringSSL_EVP_aead_aes_256_gcm() |
250 |
| - case .chacha20: |
251 |
| - return CCryptoBoringSSL_EVP_aead_chacha20_poly1305() |
252 |
| - } |
253 |
| - } |
254 |
| -} |
255 | 74 | #endif // (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) && CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
|
0 commit comments