Skip to content

Commit 55201a8

Browse files
ptoffyLukasa
andauthored
extras: Add RSA primitives decoding API (#249)
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]>
1 parent 81bee98 commit 55201a8

File tree

7 files changed

+105
-0
lines changed

7 files changed

+105
-0
lines changed

Sources/_CryptoExtras/RSA/RSA+BlindSigning.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ extension _RSA.BlindSigning {
2626
public struct PublicKey<H: HashFunction>: Sendable {
2727
public typealias Parameters = _RSA.BlindSigning.Parameters<H>
2828

29+
public struct Primitives: Sendable, Hashable {
30+
public var modulus: Data
31+
public var publicExponent: Data
32+
33+
public init(modulus: Data, publicExponent: Data) {
34+
self.modulus = modulus
35+
self.publicExponent = publicExponent
36+
}
37+
}
38+
2939
private var backing: BackingPublicKey
3040
private let parameters: Parameters
3141

@@ -113,6 +123,11 @@ extension _RSA.BlindSigning {
113123
self.backing = backing
114124
self.parameters = parameters
115125
}
126+
127+
public func getKeyPrimitives() throws -> Primitives {
128+
let (n, e) = try self.backing.getKeyPrimitives()
129+
return Primitives(modulus: n, publicExponent: e)
130+
}
116131
}
117132
}
118133

Sources/_CryptoExtras/RSA/RSA.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ extension _RSA {
4545

4646
extension _RSA.Signing {
4747
public struct PublicKey: Sendable {
48+
public struct Primitives: Sendable, Hashable {
49+
public var modulus: Data
50+
public var publicExponent: Data
51+
52+
public init(modulus: Data, publicExponent: Data) {
53+
self.modulus = modulus
54+
self.publicExponent = publicExponent
55+
}
56+
}
57+
4858
private var backing: BackingPublicKey
4959

5060
/// Construct an RSA public key from a PEM representation.
@@ -132,6 +142,11 @@ extension _RSA.Signing {
132142
fileprivate init(_ backing: BackingPublicKey) {
133143
self.backing = backing
134144
}
145+
146+
public func getKeyPrimitives() throws -> Primitives {
147+
let (n, e) = try self.backing.getKeyPrimitives()
148+
return Primitives(modulus: n, publicExponent: e)
149+
}
135150
}
136151
}
137152

@@ -426,6 +441,16 @@ extension _RSA.Signing {
426441
extension _RSA.Encryption {
427442
/// Identical to ``_RSA/Signing/PublicKey``.
428443
public struct PublicKey {
444+
public struct Primitives: Sendable, Hashable {
445+
public var modulus: Data
446+
public var publicExponent: Data
447+
448+
public init(modulus: Data, publicExponent: Data) {
449+
self.modulus = modulus
450+
self.publicExponent = publicExponent
451+
}
452+
}
453+
429454
private var backing: BackingPublicKey
430455

431456
/// Construct an RSA public key from a PEM representation.
@@ -483,6 +508,11 @@ extension _RSA.Encryption {
483508
public var pemRepresentation: String { self.backing.pemRepresentation }
484509
public var keySizeInBits: Int { self.backing.keySizeInBits }
485510
fileprivate init(_ backing: BackingPublicKey) { self.backing = backing }
511+
512+
public func getKeyPrimitives() throws -> Primitives {
513+
let (n, e) = try self.backing.getKeyPrimitives()
514+
return Primitives(modulus: n, publicExponent: e)
515+
}
486516
}
487517

488518
/// Identical to ``_RSA/Signing/PrivateKey``.

Sources/_CryptoExtras/RSA/RSA_boring.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ internal struct BoringSSLRSAPublicKey: Sendable {
6060
fileprivate init(_ backing: Backing) {
6161
self.backing = backing
6262
}
63+
64+
func getKeyPrimitives() throws -> (n: Data, e: Data) {
65+
try self.backing.getKeyPrimitives()
66+
}
6367
}
6468

6569

@@ -465,6 +469,22 @@ extension BoringSSLRSAPublicKey {
465469
deinit {
466470
CCryptoBoringSSL_EVP_PKEY_free(self.pointer)
467471
}
472+
473+
fileprivate func getKeyPrimitives() -> (n: Data, e: Data) {
474+
let key = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer)
475+
476+
func getPrimitive(_ getPointer: (OpaquePointer?) -> UnsafePointer<BIGNUM>?) -> Data {
477+
let ptr = getPointer(key)
478+
let size = Int(CCryptoBoringSSL_BN_num_bytes(ptr))
479+
var data = Data(count: size)
480+
data.withUnsafeMutableBytes { dataPtr in
481+
_ = CCryptoBoringSSL_BN_bn2bin(ptr, dataPtr.baseAddress)
482+
}
483+
return data
484+
}
485+
486+
return (getPrimitive(CCryptoBoringSSL_RSA_get0_n), getPrimitive(CCryptoBoringSSL_RSA_get0_e))
487+
}
468488
}
469489
}
470490

Sources/_CryptoExtras/RSA/RSA_security.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ internal struct SecurityRSAPublicKey: @unchecked Sendable {
7272
fileprivate init(_ backing: SecKey) {
7373
self.backing = backing
7474
}
75+
76+
func getKeyPrimitives() throws -> (n: Data, e: Data) {
77+
try BoringSSLRSAPublicKey(derRepresentation: self.derRepresentation).getKeyPrimitives()
78+
}
7579
}
7680

7781
// unchecked sendable until `SecKey` gets sendable annotations

Tests/_CryptoExtrasTests/TestRSABlindSigning.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,4 +202,18 @@ final class TestRSABlindSigning: XCTestCase {
202202
parameters: .RSABSSA_SHA384_PSS_Randomized
203203
)
204204
}
205+
206+
func testGetKeyPrimitives() throws {
207+
for testVector in RFC9474TestVector.allValues {
208+
let n = try Data(hexString: testVector.n)
209+
let e = try Data(hexString: testVector.e)
210+
211+
let primitives = try _RSA.BlindSigning.PublicKey(
212+
n: n, e: e,
213+
parameters: testVector.parameters
214+
).getKeyPrimitives()
215+
XCTAssertEqual(primitives.modulus, n)
216+
XCTAssertEqual(primitives.publicExponent, e)
217+
}
218+
}
205219
}

Tests/_CryptoExtrasTests/TestRSAEncryption.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ final class TestRSAEncryption: XCTestCase {
108108
e: bytesValues.randomElement()!
109109
)
110110
}
111+
112+
func testGetKeyPrimitives() throws {
113+
for testVector in RFC9474TestVector.allValues {
114+
let n = try Data(hexString: testVector.n)
115+
let e = try Data(hexString: testVector.e)
116+
117+
let primitives = try _RSA.Encryption.PublicKey(n: n, e: e).getKeyPrimitives()
118+
XCTAssertEqual(primitives.modulus, n)
119+
XCTAssertEqual(primitives.publicExponent, e)
120+
}
121+
}
111122
}
112123

113124
struct RSAEncryptionOAEPTestGroup: Codable {

Tests/_CryptoExtrasTests/TestRSASigning.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,17 @@ final class TestRSASigning: XCTestCase {
708708
)
709709
}
710710

711+
func testGetKeyPrimitives() throws {
712+
for testVector in RFC9474TestVector.allValues {
713+
let n = try Data(hexString: testVector.n)
714+
let e = try Data(hexString: testVector.e)
715+
716+
let primitives = try _RSA.Signing.PublicKey(n: n, e: e).getKeyPrimitives()
717+
XCTAssertEqual(primitives.modulus, n)
718+
XCTAssertEqual(primitives.publicExponent, e)
719+
}
720+
}
721+
711722
private func testPKCS1Group(_ group: RSAPKCS1TestGroup) throws {
712723
let derKey: _RSA.Signing.PublicKey
713724
let pemKey: _RSA.Signing.PublicKey

0 commit comments

Comments
 (0)