Skip to content

Commit

Permalink
chore: Use SPKI directly to extract Certificate's key
Browse files Browse the repository at this point in the history
chore: Key importing using DataProtocol instead of Data
chore: Minor refactors
  • Loading branch information
amosavian committed Jun 13, 2024
1 parent 5fb3cda commit d8e0cff
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 90 deletions.
17 changes: 15 additions & 2 deletions Sources/JWSETKit/Cryptography/Certificate/SecCertificate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extension SecTrust: JSONWebValidatingKey {
} else {
let count = SecTrustGetCertificateCount(self)
guard count > 0 else {
throw JSONWebKeyError.keyNotFound
return []
}
return (0 ..< count).compactMap {
SecTrustGetCertificateAtIndex(self, $0)
Expand All @@ -76,6 +76,11 @@ extension SecTrust: JSONWebValidatingKey {
}
}

/// Return the public key for a leaf certificate after it has been evaluated.
public var publicKey: SecKey? {
SecTrustCopyKey(self)
}

public static func create(storage: JSONWebValueStorage) throws -> Self {
let key = AnyJSONWebKey(storage: storage)
let certificates = try key.certificateChain.map { try $0.secCertificate() }
Expand All @@ -88,7 +93,7 @@ extension SecTrust: JSONWebValidatingKey {
}

public func verifySignature<S, D>(_ signature: S, for data: D, using algorithm: JSONWebSignatureAlgorithm) throws where S: DataProtocol, D: DataProtocol {
try certificateChain.first?.verifySignature(signature, for: data, using: algorithm)
try publicKey?.verifySignature(signature, for: data, using: algorithm)
}
}

Expand All @@ -114,4 +119,12 @@ extension Certificate {
try self.init(derEncoded: der)
}
}

public func == (lhs: Certificate, rhs: SecCertificate) -> Bool {
lhs == (try? Certificate(rhs))
}

public func == (lhs: SecCertificate, rhs: Certificate) -> Bool {
(try? Certificate(lhs)) == rhs
}
#endif
23 changes: 3 additions & 20 deletions Sources/JWSETKit/Cryptography/Certificate/X509Certificate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,10 @@ extension Certificate.PublicKey: JSONWebValidatingKey {
///
/// - Returns: A public key to validate signatures.
public func jsonWebKey() throws -> any JSONWebValidatingKey {
if let key = P256.Signing.PublicKey(self) {
return key
} else if let key = P384.Signing.PublicKey(self) {
return key
} else if let key = P521.Signing.PublicKey(self) {
return key
}
#if canImport(CommonCrypto)
if let key = try? SecKey(derRepresentation: derRepresentation, keyType: .rsa) {
return key
}
#elseif canImport(_CryptoExtras)
if let key = _RSA.Signing.PublicKey(self) {
return key
guard let key = try AnyJSONWebKey(importing: subjectPublicKeyInfoBytes, format: .spki).specialized() as? any JSONWebValidatingKey else {
throw JSONWebKeyError.unknownKeyType
}
#else
// This should never happen as CommonCrypto is available on Darwin platforms
// and _CryptoExtras is used on non-Darwin platform.
fatalError("Unimplemented")
#endif
throw JSONWebKeyError.unknownKeyType
return key
}

public func thumbprint<H>(format: JSONWebKeyFormat, using hashFunction: H.Type) throws -> H.Digest where H: HashFunction {
Expand Down
28 changes: 18 additions & 10 deletions Sources/JWSETKit/Cryptography/EC/CryptoKitAbstract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,23 @@ protocol CryptoECPublicKeyPortable: JSONWebKeyImportable, JSONWebKeyExportable {
var x963Representation: Data { get }
var derRepresentation: Data { get }

init(x963Representation: Data) throws
init(derRepresentation: Data) throws
init<Bytes>(x963Representation: Bytes) throws where Bytes: ContiguousBytes
init<Bytes>(derRepresentation: Bytes) throws where Bytes: RandomAccessCollection, Bytes.Element == UInt8
}

extension CryptoECPublicKeyPortable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(x963Representation: key)
if key.regions.count == 1, let keyData = key.regions.first {
try self.init(x963Representation: keyData)
} else {
try self.init(x963Representation: Data(key))
}
case .spki:
try self.init(derRepresentation: key)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
default:
throw JSONWebKeyError.invalidKeyFormat
}
Expand Down Expand Up @@ -119,19 +123,23 @@ protocol CryptoECPrivateKeyPortable: JSONWebKeyImportable, JSONWebKeyExportable
var x963Representation: Data { get }
var derRepresentation: Data { get }

init(x963Representation: Data) throws
init(derRepresentation: Data) throws
init<Bytes>(x963Representation: Bytes) throws where Bytes: ContiguousBytes
init<Bytes>(derRepresentation: Bytes) throws where Bytes: RandomAccessCollection, Bytes.Element == UInt8
}

extension CryptoECPrivateKeyPortable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(x963Representation: key)
if key.regions.count == 1, let keyData = key.regions.first {
try self.init(x963Representation: keyData)
} else {
try self.init(x963Representation: Data(key))
}
case .pkcs8:
try self.init(derRepresentation: key)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
default:
throw JSONWebKeyError.invalidKeyFormat
}
Expand Down
13 changes: 8 additions & 5 deletions Sources/JWSETKit/Cryptography/EC/Ed25519.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ extension Curve25519.Signing.PublicKey: CryptoECPublicKey {
return result.storage
}


public static func create(storage: JSONWebValueStorage) throws -> Self {
let keyData = AnyJSONWebKey(storage: storage)
guard let x = keyData.xCoordinate, !x.isEmpty else {
Expand Down Expand Up @@ -88,16 +87,20 @@ extension Curve25519.KeyAgreement.PrivateKey: CryptoEdKeyPortable {}
protocol CryptoEdKeyPortable: JSONWebKeyImportable, JSONWebKeyExportable {
var rawRepresentation: Data { get }

init(rawRepresentation: Data) throws
init<D>(rawRepresentation data: D) throws where D: ContiguousBytes
}

extension CryptoEdKeyPortable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(rawRepresentation: key)
if key.regions.count == 1, let keyData = key.regions.first {
try self.init(rawRepresentation: keyData)
} else {
try self.init(rawRepresentation: Data(key))
}
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
default:
throw JSONWebKeyError.invalidKeyFormat
}
Expand Down
14 changes: 7 additions & 7 deletions Sources/JWSETKit/Cryptography/EC/JWK-EC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ public struct JSONWebECPublicKey: MutableJSONWebKey, JSONWebValidatingKey, Senda
}

extension JSONWebKeyImportable {
fileprivate init(
key: Data, format: JSONWebKeyFormat,
fileprivate init<D>(
key: D, format: JSONWebKeyFormat,
keyLengthTable: [Int: JSONWebKeyCurve],
keyFinder: (_ curve: JSONWebKeyCurve) throws -> any JSONWebValidatingKey.Type
) throws {
) throws where D: DataProtocol {
guard let curve = keyLengthTable[key.count] else {
throw JSONWebKeyError.unknownAlgorithm
}
Expand All @@ -69,14 +69,14 @@ extension JSONWebKeyImportable {
}

extension JSONWebECPublicKey: JSONWebKeyImportable, JSONWebKeyExportable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(key: key, format: format, keyLengthTable: JSONWebKeyCurve.publicRawCurve, keyFinder: Self.signingType)
case .spki:
try self.init(key: key, format: format, keyLengthTable: JSONWebKeyCurve.spkiCurve, keyFinder: Self.signingType)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
try validate()
default:
throw JSONWebKeyError.invalidKeyFormat
Expand Down Expand Up @@ -172,14 +172,14 @@ public struct JSONWebECPrivateKey: MutableJSONWebKey, JSONWebSigningKey, Sendabl
}

extension JSONWebECPrivateKey: JSONWebKeyImportable, JSONWebKeyExportable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(key: key, format: format, keyLengthTable: JSONWebKeyCurve.privateRawCurve, keyFinder: Self.signingType)
case .pkcs8:
try self.init(key: key, format: format, keyLengthTable: JSONWebKeyCurve.pkc8Curve, keyFinder: Self.signingType)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
default:
throw JSONWebKeyError.invalidKeyFormat
}
Expand Down
12 changes: 8 additions & 4 deletions Sources/JWSETKit/Cryptography/KeyExporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public protocol JSONWebKeyImportable: JSONWebKey {
/// - key: The key in the specified format.
/// - format: The format in which the key is supplied.
/// - Throws: If the key cannot be imported in the specified format.
init(importing key: Data, format: JSONWebKeyFormat) throws
init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol
}

public protocol JSONWebKeyExportable: JSONWebKey {
Expand All @@ -86,12 +86,16 @@ public protocol JSONWebKeySymmetric: JSONWebKeyImportable, JSONWebKeyExportable
}

extension JSONWebKeySymmetric {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .raw:
try self.init(.init(data: key))
if key.regions.count == 1, let keyData = key.regions.first {
try self.init(.init(data: keyData))
} else {
try self.init(.init(data: Data(key)))
}
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
try validate()
default:
throw JSONWebKeyError.invalidKeyFormat
Expand Down
20 changes: 10 additions & 10 deletions Sources/JWSETKit/Cryptography/KeyParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public protocol JSONWebKeySpecializer {
/// - key: The key data to deserialize.
/// - format: The format of the key data.
/// - Returns: A specific `JSONWebKey` type, or `nil` if the key is not appropiate.
static func deserialize(key: Data, format: JSONWebKeyFormat) throws -> (any JSONWebKey)?
static func deserialize<D>(key: D, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol
}

enum JSONWebKeyRSASpecializer: JSONWebKeySpecializer {
Expand All @@ -74,13 +74,13 @@ enum JSONWebKeyRSASpecializer: JSONWebKeySpecializer {
}
}

static func deserialize(key: Data, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? {
static func deserialize<D>(key: D, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol {
switch format {
case .pkcs8:
guard try PKCS8PrivateKey(derEncoded: key).keyType == .rsa else { return nil }
guard try PKCS8PrivateKey(derEncoded: Data(key)).keyType == .rsa else { return nil }
return try JSONWebRSAPrivateKey(importing: key, format: format)
case .spki:
guard try SubjectPublicKeyInfo(derEncoded: key).keyType == .rsa else { return nil }
guard try SubjectPublicKeyInfo(derEncoded: Data(key)).keyType == .rsa else { return nil }
return try JSONWebRSAPublicKey(importing: key, format: format)
default:
return nil
Expand All @@ -104,15 +104,15 @@ enum JSONWebKeyEllipticCurveSpecializer: JSONWebKeySpecializer {
}
}

static func deserialize(key: Data, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? {
static func deserialize<D>(key: D, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol {
switch format {
case .pkcs8:
let pkcs8 = try PKCS8PrivateKey(derEncoded: key)
let pkcs8 = try PKCS8PrivateKey(derEncoded: Data(key))
guard try pkcs8.keyType == .ellipticCurve else { return nil }
guard pkcs8.keyCurve != nil else { return nil }
return try JSONWebECPrivateKey(importing: key, format: format)
case .spki:
let spki = try SubjectPublicKeyInfo(derEncoded: key)
let spki = try SubjectPublicKeyInfo(derEncoded: Data(key))
guard try spki.keyType == .ellipticCurve else { return nil }
guard spki.keyCurve != nil else { return nil }
return try JSONWebECPublicKey(importing: key, format: format)
Expand All @@ -138,7 +138,7 @@ enum JSONWebKeyCurve25519Specializer: JSONWebKeySpecializer {
}
}

static func deserialize(key _: Data, format _: JSONWebKeyFormat) throws -> (any JSONWebKey)? {
static func deserialize<D>(key _: D, format _: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol {
nil
}
}
Expand Down Expand Up @@ -170,7 +170,7 @@ enum JSONWebKeySymmetricSpecializer: JSONWebKeySpecializer {
}
}

static func deserialize(key: Data, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? {
static func deserialize<D>(key: D, format: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol {
switch format {
case .raw:
return try AnyJSONWebKey(storage: SymmetricKey(importing: key, format: .raw).storage).specialized()
Expand All @@ -188,7 +188,7 @@ enum JSONWebKeyCertificateChainSpecializer: JSONWebKeySpecializer {
return nil
}

static func deserialize(key _: Data, format _: JSONWebKeyFormat) throws -> (any JSONWebKey)? {
static func deserialize<D>(key _: D, format _: JSONWebKeyFormat) throws -> (any JSONWebKey)? where D: DataProtocol {
nil
}
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/JWSETKit/Cryptography/Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,9 @@ public struct AnyJSONWebKey: MutableJSONWebKey {
}

extension AnyJSONWebKey: JSONWebKeyImportable, JSONWebKeyExportable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
if format == .jwk {
let key = try JSONDecoder().decode(AnyJSONWebKey.self, from: key).specialized()
let key = try JSONDecoder().decode(AnyJSONWebKey.self, from: Data(key)).specialized()
try key.validate()
self.storage = key.storage
return
Expand Down
16 changes: 8 additions & 8 deletions Sources/JWSETKit/Cryptography/RSA/JWK-RSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public struct JSONWebRSAPublicKey: MutableJSONWebKey, JSONWebValidatingKey, JSON
self.storage = storage
}

public init(derRepresentation: Data) throws {
public init<D>(derRepresentation: D) throws where D: DataProtocol {
#if canImport(CommonCrypto)
self.storage = try SecKey(derRepresentation: derRepresentation, keyType: .rsa).storage
self.storage = try SecKey(derRepresentation: Data(derRepresentation), keyType: .rsa).storage
#elseif canImport(_CryptoExtras)
self.storage = try _RSA.Signing.PublicKey(derRepresentation: derRepresentation).storage
#else
Expand Down Expand Up @@ -91,12 +91,12 @@ public struct JSONWebRSAPublicKey: MutableJSONWebKey, JSONWebValidatingKey, JSON
}

extension JSONWebRSAPublicKey: JSONWebKeyImportable, JSONWebKeyExportable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .spki:
self = try .init(derRepresentation: key)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
try validate()
default:
throw JSONWebKeyError.invalidKeyFormat
Expand Down Expand Up @@ -189,9 +189,9 @@ public struct JSONWebRSAPrivateKey: MutableJSONWebKey, JSONWebSigningKey, JSONWe
self.storage = storage
}

public init(derRepresentation: Data) throws {
public init<D>(derRepresentation: D) throws where D: DataProtocol {
#if canImport(CommonCrypto)
self.storage = try SecKey(derRepresentation: derRepresentation, keyType: .rsa).storage
self.storage = try SecKey(derRepresentation: Data(derRepresentation), keyType: .rsa).storage
#elseif canImport(_CryptoExtras)
self.storage = try _RSA.Signing.PrivateKey(derRepresentation: derRepresentation).storage
#else
Expand Down Expand Up @@ -244,12 +244,12 @@ public struct JSONWebRSAPrivateKey: MutableJSONWebKey, JSONWebSigningKey, JSONWe
}

extension JSONWebRSAPrivateKey: JSONWebKeyImportable, JSONWebKeyExportable {
public init(importing key: Data, format: JSONWebKeyFormat) throws {
public init<D>(importing key: D, format: JSONWebKeyFormat) throws where D: DataProtocol {
switch format {
case .pkcs8:
self = try .init(derRepresentation: key)
case .jwk:
self = try JSONDecoder().decode(Self.self, from: key)
self = try JSONDecoder().decode(Self.self, from: Data(key))
try validate()
default:
throw JSONWebKeyError.invalidKeyFormat
Expand Down
Loading

0 comments on commit d8e0cff

Please sign in to comment.