diff --git a/Sources/_CryptoExtras/AES/AES_CBC.swift b/Sources/_CryptoExtras/AES/AES_CBC.swift index 3363ddbd..b6bf7479 100644 --- a/Sources/_CryptoExtras/AES/AES_CBC.swift +++ b/Sources/_CryptoExtras/AES/AES_CBC.swift @@ -141,57 +141,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..b59fe146 100644 --- a/Sources/_CryptoExtras/AES/Block Function.swift +++ b/Sources/_CryptoExtras/AES/Block Function.swift @@ -131,9 +131,14 @@ extension AES { init(_ blockBytes: BlockBytes) { self.blockBytes = blockBytes } - + init(_ iv: AES._CBC.IV) { - self.blockBytes = iv.ivBytes + self.blockBytes = iv.bytes + } + + 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..84216d69 --- /dev/null +++ b/Sources/_CryptoExtras/AES/Nonces.swift @@ -0,0 +1,322 @@ +//===----------------------------------------------------------------------===// +// +// 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. + +fileprivate struct ByteIterator: IteratorProtocol { + var currentOffset = 0 + var iterator: Array.Iterator? = nil + let length: Int + + init(_ bytes: T) { + self.length = Mirror(reflecting: bytes).children.count + withUnsafeBytes(of: bytes) { pointer in + self.iterator = Array(pointer).makeIterator() + } + } + + @inlinable + public mutating func next() -> UInt8? { + guard var iterator, + currentOffset < length else { return nil } + + let next = iterator.next() + currentOffset += 1 + return next + } +} + + +// 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 { + typealias IVTuple = ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 + ) + + 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 = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) + } + self.bytes = bytes + } + + /// 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 [16].contains(ivBytes.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + bytesPtr.copyBytes(from: ivBytes) + } + self.bytes = 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 { + guard [16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + 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 + /// 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 { + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) + } + } +} + +// 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 { + typealias IVTuple = (UInt64, UInt64) + + var bytes: IVTuple + static var emptyBytes: IVTuple = (0, 0) + + /// Creates a new random nonce. + public init() { + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) + } + self.bytes = bytes + } + + /// 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 [16].contains(ivBytes.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + bytesPtr.copyBytes(from: ivBytes) + } + self.bytes = 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 { + guard [16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + 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 + /// 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 { + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) + } + } +} + +// 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 { + typealias NonceTuple = (UInt64, UInt32, UInt32) + + var bytes: NonceTuple + static var emptyBytes: NonceTuple = (0, 0, 0) + + /// Creates a new random nonce. + public init() { + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout.size + $0.initializeWithRandomBytes(count: count) + } + self.bytes = bytes + } + + /// 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 [12, 16].contains(nonceBytes.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + bytesPtr.copyBytes(from: nonceBytes) + } + self.bytes = 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 { + guard [12, 16].contains(data.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + 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 + /// 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 { + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) + } + } +} diff --git a/Sources/_CryptoExtras/AES/Nonces.swift.gyb b/Sources/_CryptoExtras/AES/Nonces.swift.gyb new file mode 100644 index 00000000..ff8695d6 --- /dev/null +++ b/Sources/_CryptoExtras/AES/Nonces.swift.gyb @@ -0,0 +1,155 @@ +//===----------------------------------------------------------------------===// +// +// 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. + +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": """( + 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 + )""", "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: +%{ +name = cipher["name"] +nonceName = cipher["nonceName"] +tupleDefinition = cipher["tupleDefinition"] +tupleBlank = cipher["tupleBlank"] +validSizes = cipher["validSizes"] +}% + +// 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 { + typealias ${nonceName}Tuple = ${tupleDefinition} + + var bytes: ${nonceName}Tuple + static var emptyBytes: ${nonceName}Tuple = ${tupleBlank} + + /// Creates a new random nonce. + public init() { + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { + let count = MemoryLayout<${nonceName}Tuple>.size + $0.initializeWithRandomBytes(count: count) + } + self.bytes = bytes + } + + /// 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 ${validSizes}.contains(${nonceName.lower()}Bytes.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + Swift.withUnsafeMutableBytes(of: &bytes) { bytesPtr in + bytesPtr.copyBytes(from: ${nonceName.lower()}Bytes) + } + self.bytes = 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 { + guard ${validSizes}.contains(data.count) else { + throw CryptoKitError.incorrectKeySize + } + + var bytes = Self.emptyBytes + 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 + /// 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 { + var bytes = self.bytes + return try Swift.withUnsafeBytes(of: &bytes, body) + } + + mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ReturnType) rethrows -> ReturnType { + var bytes = self.bytes + return try Swift.withUnsafeMutableBytes(of: &bytes, body) + } + + /// Returns an iterator over the elements of the nonce. + public func makeIterator() -> some IteratorProtocol { + ByteIterator(bytes) + } + } +} +% end