Skip to content

[Collections] Signing: support for non-Apple platforms #3272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ string(COMPARE EQUAL ${CMAKE_SYSTEM_NAME} Windows CMAKE_INSTALL_DEFAULT)
option(USE_CMAKE_INSTALL
"Install build products using cmake's install() instead of the bootstrap script's install()"
${CMAKE_INSTALL_DEFAULT})

if(BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE YES)
endif()

if(FIND_PM_DEPS)
find_package(TSC CONFIG REQUIRED)
Expand Down
9 changes: 9 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ This product contains a derivation of Vapor's JWTKit library, specifically `JWTP
* HOMEPAGE:
* https://vapor.codes
* https://github.com/vapor/jwt-kit

---

This product contains a derivation of OpenSSL's OCSP implementation, found under the `PackageCollectionsSigningLibc` module.

* LICENSE (Apache License 2.0):
* https://www.apache.org/licenses/LICENSE-2.0
* HOMEPAGE:
* https://github.com/openssl/openssl
11 changes: 9 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,18 @@ let package = Package(
/** Package collections models */
name: "PackageCollectionsModel",
dependencies: []),


.target(
/** Package collections signing C lib */
name: "PackageCollectionsSigningLibc",
dependencies: ["Crypto"],
cSettings: [
.define("WIN32_LEAN_AND_MEAN"),
]),
.target(
/** Package collections signing */
name: "PackageCollectionsSigning",
dependencies: ["PackageCollectionsModel", "Crypto", "Basics"]),
dependencies: ["PackageCollectionsModel", "PackageCollectionsSigningLibc", "Crypto", "Basics"]),

.target(
/** Data structures and support for package collections */
Expand Down
1 change: 1 addition & 0 deletions Sources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_subdirectory(LLBuildManifest)
add_subdirectory(PackageCollections)
add_subdirectory(PackageCollectionsModel)
add_subdirectory(PackageCollectionsSigning)
add_subdirectory(PackageCollectionsSigningLibc)
add_subdirectory(PackageDescription)
add_subdirectory(PackageGraph)
add_subdirectory(PackageLoading)
Expand Down
9 changes: 8 additions & 1 deletion Sources/PackageCollectionsSigning/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ add_library(PackageCollectionsSigning
Key/ASN1/Types/ASN1Integer.swift
Key/ASN1/Types/ASN1ObjectIdentifier.swift
Key/ASN1/Types/ASN1OctetString.swift
Key/BoringSSLKey.swift
Key/Key.swift
Key/Key+EC.swift
Key/Key+RSA.swift
PackageCollectionSigning.swift
Signing/BoringSSLSigning.swift
Signing/Signature.swift
Signing/Signing.swift
Signing/Signing+ECKey.swift
Expand All @@ -36,7 +38,12 @@ target_link_libraries(PackageCollectionsSigning PUBLIC
$<$<NOT:$<PLATFORM_ID:Darwin>>:dispatch>
$<$<NOT:$<PLATFORM_ID:Darwin>>:Foundation>)
target_link_libraries(PackageCollectionsSigning PRIVATE
Crypto)
Crypto
CCryptoBoringSSL
PackageCollectionsSigningLibc)
target_include_directories(PackageCollectionsSigning PRIVATE
$<TARGET_PROPERTY:PackageCollectionsSigningLibc,INCLUDE_DIRECTORIES>)

# NOTE(compnerd) workaround for CMake not setting up include flags yet
set_target_properties(PackageCollectionsSigning PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
Expand Down
137 changes: 130 additions & 7 deletions Sources/PackageCollectionsSigning/Certificate/Certificate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ import struct Foundation.Data

#if os(macOS)
import Security
#elseif os(Linux) || os(Windows)
@_implementationOnly import CCryptoBoringSSL
#endif

#if os(macOS)
typealias Certificate = CoreCertificate
#else
#elseif os(Linux) || os(Windows)
typealias Certificate = BoringSSLCertificate
#else
typealias Certificate = UnsupportedCertificate
#endif

// MARK: - Certificate implementation using the Security framework
Expand Down Expand Up @@ -105,26 +109,145 @@ struct CoreCertificate {

// MARK: - Certificate implementation using BoringSSL

#else
#elseif os(Linux) || os(Windows)
final class BoringSSLCertificate {
private let underlying: UnsafeMutablePointer<X509>

deinit {
CCryptoBoringSSL_X509_free(self.underlying)
}

init(derEncoded data: Data) throws {
let bytes = data.copyBytes()
let x509 = try bytes.withUnsafeBufferPointer { (ptr: UnsafeBufferPointer<UInt8>) throws -> UnsafeMutablePointer<X509> in
var pointer = ptr.baseAddress
guard let x509 = CCryptoBoringSSL_d2i_X509(nil, &pointer, numericCast(ptr.count)) else {
throw CertificateError.initializationFailure
}
return x509
}
self.underlying = x509
}

func withUnsafeMutablePointer<R>(_ body: (UnsafeMutablePointer<X509>) throws -> R) rethrows -> R {
return try body(self.underlying)
}

func subject() throws -> CertificateName {
guard let subject = CCryptoBoringSSL_X509_get_subject_name(self.underlying) else {
throw CertificateError.nameExtractionFailure
}
return CertificateName(x509Name: subject)
}

func issuer() throws -> CertificateName {
guard let issuer = CCryptoBoringSSL_X509_get_issuer_name(self.underlying) else {
throw CertificateError.nameExtractionFailure
}
return CertificateName(x509Name: issuer)
}

func publicKey() throws -> PublicKey {
guard let key = CCryptoBoringSSL_X509_get_pubkey(self.underlying) else {
throw CertificateError.keyExtractionFailure
}
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }

var buffer: UnsafeMutablePointer<CUnsignedChar>?
defer { CCryptoBoringSSL_OPENSSL_free(buffer) }

let length = CCryptoBoringSSL_i2d_PublicKey(key, &buffer)
guard length > 0 else {
throw CertificateError.keyExtractionFailure
}

let data = Data(UnsafeBufferPointer(start: buffer, count: Int(length)))

switch try self.keyType(of: key) {
case .RSA:
return try BoringSSLRSAPublicKey(data: data)
case .EC:
return try ECPublicKey(data: data)
}
}

func keyType() throws -> KeyType {
guard let key = CCryptoBoringSSL_X509_get_pubkey(self.underlying) else {
throw CertificateError.keyExtractionFailure
}
defer { CCryptoBoringSSL_EVP_PKEY_free(key) }

return try self.keyType(of: key)
}

private func keyType(of key: UnsafeMutablePointer<EVP_PKEY>) throws -> KeyType {
let algorithm = CCryptoBoringSSL_OBJ_obj2nid(self.underlying.pointee.cert_info.pointee.key.pointee.algor.pointee.algorithm)

switch algorithm {
case NID_rsaEncryption:
return .RSA
case NID_X9_62_id_ecPublicKey:
return .EC
default:
throw CertificateError.unsupportedKeyType
}
}
}

private extension CertificateName {
init(x509Name: UnsafeMutablePointer<X509_NAME>) {
self.userID = x509Name.getStringValue(of: NID_userId)
self.commonName = x509Name.getStringValue(of: NID_commonName)
self.organization = x509Name.getStringValue(of: NID_organizationName)
self.organizationalUnit = x509Name.getStringValue(of: NID_organizationalUnitName)
}
}

private extension UnsafeMutablePointer where Pointee == X509_NAME {
func getStringValue(of nid: CInt) -> String? {
let index = CCryptoBoringSSL_X509_NAME_get_index_by_NID(self, nid, -1)
guard index >= 0 else {
return nil
}

let entry = CCryptoBoringSSL_X509_NAME_get_entry(self, index)
guard let data = CCryptoBoringSSL_X509_NAME_ENTRY_get_data(entry) else {
return nil
}

var value: UnsafeMutablePointer<CUnsignedChar>?
defer { CCryptoBoringSSL_OPENSSL_free(value) }

guard CCryptoBoringSSL_ASN1_STRING_to_UTF8(&value, data) >= 0 else {
return nil
}

return String.decodeCString(value, as: UTF8.self, repairingInvalidCodeUnits: true)?.result
}
}

// MARK: - Certificate implementation for unsupported platforms

#else
struct UnsupportedCertificate {
init(derEncoded data: Data) throws {
fatalError("Not implemented: \(#function)")
fatalError("Unsupported: \(#function)")
}

func subject() throws -> CertificateName {
fatalError("Not implemented: \(#function)")
fatalError("Unsupported: \(#function)")
}

func issuer() throws -> CertificateName {
fatalError("Not implemented: \(#function)")
fatalError("Unsupported: \(#function)")
}

func publicKey() throws -> PublicKey {
fatalError("Not implemented: \(#function)")
fatalError("Unsupported: \(#function)")
}

func keyType() throws -> KeyType {
fatalError("Not implemented: \(#function)")
fatalError("Unsupported: \(#function)")
}
}
#endif
Expand Down
Loading