Skip to content

Commit

Permalink
Update lower bound for RSA key sizes (#218)
Browse files Browse the repository at this point in the history
* Update lower bound for RSA key sizes

* Add unsafe initialiser for smaller keys

---------

Co-authored-by: Cory Benfield <[email protected]>
  • Loading branch information
ptoffy and Lukasa authored Mar 25, 2024
1 parent f0525da commit 89876ab
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 21 deletions.
137 changes: 126 additions & 11 deletions Sources/_CryptoExtras/RSA/RSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,50 @@ extension _RSA.Signing {

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init(pemRepresentation: String) throws {
self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)

guard self.keySizeInBits >= 2048 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafePEMRepresentation pemRepresentation: String) throws {
self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)

guard self.keySizeInBits >= 1024 else {
throw CryptoKitError.incorrectParameterSize
}

}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
self.backing = try BackingPublicKey(derRepresentation: derRepresentation)

guard self.keySizeInBits >= 2048 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init<Bytes: DataProtocol>(unsafeDERRepresentation derRepresentation: Bytes) throws {
self.backing = try BackingPublicKey(derRepresentation: derRepresentation)

guard self.keySizeInBits >= 1024 else {
throw CryptoKitError.incorrectParameterSize
}
Expand Down Expand Up @@ -103,33 +130,71 @@ extension _RSA.Signing {

/// Construct an RSA private key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init(pemRepresentation: String) throws {
self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)

guard self.keySizeInBits >= 2048 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafePEMRepresentation pemRepresentation: String) throws {
self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)

guard self.keySizeInBits >= 1024 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Construct an RSA private key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)

guard self.keySizeInBits >= 2048 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init<Bytes: DataProtocol>(unsafeDERRepresentation derRepresentation: Bytes) throws {
self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)

guard self.keySizeInBits >= 1024 else {
throw CryptoKitError.incorrectParameterSize
}
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
/// This constructor will refuse to generate keys smaller than 2048 bits. Callers that want to enforce minimum
/// key size requirements should validate `keySize` before use.
public init(keySize: _RSA.Signing.KeySize) throws {
guard keySize.bitCount >= 2048 else {
throw CryptoKitError.incorrectParameterSize
}
self.backing = try BackingPrivateKey(keySize: keySize)
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
/// key size requirements should validate `unsafekeySize` before use.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafeKeySize keySize: _RSA.Signing.KeySize) throws {
guard keySize.bitCount >= 1024 else {
throw CryptoKitError.incorrectParameterSize
}
Expand Down Expand Up @@ -335,18 +400,38 @@ extension _RSA.Encryption {

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init(pemRepresentation: String) throws {
self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafePEMRepresentation pemRepresentation: String) throws {
self.backing = try BackingPublicKey(pemRepresentation: pemRepresentation)
guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
self.backing = try BackingPublicKey(derRepresentation: derRepresentation)
guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init<Bytes: DataProtocol>(unsafeDERRepresentation derRepresentation: Bytes) throws {
self.backing = try BackingPublicKey(derRepresentation: derRepresentation)
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}
Expand All @@ -365,27 +450,57 @@ extension _RSA.Encryption {

/// Construct an RSA private key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init(pemRepresentation: String) throws {
self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)
guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key from a PEM representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafePEMRepresentation pemRepresentation: String) throws {
self.backing = try BackingPrivateKey(pemRepresentation: pemRepresentation)
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA private key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
public init<Bytes: DataProtocol>(derRepresentation: Bytes) throws {
self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)
guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Construct an RSA public key from a DER representation.
///
/// This constructor supports key sizes of 1024 bits or more. Users should validate that key sizes are appropriate
/// for their use-case.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init<Bytes: DataProtocol>(unsafeDERRepresentation derRepresentation: Bytes) throws {
self.backing = try BackingPrivateKey(derRepresentation: derRepresentation)
guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize }
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
/// This constructor will refuse to generate keys smaller than 2048 bits. Callers that want to enforce minimum
/// key size requirements should validate `keySize` before use.
public init(keySize: _RSA.Signing.KeySize) throws {
guard keySize.bitCount >= 2048 else { throw CryptoKitError.incorrectParameterSize }
self.backing = try BackingPrivateKey(keySize: keySize)
}

/// Randomly generate a new RSA private key of a given size.
///
/// This constructor will refuse to generate keys smaller than 1024 bits. Callers that want to enforce minimum
/// key size requirements should validate `keySize` before use.
/// - Warning: Key sizes less than 2048 are not recommended and should only be used for compatibility reasons.
public init(unsafeKeySize keySize: _RSA.Signing.KeySize) throws {
guard keySize.bitCount >= 1024 else { throw CryptoKitError.incorrectParameterSize }
self.backing = try BackingPrivateKey(keySize: keySize)
}
Expand Down
19 changes: 14 additions & 5 deletions Tests/_CryptoExtrasTests/TestRSAEncryption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,27 @@ final class TestRSAEncryption: XCTestCase {

func test_wycheproofOAEPVectors() throws {
try wycheproofTest(
jsonName: "rsa_oaep_2048_sha1_mgf1sha1_test",
jsonName: "rsa_oaep_misc_test",
testFunction: self.testOAEPGroup)
try wycheproofTest(
jsonName: "rsa_oaep_2048_sha256_mgf1sha256_test",
jsonName: "rsa_oaep_2048_sha1_mgf1sha1_test",
testFunction: self.testOAEPGroup)
try wycheproofTest(
jsonName: "rsa_oaep_misc_test",
jsonName: "rsa_oaep_2048_sha256_mgf1sha256_test",
testFunction: self.testOAEPGroup)
}

private func testOAEPGroup(_ group: RSAEncryptionOAEPTestGroup) throws {
let derPrivKey = try _RSA.Encryption.PrivateKey(derRepresentation: group.privateKeyDerBytes)
let pemPrivKey = try _RSA.Encryption.PrivateKey(pemRepresentation: group.privateKeyPem)
let derPrivKey: _RSA.Encryption.PrivateKey
let pemPrivKey: _RSA.Encryption.PrivateKey

if group.keysize < 2048 {
derPrivKey = try _RSA.Encryption.PrivateKey(unsafeDERRepresentation: group.privateKeyDerBytes)
pemPrivKey = try _RSA.Encryption.PrivateKey(unsafePEMRepresentation: group.privateKeyPem)
} else {
derPrivKey = try _RSA.Encryption.PrivateKey(derRepresentation: group.privateKeyDerBytes)
pemPrivKey = try _RSA.Encryption.PrivateKey(pemRepresentation: group.privateKeyPem)
}

XCTAssertEqual(derPrivKey.derRepresentation, pemPrivKey.derRepresentation)
XCTAssertEqual(derPrivKey.pemRepresentation, pemPrivKey.pemRepresentation)
Expand Down Expand Up @@ -77,6 +85,7 @@ struct RSAEncryptionOAEPTestGroup: Codable {
var sha: String
var tests: [RSAEncryptionTest]
var mgfSha: String
var keysize: Int

var privateKeyDerBytes: Data {
return try! Data(hexString: self.privateKeyPkcs8)
Expand Down
21 changes: 16 additions & 5 deletions Tests/_CryptoExtrasTests/TestRSASigning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final class TestRSASigning: XCTestCase {
try wycheproofTest(
jsonName: "rsa_signature_test",
testFunction: self.testPKCS1Group)

try wycheproofTest(
jsonName: "rsa_signature_2048_sha256_test",
testFunction: self.testPKCS1Group)
Expand Down Expand Up @@ -286,13 +286,15 @@ final class TestRSASigning: XCTestCase {
(_RSA.Signing.PrivateKey(keySize: .bits2048), 2048),
(_RSA.Signing.PrivateKey(keySize: .bits3072), 3072),
(_RSA.Signing.PrivateKey(keySize: .bits4096), 4096),
(_RSA.Signing.PrivateKey(keySize: .init(bitCount: 1024)), 1024),
(_RSA.Signing.PrivateKey(unsafeKeySize: .init(bitCount: 1024)), 1024),
]

for (key, size) in keysAndSizes {
XCTAssertEqual(size, key.keySizeInBits)
XCTAssertEqual(size, key.publicKey.keySizeInBits)
}

try XCTAssertThrowsError((_RSA.Signing.PrivateKey(keySize: .init(bitCount: 1024)), 1024))
}

func testRejectSmallKeys() throws {
Expand Down Expand Up @@ -676,8 +678,16 @@ final class TestRSASigning: XCTestCase {
}

private func testPKCS1Group(_ group: RSAPKCS1TestGroup) throws {
let derKey = try _RSA.Signing.PublicKey(derRepresentation: group.keyDerBytes)
let pemKey = try _RSA.Signing.PublicKey(pemRepresentation: group.keyPem)
let derKey: _RSA.Signing.PublicKey
let pemKey: _RSA.Signing.PublicKey

if group.keysize < 2048 {
derKey = try _RSA.Signing.PublicKey(unsafeDERRepresentation: group.keyDerBytes)
pemKey = try _RSA.Signing.PublicKey(unsafePEMRepresentation: group.keyPem)
} else {
derKey = try _RSA.Signing.PublicKey(derRepresentation: group.keyDerBytes)
pemKey = try _RSA.Signing.PublicKey(pemRepresentation: group.keyPem)
}

XCTAssertEqual(derKey.derRepresentation, pemKey.derRepresentation)
XCTAssertEqual(derKey.pemRepresentation, pemKey.pemRepresentation)
Expand Down Expand Up @@ -777,6 +787,7 @@ struct RSAPKCS1TestGroup: Codable {
var keyPem: String
var sha: String
var tests: [RSATest]
var keysize: Int

var keyDerBytes: Data {
return try! Data(hexString: self.keyDer)
Expand Down

0 comments on commit 89876ab

Please sign in to comment.