From 3ce59e879bb78cc2b166b0a26b8193b0a3db2e61 Mon Sep 17 00:00:00 2001 From: Mark Schall Date: Fri, 20 Sep 2024 18:09:15 -0400 Subject: [PATCH 1/5] Using GCM Nonce pattern for CBC, CFB, and CTR --- Sources/_CryptoExtras/AES/AES_CBC.swift | 56 +--- Sources/_CryptoExtras/AES/AES_CFB.swift | 30 +- Sources/_CryptoExtras/AES/AES_CTR.swift | 41 +-- .../_CryptoExtras/AES/Block Function.swift | 7 +- Sources/_CryptoExtras/AES/Nonces.swift | 268 ++++++++++++++++++ Sources/_CryptoExtras/AES/Nonces.swift.gyb | 113 ++++++++ 6 files changed, 392 insertions(+), 123 deletions(-) create mode 100644 Sources/_CryptoExtras/AES/Nonces.swift create mode 100644 Sources/_CryptoExtras/AES/Nonces.swift.gyb diff --git a/Sources/_CryptoExtras/AES/AES_CBC.swift b/Sources/_CryptoExtras/AES/AES_CBC.swift index 3363ddbd..5251b04e 100644 --- a/Sources/_CryptoExtras/AES/AES_CBC.swift +++ b/Sources/_CryptoExtras/AES/AES_CBC.swift @@ -20,6 +20,7 @@ extension AES { /// suite. public enum _CBC { private static var blockSize: Int { 16 } + static let nonceByteCount = 16 private static func encryptBlockInPlace( _ plaintext: inout Block, @@ -67,7 +68,7 @@ extension AES { var ciphertext = Data() ciphertext.reserveCapacity(plaintext.count + Self.blockSize) // Room for padding. - var previousBlock = Block(iv) + var previousBlock = Block(blockBytes: iv) var plaintext = plaintext[...] while plaintext.count > 0 { @@ -121,7 +122,7 @@ extension AES { var plaintext = Data() plaintext.reserveCapacity(ciphertext.count) - var previousBlock = Block(iv) + var previousBlock = Block(blockBytes: iv) var ciphertext = ciphertext[...] while ciphertext.count > 0 { @@ -141,57 +142,6 @@ extension AES { } } -extension AES._CBC { - /// An initialization vector. - public struct IV: Sendable { - // AES CBC uses a 128-bit IV. - var ivBytes: ( - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, - UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 - ) - - public init() { - var rng = SystemRandomNumberGenerator() - let (first, second) = (rng.next(), rng.next()) - - self.ivBytes = ( - UInt8(truncatingIfNeeded: first), - UInt8(truncatingIfNeeded: first >> 8), - UInt8(truncatingIfNeeded: first >> 16), - UInt8(truncatingIfNeeded: first >> 24), - UInt8(truncatingIfNeeded: first >> 32), - UInt8(truncatingIfNeeded: first >> 40), - UInt8(truncatingIfNeeded: first >> 48), - UInt8(truncatingIfNeeded: first >> 56), - UInt8(truncatingIfNeeded: second), - UInt8(truncatingIfNeeded: second >> 8), - UInt8(truncatingIfNeeded: second >> 16), - UInt8(truncatingIfNeeded: second >> 24), - UInt8(truncatingIfNeeded: second >> 32), - UInt8(truncatingIfNeeded: second >> 40), - UInt8(truncatingIfNeeded: second >> 48), - UInt8(truncatingIfNeeded: second >> 56) - ) - } - - public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - // We support a 128-bit IV. - guard ivBytes.count == 16 else { - throw CryptoKitError.incorrectKeySize - } - - self.ivBytes = ( - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - ) - - withUnsafeMutableBytes(of: &self.ivBytes) { bytesPtr in - bytesPtr.copyBytes(from: ivBytes) - } - } - } -} - extension Data { fileprivate mutating func trimPadding() throws { guard let paddingBytes = self.last else { diff --git a/Sources/_CryptoExtras/AES/AES_CFB.swift b/Sources/_CryptoExtras/AES/AES_CFB.swift index c11ade31..23e61f53 100644 --- a/Sources/_CryptoExtras/AES/AES_CFB.swift +++ b/Sources/_CryptoExtras/AES/AES_CFB.swift @@ -20,6 +20,8 @@ typealias AESCFBImpl = OpenSSLAESCFBImpl extension AES { public enum _CFB { + static let nonceByteCount = 16 + @inlinable public static func encrypt( _ plaintext: Plaintext, @@ -41,31 +43,3 @@ extension AES { } } } - -extension AES._CFB { - public struct IV: Sendable { - // AES CFB uses a 128-bit IV. - private var ivBytes: (UInt64, UInt64) - - public init() { - var rng = SystemRandomNumberGenerator() - self.ivBytes = (rng.next(), rng.next()) - } - - public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - guard ivBytes.count == 16 else { - throw CryptoKitError.incorrectParameterSize - } - - self.ivBytes = (0, 0) - - Swift.withUnsafeMutableBytes(of: &self.ivBytes) { bytesPtr in - bytesPtr.copyBytes(from: ivBytes) - } - } - - mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.ivBytes, body) - } - } -} diff --git a/Sources/_CryptoExtras/AES/AES_CTR.swift b/Sources/_CryptoExtras/AES/AES_CTR.swift index 0972e6c6..746368ed 100644 --- a/Sources/_CryptoExtras/AES/AES_CTR.swift +++ b/Sources/_CryptoExtras/AES/AES_CTR.swift @@ -19,8 +19,9 @@ import Foundation typealias AESCTRImpl = OpenSSLAESCTRImpl extension AES { - public enum _CTR { + static let nonceByteCount = 12 + @inlinable public static func encrypt( _ plaintext: Plaintext, @@ -42,41 +43,3 @@ extension AES { } } } - -extension AES._CTR { - public struct Nonce: Sendable { - // AES CTR uses a 128-bit counter. It's most usual to use a 96-bit nonce - // and a 32-bit counter at the end, so we support that specific mode of - // operation here. - private var nonceBytes: ( - UInt64, UInt32, UInt32 - ) - - public init() { - var rng = SystemRandomNumberGenerator() - self.nonceBytes = ( - rng.next(), rng.next(), rng.next() - ) - } - - public init(nonceBytes: NonceBytes) throws where NonceBytes.Element == UInt8 { - // We support a 96-bit nonce (with a 32-bit counter, initialized to 0) or a full 128-bit - // expression. - guard nonceBytes.count == 12 || nonceBytes.count == 16 else { - throw CryptoKitError.incorrectParameterSize - } - - self.nonceBytes = ( - 0, 0, 0 - ) - - Swift.withUnsafeMutableBytes(of: &self.nonceBytes) { bytesPtr in - bytesPtr.copyBytes(from: nonceBytes) - } - } - - mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.nonceBytes, body) - } - } -} diff --git a/Sources/_CryptoExtras/AES/Block Function.swift b/Sources/_CryptoExtras/AES/Block Function.swift index 3db5f73d..644ba4b8 100644 --- a/Sources/_CryptoExtras/AES/Block Function.swift +++ b/Sources/_CryptoExtras/AES/Block Function.swift @@ -131,9 +131,10 @@ extension AES { init(_ blockBytes: BlockBytes) { self.blockBytes = blockBytes } - - init(_ iv: AES._CBC.IV) { - self.blockBytes = iv.ivBytes + + init(blockBytes: BlockBytes) where BlockBytes.Element == UInt8 { + let blockBytes: [UInt8] = Array(blockBytes) + self.init(blockBytes: blockBytes) } init(blockBytes: BlockBytes) where BlockBytes.Element == UInt8 { diff --git a/Sources/_CryptoExtras/AES/Nonces.swift b/Sources/_CryptoExtras/AES/Nonces.swift new file mode 100644 index 00000000..59d681f4 --- /dev/null +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -0,0 +1,268 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.md for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Crypto +import Foundation +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. + +// MARK: - AES._CBC + IV +extension AES._CBC { + /// A value used once during a cryptographic operation and then discarded. + /// + /// Don’t reuse the same nonce for multiple calls to encryption APIs. It’s critical + /// that nonces are unique per call to encryption APIs in order to protect the + /// integrity of the encryption. + public struct IV: Sendable, ContiguousBytes, Sequence { + private var bytes: Data + + /// Creates a new random nonce. + public init() { + var data = Data(repeating: 0, count: AES._CBC.nonceByteCount) + data.withUnsafeMutableBytes { + assert($0.count == AES._CBC.nonceByteCount) + $0.initializeWithRandomBytes(count: AES._CBC.nonceByteCount) + } + self.bytes = data + } + + /// Creates a nonce from the given collection. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - ivBytes: A collection of bytes representation of the nonce. + /// The initializer throws an error if the data has the incorrect length. + public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { + guard ivBytes.count == AES._CBC.nonceByteCount else { + throw CryptoKitError.incorrectKeySize + } + + self.bytes = Data(repeating: 0, count: AES._CBC.nonceByteCount) + Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + bytesPtr.copyBytes(from: ivBytes) + } + } + + /// Creates a nonce from the given data. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - data: A data representation of the nonce. The initializer throws an + /// error if the data has the incorrect length. + public init(data: D) throws { + if data.count != AES._CBC.nonceByteCount { + throw CryptoKitError.incorrectParameterSize + } + + self.bytes = Data(data) + } + + /// Calls the given closure with a pointer to the underlying bytes of the array’s + /// contiguous storage. + /// + /// - Parameters: + /// - body: A closure with an `UnsafeRawBufferPointer` parameter that points to the + /// contiguous storage for the array. The system creates the storage if it doesn’t + /// exist. If body has a return value, that value is also used as the return value + /// for the ``withUnsafeBytes(_:)`` method. The argument is valid only for + /// the duration of the closure’s execution. + /// + /// - Returns: The return value, if any, of the body closure parameter. + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + return try self.bytes.withUnsafeBytes(body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> Array.Iterator { + self.withUnsafeBytes({ (buffPtr) in + return Array(buffPtr).makeIterator() + }) + } + } +} + +// MARK: - AES._CFB + IV +extension AES._CFB { + /// A value used once during a cryptographic operation and then discarded. + /// + /// Don’t reuse the same nonce for multiple calls to encryption APIs. It’s critical + /// that nonces are unique per call to encryption APIs in order to protect the + /// integrity of the encryption. + public struct IV: Sendable, ContiguousBytes, Sequence { + private var bytes: Data + + /// Creates a new random nonce. + public init() { + var data = Data(repeating: 0, count: AES._CFB.nonceByteCount) + data.withUnsafeMutableBytes { + assert($0.count == AES._CFB.nonceByteCount) + $0.initializeWithRandomBytes(count: AES._CFB.nonceByteCount) + } + self.bytes = data + } + + /// Creates a nonce from the given collection. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - ivBytes: A collection of bytes representation of the nonce. + /// The initializer throws an error if the data has the incorrect length. + public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { + guard ivBytes.count == AES._CFB.nonceByteCount else { + throw CryptoKitError.incorrectKeySize + } + + self.bytes = Data(repeating: 0, count: AES._CFB.nonceByteCount) + Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + bytesPtr.copyBytes(from: ivBytes) + } + } + + /// Creates a nonce from the given data. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - data: A data representation of the nonce. The initializer throws an + /// error if the data has the incorrect length. + public init(data: D) throws { + if data.count != AES._CFB.nonceByteCount { + throw CryptoKitError.incorrectParameterSize + } + + self.bytes = Data(data) + } + + /// Calls the given closure with a pointer to the underlying bytes of the array’s + /// contiguous storage. + /// + /// - Parameters: + /// - body: A closure with an `UnsafeRawBufferPointer` parameter that points to the + /// contiguous storage for the array. The system creates the storage if it doesn’t + /// exist. If body has a return value, that value is also used as the return value + /// for the ``withUnsafeBytes(_:)`` method. The argument is valid only for + /// the duration of the closure’s execution. + /// + /// - Returns: The return value, if any, of the body closure parameter. + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + return try self.bytes.withUnsafeBytes(body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> Array.Iterator { + self.withUnsafeBytes({ (buffPtr) in + return Array(buffPtr).makeIterator() + }) + } + } +} + +// MARK: - AES._CTR + Nonce +extension AES._CTR { + /// A value used once during a cryptographic operation and then discarded. + /// + /// Don’t reuse the same nonce for multiple calls to encryption APIs. It’s critical + /// that nonces are unique per call to encryption APIs in order to protect the + /// integrity of the encryption. + public struct Nonce: Sendable, ContiguousBytes, Sequence { + private var bytes: Data + + /// Creates a new random nonce. + public init() { + var data = Data(repeating: 0, count: AES._CTR.nonceByteCount) + data.withUnsafeMutableBytes { + assert($0.count == AES._CTR.nonceByteCount) + $0.initializeWithRandomBytes(count: AES._CTR.nonceByteCount) + } + self.bytes = data + } + + /// Creates a nonce from the given collection. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - nonceBytes: A collection of bytes representation of the nonce. + /// The initializer throws an error if the data has the incorrect length. + public init(nonceBytes: NonceBytes) throws where NonceBytes.Element == UInt8 { + guard nonceBytes.count == AES._CTR.nonceByteCount else { + throw CryptoKitError.incorrectKeySize + } + + self.bytes = Data(repeating: 0, count: AES._CTR.nonceByteCount) + Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + bytesPtr.copyBytes(from: nonceBytes) + } + } + + /// Creates a nonce from the given data. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - data: A data representation of the nonce. The initializer throws an + /// error if the data has the incorrect length. + public init(data: D) throws { + if data.count != AES._CBC.nonceByteCount { + throw CryptoKitError.incorrectParameterSize + } + + self.bytes = Data(data) + } + + /// Calls the given closure with a pointer to the underlying bytes of the array’s + /// contiguous storage. + /// + /// - Parameters: + /// - body: A closure with an `UnsafeRawBufferPointer` parameter that points to the + /// contiguous storage for the array. The system creates the storage if it doesn’t + /// exist. If body has a return value, that value is also used as the return value + /// for the ``withUnsafeBytes(_:)`` method. The argument is valid only for + /// the duration of the closure’s execution. + /// + /// - Returns: The return value, if any, of the body closure parameter. + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + return try self.bytes.withUnsafeBytes(body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> Array.Iterator { + self.withUnsafeBytes({ (buffPtr) in + return Array(buffPtr).makeIterator() + }) + } + } +} diff --git a/Sources/_CryptoExtras/AES/Nonces.swift.gyb b/Sources/_CryptoExtras/AES/Nonces.swift.gyb new file mode 100644 index 00000000..27f2b89f --- /dev/null +++ b/Sources/_CryptoExtras/AES/Nonces.swift.gyb @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftCrypto project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.md for the list of SwiftCrypto project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import Crypto +import Foundation +// MARK: - Generated file, do NOT edit +// any edits of this file WILL be overwritten and thus discarded +// see section `gyb` in `README` for details. +%{ +ciphers = [{"name": "AES._CBC", "nonceName": "IV", "recommendedNonceSize": "AES._CBC.nonceByteCount", "nonceValidation": "!= AES._CBC.nonceByteCount"},{"name": "AES._CFB", "nonceName": "IV", "recommendedNonceSize": "AES._CFB.nonceByteCount", "nonceValidation": "!= AES._CFB.nonceByteCount"},{"name": "AES._CTR", "nonceName": "Nonce", "recommendedNonceSize": "AES._CTR.nonceByteCount", "nonceValidation": "!= AES._CBC.nonceByteCount"}] +}% +% for cipher in ciphers: +%{ +name = cipher["name"] +nonceName = cipher["nonceName"] +nonceSize = cipher["recommendedNonceSize"] +nonceValidation = cipher["nonceValidation"] +}% + +// MARK: - ${name} + ${nonceName} +extension ${name} { + /// A value used once during a cryptographic operation and then discarded. + /// + /// Don’t reuse the same nonce for multiple calls to encryption APIs. It’s critical + /// that nonces are unique per call to encryption APIs in order to protect the + /// integrity of the encryption. + public struct ${nonceName}: Sendable, ContiguousBytes, Sequence { + private var bytes: Data + + /// Creates a new random nonce. + public init() { + var data = Data(repeating: 0, count: ${nonceSize}) + data.withUnsafeMutableBytes { + assert($0.count == ${nonceSize}) + $0.initializeWithRandomBytes(count: ${nonceSize}) + } + self.bytes = data + } + + /// Creates a nonce from the given collection. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - ${nonceName.lower()}Bytes: A collection of bytes representation of the nonce. + /// The initializer throws an error if the data has the incorrect length. + public init<${nonceName}Bytes: Collection>(${nonceName.lower()}Bytes: ${nonceName}Bytes) throws where ${nonceName}Bytes.Element == UInt8 { + guard ${nonceName.lower()}Bytes.count == ${nonceSize} else { + throw CryptoKitError.incorrectKeySize + } + + self.bytes = Data(repeating: 0, count: ${nonceSize}) + Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + bytesPtr.copyBytes(from: ${nonceName.lower()}Bytes) + } + } + + /// Creates a nonce from the given data. + /// + /// Unless your use case calls for a nonce with a specific value, use the + /// ``init()`` method to instead create a random nonce. + /// + /// - Parameters: + /// - data: A data representation of the nonce. The initializer throws an + /// error if the data has the incorrect length. + public init(data: D) throws { + if data.count ${nonceValidation} { + throw CryptoKitError.incorrectParameterSize + } + + self.bytes = Data(data) + } + + /// Calls the given closure with a pointer to the underlying bytes of the array’s + /// contiguous storage. + /// + /// - Parameters: + /// - body: A closure with an `UnsafeRawBufferPointer` parameter that points to the + /// contiguous storage for the array. The system creates the storage if it doesn’t + /// exist. If body has a return value, that value is also used as the return value + /// for the ``withUnsafeBytes(_:)`` method. The argument is valid only for + /// the duration of the closure’s execution. + /// + /// - Returns: The return value, if any, of the body closure parameter. + public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { + return try self.bytes.withUnsafeBytes(body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> Array.Iterator { + self.withUnsafeBytes({ (buffPtr) in + return Array(buffPtr).makeIterator() + }) + } + } +} +% end From 9d539cf1c5ea5a27834e24b318b6ef28a9aedd43 Mon Sep 17 00:00:00 2001 From: Mark Schall Date: Mon, 23 Sep 2024 13:24:44 -0400 Subject: [PATCH 2/5] swiftching back to tuples --- Sources/_CryptoExtras/AES/AES_CBC.swift | 1 - Sources/_CryptoExtras/AES/Nonces.swift | 108 +++++++++++++-------- Sources/_CryptoExtras/AES/Nonces.swift.gyb | 47 ++++++--- 3 files changed, 100 insertions(+), 56 deletions(-) diff --git a/Sources/_CryptoExtras/AES/AES_CBC.swift b/Sources/_CryptoExtras/AES/AES_CBC.swift index 5251b04e..56e66b80 100644 --- a/Sources/_CryptoExtras/AES/AES_CBC.swift +++ b/Sources/_CryptoExtras/AES/AES_CBC.swift @@ -20,7 +20,6 @@ extension AES { /// suite. public enum _CBC { private static var blockSize: Int { 16 } - static let nonceByteCount = 16 private static func encryptBlockInPlace( _ plaintext: inout Block, diff --git a/Sources/_CryptoExtras/AES/Nonces.swift b/Sources/_CryptoExtras/AES/Nonces.swift index 59d681f4..1d022ae8 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -26,16 +26,21 @@ extension AES._CBC { /// that nonces are unique per call to encryption APIs in order to protect the /// integrity of the encryption. public struct IV: Sendable, ContiguousBytes, Sequence { - private var bytes: Data + typealias IVTuple = ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 + ) + + private var bytes: IVTuple /// Creates a new random nonce. public init() { - var data = Data(repeating: 0, count: AES._CBC.nonceByteCount) - data.withUnsafeMutableBytes { - assert($0.count == AES._CBC.nonceByteCount) - $0.initializeWithRandomBytes(count: AES._CBC.nonceByteCount) + var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) } - self.bytes = data + self.bytes = bytes } /// Creates a nonce from the given collection. @@ -47,14 +52,15 @@ extension AES._CBC { /// - ivBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - guard ivBytes.count == AES._CBC.nonceByteCount else { + guard ivBytes.count == MemoryLayout.size else { throw CryptoKitError.incorrectKeySize } - self.bytes = Data(repeating: 0, count: AES._CBC.nonceByteCount) - Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ivBytes) } + self.bytes = bytes } /// Creates a nonce from the given data. @@ -66,11 +72,15 @@ extension AES._CBC { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != AES._CBC.nonceByteCount { + if data.count != MemoryLayout.size { throw CryptoKitError.incorrectParameterSize } - self.bytes = Data(data) + var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + data.copyBytes(to: bytesPtr) + } + self.bytes = bytes } /// Calls the given closure with a pointer to the underlying bytes of the array’s @@ -85,11 +95,13 @@ extension AES._CBC { /// /// - Returns: The return value, if any, of the body closure parameter. public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try self.bytes.withUnsafeBytes(body) + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) } mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) } /// Returns an iterator over the elements of the nonce. @@ -109,16 +121,18 @@ extension AES._CFB { /// that nonces are unique per call to encryption APIs in order to protect the /// integrity of the encryption. public struct IV: Sendable, ContiguousBytes, Sequence { - private var bytes: Data + typealias IVTuple = (UInt64, UInt64) + + private var bytes: IVTuple /// Creates a new random nonce. public init() { - var data = Data(repeating: 0, count: AES._CFB.nonceByteCount) - data.withUnsafeMutableBytes { - assert($0.count == AES._CFB.nonceByteCount) - $0.initializeWithRandomBytes(count: AES._CFB.nonceByteCount) + var bytes = IVTuple(0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) } - self.bytes = data + self.bytes = bytes } /// Creates a nonce from the given collection. @@ -130,14 +144,15 @@ extension AES._CFB { /// - ivBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - guard ivBytes.count == AES._CFB.nonceByteCount else { + guard ivBytes.count == MemoryLayout.size else { throw CryptoKitError.incorrectKeySize } - self.bytes = Data(repeating: 0, count: AES._CFB.nonceByteCount) - Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + var bytes = IVTuple(0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ivBytes) } + self.bytes = bytes } /// Creates a nonce from the given data. @@ -149,11 +164,15 @@ extension AES._CFB { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != AES._CFB.nonceByteCount { + if data.count != MemoryLayout.size { throw CryptoKitError.incorrectParameterSize } - self.bytes = Data(data) + var bytes = IVTuple(0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + data.copyBytes(to: bytesPtr) + } + self.bytes = bytes } /// Calls the given closure with a pointer to the underlying bytes of the array’s @@ -168,11 +187,13 @@ extension AES._CFB { /// /// - Returns: The return value, if any, of the body closure parameter. public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try self.bytes.withUnsafeBytes(body) + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) } mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) } /// Returns an iterator over the elements of the nonce. @@ -192,16 +213,18 @@ extension AES._CTR { /// that nonces are unique per call to encryption APIs in order to protect the /// integrity of the encryption. public struct Nonce: Sendable, ContiguousBytes, Sequence { - private var bytes: Data + typealias NonceTuple = (UInt64, UInt32, UInt32) + + private var bytes: NonceTuple /// Creates a new random nonce. public init() { - var data = Data(repeating: 0, count: AES._CTR.nonceByteCount) - data.withUnsafeMutableBytes { - assert($0.count == AES._CTR.nonceByteCount) - $0.initializeWithRandomBytes(count: AES._CTR.nonceByteCount) + var bytes = NonceTuple(0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) } - self.bytes = data + self.bytes = bytes } /// Creates a nonce from the given collection. @@ -213,14 +236,15 @@ extension AES._CTR { /// - nonceBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(nonceBytes: NonceBytes) throws where NonceBytes.Element == UInt8 { - guard nonceBytes.count == AES._CTR.nonceByteCount else { + guard nonceBytes.count == MemoryLayout.size else { throw CryptoKitError.incorrectKeySize } - self.bytes = Data(repeating: 0, count: AES._CTR.nonceByteCount) - Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + var bytes = NonceTuple(0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: nonceBytes) } + self.bytes = bytes } /// Creates a nonce from the given data. @@ -232,11 +256,15 @@ extension AES._CTR { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != AES._CBC.nonceByteCount { + if data.count != MemoryLayout.size { throw CryptoKitError.incorrectParameterSize } - self.bytes = Data(data) + var bytes = NonceTuple(0,0,0) + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + data.copyBytes(to: bytesPtr) + } + self.bytes = bytes } /// Calls the given closure with a pointer to the underlying bytes of the array’s @@ -251,11 +279,13 @@ extension AES._CTR { /// /// - Returns: The return value, if any, of the body closure parameter. public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try self.bytes.withUnsafeBytes(body) + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) } mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) } /// Returns an iterator over the elements of the nonce. diff --git a/Sources/_CryptoExtras/AES/Nonces.swift.gyb b/Sources/_CryptoExtras/AES/Nonces.swift.gyb index 27f2b89f..fb434c77 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift.gyb +++ b/Sources/_CryptoExtras/AES/Nonces.swift.gyb @@ -18,14 +18,20 @@ import Foundation // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. %{ -ciphers = [{"name": "AES._CBC", "nonceName": "IV", "recommendedNonceSize": "AES._CBC.nonceByteCount", "nonceValidation": "!= AES._CBC.nonceByteCount"},{"name": "AES._CFB", "nonceName": "IV", "recommendedNonceSize": "AES._CFB.nonceByteCount", "nonceValidation": "!= AES._CFB.nonceByteCount"},{"name": "AES._CTR", "nonceName": "Nonce", "recommendedNonceSize": "AES._CTR.nonceByteCount", "nonceValidation": "!= AES._CBC.nonceByteCount"}] +ciphers = [ + {"name": "AES._CBC", "nonceName": "IV", "tupleDefinition": """( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 + )""", "tupleBlank": "(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)"}, + {"name": "AES._CFB", "nonceName": "IV", "tupleDefinition": "(UInt64, UInt64)", "tupleBlank": "(0,0)"}, + {"name": "AES._CTR", "nonceName": "Nonce", "tupleDefinition": "(UInt64, UInt32, UInt32)", "tupleBlank": "(0,0,0)"}] }% % for cipher in ciphers: %{ name = cipher["name"] nonceName = cipher["nonceName"] -nonceSize = cipher["recommendedNonceSize"] -nonceValidation = cipher["nonceValidation"] +tupleDefinition = cipher["tupleDefinition"] +tupleBlank = cipher["tupleBlank"] }% // MARK: - ${name} + ${nonceName} @@ -36,16 +42,18 @@ extension ${name} { /// that nonces are unique per call to encryption APIs in order to protect the /// integrity of the encryption. public struct ${nonceName}: Sendable, ContiguousBytes, Sequence { - private var bytes: Data + typealias ${nonceName}Tuple = ${tupleDefinition} + + private var bytes: ${nonceName}Tuple /// Creates a new random nonce. public init() { - var data = Data(repeating: 0, count: ${nonceSize}) - data.withUnsafeMutableBytes { - assert($0.count == ${nonceSize}) - $0.initializeWithRandomBytes(count: ${nonceSize}) + var bytes = ${nonceName}Tuple${tupleBlank} + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout<${nonceName}Tuple>.size + $0.initializeWithRandomBytes(count: count) } - self.bytes = data + self.bytes = bytes } /// Creates a nonce from the given collection. @@ -57,14 +65,15 @@ extension ${name} { /// - ${nonceName.lower()}Bytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init<${nonceName}Bytes: Collection>(${nonceName.lower()}Bytes: ${nonceName}Bytes) throws where ${nonceName}Bytes.Element == UInt8 { - guard ${nonceName.lower()}Bytes.count == ${nonceSize} else { + guard ${nonceName.lower()}Bytes.count == MemoryLayout<${nonceName}Tuple>.size else { throw CryptoKitError.incorrectKeySize } - self.bytes = Data(repeating: 0, count: ${nonceSize}) - Swift.withUnsafeMutableBytes(of: &self.bytes) { bytesPtr in + var bytes = ${nonceName}Tuple${tupleBlank} + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ${nonceName.lower()}Bytes) } + self.bytes = bytes } /// Creates a nonce from the given data. @@ -76,11 +85,15 @@ extension ${name} { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count ${nonceValidation} { + if data.count != MemoryLayout<${nonceName}Tuple>.size { throw CryptoKitError.incorrectParameterSize } - self.bytes = Data(data) + var bytes = ${nonceName}Tuple${tupleBlank} + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + data.copyBytes(to: bytesPtr) + } + self.bytes = bytes } /// Calls the given closure with a pointer to the underlying bytes of the array’s @@ -95,11 +108,13 @@ extension ${name} { /// /// - Returns: The return value, if any, of the body closure parameter. public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { - return try self.bytes.withUnsafeBytes(body) + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) } mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { - return try Swift.withUnsafeMutableBytes(of: &self.bytes, body) + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) } /// Returns an iterator over the elements of the nonce. From 2ffbcaa6f968911a218e1ad296c50c826a762c65 Mon Sep 17 00:00:00 2001 From: Mark Schall Date: Tue, 1 Oct 2024 10:09:29 -0400 Subject: [PATCH 3/5] Formatting and restoring Block constructor --- Sources/_CryptoExtras/AES/AES_CBC.swift | 4 +- .../_CryptoExtras/AES/Block Function.swift | 4 ++ Sources/_CryptoExtras/AES/Nonces.swift | 48 +++++++++++-------- Sources/_CryptoExtras/AES/Nonces.swift.gyb | 25 ++++++---- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Sources/_CryptoExtras/AES/AES_CBC.swift b/Sources/_CryptoExtras/AES/AES_CBC.swift index 56e66b80..b6bf7479 100644 --- a/Sources/_CryptoExtras/AES/AES_CBC.swift +++ b/Sources/_CryptoExtras/AES/AES_CBC.swift @@ -67,7 +67,7 @@ extension AES { var ciphertext = Data() ciphertext.reserveCapacity(plaintext.count + Self.blockSize) // Room for padding. - var previousBlock = Block(blockBytes: iv) + var previousBlock = Block(iv) var plaintext = plaintext[...] while plaintext.count > 0 { @@ -121,7 +121,7 @@ extension AES { var plaintext = Data() plaintext.reserveCapacity(ciphertext.count) - var previousBlock = Block(blockBytes: iv) + var previousBlock = Block(iv) var ciphertext = ciphertext[...] while ciphertext.count > 0 { diff --git a/Sources/_CryptoExtras/AES/Block Function.swift b/Sources/_CryptoExtras/AES/Block Function.swift index 644ba4b8..b59fe146 100644 --- a/Sources/_CryptoExtras/AES/Block Function.swift +++ b/Sources/_CryptoExtras/AES/Block Function.swift @@ -132,6 +132,10 @@ extension AES { self.blockBytes = blockBytes } + init(_ iv: AES._CBC.IV) { + self.blockBytes = iv.bytes + } + init(blockBytes: BlockBytes) where BlockBytes.Element == UInt8 { let blockBytes: [UInt8] = Array(blockBytes) self.init(blockBytes: blockBytes) diff --git a/Sources/_CryptoExtras/AES/Nonces.swift b/Sources/_CryptoExtras/AES/Nonces.swift index 1d022ae8..eb6a35b4 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -31,11 +31,15 @@ extension AES._CBC { UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 ) - private var bytes: IVTuple + var bytes: IVTuple + static var emptyBytes: IVTuple = ( + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ) /// Creates a new random nonce. public init() { - var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { let count = MemoryLayout.size $0.initializeWithRandomBytes(count: count) @@ -52,11 +56,11 @@ extension AES._CBC { /// - ivBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - guard ivBytes.count == MemoryLayout.size else { + guard [16].contains(ivBytes.count) else { throw CryptoKitError.incorrectKeySize } - var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ivBytes) } @@ -72,11 +76,11 @@ extension AES._CBC { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != MemoryLayout.size { - throw CryptoKitError.incorrectParameterSize + guard [16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize } - var bytes = IVTuple(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in data.copyBytes(to: bytesPtr) } @@ -123,11 +127,12 @@ extension AES._CFB { public struct IV: Sendable, ContiguousBytes, Sequence { typealias IVTuple = (UInt64, UInt64) - private var bytes: IVTuple + var bytes: IVTuple + static var emptyBytes: IVTuple = (0, 0) /// Creates a new random nonce. public init() { - var bytes = IVTuple(0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { let count = MemoryLayout.size $0.initializeWithRandomBytes(count: count) @@ -144,11 +149,11 @@ extension AES._CFB { /// - ivBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(ivBytes: IVBytes) throws where IVBytes.Element == UInt8 { - guard ivBytes.count == MemoryLayout.size else { + guard [16].contains(ivBytes.count) else { throw CryptoKitError.incorrectKeySize } - var bytes = IVTuple(0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ivBytes) } @@ -164,11 +169,11 @@ extension AES._CFB { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != MemoryLayout.size { - throw CryptoKitError.incorrectParameterSize + guard [16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize } - var bytes = IVTuple(0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in data.copyBytes(to: bytesPtr) } @@ -215,11 +220,12 @@ extension AES._CTR { public struct Nonce: Sendable, ContiguousBytes, Sequence { typealias NonceTuple = (UInt64, UInt32, UInt32) - private var bytes: NonceTuple + var bytes: NonceTuple + static var emptyBytes: NonceTuple = (0, 0, 0) /// Creates a new random nonce. public init() { - var bytes = NonceTuple(0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { let count = MemoryLayout.size $0.initializeWithRandomBytes(count: count) @@ -236,11 +242,11 @@ extension AES._CTR { /// - nonceBytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init(nonceBytes: NonceBytes) throws where NonceBytes.Element == UInt8 { - guard nonceBytes.count == MemoryLayout.size else { + guard [12, 16].contains(nonceBytes.count) else { throw CryptoKitError.incorrectKeySize } - var bytes = NonceTuple(0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: nonceBytes) } @@ -256,11 +262,11 @@ extension AES._CTR { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != MemoryLayout.size { - throw CryptoKitError.incorrectParameterSize + guard [12, 16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize } - var bytes = NonceTuple(0,0,0) + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in data.copyBytes(to: bytesPtr) } diff --git a/Sources/_CryptoExtras/AES/Nonces.swift.gyb b/Sources/_CryptoExtras/AES/Nonces.swift.gyb index fb434c77..a3c936b6 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift.gyb +++ b/Sources/_CryptoExtras/AES/Nonces.swift.gyb @@ -22,9 +22,12 @@ ciphers = [ {"name": "AES._CBC", "nonceName": "IV", "tupleDefinition": """( UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 - )""", "tupleBlank": "(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)"}, - {"name": "AES._CFB", "nonceName": "IV", "tupleDefinition": "(UInt64, UInt64)", "tupleBlank": "(0,0)"}, - {"name": "AES._CTR", "nonceName": "Nonce", "tupleDefinition": "(UInt64, UInt32, UInt32)", "tupleBlank": "(0,0,0)"}] + )""", "tupleBlank": """( + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + )""", "validSizes": "[16]"}, + {"name": "AES._CFB", "nonceName": "IV", "tupleDefinition": "(UInt64, UInt64)", "tupleBlank": "(0, 0)", "validSizes": "[16]"}, + {"name": "AES._CTR", "nonceName": "Nonce", "tupleDefinition": "(UInt64, UInt32, UInt32)", "tupleBlank": "(0, 0, 0)", "validSizes": "[12, 16]"}] }% % for cipher in ciphers: %{ @@ -32,6 +35,7 @@ name = cipher["name"] nonceName = cipher["nonceName"] tupleDefinition = cipher["tupleDefinition"] tupleBlank = cipher["tupleBlank"] +validSizes = cipher["validSizes"] }% // MARK: - ${name} + ${nonceName} @@ -44,11 +48,12 @@ extension ${name} { public struct ${nonceName}: Sendable, ContiguousBytes, Sequence { typealias ${nonceName}Tuple = ${tupleDefinition} - private var bytes: ${nonceName}Tuple + var bytes: ${nonceName}Tuple + static var emptyBytes: ${nonceName}Tuple = ${tupleBlank} /// Creates a new random nonce. public init() { - var bytes = ${nonceName}Tuple${tupleBlank} + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { let count = MemoryLayout<${nonceName}Tuple>.size $0.initializeWithRandomBytes(count: count) @@ -65,11 +70,11 @@ extension ${name} { /// - ${nonceName.lower()}Bytes: A collection of bytes representation of the nonce. /// The initializer throws an error if the data has the incorrect length. public init<${nonceName}Bytes: Collection>(${nonceName.lower()}Bytes: ${nonceName}Bytes) throws where ${nonceName}Bytes.Element == UInt8 { - guard ${nonceName.lower()}Bytes.count == MemoryLayout<${nonceName}Tuple>.size else { + guard ${validSizes}.contains(${nonceName.lower()}Bytes.count) else { throw CryptoKitError.incorrectKeySize } - var bytes = ${nonceName}Tuple${tupleBlank} + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in bytesPtr.copyBytes(from: ${nonceName.lower()}Bytes) } @@ -85,11 +90,11 @@ extension ${name} { /// - data: A data representation of the nonce. The initializer throws an /// error if the data has the incorrect length. public init(data: D) throws { - if data.count != MemoryLayout<${nonceName}Tuple>.size { - throw CryptoKitError.incorrectParameterSize + guard ${validSizes}.contains(data.count) else { + throw CryptoKitError.incorrectKeySize } - var bytes = ${nonceName}Tuple${tupleBlank} + var bytes = Self.emptyBytes Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in data.copyBytes(to: bytesPtr) } From ae25dca5805e0c1836cd9b16f7a65822c82c45ec Mon Sep 17 00:00:00 2001 From: Mark Schall Date: Wed, 2 Oct 2024 09:16:19 -0400 Subject: [PATCH 4/5] adding a new Iterator --- Sources/_CryptoExtras/AES/Nonces.swift | 42 +++++++++++++++------- Sources/_CryptoExtras/AES/Nonces.swift.gyb | 30 +++++++++++++--- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Sources/_CryptoExtras/AES/Nonces.swift b/Sources/_CryptoExtras/AES/Nonces.swift index eb6a35b4..68b6ab96 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -18,6 +18,30 @@ import Foundation // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. +fileprivate struct ByteIterator: IteratorProtocol { + var currentOffset = 0 + var pointer: UnsafeRawBufferPointer? = nil + let length: Int + + init(_ bytes: T) { + self.length = Mirror(reflecting: bytes).children.count + withUnsafeBytes(of: bytes) { pointer in + self.pointer = pointer + } + } + + @inlinable + public mutating func next() -> UInt8? { + guard let pointer, + currentOffset < length else { return nil } + + let next = pointer.load(fromByteOffset: currentOffset, as: UInt8.self) + currentOffset += 1 + return next + } +} + + // MARK: - AES._CBC + IV extension AES._CBC { /// A value used once during a cryptographic operation and then discarded. @@ -109,10 +133,8 @@ extension AES._CBC { } /// Returns an iterator over the elements of the nonce. - public func makeIterator() -> Array.Iterator { - self.withUnsafeBytes({ (buffPtr) in - return Array(buffPtr).makeIterator() - }) + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) } } } @@ -202,10 +224,8 @@ extension AES._CFB { } /// Returns an iterator over the elements of the nonce. - public func makeIterator() -> Array.Iterator { - self.withUnsafeBytes({ (buffPtr) in - return Array(buffPtr).makeIterator() - }) + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) } } } @@ -295,10 +315,8 @@ extension AES._CTR { } /// Returns an iterator over the elements of the nonce. - public func makeIterator() -> Array.Iterator { - self.withUnsafeBytes({ (buffPtr) in - return Array(buffPtr).makeIterator() - }) + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) } } } diff --git a/Sources/_CryptoExtras/AES/Nonces.swift.gyb b/Sources/_CryptoExtras/AES/Nonces.swift.gyb index a3c936b6..ff8695d6 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift.gyb +++ b/Sources/_CryptoExtras/AES/Nonces.swift.gyb @@ -17,6 +17,30 @@ import Foundation // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. + +fileprivate struct ByteIterator: IteratorProtocol { + var currentOffset = 0 + var pointer: UnsafeRawBufferPointer? = nil + let length: Int + + init(_ bytes: T) { + self.length = Mirror(reflecting: bytes).children.count + withUnsafeBytes(of: bytes) { pointer in + self.pointer = pointer + } + } + + @inlinable + public mutating func next() -> UInt8? { + guard let pointer, + currentOffset < length else { return nil } + + let next = pointer.load(fromByteOffset: currentOffset, as: UInt8.self) + currentOffset += 1 + return next + } +} + %{ ciphers = [ {"name": "AES._CBC", "nonceName": "IV", "tupleDefinition": """( @@ -123,10 +147,8 @@ extension ${name} { } /// Returns an iterator over the elements of the nonce. - public func makeIterator() -> Array.Iterator { - self.withUnsafeBytes({ (buffPtr) in - return Array(buffPtr).makeIterator() - }) + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) } } } From fbbccdfacad76e535c249703601ac8f6f8f5f10f Mon Sep 17 00:00:00 2001 From: Mark Schall Date: Sun, 20 Oct 2024 10:32:37 -0400 Subject: [PATCH 5/5] Using Array iterator under the hood --- Sources/_CryptoExtras/AES/Nonces.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/_CryptoExtras/AES/Nonces.swift b/Sources/_CryptoExtras/AES/Nonces.swift index 68b6ab96..84216d69 100644 --- a/Sources/_CryptoExtras/AES/Nonces.swift +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -20,22 +20,22 @@ import Foundation fileprivate struct ByteIterator: IteratorProtocol { var currentOffset = 0 - var pointer: UnsafeRawBufferPointer? = nil + var iterator: Array.Iterator? = nil let length: Int init(_ bytes: T) { self.length = Mirror(reflecting: bytes).children.count withUnsafeBytes(of: bytes) { pointer in - self.pointer = pointer + self.iterator = Array(pointer).makeIterator() } } @inlinable public mutating func next() -> UInt8? { - guard let pointer, + guard var iterator, currentOffset < length else { return nil } - let next = pointer.load(fromByteOffset: currentOffset, as: UInt8.self) + let next = iterator.next() currentOffset += 1 return next }