Skip to content

Commit 0e04828

Browse files
authored
[Collections] Signing (apple, 1): certificate and key types (#3259)
This is part 1 of a series of PRs to support package collection signing on **Apple** platforms. Originally #3238. Modifications: - New `PackageCollectionsSigning` module - Add `Certificate` type - Add EC and RSA key types
1 parent 3a85331 commit 0e04828

25 files changed

+1935
-13
lines changed
559 Bytes
Binary file not shown.
923 Bytes
Binary file not shown.

Package.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,16 @@ let package = Package(
152152
/** Package collections models */
153153
name: "PackageCollectionsModel",
154154
dependencies: []),
155+
156+
.target(
157+
/** Package collections signing */
158+
name: "PackageCollectionsSigning",
159+
dependencies: ["Crypto"]),
155160

156161
.target(
157162
/** Data structures and support for package collections */
158163
name: "PackageCollections",
159-
dependencies: ["SwiftToolsSupport-auto", "Basics", "PackageModel", "SourceControl", "PackageCollectionsModel", "Crypto"]),
164+
dependencies: ["SwiftToolsSupport-auto", "Basics", "PackageModel", "SourceControl", "PackageCollectionsModel"]),
160165

161166
// MARK: Package Manager Functionality
162167

@@ -265,9 +270,12 @@ let package = Package(
265270
.testTarget(
266271
name: "PackageCollectionsModelTests",
267272
dependencies: ["PackageCollectionsModel"]),
273+
.testTarget(
274+
name: "PackageCollectionsSigningTests",
275+
dependencies: ["PackageCollectionsSigning", "SPMTestSupport"]),
268276
.testTarget(
269277
name: "PackageCollectionsTests",
270-
dependencies: ["SPMTestSupport", "PackageCollections"]),
278+
dependencies: ["PackageCollections", "SPMTestSupport"]),
271279
.testTarget(
272280
name: "SourceControlTests",
273281
dependencies: ["SourceControl", "SPMTestSupport"]),

Sources/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This source file is part of the Swift.org open source project
22
#
3-
# Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
3+
# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
44
# Licensed under Apache License v2.0 with Runtime Library Exception
55
#
66
# See http://swift.org/LICENSE.txt for license information
@@ -12,6 +12,7 @@ add_subdirectory(Commands)
1212
add_subdirectory(LLBuildManifest)
1313
add_subdirectory(PackageCollections)
1414
add_subdirectory(PackageCollectionsModel)
15+
add_subdirectory(PackageCollectionsSigning)
1516
add_subdirectory(PackageDescription)
1617
add_subdirectory(PackageGraph)
1718
add_subdirectory(PackageLoading)

Sources/PackageCollections/CMakeLists.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,11 @@ target_link_libraries(PackageCollections PUBLIC
3232
TSCBasic
3333
TSCUtility
3434
Basics
35-
Crypto
36-
CCryptoBoringSSL
3735
PackageModel
3836
SourceControl)
3937
# NOTE(compnerd) workaround for CMake not setting up include flags yet
4038
set_target_properties(PackageCollections PROPERTIES
4139
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
42-
target_link_options(PackageCollections PRIVATE
43-
"$<$<PLATFORM_ID:Darwin>:SHELL:-Xlinker -framework -Xlinker Security>")
4440

4541
if(USE_CMAKE_INSTALL)
4642
install(TARGETS PackageCollections
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# This source file is part of the Swift.org open source project
2+
#
3+
# Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
# Licensed under Apache License v2.0 with Runtime Library Exception
5+
#
6+
# See http://swift.org/LICENSE.txt for license information
7+
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
8+
9+
add_library(PackageCollectionsSigning
10+
Certificate/Certificate.swift
11+
Key/ASN1/ASN1.swift
12+
Key/ASN1/ASN1Error.swift
13+
Key/ASN1/PEMDocument.swift
14+
Key/ASN1/SEC1PrivateKey.swift
15+
Key/ASN1/SubjectPublicKeyInfo.swift
16+
Key/ASN1/Types/ASN1BitString.swift
17+
Key/ASN1/Types/ASN1Identifier.swift
18+
Key/ASN1/Types/ASN1Integer.swift
19+
Key/ASN1/Types/ASN1ObjectIdentifier.swift
20+
Key/ASN1/Types/ASN1OctetString.swift
21+
Key/Key.swift
22+
Key/Key+EC.swift
23+
Key/Key+RSA.swift
24+
Utilities/Utilities.swift)
25+
target_link_libraries(PackageCollectionsSigning PUBLIC
26+
$<$<NOT:$<PLATFORM_ID:Darwin>>:dispatch>
27+
$<$<NOT:$<PLATFORM_ID:Darwin>>:Foundation>)
28+
target_link_libraries(PackageCollectionsSigning PRIVATE
29+
Crypto)
30+
# NOTE(compnerd) workaround for CMake not setting up include flags yet
31+
set_target_properties(PackageCollectionsSigning PROPERTIES
32+
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
33+
target_link_options(PackageCollectionsSigning PRIVATE
34+
"$<$<PLATFORM_ID:Darwin>:SHELL:-Xlinker -framework -Xlinker Security>")
35+
36+
if(USE_CMAKE_INSTALL)
37+
install(TARGETS PackageCollectionsSigning
38+
ARCHIVE DESTINATION lib
39+
LIBRARY DESTINATION lib
40+
RUNTIME DESTINATION bin)
41+
endif()
42+
set_property(GLOBAL APPEND PROPERTY SwiftPM_EXPORTS PackageCollectionsSigning)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2021 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See http://swift.org/LICENSE.txt for license information
8+
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
*/
10+
11+
import struct Foundation.Data
12+
13+
#if canImport(Security)
14+
import Security
15+
#endif
16+
17+
#if canImport(Security)
18+
typealias Certificate = CoreCertificate
19+
#else
20+
typealias Certificate = BoringSSLCertificate
21+
#endif
22+
23+
// MARK: - Certificate implementation using the Security framework
24+
25+
#if canImport(Security)
26+
struct CoreCertificate {
27+
let underlying: SecCertificate
28+
29+
init(derEncoded data: Data) throws {
30+
guard let certificate = SecCertificateCreateWithData(nil, data as CFData) else {
31+
throw CertificateError.initializationFailure
32+
}
33+
self.underlying = certificate
34+
}
35+
36+
func subject() throws -> CertificateName {
37+
try self.extractName(kSecOIDX509V1SubjectName)
38+
}
39+
40+
func issuer() throws -> CertificateName {
41+
try self.extractName(kSecOIDX509V1IssuerName)
42+
}
43+
44+
private func extractName(_ name: CFString) throws -> CertificateName {
45+
guard let dict = SecCertificateCopyValues(self.underlying, [name] as CFArray, nil) as? [CFString: Any],
46+
let nameDict = dict[name] as? [CFString: Any],
47+
let propValueList = nameDict[kSecPropertyKeyValue] as? [[String: Any]] else {
48+
throw CertificateError.nameExtractionFailure
49+
}
50+
51+
let props = propValueList.reduce(into: [String: String]()) { result, item in
52+
if let label = item["label"] as? String, let value = item["value"] as? String {
53+
result[label] = value
54+
}
55+
}
56+
57+
return CertificateName(
58+
userID: props["0.9.2342.19200300.100.1.1"], // FIXME: don't hardcode?
59+
commonName: props[kSecOIDCommonName as String],
60+
organization: props[kSecOIDOrganizationName as String],
61+
organizationalUnit: props[kSecOIDOrganizationalUnitName as String]
62+
)
63+
}
64+
65+
func publicKey() throws -> PublicKey {
66+
guard let key = SecCertificateCopyKey(self.underlying) else {
67+
throw CertificateError.keyExtractionFailure
68+
}
69+
70+
var error: Unmanaged<CFError>?
71+
guard let data = SecKeyCopyExternalRepresentation(key, &error) as Data? else {
72+
throw error.map { $0.takeRetainedValue() as Error } ?? CertificateError.keyExtractionFailure
73+
}
74+
75+
switch try self.keyType(of: key) {
76+
case .RSA:
77+
return try CoreRSAPublicKey(data: data)
78+
case .EC:
79+
return try ECPublicKey(data: data)
80+
}
81+
}
82+
83+
func keyType() throws -> KeyType {
84+
guard let key = SecCertificateCopyKey(self.underlying) else {
85+
throw CertificateError.keyExtractionFailure
86+
}
87+
return try self.keyType(of: key)
88+
}
89+
90+
private func keyType(of key: SecKey) throws -> KeyType {
91+
guard let attributes = SecKeyCopyAttributes(key) as? [CFString: Any],
92+
let keyType = attributes[kSecAttrKeyType] as? String else {
93+
throw CertificateError.indeterminateKeyType
94+
}
95+
96+
if keyType == (kSecAttrKeyTypeRSA as String) {
97+
return .RSA
98+
} else if keyType == (kSecAttrKeyTypeEC as String) {
99+
return .EC
100+
} else {
101+
throw CertificateError.unsupportedKeyType
102+
}
103+
}
104+
}
105+
106+
// MARK: - Certificate implementation using BoringSSL
107+
108+
#else
109+
final class BoringSSLCertificate {
110+
init(derEncoded data: Data) throws {
111+
fatalError("Not implemented: \(#function)")
112+
}
113+
114+
func subject() throws -> CertificateName {
115+
fatalError("Not implemented: \(#function)")
116+
}
117+
118+
func issuer() throws -> CertificateName {
119+
fatalError("Not implemented: \(#function)")
120+
}
121+
122+
func publicKey() throws -> PublicKey {
123+
fatalError("Not implemented: \(#function)")
124+
}
125+
126+
func keyType() throws -> KeyType {
127+
fatalError("Not implemented: \(#function)")
128+
}
129+
}
130+
#endif
131+
132+
struct CertificateName {
133+
let userID: String?
134+
let commonName: String?
135+
let organization: String?
136+
let organizationalUnit: String?
137+
}
138+
139+
enum CertificateError: Error {
140+
case initializationFailure
141+
case nameExtractionFailure
142+
case keyExtractionFailure
143+
case indeterminateKeyType
144+
case unsupportedKeyType
145+
}

0 commit comments

Comments
 (0)