Skip to content

Commit

Permalink
extras: Provide initializers for RSA keys from RSA numbers as bytes (#…
Browse files Browse the repository at this point in the history
…247)

Co-authored-by: Cory Benfield <[email protected]>
  • Loading branch information
simonjbeaumont and Lukasa authored Jul 22, 2024
1 parent e2dd5a5 commit 8dafe0f
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 102 deletions.
12 changes: 12 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA+BlindSigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ extension _RSA.BlindSigning {
}
}

/// Construct a RSA public key with the specified parameters.
public init(n: some ContiguousBytes, e: some ContiguousBytes, parameters: Parameters) throws {
self.backing = try BackingPublicKey(n: n, e: e)
self.parameters = parameters
}

public var pkcs1DERRepresentation: Data {
self.backing.pkcs1DERRepresentation
}
Expand Down Expand Up @@ -171,6 +177,12 @@ extension _RSA.BlindSigning {
}
}

/// Construct an RSA private key with the specified parameters.
public init(n: some ContiguousBytes, e: some ContiguousBytes, d: some ContiguousBytes, p: some ContiguousBytes, q: some ContiguousBytes, parameters: Parameters) throws {
self.backing = try BackingPrivateKey(n: n, e: e, d: d, p: p, q: q)
self.parameters = parameters
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 2048 bits. Callers that want to enforce minimum
Expand Down
45 changes: 45 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ extension _RSA.Signing {
}
}

/// Construct an RSA public key with the specified parameters.
///
/// Only the BoringSSL backend provides APIs to create a key from its parameters so we first create a BoringSSL
/// key, and then pass it to the platform-specific initializer that accepts a BoringSSL key.
///
/// On Darwin platforms, this will serialize it to PEM format, and then construct a platform-specific key from
/// the PEM representation.
public init(n: some ContiguousBytes, e: some ContiguousBytes) throws {
self.backing = try BackingPublicKey(BoringSSLRSAPublicKey(n: n, e: e))
}

public var pkcs1DERRepresentation: Data {
self.backing.pkcs1DERRepresentation
}
Expand Down Expand Up @@ -178,6 +189,17 @@ extension _RSA.Signing {
}
}

/// Construct an RSA private key with the specified parameters.
///
/// Only the BoringSSL backend provides APIs to create a key from its parameters so we first create a BoringSSL
/// key, and then pass it to the platform-specific initializer that accepts a BoringSSL key.
///
/// On Darwin platforms, this will serialize it to DER format, and then construct a platform-specific key from
/// the DER representation.
public init(n: some ContiguousBytes, e: some ContiguousBytes, d: some ContiguousBytes, p: some ContiguousBytes, q: some ContiguousBytes) throws {
self.backing = try BackingPrivateKey(BoringSSLRSAPrivateKey(n: n, e: e, d: d, p: p, q: q))
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 2048 bits. Callers that want to enforce minimum
Expand Down Expand Up @@ -444,6 +466,17 @@ extension _RSA.Encryption {
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key with the specified parameters.
///
/// Only the BoringSSL backend provides APIs to create a key from its parameters so we first create a BoringSSL
/// key, and then pass it to the platform-specific initializer that accepts a BoringSSL key.
///
/// On Darwin platforms, this will serialize it to DER format, and then construct a platform-specific key from
/// the DER representation.
public init(n: some ContiguousBytes, e: some ContiguousBytes) throws {
self.backing = try BackingPublicKey(BoringSSLRSAPublicKey(n: n, e: e))
}

public var pkcs1DERRepresentation: Data { self.backing.pkcs1DERRepresentation }
public var pkcs1PEMRepresentation: String { self.backing.pkcs1PEMRepresentation }
public var derRepresentation: Data { self.backing.derRepresentation }
Expand Down Expand Up @@ -494,6 +527,18 @@ extension _RSA.Encryption {
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}


/// Construct an RSA private key with the specified parameters.
///
/// Only the BoringSSL backend provides APIs to create a key from its parameters so we first create a BoringSSL
/// key, and then pass it to the platform-specific initializer that accepts a BoringSSL key.
///
/// On Darwin platforms, this will serialize it to PEM format, and then construct a platform-specific key from
/// the PEM representation.
public init(n: some ContiguousBytes, e: some ContiguousBytes, d: some ContiguousBytes, p: some ContiguousBytes, q: some ContiguousBytes) throws {
self.backing = try BackingPrivateKey(BoringSSLRSAPrivateKey(n: n, e: e, d: d, p: p, q: q))
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 2048 bits. Callers that want to enforce minimum
Expand Down
67 changes: 67 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA_boring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@ internal struct BoringSSLRSAPublicKey: Sendable {
self.backing = try Backing(derRepresentation: derRepresentation)
}

init(n: some ContiguousBytes, e: some ContiguousBytes) throws {
self.backing = try Backing(n: n, e: e)
}

init(_ other: BoringSSLRSAPublicKey) throws {
self = other
}

var pkcs1DERRepresentation: Data {
self.backing.pkcs1DERRepresentation
}
Expand Down Expand Up @@ -66,6 +74,14 @@ internal struct BoringSSLRSAPrivateKey: Sendable {
self.backing = try Backing(derRepresentation: derRepresentation)
}

init(n: some ContiguousBytes, e: some ContiguousBytes, d: some ContiguousBytes, p: some ContiguousBytes, q: some ContiguousBytes) throws {
self.backing = try Backing(n: n, e: e, d: d, p: p, q: q)
}

init(_ other: BoringSSLRSAPrivateKey) throws {
self = other
}

init(keySize: _RSA.Signing.KeySize) throws {
self.backing = try Backing(keySize: keySize)
}
Expand Down Expand Up @@ -222,6 +238,20 @@ extension BoringSSLRSAPublicKey {
}
}

fileprivate init(n: some ContiguousBytes, e: some ContiguousBytes) throws {
self.pointer = CCryptoBoringSSL_EVP_PKEY_new()
let n = try ArbitraryPrecisionInteger(bytes: n)
let e = try ArbitraryPrecisionInteger(bytes: e)

// Create BoringSSL RSA key.
guard let rsaPtr = n.withUnsafeBignumPointer({ n in
e.withUnsafeBignumPointer { e in
CCryptoBoringSSL_RSA_new_public_key(n, e)
}
}) else { throw CryptoKitError.internalBoringSSLError() }
CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPtr)
}

fileprivate var pkcs1DERRepresentation: Data {
return BIOHelper.withWritableMemoryBIO { bio in
let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer)
Expand Down Expand Up @@ -486,6 +516,43 @@ extension BoringSSLRSAPrivateKey {
CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPrivateKey)
}


fileprivate init(n: some ContiguousBytes, e: some ContiguousBytes, d: some ContiguousBytes, p: some ContiguousBytes, q: some ContiguousBytes) throws {
self.pointer = CCryptoBoringSSL_EVP_PKEY_new()
let n = try ArbitraryPrecisionInteger(bytes: n)
let e = try ArbitraryPrecisionInteger(bytes: e)
let d = try ArbitraryPrecisionInteger(bytes: d)
let p = try ArbitraryPrecisionInteger(bytes: p)
let q = try ArbitraryPrecisionInteger(bytes: q)

// Compute the CRT params.
let dp = try FiniteFieldArithmeticContext(fieldSize: p - 1).residue(d)
let dq = try FiniteFieldArithmeticContext(fieldSize: q - 1).residue(d)
guard let qi = try FiniteFieldArithmeticContext(fieldSize: p).inverse(q) else {
throw CryptoKitError.internalBoringSSLError()
}

// Create BoringSSL RSA key.
guard let rsaPtr = n.withUnsafeBignumPointer({ n in
e.withUnsafeBignumPointer { e in
d.withUnsafeBignumPointer { d in
p.withUnsafeBignumPointer { p in
q.withUnsafeBignumPointer { q in
dp.withUnsafeBignumPointer { dp in
dq.withUnsafeBignumPointer { dq in
qi.withUnsafeBignumPointer { qi in
CCryptoBoringSSL_RSA_new_private_key(n, e, d, p, q, dp, dq, qi)
}
}
}
}
}
}
}
}) else { throw CryptoKitError.internalBoringSSLError() }
CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPtr)
}

private static func pkcs8DERPrivateKey<Bytes: ContiguousBytes>(_ derRepresentation: Bytes) -> OpaquePointer? {
return derRepresentation.withUnsafeBytes { derPtr in
return BIOHelper.withReadOnlyMemoryBIO(wrapping: derPtr) { bio in
Expand Down
8 changes: 8 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA_security.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ internal struct SecurityRSAPublicKey: @unchecked Sendable {
self.backing = unwrappedKey
}

init(_ boringSSLKey: BoringSSLRSAPublicKey) throws {
try self.init(derRepresentation: boringSSLKey.derRepresentation)
}

var pkcs1DERRepresentation: Data {
var error: Unmanaged<CFError>? = nil
let representation = SecKeyCopyExternalRepresentation(self.backing, &error)
Expand Down Expand Up @@ -135,6 +139,10 @@ internal struct SecurityRSAPrivateKey: @unchecked Sendable {
self.backing = unwrappedKey
}

init(_ boringSSLKey: BoringSSLRSAPrivateKey) throws {
try self.init(derRepresentation: boringSSLKey.derRepresentation)
}

var derRepresentation: Data {
var error: Unmanaged<CFError>? = nil
let representation = SecKeyCopyExternalRepresentation(self.backing, &error)
Expand Down
83 changes: 0 additions & 83 deletions Sources/_CryptoExtras/Util/BoringSSLHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,86 +92,3 @@ extension FixedWidthInteger {
return try block(&bn)
}
}

extension _RSA.BlindSigning.PublicKey {
/// Construct a platform-specific RSA public key with the specified parameters.
///
/// This constructor is used in tests in cases where test vectors provide the key information this way.
///
/// Only the BoringSSL backend provides APIs to create the key from its parameters so we first create a BoringSSL
/// key, serialize it to PEM format, and then construct a platform specific key from the PEM representation.
internal init(nHexString: String, eHexString: String, parameters: Parameters) throws {
let n = try ArbitraryPrecisionInteger(hexString: nHexString)
let e = try ArbitraryPrecisionInteger(hexString: eHexString)

// Create BoringSSL RSA key.
guard let rsaPtr = n.withUnsafeBignumPointer({ n in
e.withUnsafeBignumPointer { e in
CCryptoBoringSSL_RSA_new_public_key(n, e)
}
}) else { throw CryptoKitError.internalBoringSSLError() }
defer { CCryptoBoringSSL_RSA_free(rsaPtr) }

// Get PEM representation for key.
let pemRepresentation = BIOHelper.withWritableMemoryBIO { bio in
precondition(CCryptoBoringSSL_PEM_write_bio_RSAPublicKey(bio, rsaPtr) == 1)
return try! String(copyingUTF8MemoryBIO: bio)
}

// Create a key (which might be backed by Security framework) from PEM representation.
try self.init(pemRepresentation: pemRepresentation, parameters: parameters)
}
}


extension _RSA.BlindSigning.PrivateKey {
/// Construct a platform-specific RSA private key with the specified parameters.
///
/// This constructor is used in tests in cases where test vectors provide the key information this way.
///
/// Only the BoringSSL backend provides APIs to create the key from its parameters so we first create a BoringSSL
/// key, serialize it to PEM format, and then construct a platform specific key from the PEM representation.
internal init(nHexString: String, eHexString: String, dHexString: String, pHexString: String, qHexString: String, parameters: Parameters) throws {
let n = try ArbitraryPrecisionInteger(hexString: nHexString)
let e = try ArbitraryPrecisionInteger(hexString: eHexString)
let d = try ArbitraryPrecisionInteger(hexString: dHexString)
let p = try ArbitraryPrecisionInteger(hexString: pHexString)
let q = try ArbitraryPrecisionInteger(hexString: qHexString)

// Compute the CRT params.
let dp = try FiniteFieldArithmeticContext(fieldSize: p - 1).residue(d)
let dq = try FiniteFieldArithmeticContext(fieldSize: q - 1).residue(d)
guard let qi = try FiniteFieldArithmeticContext(fieldSize: p).inverse(q) else {
throw CryptoKitError.internalBoringSSLError()
}

// Create BoringSSL RSA key.
guard let rsaPtr = n.withUnsafeBignumPointer({ n in
e.withUnsafeBignumPointer { e in
d.withUnsafeBignumPointer { d in
p.withUnsafeBignumPointer { p in
q.withUnsafeBignumPointer { q in
dp.withUnsafeBignumPointer { dp in
dq.withUnsafeBignumPointer { dq in
qi.withUnsafeBignumPointer { qi in
CCryptoBoringSSL_RSA_new_private_key(n, e, d, p, q, dp, dq, qi)
}
}
}
}
}
}
}
}) else { throw CryptoKitError.internalBoringSSLError() }
defer { CCryptoBoringSSL_RSA_free(rsaPtr) }

// Get PEM representation for key.
let pemRepresentation = BIOHelper.withWritableMemoryBIO { bio in
precondition(CCryptoBoringSSL_PEM_write_bio_RSAPrivateKey(bio, rsaPtr, nil, nil, 0, nil, nil) == 1)
return try! String(copyingUTF8MemoryBIO: bio)
}

// Create a key (which might be backed by Security framework) from PEM representation.
try self.init(pemRepresentation: pemRepresentation, parameters: parameters)
}
}
Loading

0 comments on commit 8dafe0f

Please sign in to comment.