Skip to content

Commit

Permalink
extras: Add RSA primitives decoding API (apple#249)
Browse files Browse the repository at this point in the history
Motivation:

At times we might need to get the raw elements of an RSA key, e.g when debugging.

Modifications:

Add a getKeyPrimitives method to the RSA (public and private) key structures that returns the elements.
To do this we leverage the built in BoringSSL methods (CCryptoBoringSSL_RSA_get0_n etc.) which return the raw elements of the key. In the Security version of the key we convert the key to a BoringSSL one and just call the BoringSSL method.

Once we get ASN.1 support in _CryptoExtras it might make sense switching the implementation of this method to decode the ASN.1 structure and returns the elements from there.

Result:

New APIs to fetch the key primitives.

---------

Co-authored-by: Cory Benfield <[email protected]>
  • Loading branch information
ptoffy and Lukasa authored Sep 16, 2024
1 parent 81bee98 commit 55201a8
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA+BlindSigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ extension _RSA.BlindSigning {
public struct PublicKey<H: HashFunction>: Sendable {
public typealias Parameters = _RSA.BlindSigning.Parameters<H>

public struct Primitives: Sendable, Hashable {
public var modulus: Data
public var publicExponent: Data

public init(modulus: Data, publicExponent: Data) {
self.modulus = modulus
self.publicExponent = publicExponent
}
}

private var backing: BackingPublicKey
private let parameters: Parameters

Expand Down Expand Up @@ -113,6 +123,11 @@ extension _RSA.BlindSigning {
self.backing = backing
self.parameters = parameters
}

public func getKeyPrimitives() throws -> Primitives {
let (n, e) = try self.backing.getKeyPrimitives()
return Primitives(modulus: n, publicExponent: e)
}
}
}

Expand Down
30 changes: 30 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ extension _RSA {

extension _RSA.Signing {
public struct PublicKey: Sendable {
public struct Primitives: Sendable, Hashable {
public var modulus: Data
public var publicExponent: Data

public init(modulus: Data, publicExponent: Data) {
self.modulus = modulus
self.publicExponent = publicExponent
}
}

private var backing: BackingPublicKey

/// Construct an RSA public key from a PEM representation.
Expand Down Expand Up @@ -132,6 +142,11 @@ extension _RSA.Signing {
fileprivate init(_ backing: BackingPublicKey) {
self.backing = backing
}

public func getKeyPrimitives() throws -> Primitives {
let (n, e) = try self.backing.getKeyPrimitives()
return Primitives(modulus: n, publicExponent: e)
}
}
}

Expand Down Expand Up @@ -426,6 +441,16 @@ extension _RSA.Signing {
extension _RSA.Encryption {
/// Identical to ``_RSA/Signing/PublicKey``.
public struct PublicKey {
public struct Primitives: Sendable, Hashable {
public var modulus: Data
public var publicExponent: Data

public init(modulus: Data, publicExponent: Data) {
self.modulus = modulus
self.publicExponent = publicExponent
}
}

private var backing: BackingPublicKey

/// Construct an RSA public key from a PEM representation.
Expand Down Expand Up @@ -483,6 +508,11 @@ extension _RSA.Encryption {
public var pemRepresentation: String { self.backing.pemRepresentation }
public var keySizeInBits: Int { self.backing.keySizeInBits }
fileprivate init(_ backing: BackingPublicKey) { self.backing = backing }

public func getKeyPrimitives() throws -> Primitives {
let (n, e) = try self.backing.getKeyPrimitives()
return Primitives(modulus: n, publicExponent: e)
}
}

/// Identical to ``_RSA/Signing/PrivateKey``.
Expand Down
20 changes: 20 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA_boring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ internal struct BoringSSLRSAPublicKey: Sendable {
fileprivate init(_ backing: Backing) {
self.backing = backing
}

func getKeyPrimitives() throws -> (n: Data, e: Data) {
try self.backing.getKeyPrimitives()
}
}


Expand Down Expand Up @@ -465,6 +469,22 @@ extension BoringSSLRSAPublicKey {
deinit {
CCryptoBoringSSL_EVP_PKEY_free(self.pointer)
}

fileprivate func getKeyPrimitives() -> (n: Data, e: Data) {
let key = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer)

func getPrimitive(_ getPointer: (OpaquePointer?) -> UnsafePointer<BIGNUM>?) -> Data {
let ptr = getPointer(key)
let size = Int(CCryptoBoringSSL_BN_num_bytes(ptr))
var data = Data(count: size)
data.withUnsafeMutableBytes { dataPtr in
_ = CCryptoBoringSSL_BN_bn2bin(ptr, dataPtr.baseAddress)
}
return data
}

return (getPrimitive(CCryptoBoringSSL_RSA_get0_n), getPrimitive(CCryptoBoringSSL_RSA_get0_e))
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions Sources/_CryptoExtras/RSA/RSA_security.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ internal struct SecurityRSAPublicKey: @unchecked Sendable {
fileprivate init(_ backing: SecKey) {
self.backing = backing
}

func getKeyPrimitives() throws -> (n: Data, e: Data) {
try BoringSSLRSAPublicKey(derRepresentation: self.derRepresentation).getKeyPrimitives()
}
}

// unchecked sendable until `SecKey` gets sendable annotations
Expand Down
14 changes: 14 additions & 0 deletions Tests/_CryptoExtrasTests/TestRSABlindSigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,18 @@ final class TestRSABlindSigning: XCTestCase {
parameters: .RSABSSA_SHA384_PSS_Randomized
)
}

func testGetKeyPrimitives() throws {
for testVector in RFC9474TestVector.allValues {
let n = try Data(hexString: testVector.n)
let e = try Data(hexString: testVector.e)

let primitives = try _RSA.BlindSigning.PublicKey(
n: n, e: e,
parameters: testVector.parameters
).getKeyPrimitives()
XCTAssertEqual(primitives.modulus, n)
XCTAssertEqual(primitives.publicExponent, e)
}
}
}
11 changes: 11 additions & 0 deletions Tests/_CryptoExtrasTests/TestRSAEncryption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ final class TestRSAEncryption: XCTestCase {
e: bytesValues.randomElement()!
)
}

func testGetKeyPrimitives() throws {
for testVector in RFC9474TestVector.allValues {
let n = try Data(hexString: testVector.n)
let e = try Data(hexString: testVector.e)

let primitives = try _RSA.Encryption.PublicKey(n: n, e: e).getKeyPrimitives()
XCTAssertEqual(primitives.modulus, n)
XCTAssertEqual(primitives.publicExponent, e)
}
}
}

struct RSAEncryptionOAEPTestGroup: Codable {
Expand Down
11 changes: 11 additions & 0 deletions Tests/_CryptoExtrasTests/TestRSASigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,17 @@ final class TestRSASigning: XCTestCase {
)
}

func testGetKeyPrimitives() throws {
for testVector in RFC9474TestVector.allValues {
let n = try Data(hexString: testVector.n)
let e = try Data(hexString: testVector.e)

let primitives = try _RSA.Signing.PublicKey(n: n, e: e).getKeyPrimitives()
XCTAssertEqual(primitives.modulus, n)
XCTAssertEqual(primitives.publicExponent, e)
}
}

private func testPKCS1Group(_ group: RSAPKCS1TestGroup) throws {
let derKey: _RSA.Signing.PublicKey
let pemKey: _RSA.Signing.PublicKey
Expand Down

0 comments on commit 55201a8

Please sign in to comment.