Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update lower bound for RSA key sizes #218

Merged
merged 3 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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