From 0fea5a4d2fb741bcc246a5d37607c65aaf3f1c98 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Fri, 4 Oct 2024 15:45:08 +0100 Subject: [PATCH] Update code to 2024 SDK release This patch updates Crypto to the 2024 release version of CryptoKit. As with prior years, there are small tweaks to the API surface that cause this to manifest as a semver major. In this instance, the relevant change is the removal of the long-deprecated setters for the hash function block byte counts. This is another change that meets the technical definition of a semver major, but is practically extraordinarily unlikely to manifest in an actual problem. Nonetheless, we do have to acknowledge the reality that this can break compiling code (e.g. in cases where users have defined protocols that rely on having a setter available, even though they never call through it). To that end, this pushes Crypto up to 4.0. --- README.md | 6 +- Sources/Crypto/AEADs/AES/GCM/AES-GCM.swift | 7 +- .../Crypto/AEADs/ChachaPoly/ChaChaPoly.swift | 8 +- Sources/Crypto/AEADs/Cipher.swift | 6 + Sources/Crypto/AEADs/Nonces.swift | 15 +- Sources/Crypto/AEADs/Nonces.swift.gyb | 18 +- Sources/Crypto/ASN1/ASN1.swift | 5 + .../ASN1/Basic ASN1 Types/ASN1Any.swift | 5 + .../ASN1/Basic ASN1 Types/ASN1BitString.swift | 5 + .../ASN1/Basic ASN1 Types/ASN1Boolean.swift | 5 + .../Basic ASN1 Types/ASN1Identifier.swift | 5 + .../ASN1/Basic ASN1 Types/ASN1Integer.swift | 9 +- .../ASN1/Basic ASN1 Types/ASN1Null.swift | 5 + .../Basic ASN1 Types/ASN1OctetString.swift | 5 + .../ASN1/Basic ASN1 Types/ASN1Strings.swift | 5 + .../Basic ASN1 Types/ArraySliceBigint.swift | 6 + .../Basic ASN1 Types/GeneralizedTime.swift | 5 + .../Basic ASN1 Types/ObjectIdentifier.swift | 5 + Sources/Crypto/ASN1/ECDSASignature.swift | 9 + Sources/Crypto/ASN1/PEMDocument.swift | 9 + Sources/Crypto/ASN1/PKCS8PrivateKey.swift | 4 + Sources/Crypto/ASN1/SEC1PrivateKey.swift | 9 + .../Crypto/ASN1/SubjectPublicKeyInfo.swift | 9 + Sources/Crypto/CMakeLists.txt | 1 + Sources/Crypto/CryptoKitErrors.swift | 4 + Sources/Crypto/Digests/Digest.swift | 11 +- Sources/Crypto/Digests/Digests.swift | 2 + Sources/Crypto/Digests/Digests.swift.gyb | 8 +- Sources/Crypto/Digests/HashFunctions.swift | 14 +- .../Crypto/Digests/HashFunctions_SHA2.swift | 36 +- .../Crypto/HPKE/Ciphersuite/HPKE-AEAD.swift | 9 + .../HPKE/Ciphersuite/HPKE-Ciphersuite.swift | 5 + .../Crypto/HPKE/Ciphersuite/HPKE-KDF.swift | 11 +- .../Ciphersuite/HPKE-KexKeyDerivation.swift | 15 +- .../Ciphersuite/HPKE-LabeledExtract.swift | 6 + .../Crypto/HPKE/Ciphersuite/HPKE-Utils.swift | 11 + .../Ciphersuite/KEM/Conformances/DHKEM.swift | 12 +- .../Conformances/HPKE-KEM-Curve25519.swift | 7 +- .../KEM/Conformances/HPKE-NIST-EC-KEMs.swift | 11 +- .../HPKE/Ciphersuite/KEM/HPKE-KEM.swift | 15 + Sources/Crypto/HPKE/HPKE-Errors.swift | 5 + Sources/Crypto/HPKE/HPKE.swift | 26 +- .../HPKE/Key Schedule/HPKE-Context.swift | 7 +- .../HPKE/Key Schedule/HPKE-KeySchedule.swift | 18 +- Sources/Crypto/Insecure/Insecure.swift | 4 + .../Insecure/Insecure_HashFunctions.swift | 24 +- Sources/Crypto/KEM/KEM.swift | 9 + Sources/Crypto/Key Agreement/DH.swift | 70 +- Sources/Crypto/Key Agreement/ECDH.swift | 12 +- Sources/Crypto/Key Agreement/ECDH.swift.gyb | 8 +- Sources/Crypto/Key Derivation/ANSIx963.swift | 70 ++ Sources/Crypto/Key Derivation/HKDF.swift | 15 +- Sources/Crypto/Key Wrapping/AESWrap.swift | 9 +- Sources/Crypto/Keys/EC/Ed25519Keys.swift | 6 +- Sources/Crypto/Keys/EC/NISTCurvesKeys.swift | 6 +- Sources/Crypto/Keys/EC/X25519Keys.swift | 6 +- .../Crypto/Keys/Symmetric/SymmetricKeys.swift | 5 + .../HMAC/HMAC.swift | 8 + .../MACFunctions.swift | 10 +- .../MessageAuthenticationCode.swift | 10 +- Sources/Crypto/Signatures/ECDSA.swift | 16 +- Sources/Crypto/Signatures/ECDSA.swift.gyb | 8 +- Sources/Crypto/Signatures/Ed25519.swift | 8 +- Sources/Crypto/Signatures/Signature.swift | 4 + Sources/Crypto/Util/PrettyBytes.swift | 12 +- Sources/Crypto/Util/SafeCompare.swift | 10 +- Sources/Crypto/Util/SecureBytes.swift | 37 +- Sources/Crypto/Util/Zeroization.swift | 10 +- Tests/CryptoTests/ASN1/ASN1Tests.swift | 146 ++- .../ASN1/GeneralizedTimeTests.swift | 2 +- .../AES-GCM-Runner.swift | 26 +- .../ChaChaPoly-Runner.swift | 26 +- Tests/CryptoTests/Digests/DigestsTests.swift | 1 - .../Encodings/ECKeyEncodingsTests.swift | 834 +++++++----------- Tests/CryptoTests/HPKE/HPKETests.swift | 37 +- .../ECprivateKeysFromSeeds.swift | 47 - .../SecureBytes/SecureBytesTests.swift | 7 +- .../ECDSA/ECDSASignatureTests.swift | 26 +- .../Signatures/EdDSA/Ed25519-Runner.swift | 2 +- Tests/CryptoTests/Utils/PrettyBytes.swift | 12 +- Tests/CryptoTests/Utils/XCTestUtils.swift | 13 + 81 files changed, 1047 insertions(+), 908 deletions(-) create mode 100644 Sources/Crypto/Key Derivation/ANSIx963.swift delete mode 100644 Tests/CryptoTests/Key Derivation/ECprivateKeysFromSeeds.swift diff --git a/README.md b/README.md index 9a72b1c3..d8f93421 100644 --- a/README.md +++ b/README.md @@ -130,10 +130,14 @@ SemVer and Swift Crypto's Public API guarantees should result in a working progr Swift Crypto 2.0.0 was released in September 2021. The only breaking change between Swift Crypto 2.0.0 and 1.0.0 was the addition of new cases in the `CryptoKitError` enumeration. For most users, then, it's safe to depend on either the 1.0.0 _or_ 2.0.0 series of releases. +Swift Crypto 3.0.0 was released in September 2023. The only breaking change between Swift Crypto 3.0.0 and 2.0.0 was the addition of new cases in the `CryptoKitError` enumeration. For most users, then, it's safe to depend on either the 1.0.0 _or_ 2.0.0 _or_ 3.0.0 series of releases. + +Swift Crypto 4.0.0 was released in October 2024. The only breaking change was the removal of the non-functional setters for `blockByteSize` on the hash functions, which triggered a `fatalError` if they were ever called. For most users, then, it is safe to depend on the entire range from 1.0.0 to 4.0.0 inclusive. + To do so, please use the following dependency in your `Package.swift`: ```swift -.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "3.0.0"), +.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "5.0.0"), ``` ### Developing Swift Crypto on macOS diff --git a/Sources/Crypto/AEADs/AES/GCM/AES-GCM.swift b/Sources/Crypto/AEADs/AES/GCM/AES-GCM.swift index d122556e..b287a8b6 100644 --- a/Sources/Crypto/AEADs/AES/GCM/AES-GCM.swift +++ b/Sources/Crypto/AEADs/AES/GCM/AES-GCM.swift @@ -14,14 +14,17 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias AESGCMImpl = CoreCryptoGCMImpl -import Security #else typealias AESGCMImpl = OpenSSLAESGCMImpl #endif +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension AES { /// The Advanced Encryption Standard (AES) Galois Counter Mode (GCM) cipher diff --git a/Sources/Crypto/AEADs/ChachaPoly/ChaChaPoly.swift b/Sources/Crypto/AEADs/ChachaPoly/ChaChaPoly.swift index 5b180f60..f21b48cd 100644 --- a/Sources/Crypto/AEADs/ChachaPoly/ChaChaPoly.swift +++ b/Sources/Crypto/AEADs/ChachaPoly/ChaChaPoly.swift @@ -14,14 +14,18 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias ChaChaPolyImpl = CoreCryptoChaChaPolyImpl -import Security #else typealias ChaChaPolyImpl = OpenSSLChaChaPolyImpl #endif +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif + /// An implementation of the ChaCha20-Poly1305 cipher. public enum ChaChaPoly: Cipher { diff --git a/Sources/Crypto/AEADs/Cipher.swift b/Sources/Crypto/AEADs/Cipher.swift index 62cf4639..ceadb60c 100644 --- a/Sources/Crypto/AEADs/Cipher.swift +++ b/Sources/Crypto/AEADs/Cipher.swift @@ -14,7 +14,13 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif + protocol AEADSealedBox { associatedtype Nonce: Sequence diff --git a/Sources/Crypto/AEADs/Nonces.swift b/Sources/Crypto/AEADs/Nonces.swift index 9837275e..e92dadb4 100644 --- a/Sources/Crypto/AEADs/Nonces.swift +++ b/Sources/Crypto/AEADs/Nonces.swift @@ -14,11 +14,18 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif // 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.GCM + Nonce extension AES.GCM { /// A value used once during a cryptographic operation and then discarded. @@ -47,8 +54,8 @@ extension AES.GCM { /// ``init()`` method to instead create a random nonce. /// /// - Parameters: - /// - data: A 12-byte data representation of the nonce. The initializer throws an - /// error if the data has a length other than 12 bytes. +/// - data: A data representation of the nonce. +/// The initializer throws an error if the data has a length smaller than 12 bytes. public init(data: D) throws { if data.count < AES.GCM.defaultNonceByteCount { throw CryptoKitError.incorrectParameterSize @@ -109,8 +116,8 @@ extension ChaChaPoly { /// ``init()`` method to instead create a random nonce. /// /// - Parameters: - /// - data: A 12-byte data representation of the nonce. The initializer throws an - /// error if the data has a length other than 12 bytes. +/// - data: A 12-byte data representation of the nonce. +/// The initializer throws an error if the data isn't 12 bytes long. public init(data: D) throws { if data.count != ChaChaPoly.nonceByteCount { throw CryptoKitError.incorrectParameterSize diff --git a/Sources/Crypto/AEADs/Nonces.swift.gyb b/Sources/Crypto/AEADs/Nonces.swift.gyb index 91dd918a..5e73ed0e 100644 --- a/Sources/Crypto/AEADs/Nonces.swift.gyb +++ b/Sources/Crypto/AEADs/Nonces.swift.gyb @@ -14,18 +14,31 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif // 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.GCM", "recommendedNonceSize": "AES.GCM.defaultNonceByteCount", "nonceValidation": "< AES.GCM.defaultNonceByteCount"},{"name": "ChaChaPoly", "recommendedNonceSize": "ChaChaPoly.nonceByteCount", "nonceValidation": "!= ChaChaPoly.nonceByteCount"}] +ciphers = [{"name": "AES.GCM", "recommendedNonceSize": "AES.GCM.defaultNonceByteCount", "nonceValidation": "< AES.GCM.defaultNonceByteCount", "dataDescription": "/// - data: A data representation of the nonce.\n/// The initializer throws an error if the data has a length smaller than 12 bytes."}] + +if "NO_CHACHAPOLY" in globals(): + pass +else: + ciphers.append({"name": "ChaChaPoly", "recommendedNonceSize": "ChaChaPoly.nonceByteCount", "nonceValidation": "!= ChaChaPoly.nonceByteCount", "dataDescription": "/// - data: A 12-byte data representation of the nonce.\n/// The initializer throws an error if the data isn't 12 bytes long."}) }% + + % for cipher in ciphers: %{ name = cipher["name"] nonceSize = cipher["recommendedNonceSize"] nonceValidation = cipher["nonceValidation"] +dataDescription = cipher["dataDescription"] }% // MARK: - ${name} + Nonce @@ -56,8 +69,7 @@ extension ${name} { /// ``init()`` method to instead create a random nonce. /// /// - Parameters: - /// - data: A 12-byte data representation of the nonce. The initializer throws an - /// error if the data has a length other than 12 bytes. +${dataDescription} public init(data: D) throws { if data.count ${nonceValidation} { throw CryptoKitError.incorrectParameterSize diff --git a/Sources/Crypto/ASN1/ASN1.swift b/Sources/Crypto/ASN1/ASN1.swift index 2f946ec3..4fb72010 100644 --- a/Sources/Crypto/ASN1/ASN1.swift +++ b/Sources/Crypto/ASN1/ASN1.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif // This module implements "just enough" ASN.1. Specifically, we implement exactly enough ASN.1 DER parsing to handle // the following use-cases: diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Any.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Any.swift index e43a0185..d6f842d7 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Any.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Any.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// An ASN1 ANY represents...well, anything. diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1BitString.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1BitString.swift index 3bca15ab..0dcf4965 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1BitString.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1BitString.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// A bitstring is a representation of...well...some bits. diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Boolean.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Boolean.swift index 02a005bf..ef514d5a 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Boolean.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Boolean.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension Bool: ASN1ImplicitlyTaggable { static var defaultIdentifier: ASN1.ASN1Identifier { diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Identifier.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Identifier.swift index 90cfeb3f..02635be6 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Identifier.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Identifier.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// An `ASN1Identifier` is a representation of the abstract notion of an ASN.1 identifier. Identifiers have a number of properties that relate to both the specific diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Integer.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Integer.swift index 47e329ac..da1b18cb 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Integer.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Integer.swift @@ -14,10 +14,15 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif -/// A protocol that represents any internal object that can present itself as an INTEGER, or be parsed from -/// an INTEGER. +/// A protocol that represents any internal object that can present itself as a INTEGER, or be parsed from +/// a INTEGER. /// /// This is not a very good solution for a fully-fledged ASN.1 library: we'd rather have a better numerics /// protocol that could both initialize from and serialize to either bytes or words. However, no such diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Null.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Null.swift index 53259ea0..1dbfa65d 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Null.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Null.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// An ASN1 NULL represents nothing. diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1OctetString.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1OctetString.swift index e3f889cf..90d6735e 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1OctetString.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1OctetString.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// An octet string is a representation of a string of octets. diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Strings.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Strings.swift index d2a9517b..a8c19ba0 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Strings.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ASN1Strings.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// A UTF8String is roughly what it sounds like. We note that all the string types are encoded as implicitly tagged diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ArraySliceBigint.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ArraySliceBigint.swift index b472489f..604b4e04 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ArraySliceBigint.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ArraySliceBigint.swift @@ -15,6 +15,12 @@ @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else +import Foundation +#endif + // For temporary purposes we pretend that ArraySlice is our "bigint" type. We don't really need anything else. extension ArraySlice: ASN1Serializable where Element == UInt8 { } diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/GeneralizedTime.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/GeneralizedTime.swift index 63d73874..6c2721e5 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/GeneralizedTime.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/GeneralizedTime.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { struct GeneralizedTime: ASN1ImplicitlyTaggable, Hashable { diff --git a/Sources/Crypto/ASN1/Basic ASN1 Types/ObjectIdentifier.swift b/Sources/Crypto/ASN1/Basic ASN1 Types/ObjectIdentifier.swift index a7dd5ce5..62664e06 100644 --- a/Sources/Crypto/ASN1/Basic ASN1 Types/ObjectIdentifier.swift +++ b/Sources/Crypto/ASN1/Basic ASN1 Types/ObjectIdentifier.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { /// An Object Identifier is a representation of some kind of object: really any kind of object. diff --git a/Sources/Crypto/ASN1/ECDSASignature.swift b/Sources/Crypto/ASN1/ECDSASignature.swift index 85686409..e933eab1 100644 --- a/Sources/Crypto/ASN1/ECDSASignature.swift +++ b/Sources/Crypto/ASN1/ECDSASignature.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif extension ASN1 { /// An ECDSA signature is laid out as follows: diff --git a/Sources/Crypto/ASN1/PEMDocument.swift b/Sources/Crypto/ASN1/PEMDocument.swift index 98ba079d..5a844903 100644 --- a/Sources/Crypto/ASN1/PEMDocument.swift +++ b/Sources/Crypto/ASN1/PEMDocument.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif extension ASN1 { /// A PEM document is some data, and a discriminator type that is used to advertise the content. diff --git a/Sources/Crypto/ASN1/PKCS8PrivateKey.swift b/Sources/Crypto/ASN1/PKCS8PrivateKey.swift index 8df9dec1..a573b4bb 100644 --- a/Sources/Crypto/ASN1/PKCS8PrivateKey.swift +++ b/Sources/Crypto/ASN1/PKCS8PrivateKey.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension ASN1 { // A PKCS#8 private key is one of two formats, depending on the version: diff --git a/Sources/Crypto/ASN1/SEC1PrivateKey.swift b/Sources/Crypto/ASN1/SEC1PrivateKey.swift index dd347123..cfb9f624 100644 --- a/Sources/Crypto/ASN1/SEC1PrivateKey.swift +++ b/Sources/Crypto/ASN1/SEC1PrivateKey.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif extension ASN1 { // For private keys, SEC 1 uses: diff --git a/Sources/Crypto/ASN1/SubjectPublicKeyInfo.swift b/Sources/Crypto/ASN1/SubjectPublicKeyInfo.swift index 31f6b45b..6a6b4d6c 100644 --- a/Sources/Crypto/ASN1/SubjectPublicKeyInfo.swift +++ b/Sources/Crypto/ASN1/SubjectPublicKeyInfo.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif extension ASN1 { struct SubjectPublicKeyInfo: ASN1ImplicitlyTaggable { diff --git a/Sources/Crypto/CMakeLists.txt b/Sources/Crypto/CMakeLists.txt index da0fb6ec..01c68514 100644 --- a/Sources/Crypto/CMakeLists.txt +++ b/Sources/Crypto/CMakeLists.txt @@ -63,6 +63,7 @@ add_library(Crypto "Key Agreement/BoringSSL/ECDH_boring.swift" "Key Agreement/DH.swift" "Key Agreement/ECDH.swift" + "Key Derivation/ANSIx963.swift" "Key Derivation/HKDF.swift" "Key Wrapping/AESWrap.swift" "Key Wrapping/BoringSSL/AESWrap_boring.swift" diff --git a/Sources/Crypto/CryptoKitErrors.swift b/Sources/Crypto/CryptoKitErrors.swift index 31c4523f..98955ed6 100644 --- a/Sources/Crypto/CryptoKitErrors.swift +++ b/Sources/Crypto/CryptoKitErrors.swift @@ -12,7 +12,11 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif #else /// General cryptography errors used by CryptoKit. public enum CryptoKitError: Error { diff --git a/Sources/Crypto/Digests/Digest.swift b/Sources/Crypto/Digests/Digest.swift index 653a026a..648db474 100644 --- a/Sources/Crypto/Digests/Digest.swift +++ b/Sources/Crypto/Digests/Digest.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif /// A type that represents the output of a hash. public protocol Digest: Hashable, ContiguousBytes, CustomStringConvertible, Sequence where Element == UInt8 { @@ -79,7 +88,7 @@ extension Digest { return safeCompare(lhs, rhs.regions.first!) } } - + public var description: String { return "\(Self.self): \(Array(self).hexString)" } diff --git a/Sources/Crypto/Digests/Digests.swift b/Sources/Crypto/Digests/Digests.swift index ff08b436..6b0c8208 100644 --- a/Sources/Crypto/Digests/Digests.swift +++ b/Sources/Crypto/Digests/Digests.swift @@ -18,6 +18,8 @@ // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. + + // MARK: - SHA256Digest + DigestPrivate /// The output of a Secure Hashing Algorithm 2 (SHA-2) hash with a 256-bit digest. public struct SHA256Digest: DigestPrivate { diff --git a/Sources/Crypto/Digests/Digests.swift.gyb b/Sources/Crypto/Digests/Digests.swift.gyb index 401e576c..45539c36 100644 --- a/Sources/Crypto/Digests/Digests.swift.gyb +++ b/Sources/Crypto/Digests/Digests.swift.gyb @@ -17,9 +17,15 @@ // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. + %{ -digests_and_length = [{"name": "SHA256", "count": 32},{"name": "SHA384","count":48},{"name":"SHA512", "count": 64},{"name":"SHA1", "count":20, "prefix":"Insecure "},{"name":"MD5", "count":16, "prefix":"Insecure "}] +digests_and_length = [{"name": "SHA256", "count": 32},{"name": "SHA384","count":48},{"name":"SHA512", "count": 64}] +if "NO_INSECURE_DIGEST" in globals(): + pass +else: + digests_and_length.extend([{"name":"SHA1", "count":20, "prefix":"Insecure "},{"name":"MD5", "count":16, "prefix":"Insecure "}]) }% + % for HF in digests_and_length: %{ name = HF["name"] diff --git a/Sources/Crypto/Digests/HashFunctions.swift b/Sources/Crypto/Digests/HashFunctions.swift index 20280e06..fefc747b 100644 --- a/Sources/Crypto/Digests/HashFunctions.swift +++ b/Sources/Crypto/Digests/HashFunctions.swift @@ -14,13 +14,17 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias DigestImpl = CoreCryptoDigestImpl #else typealias DigestImpl = OpenSSLDigestImpl #endif +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif /// A type that performs cryptographically secure hashing. /// @@ -43,9 +47,13 @@ import Foundation public protocol HashFunction { /// The number of bytes that represents the hash function’s internal state. static var blockByteCount: Int { get } - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION /// The type of the digest returned by the hash function. + #if CRYPTOKIT_STATIC_LIBRARY + associatedtype Digest: CryptoKit_Static.Digest + #else associatedtype Digest: CryptoKit.Digest + #endif #else associatedtype Digest: Crypto.Digest #endif @@ -107,7 +115,7 @@ extension HashFunction { return hasher.finalize() } - /// Computes the SHA1 digest of the bytes in the given data instance and + /// Computes the digest of the bytes in the given data instance and /// returns the computed digest. /// /// Use this method if all your data fits into a single data instance. If diff --git a/Sources/Crypto/Digests/HashFunctions_SHA2.swift b/Sources/Crypto/Digests/HashFunctions_SHA2.swift index d14b9c04..116a857a 100644 --- a/Sources/Crypto/Digests/HashFunctions_SHA2.swift +++ b/Sources/Crypto/Digests/HashFunctions_SHA2.swift @@ -28,17 +28,9 @@ /// data, and then calling the ``finalize()`` method to get the result. public struct SHA256: HashFunctionImplementationDetails { /// The number of bytes that represents the hash function’s internal state. - public static var blockByteCount: Int { - get { 64 } - - set { fatalError("Cannot set SHA256.blockByteCount") } - } + public static let blockByteCount: Int = 64 /// The number of bytes in a SHA256 digest. - public static var byteCount: Int { - get { 32 } - - set { fatalError("Cannot set SHA256.byteCount") } - } + public static let byteCount: Int = 32 /// The digest type for a SHA256 hash function. public typealias Digest = SHA256Digest @@ -110,17 +102,9 @@ public struct SHA256: HashFunctionImplementationDetails { /// data, and then calling the ``finalize()`` method to get the result. public struct SHA384: HashFunctionImplementationDetails { /// The number of bytes that represents the hash function’s internal state. - public static var blockByteCount: Int { - get { 128 } - - set { fatalError("Cannot set SHA384.blockByteCount") } - } + public static let blockByteCount: Int = 128 /// The number of bytes in a SHA384 digest. - public static var byteCount: Int { - get { 48 } - - set { fatalError("Cannot set SHA384.byteCount") } - } + public static let byteCount: Int = 48 /// The digest type for a SHA384 hash function. public typealias Digest = SHA384Digest @@ -193,17 +177,9 @@ public struct SHA384: HashFunctionImplementationDetails { /// data, and then calling the ``finalize()`` method to get the result. public struct SHA512: HashFunctionImplementationDetails { /// The number of bytes that represents the hash function’s internal state. - public static var blockByteCount: Int { - get { 128 } - - set { fatalError("Cannot set SHA512.blockByteCount") } - } + public static let blockByteCount: Int = 128 /// The number of bytes in a SHA512 digest. - public static var byteCount: Int { - get { 64 } - - set { fatalError("Cannot set SHA512.byteCount") } - } + public static let byteCount: Int = 64 /// The digest type for a SHA512 hash function. public typealias Digest = SHA512Digest diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-AEAD.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-AEAD.swift index 19248974..a73a6319 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-AEAD.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-AEAD.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { @@ -31,6 +36,7 @@ extension HPKE { /// In export-only mode, HPKE negotiates key derivation, but you can't use it to encrypt or decrypt data. case exportOnly + /// Return the AEAD algorithm identifier as defined in section 7.3 of [RFC 9180](https://www.ietf.org/rfc/rfc9180.pdf). internal var value: UInt16 { switch self { case .AES_GCM_128: return 0x0001 @@ -44,6 +50,7 @@ extension HPKE { return self == .exportOnly } + /// Return the AEAD key size in bytes internal var keyByteCount: Int { switch self { case .AES_GCM_128: @@ -57,6 +64,7 @@ extension HPKE { } } + /// Return the AEAD nonce size in bytes internal var nonceByteCount: Int { switch self { case .AES_GCM_128, .AES_GCM_256, .chaChaPoly: @@ -66,6 +74,7 @@ extension HPKE { } } + /// Return the AEAD tag size in bytes internal var tagByteCount: Int { switch self { case .AES_GCM_128, .AES_GCM_256, .chaChaPoly: diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-Ciphersuite.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-Ciphersuite.swift index 25e5b08c..fbc08c13 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-Ciphersuite.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-Ciphersuite.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-KDF.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-KDF.swift index 1d596f12..d055815c 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-KDF.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-KDF.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif extension HPKE { /// The key derivation functions to use in HPKE. @@ -26,7 +35,7 @@ extension HPKE { /// An HMAC-based key derivation function that uses SHA-2 hashing with a 512-bit digest. case HKDF_SHA512 - /// Assigned value + /// Return the KDF algorithm identifier as defined in section 7.2 of [RFC 9180](https://www.ietf.org/rfc/rfc9180.pdf). internal var value: UInt16 { switch self { case .HKDF_SHA256: return 0x0001 diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-KexKeyDerivation.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-KexKeyDerivation.swift index 0e02a54e..efd1a894 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-KexKeyDerivation.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-KexKeyDerivation.swift @@ -14,7 +14,13 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif + private let suiteIDLabel = Data("KEM".utf8) @@ -24,9 +30,14 @@ extension HPKE { pkRm: Data, pkSm: Data? = nil, kem: HPKE.KEM, kdf: HPKE.KDF) -> SymmetricKey { var suiteID = suiteIDLabel suiteID.append(kem.identifier) - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if CRYPTOKIT_STATIC_LIBRARY + return CryptoKit_Static.ExtractAndExpand(zz: dh, kemContext: kemContext(enc: enc, pkRm: pkRm, pkSm: pkSm), + suiteID: suiteID, kem: kem, kdf: kdf) + #else return CryptoKit.ExtractAndExpand(zz: dh, kemContext: kemContext(enc: enc, pkRm: pkRm, pkSm: pkSm), suiteID: suiteID, kem: kem, kdf: kdf) + #endif #else return Crypto.ExtractAndExpand(zz: dh, kemContext: kemContext(enc: enc, pkRm: pkRm, pkSm: pkSm), suiteID: suiteID, kem: kem, kdf: kdf) @@ -37,7 +48,7 @@ extension HPKE { var context = Data() context.append(enc) context.append(pkRm) - if let pkSm = pkSm { context.append(pkSm) } + if let pkSm { context.append(pkSm) } return context } } diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-LabeledExtract.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-LabeledExtract.swift index da22c83d..dfc1b63f 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-LabeledExtract.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-LabeledExtract.swift @@ -14,7 +14,13 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif + private let protocolLabel = Data("HPKE-v1".utf8) private let eaePRKLabel = Data("eae_prk".utf8) diff --git a/Sources/Crypto/HPKE/Ciphersuite/HPKE-Utils.swift b/Sources/Crypto/HPKE/Ciphersuite/HPKE-Utils.swift index 92c9657f..8207acac 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/HPKE-Utils.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/HPKE-Utils.swift @@ -14,13 +14,24 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif + internal func I2OSP(value: Int, outputByteCount: Int) -> Data { precondition(outputByteCount > 0, "Cannot I2OSP with no output length.") precondition(value >= 0, "I2OSP requires a non-null value.") + #if !CRYPTOKIT_NO_ACCESS_TO_FOUNDATION let requiredBytes = Int(ceil(log2(Double(max(value, 1) + 1)) / 8)) + #else + let requiredBytes = Int((log2_bridge(Double(max(value, 1) + 1)) / 8).rounded(.up)) + #endif + precondition(outputByteCount >= requiredBytes) var data = Data(repeating: 0, count: outputByteCount) diff --git a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/DHKEM.swift b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/DHKEM.swift index e41183e1..fcea1cb8 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/DHKEM.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/DHKEM.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif /// A type that ``HPKE`` uses to encode the public key. public protocol HPKEPublicKeySerialization { @@ -40,7 +45,6 @@ public protocol HPKEDiffieHellmanPublicKey: HPKEPublicKeySerialization where Eph } /// A type that represents the private key in a Diffie-Hellman key exchange. - public protocol HPKEDiffieHellmanPrivateKey: DiffieHellmanKeyAgreement where PublicKey: HPKEDiffieHellmanPublicKey {} /// A type that represents the generation of private keys in a Diffie-Hellman key exchange. @@ -56,8 +60,12 @@ extension HPKE { let kem: HPKE.KEM let key: DHPK - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if CRYPTOKIT_STATIC_LIBRARY + typealias EncapsulationResult = CryptoKit_Static.KEM.EncapsulationResult + #else typealias EncapsulationResult = CryptoKit.KEM.EncapsulationResult + #endif #else typealias EncapsulationResult = Crypto.KEM.EncapsulationResult #endif diff --git a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-KEM-Curve25519.swift b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-KEM-Curve25519.swift index e846526e..71ad919e 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-KEM-Curve25519.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-KEM-Curve25519.swift @@ -14,10 +14,15 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif -extension Curve25519.KeyAgreement.PrivateKey: HPKEDiffieHellmanPrivateKeyGeneration {} +extension Curve25519.KeyAgreement.PrivateKey: HPKEDiffieHellmanPrivateKeyGeneration {} extension Curve25519.KeyAgreement.PublicKey: HPKEDiffieHellmanPublicKey { /// The type of the ephemeral private key associated with this public key. diff --git a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-NIST-EC-KEMs.swift b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-NIST-EC-KEMs.swift index 1b921297..8a251580 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-NIST-EC-KEMs.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/KEM/Conformances/HPKE-NIST-EC-KEMs.swift @@ -12,11 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif #else -import Foundation - +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else +import Foundation +#endif extension P256.KeyAgreement.PrivateKey: HPKEDiffieHellmanPrivateKeyGeneration { /// Creates a NIST P-256 elliptic curve private key for use with Diffie-Hellman key exchange. diff --git a/Sources/Crypto/HPKE/Ciphersuite/KEM/HPKE-KEM.swift b/Sources/Crypto/HPKE/Ciphersuite/KEM/HPKE-KEM.swift index c9c9ad96..0c0755f0 100644 --- a/Sources/Crypto/HPKE/Ciphersuite/KEM/HPKE-KEM.swift +++ b/Sources/Crypto/HPKE/Ciphersuite/KEM/HPKE-KEM.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { /// The key encapsulation mechanisms to use in HPKE. @@ -32,6 +36,7 @@ extension HPKE { /// and SHA-2 hashing with a 256-bit digest. case Curve25519_HKDF_SHA256 + /// Return the KEM algorithm identifier as defined in section 7.1 of [RFC 9180](https://www.ietf.org/rfc/rfc9180.pdf). internal var value: UInt16 { switch self { case .P256_HKDF_SHA256: return 0x0010 @@ -62,6 +67,16 @@ extension HPKE { case .Curve25519_HKDF_SHA256: return 32 } } + + /// Return the size of the encapsulation in bytes + internal var nEnc: UInt16 { + switch self { + case .P256_HKDF_SHA256: return 65 + case .P384_HKDF_SHA384: return 97 + case .P521_HKDF_SHA512: return 133 + case .Curve25519_HKDF_SHA256: return 32 + } + } } } diff --git a/Sources/Crypto/HPKE/HPKE-Errors.swift b/Sources/Crypto/HPKE/HPKE-Errors.swift index ffc621a6..d357c9e8 100644 --- a/Sources/Crypto/HPKE/HPKE-Errors.swift +++ b/Sources/Crypto/HPKE/HPKE-Errors.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { /// Hybrid public key encryption (HPKE) errors that CryptoKit uses. diff --git a/Sources/Crypto/HPKE/HPKE.swift b/Sources/Crypto/HPKE/HPKE.swift index 93fb7f1e..af55b410 100644 --- a/Sources/Crypto/HPKE/HPKE.swift +++ b/Sources/Crypto/HPKE/HPKE.swift @@ -12,9 +12,17 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif /// A container for hybrid public key encryption (HPKE) operations. /// @@ -48,6 +56,10 @@ import Foundation public enum HPKE {} extension HPKE { + /// Static constant used to store the fixed-string label for the HPKE export API + /// See: https://datatracker.ietf.org/doc/html/rfc9180#name-secret-export + fileprivate static let exportLabel = Data("sec".utf8) + /// A type that represents the sending side of an HPKE message exchange. /// /// To create encrypted messages, initialize a `Sender` specifying the appropriate cipher suite, @@ -60,7 +72,7 @@ extension HPKE { private var context: Context /// The encapsulated symmetric key that the recipient uses to decrypt messages. public let encapsulatedKey: Data - + /// The exporter secret. internal var exporterSecret: SymmetricKey { return context.keySchedule.exporterSecret @@ -74,13 +86,13 @@ extension HPKE { public func exportSecret(context: Context, outputByteCount: Int) throws -> SymmetricKey { precondition(outputByteCount > 0); return LabeledExpand(prk: self.exporterSecret, - label: Data("sec".utf8), + label: exportLabel, info: context, outputByteCount: UInt16(outputByteCount), suiteID: self.context.keySchedule.ciphersuite.identifier, kdf: self.context.keySchedule.ciphersuite.kdf) } - + /// Creates a sender in base mode. /// /// The `Sender` encrypts messages in base mode with a symmetric encryption key it derives using a key derivation function (KDF). @@ -196,12 +208,12 @@ extension HPKE { public struct Recipient { private var context: Context - + /// The exporter secret. internal var exporterSecret: SymmetricKey { return context.keySchedule.exporterSecret } - + /// Exports a secret given domain-separation context and the desired output length. /// - Parameters: /// - context: Application-specific information providing context on the use of this key. @@ -210,13 +222,13 @@ extension HPKE { public func exportSecret(context: Context, outputByteCount: Int) throws -> SymmetricKey { precondition(outputByteCount > 0); return LabeledExpand(prk: self.exporterSecret, - label: Data("sec".utf8), + label: exportLabel, info: context, outputByteCount: UInt16(outputByteCount), suiteID: self.context.keySchedule.ciphersuite.identifier, kdf: self.context.keySchedule.ciphersuite.kdf) } - + /// Creates a recipient in base mode. /// /// The `Receiver` decrypts messages in base mode using the encapsulated key with the key schedule information (`info` data). diff --git a/Sources/Crypto/HPKE/Key Schedule/HPKE-Context.swift b/Sources/Crypto/HPKE/Key Schedule/HPKE-Context.swift index e63c81b8..966e119b 100644 --- a/Sources/Crypto/HPKE/Key Schedule/HPKE-Context.swift +++ b/Sources/Crypto/HPKE/Key Schedule/HPKE-Context.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { struct Context { @@ -45,7 +50,7 @@ extension HPKE { let skRKEM = try HPKE.DHKEM.PrivateKey(skR, kem: ciphersuite.kem) let sharedSecret: SymmetricKey - if let pkS = pkS { + if let pkS { sharedSecret = try skRKEM.decapsulate(enc, authenticating: pkS) } else { sharedSecret = try skRKEM.decapsulate(enc) diff --git a/Sources/Crypto/HPKE/Key Schedule/HPKE-KeySchedule.swift b/Sources/Crypto/HPKE/Key Schedule/HPKE-KeySchedule.swift index 02960a31..bcf33576 100644 --- a/Sources/Crypto/HPKE/Key Schedule/HPKE-KeySchedule.swift +++ b/Sources/Crypto/HPKE/Key Schedule/HPKE-KeySchedule.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension HPKE { internal struct KeySchedule { @@ -101,8 +106,19 @@ extension HPKE { self.ciphersuite = ciphersuite } + var maxSequenceNumber: UInt64 { + get { + let nonceBitCount = UInt64(self.ciphersuite.aead.nonceByteCount * 8) + if (nonceBitCount >= 64) { + return UInt64.max + } + + return ((UInt64(1) << nonceBitCount) - 1) + } + } + mutating func incrementSequenceNumber() throws { - if self.sequenceNumber >= ((1 << (self.ciphersuite.aead.nonceByteCount)) - 1) { + if self.sequenceNumber >= maxSequenceNumber { throw HPKE.Errors.outOfRangeSequenceNumber } sequenceNumber += 1 diff --git a/Sources/Crypto/Insecure/Insecure.swift b/Sources/Crypto/Insecure/Insecure.swift index 1fe53ee1..962ceccf 100644 --- a/Sources/Crypto/Insecure/Insecure.swift +++ b/Sources/Crypto/Insecure/Insecure.swift @@ -12,7 +12,11 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif #else /// A container for older, cryptographically insecure algorithms. /// diff --git a/Sources/Crypto/Insecure/Insecure_HashFunctions.swift b/Sources/Crypto/Insecure/Insecure_HashFunctions.swift index 5352e6d8..a0e70d0a 100644 --- a/Sources/Crypto/Insecure/Insecure_HashFunctions.swift +++ b/Sources/Crypto/Insecure/Insecure_HashFunctions.swift @@ -34,18 +34,10 @@ extension Insecure { public struct SHA1: HashFunctionImplementationDetails { /// The number of bytes that represents the hash function’s internal /// state. - public static var blockByteCount: Int { - get { 64 } - - set { fatalError("Cannot set SHA1.blockByteCount") } - } + public static let blockByteCount: Int = 64 /// The number of bytes in a SHA1 digest. - public static var byteCount: Int { - get { 20 } - - set { fatalError("Cannot set SHA1.byteCount") } - } + public static let byteCount: Int = 20 /// The digest type for a SHA1 hash function. public typealias Digest = Insecure.SHA1Digest @@ -122,17 +114,9 @@ extension Insecure { public struct MD5: HashFunctionImplementationDetails { /// The number of bytes that represents the hash function’s internal /// state. - public static var blockByteCount: Int { - get { 64 } - - set { fatalError("Cannot set MD5.blockByteCount") } - } + public static let blockByteCount: Int = 64 /// The number of bytes in an MD5 digest. - public static var byteCount: Int { - get { 16 } - - set { fatalError("Cannot set MD5.byteCount") } - } + public static let byteCount: Int = 16 /// The digest type for a MD5 hash function. public typealias Digest = Insecure.MD5Digest diff --git a/Sources/Crypto/KEM/KEM.swift b/Sources/Crypto/KEM/KEM.swift index 80bd46ae..57886703 100644 --- a/Sources/Crypto/KEM/KEM.swift +++ b/Sources/Crypto/KEM/KEM.swift @@ -12,9 +12,18 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif /// A Key Encapsulation Mechanism public enum KEM { diff --git a/Sources/Crypto/Key Agreement/DH.swift b/Sources/Crypto/Key Agreement/DH.swift index 42326b49..98631f1b 100644 --- a/Sources/Crypto/Key Agreement/DH.swift +++ b/Sources/Crypto/Key Agreement/DH.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif /// A Diffie-Hellman Key Agreement Key public protocol DiffieHellmanKeyAgreement { @@ -36,7 +41,7 @@ public protocol DiffieHellmanKeyAgreement { /// Generate a shared secret by calling your private key’s /// `sharedSecretFromKeyAgreement(publicKeyShare:)` method with the public key /// from another party. The other party computes the same secret by passing your -/// public key to the equivalent method on their own private key. +/// public key to the the equivalent method on their own private key. /// /// The shared secret isn’t suitable as a symmetric cryptographic key /// (``SymmetricKey``) by itself. However, you use it to generate a key by @@ -49,7 +54,15 @@ public protocol DiffieHellmanKeyAgreement { /// ``ChaChaPoly`` or ``AES``. public struct SharedSecret: ContiguousBytes { var ss: SecureBytes - + + internal init(withExternalSS ss: SS) { + self.ss = SecureBytes(bytes: ss) + } + + internal init(ss: SecureBytes){ + self.ss = ss + } + /// Invokes the given closure with a buffer pointer covering the raw bytes /// of the shared secret. /// @@ -72,57 +85,10 @@ public struct SharedSecret: ContiguousBytes { /// /// - Returns: The derived symmetric key. public func x963DerivedSymmetricKey(using hashFunction: H.Type, sharedInfo: SI, outputByteCount: Int) -> SymmetricKey { - // SEC1 defines 3 inputs to the KDF: - // - // 1. An octet string Z which is the shared secret value. That's `self` here. - // 2. An integer `keydatalen` which is the length in octets of the keying data to be generated. Here that's `outputByteCount`. - // 3. An optional octet string `SharedInfo` which consists of other shared data. Here, that's `sharedInfo`. - // - // We then need to perform the following steps: - // - // 1. Check that keydatalen < hashlen × (2³² − 1). If keydatalen ≥ hashlen × (2³² − 1), fail. - // 2. Initiate a 4 octet, big-endian octet string Counter as 0x00000001. - // 3. For i = 1 to ⌈keydatalen/hashlen⌉, do the following: - // 1. Compute: Ki = Hash(Z || Counter || [SharedInfo]). - // 2. Increment Counter. - // 3. Increment i. - // 4. Set K to be the leftmost keydatalen octets of: K1 || K2 || . . . || K⌈keydatalen/hashlen⌉. - // 5. Output K. - // - // The loop in step 3 is not very Swifty, so instead we generate the counter directly. - // Step 1: Check that keydatalen < hashlen × (2³² − 1). - // We do this math in UInt64-space, because we'll overflow 32-bit integers. - guard UInt64(outputByteCount) < (UInt64(H.Digest.byteCount) * UInt64(UInt32.max)) else { - fatalError("Invalid parameter size") - } - - var key = SecureBytes() - key.reserveCapacity(outputByteCount) - - var remainingBytes = outputByteCount - var counter = UInt32(1) - while remainingBytes > 0 { - // 1. Compute: Ki = Hash(Z || Counter || [SharedInfo]). - var hasher = H() - hasher.update(self) - hasher.update(counter.bigEndian) - hasher.update(data: sharedInfo) - let digest = hasher.finalize() - - // 2. Increment Counter. - counter += 1 - - // Append the bytes of the digest. We don't want to append more than the remaining number of bytes. - let bytesToAppend = min(remainingBytes, H.Digest.byteCount) - digest.withUnsafeBytes { digestPtr in - key.append(digestPtr.prefix(bytesToAppend)) - } - remainingBytes -= bytesToAppend + return self.ss.withUnsafeBytes { ssBytes in + return ANSIKDFx963.deriveKey(inputKeyMaterial: SymmetricKey(data: ssBytes), info: sharedInfo, outputByteCount: outputByteCount) } - - precondition(key.count == outputByteCount) - return SymmetricKey(data: key) } /// Derives a symmetric encryption key from the secret using HKDF key @@ -173,7 +139,7 @@ extension SharedSecret: CustomStringConvertible, Equatable { return safeCompare(lhs, rhs.regions.first!) } } - + public var description: String { return "\(Self.self): \(ss.hexString)" } diff --git a/Sources/Crypto/Key Agreement/ECDH.swift b/Sources/Crypto/Key Agreement/ECDH.swift index 941fc61b..a6ceb8da 100644 --- a/Sources/Crypto/Key Agreement/ECDH.swift +++ b/Sources/Crypto/Key Agreement/ECDH.swift @@ -14,7 +14,7 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias NISTCurvePublicKeyImpl = CoreCryptoNISTCurvePublicKeyImpl typealias NISTCurvePrivateKeyImpl = CoreCryptoNISTCurvePrivateKeyImpl #else @@ -22,7 +22,11 @@ typealias NISTCurvePublicKeyImpl = OpenSSLNISTCurvePublicKeyImpl typealias NISTCurvePrivateKeyImpl = OpenSSLNISTCurvePrivateKeyImpl #endif +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded @@ -1325,7 +1329,7 @@ extension P256.KeyAgreement.PrivateKey: DiffieHellmanKeyAgreement { /// key from this user to create the shared secret. /// - Returns: The computed shared secret. public func sharedSecretFromKeyAgreement(with publicKeyShare: P256.KeyAgreement.PublicKey) throws -> SharedSecret { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSharedSecretFromKeyAgreement(with: publicKeyShare) #else return try self.openSSLSharedSecretFromKeyAgreement(with: publicKeyShare) @@ -1341,7 +1345,7 @@ extension P384.KeyAgreement.PrivateKey: DiffieHellmanKeyAgreement { /// key from this user to create the shared secret. /// - Returns: The computed shared secret. public func sharedSecretFromKeyAgreement(with publicKeyShare: P384.KeyAgreement.PublicKey) throws -> SharedSecret { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSharedSecretFromKeyAgreement(with: publicKeyShare) #else return try self.openSSLSharedSecretFromKeyAgreement(with: publicKeyShare) @@ -1357,7 +1361,7 @@ extension P521.KeyAgreement.PrivateKey: DiffieHellmanKeyAgreement { /// key from this user to create the shared secret. /// - Returns: The computed shared secret. public func sharedSecretFromKeyAgreement(with publicKeyShare: P521.KeyAgreement.PublicKey) throws -> SharedSecret { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSharedSecretFromKeyAgreement(with: publicKeyShare) #else return try self.openSSLSharedSecretFromKeyAgreement(with: publicKeyShare) diff --git a/Sources/Crypto/Key Agreement/ECDH.swift.gyb b/Sources/Crypto/Key Agreement/ECDH.swift.gyb index 5c738f0c..a40e9dc9 100644 --- a/Sources/Crypto/Key Agreement/ECDH.swift.gyb +++ b/Sources/Crypto/Key Agreement/ECDH.swift.gyb @@ -14,7 +14,7 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias NISTCurvePublicKeyImpl = CoreCryptoNISTCurvePublicKeyImpl typealias NISTCurvePrivateKeyImpl = CoreCryptoNISTCurvePrivateKeyImpl #else @@ -22,7 +22,11 @@ typealias NISTCurvePublicKeyImpl = OpenSSLNISTCurvePublicKeyImpl typealias NISTCurvePrivateKeyImpl = OpenSSLNISTCurvePrivateKeyImpl #endif +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded @@ -278,7 +282,7 @@ extension ${CURVE}.KeyAgreement.PrivateKey: DiffieHellmanKeyAgreement { /// key from this user to create the shared secret. /// - Returns: The computed shared secret. public func sharedSecretFromKeyAgreement(with publicKeyShare: ${CURVE}.KeyAgreement.PublicKey) throws -> SharedSecret { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSharedSecretFromKeyAgreement(with: publicKeyShare) #else return try self.openSSLSharedSecretFromKeyAgreement(with: publicKeyShare) diff --git a/Sources/Crypto/Key Derivation/ANSIx963.swift b/Sources/Crypto/Key Derivation/ANSIx963.swift new file mode 100644 index 00000000..b1bb2c5a --- /dev/null +++ b/Sources/Crypto/Key Derivation/ANSIx963.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftCrypto open source project +// +// Copyright (c) 2019-2020 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 +// +//===----------------------------------------------------------------------===// +#if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +@_exported import CryptoKit +#else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else +import Foundation +#endif + +internal struct ANSIKDFx963 { + public static func deriveKey(inputKeyMaterial: SymmetricKey, info: Info, outputByteCount: Int) -> SymmetricKey { + + guard UInt64(outputByteCount) < (UInt64(H.Digest.byteCount) * UInt64(UInt32.max)) else { + fatalError("Invalid parameter size") + } + + var key = SecureBytes() + key.reserveCapacity(outputByteCount) + + var remainingBytes = outputByteCount + var counter = UInt32(1) + + while remainingBytes > 0 { + // 1. Compute: Ki = Hash(Z || Counter || [SharedInfo]). + var hasher = H() + inputKeyMaterial.withUnsafeBytes { ikmBytes in + hasher.update(data: ikmBytes) + } + hasher.update(counter.bigEndian) + hasher.update(data: info) + let digest = hasher.finalize() + + // 2. Increment Counter. + counter += 1 + + // Append the bytes of the digest. We don't want to append more than the remaining number of bytes. + let bytesToAppend = min(remainingBytes, H.Digest.byteCount) + digest.withUnsafeBytes { digestPtr in + key.append(digestPtr.prefix(bytesToAppend)) + } + remainingBytes -= bytesToAppend + } + + precondition(key.count == outputByteCount) + return SymmetricKey(data: key) + } + + public static func deriveKey(inputKeyMaterial: SymmetricKey, + outputByteCount: Int) -> SymmetricKey { + return deriveKey(inputKeyMaterial: inputKeyMaterial, info: [UInt8](), outputByteCount: outputByteCount) + } +} + + +#endif + diff --git a/Sources/Crypto/Key Derivation/HKDF.swift b/Sources/Crypto/Key Derivation/HKDF.swift index 5f47c2fb..62ff2a26 100644 --- a/Sources/Crypto/Key Derivation/HKDF.swift +++ b/Sources/Crypto/Key Derivation/HKDF.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif /// A standards-based implementation of an HMAC-based Key Derivation Function /// (HKDF). @@ -112,7 +117,7 @@ public struct HKDF { /// hashed authentication code. public static func extract(inputKeyMaterial: SymmetricKey, salt: Salt?) -> HashedAuthenticationCode { let key: SymmetricKey - if let salt = salt { + if let salt { if salt.regions.count != 1 { let contiguousBytes = Array(salt) key = SymmetricKey(data: contiguousBytes) @@ -142,21 +147,23 @@ public struct HKDF { /// /// - Returns: The derived symmetric key. public static func expand(pseudoRandomKey prk: PRK, info: Info?, outputByteCount: Int) -> SymmetricKey { - let iterations: UInt8 = UInt8(ceil((Float(outputByteCount) / Float(H.Digest.byteCount)))) + + let iterations: UInt8 = UInt8((Double(outputByteCount) / Double(H.Digest.byteCount)).rounded(.up)) + var output = SecureBytes() let key = SymmetricKey(data: prk) var TMinusOne = SecureBytes() for i in 1...iterations { var hmac = HMAC(key: key) hmac.update(data: TMinusOne) - if let info = info { + if let info { hmac.update(data: info) } withUnsafeBytes(of: i) { counter in hmac.update(bufferPointer: counter) } - TMinusOne = SecureBytes(hmac.finalize()) + TMinusOne = SecureBytes(bytes: hmac.finalize()) output.append(TMinusOne) } diff --git a/Sources/Crypto/Key Wrapping/AESWrap.swift b/Sources/Crypto/Key Wrapping/AESWrap.swift index 50519d63..935a113e 100644 --- a/Sources/Crypto/Key Wrapping/AESWrap.swift +++ b/Sources/Crypto/Key Wrapping/AESWrap.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftCrypto open source project // -// Copyright (c) 2019-2021 Apple Inc. and the SwiftCrypto project authors +// Copyright (c) 2019-2020 Apple Inc. and the SwiftCrypto project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -14,9 +14,14 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias AESWRAPImpl = CoreCryptoAESWRAPImpl #else typealias AESWRAPImpl = BoringSSLAESWRAPImpl diff --git a/Sources/Crypto/Keys/EC/Ed25519Keys.swift b/Sources/Crypto/Keys/EC/Ed25519Keys.swift index 71e8ba45..1d72de96 100644 --- a/Sources/Crypto/Keys/EC/Ed25519Keys.swift +++ b/Sources/Crypto/Keys/EC/Ed25519Keys.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension Curve25519.Signing { static var keyByteCount: Int { @@ -26,7 +30,7 @@ extension Curve25519 { /// A mechanism used to create or verify a cryptographic signature using /// Ed25519. public enum Signing { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias Curve25519PrivateKeyImpl = Curve25519.Signing.CoreCryptoCurve25519PrivateKeyImpl typealias Curve25519PublicKeyImpl = Curve25519.Signing.CoreCryptoCurve25519PublicKeyImpl #else diff --git a/Sources/Crypto/Keys/EC/NISTCurvesKeys.swift b/Sources/Crypto/Keys/EC/NISTCurvesKeys.swift index fe0541e9..3cd7f086 100644 --- a/Sources/Crypto/Keys/EC/NISTCurvesKeys.swift +++ b/Sources/Crypto/Keys/EC/NISTCurvesKeys.swift @@ -11,12 +11,16 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else -#if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias SupportedCurveDetailsImpl = CorecryptoSupportedNISTCurve #else typealias SupportedCurveDetailsImpl = OpenSSLSupportedNISTCurve diff --git a/Sources/Crypto/Keys/EC/X25519Keys.swift b/Sources/Crypto/Keys/EC/X25519Keys.swift index acb04e9e..495e0d47 100644 --- a/Sources/Crypto/Keys/EC/X25519Keys.swift +++ b/Sources/Crypto/Keys/EC/X25519Keys.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif extension Curve25519.KeyAgreement { static var keyByteCount: Int { @@ -26,7 +30,7 @@ extension Curve25519 { /// A mechanism used to create a shared secret between two users by /// performing X25519 key agreement. public enum KeyAgreement { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION typealias Curve25519PrivateKeyImpl = Curve25519.KeyAgreement.CoreCryptoCurve25519PrivateKeyImpl typealias Curve25519PublicKeyImpl = Curve25519.KeyAgreement.CoreCryptoCurve25519PublicKeyImpl #else diff --git a/Sources/Crypto/Keys/Symmetric/SymmetricKeys.swift b/Sources/Crypto/Keys/Symmetric/SymmetricKeys.swift index 1696436d..2d059a81 100644 --- a/Sources/Crypto/Keys/Symmetric/SymmetricKeys.swift +++ b/Sources/Crypto/Keys/Symmetric/SymmetricKeys.swift @@ -14,7 +14,12 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else + +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif /// The sizes that a symmetric cryptographic key can take. /// diff --git a/Sources/Crypto/Message Authentication Codes/HMAC/HMAC.swift b/Sources/Crypto/Message Authentication Codes/HMAC/HMAC.swift index 53b1f15a..4baaea33 100644 --- a/Sources/Crypto/Message Authentication Codes/HMAC/HMAC.swift +++ b/Sources/Crypto/Message Authentication Codes/HMAC/HMAC.swift @@ -12,9 +12,17 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif /// A hash-based message authentication algorithm. /// diff --git a/Sources/Crypto/Message Authentication Codes/MACFunctions.swift b/Sources/Crypto/Message Authentication Codes/MACFunctions.swift index 012423dd..b51debe0 100644 --- a/Sources/Crypto/Message Authentication Codes/MACFunctions.swift +++ b/Sources/Crypto/Message Authentication Codes/MACFunctions.swift @@ -14,12 +14,20 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif protocol MACAlgorithm { associatedtype Key - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + #if CRYPTOKIT_STATIC_LIBRARY + associatedtype MAC: CryptoKit_Static.MessageAuthenticationCode + #else associatedtype MAC: CryptoKit.MessageAuthenticationCode + #endif #else associatedtype MAC: Crypto.MessageAuthenticationCode #endif diff --git a/Sources/Crypto/Message Authentication Codes/MessageAuthenticationCode.swift b/Sources/Crypto/Message Authentication Codes/MessageAuthenticationCode.swift index 10b6e12c..8e30b662 100644 --- a/Sources/Crypto/Message Authentication Codes/MessageAuthenticationCode.swift +++ b/Sources/Crypto/Message Authentication Codes/MessageAuthenticationCode.swift @@ -12,9 +12,17 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif /// A type that represents a message authentication code. public protocol MessageAuthenticationCode: Hashable, ContiguousBytes, CustomStringConvertible, Sequence where Element == UInt8 { @@ -59,7 +67,7 @@ extension MessageAuthenticationCode { return Array(buffPtr.bindMemory(to: UInt8.self)).makeIterator() }) } - + public var description: String { return "\(Self.self): \(Array(self).hexString)" } diff --git a/Sources/Crypto/Signatures/ECDSA.swift b/Sources/Crypto/Signatures/ECDSA.swift index a609aed3..fef7cee7 100644 --- a/Sources/Crypto/Signatures/ECDSA.swift +++ b/Sources/Crypto/Signatures/ECDSA.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if !CRYPTOKIT_NO_ACCESS_TO_FOUNDATION import Foundation +#else +import SwiftSystem +#endif // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. @@ -140,7 +144,7 @@ extension P256.Signing.PrivateKey: DigestSigner { /// algorithm employs randomization to generate a different signature on /// every call, even for the same data and key. public func signature(for digest: D) throws -> P256.Signing.ECDSASignature { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSignature(for: digest) #else return try self.openSSLSignature(for: digest) @@ -173,7 +177,7 @@ extension P256.Signing.PublicKey: DigestValidator { /// - Returns: A Boolean value that’s `true` if the signature is valid for /// the given digest; otherwise, `false`. public func isValidSignature(_ signature: P256.Signing.ECDSASignature, for digest: D) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return self.coreCryptoIsValidSignature(signature, for: digest) #else return self.openSSLIsValidSignature(signature, for: digest) @@ -303,7 +307,7 @@ extension P384.Signing.PrivateKey: DigestSigner { /// algorithm employs randomization to generate a different signature on /// every call, even for the same data and key. public func signature(for digest: D) throws -> P384.Signing.ECDSASignature { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSignature(for: digest) #else return try self.openSSLSignature(for: digest) @@ -336,7 +340,7 @@ extension P384.Signing.PublicKey: DigestValidator { /// - Returns: A Boolean value that’s `true` if the signature is valid for /// the given digest; otherwise, `false`. public func isValidSignature(_ signature: P384.Signing.ECDSASignature, for digest: D) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return self.coreCryptoIsValidSignature(signature, for: digest) #else return self.openSSLIsValidSignature(signature, for: digest) @@ -466,7 +470,7 @@ extension P521.Signing.PrivateKey: DigestSigner { /// algorithm employs randomization to generate a different signature on /// every call, even for the same data and key. public func signature(for digest: D) throws -> P521.Signing.ECDSASignature { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSignature(for: digest) #else return try self.openSSLSignature(for: digest) @@ -499,7 +503,7 @@ extension P521.Signing.PublicKey: DigestValidator { /// - Returns: A Boolean value that’s `true` if the signature is valid for /// the given digest; otherwise, `false`. public func isValidSignature(_ signature: P521.Signing.ECDSASignature, for digest: D) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return self.coreCryptoIsValidSignature(signature, for: digest) #else return self.openSSLIsValidSignature(signature, for: digest) diff --git a/Sources/Crypto/Signatures/ECDSA.swift.gyb b/Sources/Crypto/Signatures/ECDSA.swift.gyb index 56bceed8..d3a11eb5 100644 --- a/Sources/Crypto/Signatures/ECDSA.swift.gyb +++ b/Sources/Crypto/Signatures/ECDSA.swift.gyb @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if !CRYPTOKIT_NO_ACCESS_TO_FOUNDATION import Foundation +#else +import SwiftSystem +#endif // MARK: - Generated file, do NOT edit // any edits of this file WILL be overwritten and thus discarded // see section `gyb` in `README` for details. @@ -150,7 +154,7 @@ extension ${CURVE}.Signing.PrivateKey: DigestSigner { /// algorithm employs randomization to generate a different signature on /// every call, even for the same data and key. public func signature(for digest: D) throws -> ${CURVE}.Signing.ECDSASignature { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSignature(for: digest) #else return try self.openSSLSignature(for: digest) @@ -183,7 +187,7 @@ extension ${CURVE}.Signing.PublicKey: DigestValidator { /// - Returns: A Boolean value that’s `true` if the signature is valid for /// the given digest; otherwise, `false`. public func isValidSignature(_ signature: ${CURVE}.Signing.ECDSASignature, for digest: D) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return self.coreCryptoIsValidSignature(signature, for: digest) #else return self.openSSLIsValidSignature(signature, for: digest) diff --git a/Sources/Crypto/Signatures/Ed25519.swift b/Sources/Crypto/Signatures/Ed25519.swift index a5151361..fca8819d 100644 --- a/Sources/Crypto/Signatures/Ed25519.swift +++ b/Sources/Crypto/Signatures/Ed25519.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif protocol DigestValidator { associatedtype Signature @@ -44,7 +48,7 @@ extension Curve25519.Signing.PublicKey: DataValidator { /// - Returns: A Boolean value that’s `true` when the signature is valid for /// the given data. public func isValidSignature(_ signature: S, for data: D) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return self.coreCryptoIsValidSignature(signature, for: data) #else return self.openSSLIsValidSignature(signature, for: data) @@ -65,7 +69,7 @@ extension Curve25519.Signing.PrivateKey: Signer { /// different signature on every call, even for the same data and key, to /// guard against side-channel attacks. public func signature(for data: D) throws -> Data { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return try self.coreCryptoSignature(for: data) #else return try self.openSSLSignature(for: data) diff --git a/Sources/Crypto/Signatures/Signature.swift b/Sources/Crypto/Signatures/Signature.swift index 9aa952a6..e0f84e14 100644 --- a/Sources/Crypto/Signatures/Signature.swift +++ b/Sources/Crypto/Signatures/Signature.swift @@ -14,7 +14,11 @@ #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API @_exported import CryptoKit #else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif protocol SignatureVerification { func verifySignature(signature: Data, data: Data) throws -> Bool diff --git a/Sources/Crypto/Util/PrettyBytes.swift b/Sources/Crypto/Util/PrettyBytes.swift index 2733f9ed..7e39a052 100644 --- a/Sources/Crypto/Util/PrettyBytes.swift +++ b/Sources/Crypto/Util/PrettyBytes.swift @@ -11,7 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif enum ByteHexEncodingErrors: Error { case incorrectHexValue @@ -50,16 +54,17 @@ extension DataProtocol { } } - return String(bytes: hexChars, encoding: .utf8)! + return String(decoding: hexChars, as: UTF8.self) } } -extension MutableDataProtocol { +extension RangeReplaceableCollection where Element == UInt8 { mutating func appendByte(_ byte: UInt64) { - withUnsafePointer(to: byte.littleEndian, { self.append(contentsOf: UnsafeRawBufferPointer(start: $0, count: 8)) }) + withUnsafeBytes(of: byte.littleEndian, { self.append(contentsOf: $0) }) } } +#if !CRYPTOKIT_NO_ACCESS_TO_FOUNDATION extension Data { init(hexString: String) throws { self.init() @@ -98,3 +103,4 @@ extension Array where Element == UInt8 { } } +#endif diff --git a/Sources/Crypto/Util/SafeCompare.swift b/Sources/Crypto/Util/SafeCompare.swift index 97c80d1c..29f8d56f 100644 --- a/Sources/Crypto/Util/SafeCompare.swift +++ b/Sources/Crypto/Util/SafeCompare.swift @@ -12,12 +12,20 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif internal func safeCompare(_ lhs: LHS, _ rhs: RHS) -> Bool { - #if !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API + #if (!CRYPTO_IN_SWIFTPM_FORCE_BUILD_API) || CRYPTOKIT_NO_ACCESS_TO_FOUNDATION return coreCryptoSafeCompare(lhs, rhs) #else return openSSLSafeCompare(lhs, rhs) diff --git a/Sources/Crypto/Util/SecureBytes.swift b/Sources/Crypto/Util/SecureBytes.swift index b67f6970..513d2583 100644 --- a/Sources/Crypto/Util/SecureBytes.swift +++ b/Sources/Crypto/Util/SecureBytes.swift @@ -12,9 +12,17 @@ // //===----------------------------------------------------------------------===// #if CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API +#if CRYPTOKIT_STATIC_LIBRARY +@_exported import CryptoKit_Static +#else @_exported import CryptoKit +#endif +#else +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem #else import Foundation +#endif private let emptyStorage:SecureBytes.Backing = SecureBytes.Backing.createEmpty() @@ -170,11 +178,11 @@ extension SecureBytes: RangeReplaceableCollection { // The default implementation of this from RangeReplaceableCollection can't take advantage of `ContiguousBytes`, so we override it here @inlinable - public mutating func append(contentsOf newElements: Elements) where Elements.Element == UInt8 { + public mutating func append(contentsOf newElements: some Sequence) { let done:Void? = newElements.withContiguousStorageIfAvailable { replaceSubrange(endIndex..(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? { return try self.backing.withContiguousStorageIfAvailable(body) @@ -248,12 +256,12 @@ extension SecureBytes { @usableFromInline internal class Backing: ManagedBuffer { - + @usableFromInline class func createEmpty() -> Backing { return Backing.create(minimumCapacity: 0, makingHeaderWith: { _ in BackingHeader(count: 0, capacity: 0) }) as! Backing } - + @usableFromInline class func create(capacity: Int) -> Backing { let capacity = Int(UInt32(capacity).nextPowerOf2ClampedToMax()) @@ -445,7 +453,7 @@ extension SecureBytes.Backing: ContiguousBytes { return try body(UnsafeMutableRawBufferPointer(start: elementsPtr, count: capacity)) } } - + func withContiguousStorageIfAvailable(_ body: (UnsafeBufferPointer) throws -> R) rethrows -> R? { let count = self.count @@ -493,6 +501,12 @@ extension Data { /// This is our best-effort attempt to expose the data in an auto-zeroing fashion. Any mutating function called on /// the constructed `Data` object will cause the bytes to be copied out: we can't avoid that. init(_ secureBytes: SecureBytes) { + #if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + self = secureBytes.withUnsafeBytes { + // We make a mutable copy of this pointer here because we know Data won't write through it. + return Data($0) + } + #else // We need to escape into unmanaged land here in order to keep the backing storage alive. let unmanagedBacking = Unmanaged.passRetained(secureBytes.backing) @@ -502,6 +516,7 @@ extension Data { // We make a mutable copy of this pointer here because we know Data won't write through it. return Data(bytesNoCopy: UnsafeMutableRawPointer(mutating: $0.baseAddress!), count: $0.count, deallocator: .custom { (_: UnsafeMutableRawPointer, _: Int) in unmanagedBacking.release() }) } + #endif } /// A custom initializer for Data that attempts to share the same storage as the current SecureBytes instance. @@ -513,7 +528,16 @@ extension Data { let base = secureByteSlice.base let baseOffset = secureByteSlice.startIndex.offset let endOffset = secureByteSlice.endIndex.offset + + #if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION + self = base.withUnsafeBytes { + // Slice the base pointer down to just the range we want. + let slicedPointer = UnsafeRawBufferPointer(rebasing: $0[baseOffset.. Bool { - switch (lhs, rhs) { - case (.incorrectKeySize, .incorrectKeySize): - return true - case (.incorrectParameterSize, .incorrectParameterSize): - return true - case (.authenticationFailure, .authenticationFailure): - return true - case (.wrapFailure, .wrapFailure): - return true - case (.unwrapFailure, .unwrapFailure): - return true - case (.underlyingCoreCryptoError(let lhsError), .underlyingCoreCryptoError(let rhsError)): - return lhsError == rhsError - default: - return false - } - } -} - -#endif // Linux or !SwiftPM diff --git a/Tests/CryptoTests/SecureBytes/SecureBytesTests.swift b/Tests/CryptoTests/SecureBytes/SecureBytesTests.swift index 031bc1ce..b41c1cfd 100644 --- a/Tests/CryptoTests/SecureBytes/SecureBytesTests.swift +++ b/Tests/CryptoTests/SecureBytes/SecureBytesTests.swift @@ -172,12 +172,7 @@ final class SecureBytesTests: XCTestCase { throw CryptoKitError.incorrectKeySize } } - XCTAssertThrowsError(try testThrowingInitialization()) { error in - guard case .some(.incorrectKeySize) = error as? CryptoKitError else { - XCTFail("unexpected error: \(error)") - return - } - } + XCTAssertThrowsError(try testThrowingInitialization(), error: CryptoKitError.incorrectKeySize) } func testAppendingDataPerformsACoW() { diff --git a/Tests/CryptoTests/Signatures/ECDSA/ECDSASignatureTests.swift b/Tests/CryptoTests/Signatures/ECDSA/ECDSASignatureTests.swift index 6dbb64ef..25009685 100644 --- a/Tests/CryptoTests/Signatures/ECDSA/ECDSASignatureTests.swift +++ b/Tests/CryptoTests/Signatures/ECDSA/ECDSASignatureTests.swift @@ -226,26 +226,12 @@ class SignatureTests: XCTestCase { } func testProperSignatureSizes() throws { - XCTAssertThrowsError(try P256.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8))) { error in - guard case .some(.incorrectParameterSize) = error as? CryptoKitError else { - XCTFail("Incorrect error: \(error)") - return - } - } - - XCTAssertThrowsError(try P384.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8))) { error in - guard case .some(.incorrectParameterSize) = error as? CryptoKitError else { - XCTFail("Incorrect error: \(error)") - return - } - } - - XCTAssertThrowsError(try P521.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8))) { error in - guard case .some(.incorrectParameterSize) = error as? CryptoKitError else { - XCTFail("Incorrect error: \(error)") - return - } - } + XCTAssertThrowsError(try P256.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8)), + error: CryptoKitError.incorrectParameterSize) + XCTAssertThrowsError(try P384.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8)), + error: CryptoKitError.incorrectParameterSize) + XCTAssertThrowsError(try P521.Signing.ECDSASignature(rawRepresentation: Array("hello".utf8)), + error: CryptoKitError.incorrectParameterSize) } func testP256SigningDiscontiguousData() throws { diff --git a/Tests/CryptoTests/Signatures/EdDSA/Ed25519-Runner.swift b/Tests/CryptoTests/Signatures/EdDSA/Ed25519-Runner.swift index dc33f013..47d93681 100644 --- a/Tests/CryptoTests/Signatures/EdDSA/Ed25519-Runner.swift +++ b/Tests/CryptoTests/Signatures/EdDSA/Ed25519-Runner.swift @@ -57,7 +57,7 @@ class Ed25519Tests: XCTestCase { let signatureOnContiguous = try orFail { try privateKey.signature(for: someContiguousData) } let signatureOnDiscontiguous = try orFail { try privateKey.signature(for: someDiscontiguousData) } - #if !(canImport(Darwin)) + #if !canImport(Darwin) XCTAssertEqual(signatureOnContiguous, signatureOnDiscontiguous) #endif diff --git a/Tests/CryptoTests/Utils/PrettyBytes.swift b/Tests/CryptoTests/Utils/PrettyBytes.swift index 2733f9ed..7e39a052 100644 --- a/Tests/CryptoTests/Utils/PrettyBytes.swift +++ b/Tests/CryptoTests/Utils/PrettyBytes.swift @@ -11,7 +11,11 @@ // SPDX-License-Identifier: Apache-2.0 // //===----------------------------------------------------------------------===// +#if CRYPTOKIT_NO_ACCESS_TO_FOUNDATION +import SwiftSystem +#else import Foundation +#endif enum ByteHexEncodingErrors: Error { case incorrectHexValue @@ -50,16 +54,17 @@ extension DataProtocol { } } - return String(bytes: hexChars, encoding: .utf8)! + return String(decoding: hexChars, as: UTF8.self) } } -extension MutableDataProtocol { +extension RangeReplaceableCollection where Element == UInt8 { mutating func appendByte(_ byte: UInt64) { - withUnsafePointer(to: byte.littleEndian, { self.append(contentsOf: UnsafeRawBufferPointer(start: $0, count: 8)) }) + withUnsafeBytes(of: byte.littleEndian, { self.append(contentsOf: $0) }) } } +#if !CRYPTOKIT_NO_ACCESS_TO_FOUNDATION extension Data { init(hexString: String) throws { self.init() @@ -98,3 +103,4 @@ extension Array where Element == UInt8 { } } +#endif diff --git a/Tests/CryptoTests/Utils/XCTestUtils.swift b/Tests/CryptoTests/Utils/XCTestUtils.swift index 01ff197a..69e4b907 100644 --- a/Tests/CryptoTests/Utils/XCTestUtils.swift +++ b/Tests/CryptoTests/Utils/XCTestUtils.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// import XCTest +import Crypto // Xcode 11.4 catches errors thrown during tests and reports them on the // correct line. But Linux and older Xcodes do not, so we need to use this @@ -52,5 +53,17 @@ extension XCTestCase { return wrapped } +} +func XCTAssertThrowsError( + _ expression: @autoclosure () throws -> T, + error: E, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line) +{ + XCTAssertThrowsError(try expression(), message(), file: file, line: line) { foundError in + XCTAssertEqual(foundError as? E, error, message(), file: file, line: line) + } } +