Skip to content

Commit d11194a

Browse files
authored
Provide AES-GCM-SIV in CryptoExtras (#133)
# Motivation BoringSSL exposes AES-GCM-SIV algorithms which are in general useful to have and provide a nonce-misuse resistant mode of AES-GCM. Since, `CryptoKit` is not exposing AES-GCM-SIV we need to add this to `_CryptoExtras` # Modification Exposes `AES-GCM-SIV` through `_CryptoExtras`. # Result We can now use `AES-GCM-SIV` through `_CryptoExtras`.
1 parent 1c59986 commit d11194a

File tree

18 files changed

+2856
-213
lines changed

18 files changed

+2856
-213
lines changed

Package.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ if development {
3636
]
3737
dependencies = [
3838
"CCryptoBoringSSL",
39-
"CCryptoBoringSSLShims"
39+
"CCryptoBoringSSLShims",
40+
"CryptoBoringWrapper"
4041
]
4142
} else {
4243
swiftSettings = [
@@ -49,7 +50,8 @@ if development {
4950
]
5051
dependencies = [
5152
.target(name: "CCryptoBoringSSL", condition: .when(platforms: platforms)),
52-
.target(name: "CCryptoBoringSSLShims", condition: .when(platforms: platforms))
53+
.target(name: "CCryptoBoringSSLShims", condition: .when(platforms: platforms)),
54+
.target(name: "CryptoBoringWrapper", condition: .when(platforms: platforms))
5355
]
5456
}
5557

@@ -105,7 +107,17 @@ let package = Package(
105107
],
106108
swiftSettings: swiftSettings
107109
),
108-
.target(name: "_CryptoExtras", dependencies: ["CCryptoBoringSSL", "CCryptoBoringSSLShims", "Crypto"]),
110+
.target(name: "_CryptoExtras", dependencies: ["CCryptoBoringSSL", "CCryptoBoringSSLShims", "CryptoBoringWrapper", "Crypto"]),
111+
.target(
112+
name: "CryptoBoringWrapper",
113+
dependencies: [
114+
"CCryptoBoringSSL",
115+
"CCryptoBoringSSLShims"
116+
],
117+
exclude: [
118+
"CMakeLists.txt",
119+
]
120+
),
109121
.executableTarget(name: "crypto-shasum", dependencies: ["Crypto"]),
110122
.testTarget(name: "CryptoTests", dependencies: ["Crypto"], swiftSettings: swiftSettings),
111123
.testTarget(name: "_CryptoExtrasTests", dependencies: ["_CryptoExtras"]),

Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ int CCryptoBoringSSLShims_EVP_AEAD_CTX_open_gather(const EVP_AEAD_CTX *ctx, void
5858
const void *in_tag, size_t in_tag_len,
5959
const void *ad, size_t ad_len);
6060

61+
62+
int CCryptoBoringSSLShims_EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, void *out, size_t *out_len, size_t max_out_len,
63+
const void *nonce, size_t nonce_len,
64+
const void *in, size_t in_len,
65+
const void *ad, size_t ad_len);
66+
67+
6168
void CCryptoBoringSSLShims_ED25519_keypair(void *out_public_key, void *out_private_key);
6269

6370
void CCryptoBoringSSLShims_ED25519_keypair_from_seed(void *out_public_key,

Sources/CCryptoBoringSSLShims/shims.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ int CCryptoBoringSSLShims_EVP_AEAD_CTX_open_gather(const EVP_AEAD_CTX *ctx, void
5555
return CCryptoBoringSSL_EVP_AEAD_CTX_open_gather(ctx, out, nonce, nonce_len, in, in_len, in_tag, in_tag_len, ad, ad_len);
5656
}
5757

58+
int CCryptoBoringSSLShims_EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, void *out, size_t *out_len, size_t max_out_len,
59+
const void *nonce, size_t nonce_len,
60+
const void *in, size_t in_len,
61+
const void *ad, size_t ad_len) {
62+
return CCryptoBoringSSL_EVP_AEAD_CTX_open(ctx, out, out_len, max_out_len, nonce, nonce_len, in, in_len, ad, ad_len);
63+
}
64+
5865
void CCryptoBoringSSLShims_ED25519_keypair(void *out_public_key, void *out_private_key) {
5966
CCryptoBoringSSL_ED25519_keypair(out_public_key, out_private_key);
6067
}

Sources/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414

1515
add_subdirectory(CCryptoBoringSSL)
1616
add_subdirectory(CCryptoBoringSSLShims)
17+
add_subdirectory(CryptoBoringWrapper)
1718
add_subdirectory(Crypto)

Sources/Crypto/AEADs/AES/GCM/BoringSSL/AES-GCM_boring.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
@_exported import CryptoKit
1616
#else
1717
@_implementationOnly import CCryptoBoringSSL
18+
@_implementationOnly import CryptoBoringWrapper
1819
import Foundation
1920

2021
enum OpenSSLAESGCMImpl {

Sources/Crypto/AEADs/ChachaPoly/BoringSSL/ChaChaPoly_boring.swift

Lines changed: 23 additions & 204 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,31 @@
1616
#else
1717
@_implementationOnly import CCryptoBoringSSL
1818
@_implementationOnly import CCryptoBoringSSLShims
19+
@_implementationOnly import CryptoBoringWrapper
1920
import Foundation
2021

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+
2144
enum OpenSSLChaChaPolyImpl {
2245
static func encrypt<M: DataProtocol, AD: DataProtocol>(key: SymmetricKey, message: M, nonce: ChaChaPoly.Nonce?, authenticatedData: AD?) throws -> ChaChaPoly.SealedBox {
2346
guard key.bitCount == ChaChaPoly.keyBitsCount else {
@@ -48,208 +71,4 @@ enum OpenSSLChaChaPolyImpl {
4871
}
4972
}
5073
}
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-
}
25574
#endif // (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) && CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API

Sources/Crypto/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,14 @@ target_compile_definitions(Crypto PRIVATE
8585
CRYPTO_IN_SWIFTPM_FORCE_BUILD_API)
8686
target_include_directories(Crypto PRIVATE
8787
$<TARGET_PROPERTY:CCryptoBoringSSL,INCLUDE_DIRECTORIES>
88-
$<TARGET_PROPERTY:CCryptoBoringSSLShims,INCLUDE_DIRECTORIES>)
88+
$<TARGET_PROPERTY:CCryptoBoringSSLShims,INCLUDE_DIRECTORIES>
89+
$<TARGET_PROPERTY:CryptoBoringWrapper,INCLUDE_DIRECTORIES>)
8990
target_link_libraries(Crypto PUBLIC
9091
$<$<NOT:$<PLATFORM_ID:Darwin>>:dispatch>
9192
$<$<NOT:$<PLATFORM_ID:Darwin>>:Foundation>
9293
CCryptoBoringSSL
93-
CCryptoBoringSSLShims)
94+
CCryptoBoringSSLShims
95+
CryptoBoringWrapper)
9496
set_target_properties(Crypto PROPERTIES
9597
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
9698

0 commit comments

Comments
 (0)