Skip to content

Commit 26870cf

Browse files
committed
[Collections] Signing (all, 1): certificate and key types
This is part 1 of a series of PRs to support package collection signing on **all** platforms. Originally swiftlang#3238. Depends on swiftlang#3259
1 parent 6af862d commit 26870cf

File tree

4 files changed

+223
-15
lines changed

4 files changed

+223
-15
lines changed

Sources/PackageCollectionsSigning/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ add_library(PackageCollectionsSigning
1919
Key/ASN1/Types/ASN1Integer.swift
2020
Key/ASN1/Types/ASN1ObjectIdentifier.swift
2121
Key/ASN1/Types/ASN1OctetString.swift
22+
Key/BoringSSLKey.swift
2223
Key/Key.swift
2324
Key/Key+EC.swift
2425
Key/Key+RSA.swift
@@ -36,7 +37,8 @@ target_link_libraries(PackageCollectionsSigning PUBLIC
3637
$<$<NOT:$<PLATFORM_ID:Darwin>>:dispatch>
3738
$<$<NOT:$<PLATFORM_ID:Darwin>>:Foundation>)
3839
target_link_libraries(PackageCollectionsSigning PRIVATE
39-
Crypto)
40+
Crypto
41+
CCryptoBoringSSL)
4042
# NOTE(compnerd) workaround for CMake not setting up include flags yet
4143
set_target_properties(PackageCollectionsSigning PROPERTIES
4244
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})

Sources/PackageCollectionsSigning/Certificate/Certificate.swift

Lines changed: 97 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import struct Foundation.Data
1212

1313
#if os(macOS)
1414
import Security
15+
#else
16+
@_implementationOnly import CCryptoBoringSSL
1517
#endif
1618

1719
#if os(macOS)
@@ -107,24 +109,114 @@ struct CoreCertificate {
107109

108110
#else
109111
final class BoringSSLCertificate {
112+
let underlying: UnsafeMutablePointer<X509>
113+
114+
deinit {
115+
CCryptoBoringSSL_X509_free(self.underlying)
116+
}
117+
110118
init(derEncoded data: Data) throws {
111-
fatalError("Not implemented: \(#function)")
119+
let bytes = data.copyBytes()
120+
let x509 = try bytes.withUnsafeBufferPointer { (ptr: UnsafeBufferPointer<UInt8>) throws -> UnsafeMutablePointer<X509> in
121+
var pointer = ptr.baseAddress
122+
guard let x509 = CCryptoBoringSSL_d2i_X509(nil, &pointer, numericCast(bytes.count)) else {
123+
throw CertificateError.initializationFailure
124+
}
125+
return x509
126+
}
127+
self.underlying = x509
112128
}
113129

114130
func subject() throws -> CertificateName {
115-
fatalError("Not implemented: \(#function)")
131+
guard let subject = CCryptoBoringSSL_X509_get_subject_name(self.underlying) else {
132+
throw CertificateError.nameExtractionFailure
133+
}
134+
return CertificateName(x509Name: subject)
116135
}
117136

118137
func issuer() throws -> CertificateName {
119-
fatalError("Not implemented: \(#function)")
138+
guard let issuer = CCryptoBoringSSL_X509_get_issuer_name(self.underlying) else {
139+
throw CertificateError.nameExtractionFailure
140+
}
141+
return CertificateName(x509Name: issuer)
120142
}
121143

122144
func publicKey() throws -> PublicKey {
123-
fatalError("Not implemented: \(#function)")
145+
guard let key = CCryptoBoringSSL_X509_get_pubkey(self.underlying) else {
146+
throw CertificateError.keyExtractionFailure
147+
}
148+
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }
149+
150+
var buffer: UnsafeMutablePointer<CUnsignedChar>?
151+
defer { CCryptoBoringSSL_OPENSSL_free(buffer) }
152+
153+
let length = CCryptoBoringSSL_i2d_PublicKey(key, &buffer)
154+
guard length > 0 else {
155+
throw CertificateError.keyExtractionFailure
156+
}
157+
158+
let data = Data(UnsafeBufferPointer(start: buffer, count: Int(length)))
159+
160+
switch try self.keyType(of: key) {
161+
case .RSA:
162+
return try BoringSSLRSAPublicKey(data: data)
163+
case .EC:
164+
return try ECPublicKey(data: data)
165+
}
124166
}
125167

126168
func keyType() throws -> KeyType {
127-
fatalError("Not implemented: \(#function)")
169+
guard let key = CCryptoBoringSSL_X509_get_pubkey(self.underlying) else {
170+
throw CertificateError.keyExtractionFailure
171+
}
172+
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }
173+
174+
return try self.keyType(of: key)
175+
}
176+
177+
private func keyType(of key: UnsafeMutablePointer<EVP_PKEY>) throws -> KeyType {
178+
let algorithm = CCryptoBoringSSL_OBJ_obj2nid(self.underlying.pointee.cert_info.pointee.key.pointee.algor.pointee.algorithm)
179+
180+
switch algorithm {
181+
case NID_rsaEncryption:
182+
return .RSA
183+
case NID_X9_62_id_ecPublicKey:
184+
return .EC
185+
default:
186+
throw CertificateError.unsupportedKeyType
187+
}
188+
}
189+
}
190+
191+
private extension CertificateName {
192+
init(x509Name: UnsafeMutablePointer<X509_NAME>) {
193+
self.userID = x509Name.getStringValue(of: NID_userId)
194+
self.commonName = x509Name.getStringValue(of: NID_commonName)
195+
self.organization = x509Name.getStringValue(of: NID_organizationName)
196+
self.organizationalUnit = x509Name.getStringValue(of: NID_organizationalUnitName)
197+
}
198+
}
199+
200+
private extension UnsafeMutablePointer where Pointee == X509_NAME {
201+
func getStringValue(of nid: CInt) -> String? {
202+
let index = CCryptoBoringSSL_X509_NAME_get_index_by_NID(self, nid, -1)
203+
guard index >= 0 else {
204+
return nil
205+
}
206+
207+
let entry = CCryptoBoringSSL_X509_NAME_get_entry(self, index)
208+
guard let data = CCryptoBoringSSL_X509_NAME_ENTRY_get_data(entry) else {
209+
return nil
210+
}
211+
212+
var value: UnsafeMutablePointer<CUnsignedChar>?
213+
defer { CCryptoBoringSSL_OPENSSL_free(value) }
214+
215+
guard CCryptoBoringSSL_ASN1_STRING_to_UTF8(&value, data) >= 0 else {
216+
return nil
217+
}
218+
219+
return String.decodeCString(value, as: UTF8.self, repairingInvalidCodeUnits: true)?.result
128220
}
129221
}
130222
#endif
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
//===----------------------------------------------------------------------===//
12+
//
13+
// This source file is part of the Vapor open source project
14+
//
15+
// Copyright (c) 2017-2020 Vapor project authors
16+
// Licensed under MIT
17+
//
18+
// See LICENSE for license information
19+
//
20+
// SPDX-License-Identifier: MIT
21+
//
22+
//===----------------------------------------------------------------------===//
23+
24+
#if !canImport(Security)
25+
import Foundation
26+
27+
@_implementationOnly import CCryptoBoringSSL
28+
29+
protocol BoringSSLKey {}
30+
31+
extension BoringSSLKey {
32+
// Source: https://github.com/vapor/jwt-kit/blob/master/Sources/JWTKit/Utilities/OpenSSLSigner.swift
33+
static func load<Data, KeyType>(pem data: Data,
34+
_ createKey: (UnsafeMutablePointer<BIO>) -> (KeyType?)) throws -> KeyType where Data: DataProtocol {
35+
let bytes = data.copyBytes()
36+
37+
// Not doing `CCryptoBoringSSL_BIO_new_mem_buf(bytes, numericCast(bytes.count))` because
38+
// it causes `bioConversionFailure` error on *some* Linux builds (e.g., SPM Linux smoke test)
39+
let bio = CCryptoBoringSSL_BIO_new(CCryptoBoringSSL_BIO_s_mem())
40+
defer { CCryptoBoringSSL_BIO_free(bio) }
41+
42+
guard let bioPointer = bio, CCryptoBoringSSL_BIO_write(bioPointer, bytes, numericCast(bytes.count)) > 0 else {
43+
throw BoringSSLKeyError.bioInitializationFailure
44+
}
45+
guard let key = createKey(bioPointer) else {
46+
throw BoringSSLKeyError.bioConversionFailure
47+
}
48+
49+
return key
50+
}
51+
}
52+
53+
enum BoringSSLKeyError: Error {
54+
case failedToLoadKeyFromBytes
55+
case rsaConversionFailure
56+
case bioInitializationFailure
57+
case bioConversionFailure
58+
}
59+
#endif

Sources/PackageCollectionsSigning/Key/Key+RSA.swift

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,25 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This source file is part of the Vapor open source project
14+
//
15+
// Copyright (c) 2017-2020 Vapor project authors
16+
// Licensed under MIT
17+
//
18+
// See LICENSE for license information
19+
//
20+
// SPDX-License-Identifier: MIT
21+
//
22+
//===----------------------------------------------------------------------===//
23+
1124
import Foundation
1225

1326
#if os(macOS)
1427
import Security
28+
#else
29+
@_implementationOnly import CCryptoBoringSSL
1530
#endif
1631

1732
#if os(macOS)
@@ -86,29 +101,69 @@ struct CoreRSAPublicKey: PublicKey {
86101

87102
// MARK: - RSA key implementations using BoringSSL
88103

104+
// Reference: https://github.com/vapor/jwt-kit/blob/master/Sources/JWTKit/RSA/RSAKey.swift
105+
89106
#else
90-
final class BoringSSLRSAPrivateKey: PrivateKey {
91-
var sizeInBits: Int {
92-
fatalError("Not implemented")
107+
final class BoringSSLRSAPrivateKey: PrivateKey, BoringSSLKey {
108+
let underlying: UnsafeMutablePointer<CCryptoBoringSSL.RSA>
109+
static let algorithm: OpaquePointer = CCryptoBoringSSL_EVP_sha256()
110+
111+
deinit {
112+
CCryptoBoringSSL_RSA_free(self.underlying)
93113
}
94114

95115
init<Data>(pem data: Data) throws where Data: DataProtocol {
96-
fatalError("Not implemented: \(#function)")
116+
let key = try Self.load(pem: data) { bio in
117+
CCryptoBoringSSL_PEM_read_bio_PrivateKey(bio, nil, nil, nil)
118+
}
119+
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }
120+
121+
guard let pointer = CCryptoBoringSSL_EVP_PKEY_get1_RSA(key) else {
122+
throw BoringSSLKeyError.rsaConversionFailure
123+
}
124+
125+
self.underlying = pointer
97126
}
98127
}
99128

100-
final class BoringSSLRSAPublicKey: PublicKey {
101-
var sizeInBits: Int {
102-
fatalError("Not implemented")
129+
final class BoringSSLRSAPublicKey: PublicKey, BoringSSLKey {
130+
let underlying: UnsafeMutablePointer<CCryptoBoringSSL.RSA>
131+
static let algorithm: OpaquePointer = CCryptoBoringSSL_EVP_sha256()
132+
133+
deinit {
134+
CCryptoBoringSSL_RSA_free(self.underlying)
103135
}
104136

105137
/// `data` should be in the PKCS #1 format
106138
init(data: Data) throws {
107-
fatalError("Not implemented: \(#function)")
139+
let bytes = data.copyBytes()
140+
let key = try bytes.withUnsafeBufferPointer { (ptr: UnsafeBufferPointer<UInt8>) throws -> UnsafeMutablePointer<EVP_PKEY> in
141+
var pointer = ptr.baseAddress
142+
guard let key = CCryptoBoringSSL_d2i_PublicKey(EVP_PKEY_RSA, nil, &pointer, numericCast(data.count)) else {
143+
throw BoringSSLKeyError.failedToLoadKeyFromBytes
144+
}
145+
return key
146+
}
147+
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }
148+
149+
guard let pointer = CCryptoBoringSSL_EVP_PKEY_get1_RSA(key) else {
150+
throw BoringSSLKeyError.rsaConversionFailure
151+
}
152+
153+
self.underlying = pointer
108154
}
109155

110156
init<Data>(pem data: Data) throws where Data: DataProtocol {
111-
fatalError("Not implemented: \(#function)")
157+
let key = try Self.load(pem: data) { bio in
158+
CCryptoBoringSSL_PEM_read_bio_PUBKEY(bio, nil, nil, nil)
159+
}
160+
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }
161+
162+
guard let pointer = CCryptoBoringSSL_EVP_PKEY_get1_RSA(key) else {
163+
throw BoringSSLKeyError.rsaConversionFailure
164+
}
165+
166+
self.underlying = pointer
112167
}
113168
}
114169
#endif

0 commit comments

Comments
 (0)