Skip to content

Commit

Permalink
Add support for CI that cannot load p12 files (#409)
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-fowler committed Mar 22, 2024
1 parent 0b31a0f commit a71c880
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Sources/Hummingbird/Server/RequestID.swift
Expand Up @@ -15,7 +15,7 @@
import Atomics

/// Generate Unique ID for each request
public struct RequestID: CustomStringConvertible {
public struct RequestID: CustomStringConvertible, Sendable {
let low: UInt64

public init() {
Expand Down
30 changes: 23 additions & 7 deletions Sources/HummingbirdCore/Server/TSTLSOptions.swift
Expand Up @@ -15,21 +15,28 @@
#if canImport(Network)
import Foundation
import Network
import Security

/// Wrapper for NIO transport services TLS options
public struct TSTLSOptions: Sendable {
struct Error: Swift.Error, Equatable {
public struct Error: Swift.Error, Equatable {
enum _Internal: Equatable {
case invalidFormat
case interactionNotAllowed
case verificationFailed
}

private let value: _Internal
init(_ value: _Internal) {
self.value = value
}

// static invalid conversion
static var invalidFormat: Self { .init(.invalidFormat) }
// invalid format
public static var invalidFormat: Self { .init(.invalidFormat) }
// unable to import p12 as no interaction is allowed
public static var interactionNotAllowed: Self { .init(.interactionNotAllowed) }
// MAC verification failed during PKCS12 import (wrong password?)
public static var verificationFailed: Self { .init(.verificationFailed) }
}

public struct Identity {
Expand All @@ -40,16 +47,25 @@ public struct TSTLSOptions: Sendable {
}

public static func p12(filename: String, password: String) throws -> Self {
guard let secIdentity = Self.loadP12(filename: filename, password: password) else { throw Error.invalidFormat }
guard let secIdentity = try Self.loadP12(filename: filename, password: password) else { throw Error.invalidFormat }
return .init(secIdentity: secIdentity)
}

private static func loadP12(filename: String, password: String) -> SecIdentity? {
guard let data = try? Data(contentsOf: URL(fileURLWithPath: filename)) else { return nil }
private static func loadP12(filename: String, password: String) throws -> SecIdentity? {
let data = try Data(contentsOf: URL(fileURLWithPath: filename))
let options: [String: String] = [kSecImportExportPassphrase as String: password]
var rawItems: CFArray?
let result = SecPKCS12Import(data as CFData, options as CFDictionary, &rawItems)
guard result == errSecSuccess else { return nil }
switch result {
case errSecSuccess:
break
case errSecInteractionNotAllowed:
throw Error.interactionNotAllowed
case errSecPkcs12VerifyFailure:
throw Error.verificationFailed
default:
throw Error.invalidFormat
}
let items = rawItems! as! [[String: Any]]
let firstItem = items[0]
return firstItem[kSecImportItemIdentity as String] as! SecIdentity?
Expand Down
10 changes: 7 additions & 3 deletions Tests/HummingbirdCoreTests/TSTests.swift
Expand Up @@ -48,9 +48,13 @@ class TransportServicesTests: XCTestCase {
func testTLS() async throws {
let eventLoopGroup = NIOTSEventLoopGroup()
let p12Path = Bundle.module.path(forResource: "server", ofType: "p12")!
let tlsOptions = try XCTUnwrap(TSTLSOptions.options(
serverIdentity: .p12(filename: p12Path, password: "HBTests")
))
let tlsOptions: TSTLSOptions
do {
let identity = try TSTLSOptions.Identity.p12(filename: p12Path, password: "HBTests")
tlsOptions = try XCTUnwrap(TSTLSOptions.options(serverIdentity: identity))
} catch let error as TSTLSOptions.Error where error == .interactionNotAllowed {
throw XCTSkip("Unable to import PKCS12 bundle: no interaction allowed")
}
try await testServer(
responder: helloResponder,
configuration: .init(address: .hostname(port: 0), serverName: testServerName, tlsOptions: tlsOptions),
Expand Down

0 comments on commit a71c880

Please sign in to comment.