From 6edc4e0cf1d8ef8836b1aeac43072a7856e40e3a Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 14 Feb 2024 14:00:19 +0100 Subject: [PATCH 1/2] Update lower bound for RSA key sizes --- Sources/_CryptoExtras/RSA/RSA.swift | 40 +++++++++---------- .../TestRSAEncryption.swift | 3 +- Tests/_CryptoExtrasTests/TestRSASigning.swift | 14 ++++--- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Sources/_CryptoExtras/RSA/RSA.swift b/Sources/_CryptoExtras/RSA/RSA.swift index 8fe7e10c..d0550a67 100644 --- a/Sources/_CryptoExtras/RSA/RSA.swift +++ b/Sources/_CryptoExtras/RSA/RSA.swift @@ -49,24 +49,24 @@ 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 >= 1024 else { + 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 + /// This constructor supports key sizes of 2048 bits or more. Users should validate that key sizes are appropriate /// for their use-case. public init(derRepresentation: Bytes) throws { self.backing = try BackingPublicKey(derRepresentation: derRepresentation) - guard self.keySizeInBits >= 1024 else { + guard self.keySizeInBits >= 2048 else { throw CryptoKitError.incorrectParameterSize } } @@ -103,34 +103,34 @@ 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 >= 1024 else { + guard self.keySizeInBits >= 2048 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(derRepresentation: Bytes) throws { self.backing = try BackingPrivateKey(derRepresentation: derRepresentation) - guard self.keySizeInBits >= 1024 else { + guard self.keySizeInBits >= 2048 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 >= 1024 else { + guard keySize.bitCount >= 2048 else { throw CryptoKitError.incorrectParameterSize } self.backing = try BackingPrivateKey(keySize: keySize) @@ -335,20 +335,20 @@ 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 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(derRepresentation: Bytes) throws { self.backing = try BackingPublicKey(derRepresentation: derRepresentation) - guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize } + guard self.keySizeInBits >= 2048, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize } } public var pkcs1DERRepresentation: Data { self.backing.pkcs1DERRepresentation } @@ -365,28 +365,28 @@ 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 >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize } + guard self.keySizeInBits >= 2048, 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(derRepresentation: Bytes) throws { self.backing = try BackingPrivateKey(derRepresentation: derRepresentation) - guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize } + guard self.keySizeInBits >= 2048, 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 >= 1024 else { throw CryptoKitError.incorrectParameterSize } + guard keySize.bitCount >= 2048 else { throw CryptoKitError.incorrectParameterSize } self.backing = try BackingPrivateKey(keySize: keySize) } diff --git a/Tests/_CryptoExtrasTests/TestRSAEncryption.swift b/Tests/_CryptoExtrasTests/TestRSAEncryption.swift index 3ff65ac2..ed36f78a 100644 --- a/Tests/_CryptoExtrasTests/TestRSAEncryption.swift +++ b/Tests/_CryptoExtrasTests/TestRSAEncryption.swift @@ -25,9 +25,10 @@ final class TestRSAEncryption: XCTestCase { try wycheproofTest( jsonName: "rsa_oaep_2048_sha256_mgf1sha256_test", testFunction: self.testOAEPGroup) - try wycheproofTest( + try XCTAssertThrowsError(wycheproofTest( jsonName: "rsa_oaep_misc_test", testFunction: self.testOAEPGroup) + ) } private func testOAEPGroup(_ group: RSAEncryptionOAEPTestGroup) throws { diff --git a/Tests/_CryptoExtrasTests/TestRSASigning.swift b/Tests/_CryptoExtrasTests/TestRSASigning.swift index c89c8256..e6558960 100644 --- a/Tests/_CryptoExtrasTests/TestRSASigning.swift +++ b/Tests/_CryptoExtrasTests/TestRSASigning.swift @@ -18,10 +18,6 @@ import _CryptoExtras final class TestRSASigning: XCTestCase { func test_wycheproofPKCS1Vectors() throws { - try wycheproofTest( - jsonName: "rsa_signature_test", - testFunction: self.testPKCS1Group) - try wycheproofTest( jsonName: "rsa_signature_2048_sha256_test", testFunction: self.testPKCS1Group) @@ -41,6 +37,11 @@ final class TestRSASigning: XCTestCase { try wycheproofTest( jsonName: "rsa_signature_4096_sha512_test", testFunction: self.testPKCS1Group) + + try XCTAssertThrowsError(wycheproofTest( + jsonName: "rsa_signature_test", + testFunction: self.testPKCS1Group) + ) } func test_wycheproofPSSVectors() throws { @@ -286,13 +287,14 @@ 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), ] - + 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 { From a4b8f24662a789ec4a72b110554fcc2139c90b61 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 15 Feb 2024 10:44:03 +0100 Subject: [PATCH 2/2] Add unsafe initialiser for smaller keys --- Sources/_CryptoExtras/RSA/RSA.swift | 115 ++++++++++++++++++ .../TestRSAEncryption.swift | 20 ++- Tests/_CryptoExtrasTests/TestRSASigning.swift | 23 ++-- 3 files changed, 145 insertions(+), 13 deletions(-) diff --git a/Sources/_CryptoExtras/RSA/RSA.swift b/Sources/_CryptoExtras/RSA/RSA.swift index d0550a67..fbcec8ca 100644 --- a/Sources/_CryptoExtras/RSA/RSA.swift +++ b/Sources/_CryptoExtras/RSA/RSA.swift @@ -58,6 +58,20 @@ extension _RSA.Signing { 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. /// @@ -70,6 +84,19 @@ extension _RSA.Signing { 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(unsafeDERRepresentation derRepresentation: Bytes) throws { + self.backing = try BackingPublicKey(derRepresentation: derRepresentation) + + guard self.keySizeInBits >= 1024 else { + throw CryptoKitError.incorrectParameterSize + } + } public var pkcs1DERRepresentation: Data { self.backing.pkcs1DERRepresentation @@ -112,6 +139,19 @@ extension _RSA.Signing { 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. /// @@ -124,6 +164,19 @@ extension _RSA.Signing { 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(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. /// @@ -135,6 +188,18 @@ extension _RSA.Signing { } 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 + } + self.backing = try BackingPrivateKey(keySize: keySize) + } public var derRepresentation: Data { self.backing.derRepresentation @@ -341,6 +406,16 @@ extension _RSA.Encryption { 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 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. /// @@ -350,6 +425,16 @@ extension _RSA.Encryption { 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(unsafeDERRepresentation derRepresentation: Bytes) throws { + self.backing = try BackingPublicKey(derRepresentation: derRepresentation) + guard self.keySizeInBits >= 1024, self.keySizeInBits % 8 == 0 else { throw CryptoKitError.incorrectParameterSize } + } public var pkcs1DERRepresentation: Data { self.backing.pkcs1DERRepresentation } public var pkcs1PEMRepresentation: String { self.backing.pkcs1PEMRepresentation } @@ -371,6 +456,16 @@ extension _RSA.Encryption { 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. /// @@ -380,6 +475,16 @@ extension _RSA.Encryption { 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(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. /// @@ -390,6 +495,16 @@ extension _RSA.Encryption { 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) + } + public var derRepresentation: Data { self.backing.derRepresentation } public var pemRepresentation: String { self.backing.pemRepresentation } public var pkcs8PEMRepresentation: String { self.backing.pkcs8PEMRepresentation } diff --git a/Tests/_CryptoExtrasTests/TestRSAEncryption.swift b/Tests/_CryptoExtrasTests/TestRSAEncryption.swift index ed36f78a..84d63e57 100644 --- a/Tests/_CryptoExtrasTests/TestRSAEncryption.swift +++ b/Tests/_CryptoExtrasTests/TestRSAEncryption.swift @@ -19,21 +19,28 @@ import _CryptoExtras final class TestRSAEncryption: XCTestCase { func test_wycheproofOAEPVectors() throws { + try wycheproofTest( + jsonName: "rsa_oaep_misc_test", + testFunction: self.testOAEPGroup) try wycheproofTest( jsonName: "rsa_oaep_2048_sha1_mgf1sha1_test", testFunction: self.testOAEPGroup) try wycheproofTest( jsonName: "rsa_oaep_2048_sha256_mgf1sha256_test", testFunction: self.testOAEPGroup) - try XCTAssertThrowsError(wycheproofTest( - jsonName: "rsa_oaep_misc_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) @@ -78,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) diff --git a/Tests/_CryptoExtrasTests/TestRSASigning.swift b/Tests/_CryptoExtrasTests/TestRSASigning.swift index e6558960..cc18b576 100644 --- a/Tests/_CryptoExtrasTests/TestRSASigning.swift +++ b/Tests/_CryptoExtrasTests/TestRSASigning.swift @@ -18,6 +18,10 @@ import _CryptoExtras final class TestRSASigning: XCTestCase { func test_wycheproofPKCS1Vectors() throws { + try wycheproofTest( + jsonName: "rsa_signature_test", + testFunction: self.testPKCS1Group) + try wycheproofTest( jsonName: "rsa_signature_2048_sha256_test", testFunction: self.testPKCS1Group) @@ -37,11 +41,6 @@ final class TestRSASigning: XCTestCase { try wycheproofTest( jsonName: "rsa_signature_4096_sha512_test", testFunction: self.testPKCS1Group) - - try XCTAssertThrowsError(wycheproofTest( - jsonName: "rsa_signature_test", - testFunction: self.testPKCS1Group) - ) } func test_wycheproofPSSVectors() throws { @@ -287,6 +286,7 @@ 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(unsafeKeySize: .init(bitCount: 1024)), 1024), ] for (key, size) in keysAndSizes { @@ -678,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) @@ -779,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)