Skip to content

Commit 4832fa0

Browse files
authored
Update Swift Crypto for the Ventura beta API (#123)
* Bring over API files from macOS Ventura * Implement the new API surface This patch implements the new API surface, and removes anything that's no longer compatible with the expected API surface from CryptoKit
1 parent 126981a commit 4832fa0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+2755
-360
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ extension AES.GCM {
116116
self.combinedRepresentation = combined
117117
self.nonceByteCount = AES.GCM.defaultNonceByteCount
118118
}
119+
120+
internal init(combined: Data, nonceByteCount: Int) {
121+
self.combinedRepresentation = combined
122+
self.nonceByteCount = nonceByteCount
123+
}
119124

120125
@inlinable
121126
public init<D: DataProtocol>(combined: D) throws {

Sources/Crypto/AEADs/ChachaPoly/ChaChaPoly.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@ extension ChaChaPoly {
122122

123123
self.combined = Data(nonce) + ciphertext + tag
124124
}
125+
126+
internal init(combined: Data, nonceByteCount: Int) {
127+
assert(nonceByteCount == ChaChaPoly.nonceByteCount)
128+
self.combined = combined
129+
}
125130
}
126131
}
127132
#endif // Linux or !SwiftPM

Sources/Crypto/AEADs/Nonces.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ extension AES.GCM {
2626

2727
/// Generates a fresh random Nonce. Unless required by a specification to provide a specific Nonce, this is the recommended initializer.
2828
public init() {
29-
try! self.init(data: SecureBytes(count: AES.GCM.defaultNonceByteCount))
29+
var data = Data(repeating: 0, count: AES.GCM.defaultNonceByteCount)
30+
data.withUnsafeMutableBytes {
31+
assert($0.count == AES.GCM.defaultNonceByteCount)
32+
$0.initializeWithRandomBytes(count: AES.GCM.defaultNonceByteCount)
33+
}
34+
self.bytes = data
3035
}
3136

3237
public init<D: DataProtocol>(data: D) throws {
@@ -56,7 +61,12 @@ extension ChaChaPoly {
5661

5762
/// Generates a fresh random Nonce. Unless required by a specification to provide a specific Nonce, this is the recommended initializer.
5863
public init() {
59-
try! self.init(data: SecureBytes(count: ChaChaPoly.nonceByteCount))
64+
var data = Data(repeating: 0, count: ChaChaPoly.nonceByteCount)
65+
data.withUnsafeMutableBytes {
66+
assert($0.count == ChaChaPoly.nonceByteCount)
67+
$0.initializeWithRandomBytes(count: ChaChaPoly.nonceByteCount)
68+
}
69+
self.bytes = data
6070
}
6171

6272
public init<D: DataProtocol>(data: D) throws {

Sources/Crypto/AEADs/Nonces.swift.gyb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ extension ${name} {
3535

3636
/// Generates a fresh random Nonce. Unless required by a specification to provide a specific Nonce, this is the recommended initializer.
3737
public init() {
38-
try! self.init(data: SecureBytes(count: ${nonceSize}))
38+
var data = Data(repeating: 0, count: ${nonceSize})
39+
data.withUnsafeMutableBytes {
40+
assert($0.count == ${nonceSize})
41+
$0.initializeWithRandomBytes(count: ${nonceSize})
42+
}
43+
self.bytes = data
3944
}
4045

4146
public init<D: DataProtocol>(data: D) throws {

Sources/Crypto/ASN1/ASN1.swift

Lines changed: 147 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ extension ASN1.ASN1ParserNode: CustomStringConvertible {
132132
}
133133
}
134134

135-
// MARK: - Sequence
135+
// MARK: - Sequence, SequenceOf, and Set
136136
extension ASN1 {
137137
/// Parse the node as an ASN.1 sequence.
138-
internal static func sequence<T>(_ node: ASN1Node, _ builder: (inout ASN1.ASN1NodeCollection.Iterator) throws -> T) throws -> T {
139-
guard node.identifier == .sequence, case .constructed(let nodes) = node.content else {
138+
internal static func sequence<T>(_ node: ASN1Node, identifier: ASN1.ASN1Identifier, _ builder: (inout ASN1.ASN1NodeCollection.Iterator) throws -> T) throws -> T {
139+
guard node.identifier == identifier, case .constructed(let nodes) = node.content else {
140140
throw CryptoKitASN1Error.unexpectedFieldType
141141
}
142142

@@ -150,6 +150,29 @@ extension ASN1 {
150150

151151
return result
152152
}
153+
154+
internal static func sequence<T: ASN1Parseable>(of: T.Type = T.self, identifier: ASN1.ASN1Identifier, rootNode: ASN1Node) throws -> [T] {
155+
guard rootNode.identifier == identifier, case .constructed(let nodes) = rootNode.content else {
156+
throw CryptoKitASN1Error.unexpectedFieldType
157+
}
158+
159+
return try nodes.map { try T(asn1Encoded: $0) }
160+
}
161+
162+
internal static func sequence<T: ASN1Parseable>(of: T.Type = T.self, identifier: ASN1.ASN1Identifier, nodes: inout ASN1.ASN1NodeCollection.Iterator) throws -> [T] {
163+
guard let node = nodes.next() else {
164+
// Not present, throw.
165+
throw CryptoKitASN1Error.invalidASN1Object
166+
}
167+
168+
return try sequence(of: T.self, identifier: identifier, rootNode: node)
169+
}
170+
171+
/// Parse the node as an ASN.1 set.
172+
internal static func set<T>(_ node: ASN1Node, identifier: ASN1.ASN1Identifier, _ builder: (inout ASN1.ASN1NodeCollection.Iterator) throws -> T) throws -> T {
173+
// Shhhh these two are secretly the same with identifier.
174+
return try sequence(node, identifier: identifier, builder)
175+
}
153176
}
154177

155178
// MARK: - Optional explicitly tagged
@@ -189,6 +212,54 @@ extension ASN1 {
189212
}
190213
}
191214

215+
// MARK: - DEFAULT
216+
extension ASN1 {
217+
/// Parses a value that is encoded with a DEFAULT. Such a value is optional, and if absent will
218+
/// be replaced with its default.
219+
///
220+
/// Expects to be used with the `ASN1.sequence` helper function.
221+
internal static func decodeDefault<T: ASN1Parseable & Equatable>(_ nodes: inout ASN1.ASN1NodeCollection.Iterator, identifier: ASN1.ASN1Identifier, defaultValue: T, _ builder: (ASN1Node) throws -> T) throws -> T {
222+
// A weird trick here: we only want to consume the next node _if_ it has the right tag. To achieve that,
223+
// we work on a copy.
224+
var localNodesCopy = nodes
225+
guard let node = localNodesCopy.next() else {
226+
// Whoops, nothing here.
227+
return defaultValue
228+
}
229+
230+
guard node.identifier == identifier else {
231+
// Node is a mismatch, with the wrong identifier. Our optional isn't present.
232+
return defaultValue
233+
}
234+
235+
// We have the right optional, so let's consume it.
236+
nodes = localNodesCopy
237+
let parsed = try builder(node)
238+
239+
// DER forbids encoding DEFAULT values at their default state.
240+
// We can lift this in BER.
241+
guard parsed != defaultValue else {
242+
throw CryptoKitASN1Error.invalidASN1Object
243+
}
244+
245+
return parsed
246+
}
247+
248+
internal static func decodeDefaultExplicitlyTagged<T: ASN1Parseable & Equatable>(_ nodes: inout ASN1.ASN1NodeCollection.Iterator, tagNumber: Int, tagClass: ASN1.ASN1Identifier.TagClass, defaultValue: T, _ builder: (ASN1Node) throws -> T) throws -> T {
249+
if let result = try optionalExplicitlyTagged(&nodes, tagNumber: tagNumber, tagClass: tagClass, builder) {
250+
guard result != defaultValue else {
251+
// DER forbids encoding DEFAULT values at their default state.
252+
// We can lift this in BER.
253+
throw CryptoKitASN1Error.invalidASN1Object
254+
}
255+
256+
return result
257+
} else {
258+
return defaultValue
259+
}
260+
}
261+
}
262+
192263
// MARK: - Parsing
193264
extension ASN1 {
194265
/// A parsed representation of ASN.1.
@@ -369,7 +440,7 @@ extension ASN1.ASN1Node {
369440
// MARK: - Serializing
370441
extension ASN1 {
371442
struct Serializer {
372-
private(set) var serializedBytes: [UInt8]
443+
var serializedBytes: [UInt8]
373444

374445
init() {
375446
// We allocate a 1kB array because that should cover us most of the time.
@@ -393,9 +464,37 @@ extension ASN1 {
393464
}
394465

395466
mutating func serialize<T: ASN1Serializable>(_ node: T, explicitlyTaggedWithTagNumber tagNumber: Int, tagClass: ASN1.ASN1Identifier.TagClass) throws {
467+
return try self.serialize(explicitlyTaggedWithTagNumber: tagNumber, tagClass: tagClass) { coder in
468+
try coder.serialize(node)
469+
}
470+
}
471+
472+
mutating func serialize(explicitlyTaggedWithTagNumber tagNumber: Int, tagClass: ASN1.ASN1Identifier.TagClass, _ block: (inout Serializer) throws -> Void) rethrows {
396473
let identifier = ASN1Identifier(explicitTagWithNumber: tagNumber, tagClass: tagClass)
397474
try self.appendConstructedNode(identifier: identifier) { coder in
398-
try coder.serialize(node)
475+
try block(&coder)
476+
}
477+
}
478+
479+
mutating func serializeSequenceOf<Elements: Sequence>(_ elements: Elements, identifier: ASN1.ASN1Identifier = .sequence) throws where Elements.Element: ASN1Serializable {
480+
try self.appendConstructedNode(identifier: identifier) { coder in
481+
for element in elements {
482+
try coder.serialize(element)
483+
}
484+
}
485+
}
486+
487+
mutating func serialize(_ node: ASN1.ASN1Node) {
488+
let identifier = node.identifier
489+
self._appendNode(identifier: identifier) { coder in
490+
switch node.content {
491+
case .constructed(let nodes):
492+
for node in nodes {
493+
coder.serialize(node)
494+
}
495+
case .primitive(let baseData):
496+
coder.serializedBytes.append(contentsOf: baseData)
497+
}
399498
}
400499
}
401500

@@ -438,7 +537,7 @@ extension ASN1 {
438537
for shift in (0..<(lengthBytesNeeded - 1)).reversed() {
439538
// Shift and mask the integer.
440539
self.serializedBytes.formIndex(after: &writeIndex)
441-
self.serializedBytes[writeIndex] = UInt8(truncatingIfNeeded: contentLength >> (shift * 8))
540+
self.serializedBytes[writeIndex] = UInt8(truncatingIfNeeded: (contentLength >> (shift * 8)))
442541
}
443542

444543
assert(writeIndex == self.serializedBytes.index(lengthIndex, offsetBy: lengthBytesNeeded - 1))
@@ -463,12 +562,54 @@ extension ASN1Parseable {
463562
internal init(asn1Encoded: [UInt8]) throws {
464563
self = try .init(asn1Encoded: ASN1.parse(asn1Encoded))
465564
}
565+
566+
internal init(asn1Encoded: ArraySlice<UInt8>) throws {
567+
self = try .init(asn1Encoded: ASN1.parse(asn1Encoded))
568+
}
466569
}
467570

468571
internal protocol ASN1Serializable {
469572
func serialize(into coder: inout ASN1.Serializer) throws
470573
}
471574

575+
/// Covers ASN.1 types that may be implicitly tagged. Not all nodes can be!
576+
internal protocol ASN1ImplicitlyTaggable: ASN1Parseable, ASN1Serializable {
577+
/// The tag that the first node will use "by default" if the grammar omits
578+
/// any more specific tag definition.
579+
static var defaultIdentifier: ASN1.ASN1Identifier { get }
580+
581+
init(asn1Encoded: ASN1.ASN1Node, withIdentifier identifier: ASN1.ASN1Identifier) throws
582+
583+
func serialize(into coder: inout ASN1.Serializer, withIdentifier identifier: ASN1.ASN1Identifier) throws
584+
}
585+
586+
extension ASN1ImplicitlyTaggable {
587+
internal init(asn1Encoded sequenceNodeIterator: inout ASN1.ASN1NodeCollection.Iterator,
588+
withIdentifier identifier: ASN1.ASN1Identifier = Self.defaultIdentifier) throws {
589+
guard let node = sequenceNodeIterator.next() else {
590+
throw CryptoKitASN1Error.invalidASN1Object
591+
}
592+
593+
self = try .init(asn1Encoded: node, withIdentifier: identifier)
594+
}
595+
596+
internal init(asn1Encoded: [UInt8], withIdentifier identifier: ASN1.ASN1Identifier = Self.defaultIdentifier) throws {
597+
self = try .init(asn1Encoded: ASN1.parse(asn1Encoded), withIdentifier: identifier)
598+
}
599+
600+
internal init(asn1Encoded: ArraySlice<UInt8>, withIdentifier identifier: ASN1.ASN1Identifier = Self.defaultIdentifier) throws {
601+
self = try .init(asn1Encoded: ASN1.parse(asn1Encoded), withIdentifier: identifier)
602+
}
603+
604+
init(asn1Encoded: ASN1.ASN1Node) throws {
605+
try self.init(asn1Encoded: asn1Encoded, withIdentifier: Self.defaultIdentifier)
606+
}
607+
608+
func serialize(into coder: inout ASN1.Serializer) throws {
609+
try self.serialize(into: &coder, withIdentifier: Self.defaultIdentifier)
610+
}
611+
}
612+
472613
extension ArraySlice where Element == UInt8 {
473614
fileprivate mutating func readASN1Length() throws -> UInt? {
474615
guard let firstByte = self.popFirst() else {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftCrypto open source project
4+
//
5+
// Copyright (c) 2019-2020 Apple Inc. and the SwiftCrypto project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.md for the list of SwiftCrypto project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
#if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) && CRYPTO_IN_SWIFTPM && !CRYPTO_IN_SWIFTPM_FORCE_BUILD_API
15+
@_exported import CryptoKit
16+
#else
17+
import Foundation
18+
19+
extension ASN1 {
20+
/// An ASN1 ANY represents...well, anything.
21+
///
22+
/// In this case we store the ASN.1 ANY as a serialized representation. This is a bit annoying,
23+
/// but it's the only safe way to manage this data, as we cannot arbitrarily parse it.
24+
///
25+
/// The only things we allow users to do with ASN.1 ANYs is to try to decode them as something else,
26+
/// to create them from something else, or to serialize them.
27+
struct ASN1Any: ASN1Parseable, ASN1Serializable, Hashable {
28+
fileprivate var serializedBytes: ArraySlice<UInt8>
29+
30+
init<ASN1Type: ASN1Serializable>(erasing: ASN1Type) throws {
31+
var serializer = ASN1.Serializer()
32+
try erasing.serialize(into: &serializer)
33+
self.serializedBytes = ArraySlice(serializer.serializedBytes)
34+
}
35+
36+
init<ASN1Type: ASN1ImplicitlyTaggable>(erasing: ASN1Type, withIdentifier identifier: ASN1.ASN1Identifier) throws {
37+
var serializer = ASN1.Serializer()
38+
try erasing.serialize(into: &serializer, withIdentifier: identifier)
39+
self.serializedBytes = ArraySlice(serializer.serializedBytes)
40+
}
41+
42+
init(asn1Encoded rootNode: ASN1.ASN1Node) {
43+
// This is a bit sad: we just re-serialize this data. In an ideal world
44+
// we'd update the parse representation so that all nodes can point at their
45+
// complete backing storage, but for now this is better.
46+
var serializer = ASN1.Serializer()
47+
serializer.serialize(rootNode)
48+
self.serializedBytes = ArraySlice(serializer.serializedBytes)
49+
}
50+
51+
func serialize(into coder: inout ASN1.Serializer) throws {
52+
// Dangerous to just reach in there like this, but it's the right way to serialize this.
53+
coder.serializedBytes.append(contentsOf: self.serializedBytes)
54+
}
55+
}
56+
}
57+
58+
extension ASN1Parseable {
59+
init(asn1Any: ASN1.ASN1Any) throws {
60+
try self.init(asn1Encoded: asn1Any.serializedBytes)
61+
}
62+
}
63+
64+
extension ASN1ImplicitlyTaggable {
65+
init(asn1Any: ASN1.ASN1Any, withIdentifier identifier: ASN1.ASN1Identifier) throws {
66+
try self.init(asn1Encoded: asn1Any.serializedBytes, withIdentifier: identifier)
67+
}
68+
}
69+
70+
#endif // Linux or !SwiftPM

Sources/Crypto/ASN1/Basic ASN1 Types/ASN1BitString.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ import Foundation
1818

1919
extension ASN1 {
2020
/// A bitstring is a representation of...well...some bits.
21-
struct ASN1BitString: ASN1Parseable, ASN1Serializable {
21+
struct ASN1BitString: ASN1ImplicitlyTaggable {
22+
static var defaultIdentifier: ASN1.ASN1Identifier {
23+
.primitiveBitString
24+
}
25+
2226
var bytes: ArraySlice<UInt8>
2327

24-
init(asn1Encoded node: ASN1.ASN1Node) throws {
25-
guard node.identifier == .primitiveBitString else {
28+
init(asn1Encoded node: ASN1.ASN1Node, withIdentifier identifier: ASN1.ASN1Identifier) throws {
29+
guard node.identifier == identifier else {
2630
throw CryptoKitASN1Error.unexpectedFieldType
2731
}
2832

@@ -43,8 +47,8 @@ extension ASN1 {
4347
self.bytes = bytes
4448
}
4549

46-
func serialize(into coder: inout ASN1.Serializer) throws {
47-
coder.appendPrimitiveNode(identifier: .primitiveBitString) { bytes in
50+
func serialize(into coder: inout ASN1.Serializer, withIdentifier identifier: ASN1.ASN1Identifier) throws {
51+
coder.appendPrimitiveNode(identifier: identifier) { bytes in
4852
bytes.append(0)
4953
bytes.append(contentsOf: self.bytes)
5054
}

0 commit comments

Comments
 (0)