Skip to content

Commit 6f70fa9

Browse files
mschragMike Schrag
andauthored
Add RSA PKCSv1.5 padding support for legacy compat (#425)
Add RSA PKCSv1.5 padding support for legacy compat ### Checklist - [X] I've run tests to see all new and existing tests pass - [X] I've followed the code style of the rest of the project - [X] I've read the [Contribution Guidelines](CONTRIBUTING.md) - [X] I've updated the documentation if necessary #### If you've made changes to `gyb` files - [n/a] I've run `./scripts/generate_boilerplate_files_with_gyb.sh` and included updated generated files in a commit of this pull request ### Motivation: I have a scenario that requires me to be able to encrypt and decrypt values that use RSA PKCSv1.5 padding. This is known to be a weak algorithm, so we explicitly want to discourage its use, but there are scenarios where you need the algorithm for legacy compatibility, so we want to provide the capability, even if we strongly discourage its use. ### Modifications: Added a new enum + constant + passthrough to allow users to specify RSA PKCSv1.5. From the internal review, we thought it would be best to explicitly name the constants in a way that discouraged use without very intentional needs, hence the "weak" and "insecure" references in all of the naming. ### Result: You can now specify PKCSv1.5 padding for use in encryption and decryption. This is only adding constants, and the enums are not public, so there should not be any impact on compatibility (source or binary). Co-authored-by: Mike Schrag <[email protected]>
1 parent e8ed886 commit 6f70fa9

File tree

3 files changed

+80
-1
lines changed

3 files changed

+80
-1
lines changed

Sources/CryptoExtras/RSA/RSA.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ extension _RSA.Encryption {
655655
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *)
656656
public struct Padding: Sendable {
657657
internal enum Backing {
658+
case _weakAndInsecure_pkcs1v1_5
658659
case pkcs1_oaep(Digest)
659660
}
660661

@@ -663,7 +664,18 @@ extension _RSA.Encryption {
663664
private init(_ backing: Backing) {
664665
self.backing = backing
665666
}
666-
667+
668+
/// PKCS#1 v1.5 padding
669+
///
670+
/// As defined by [RFC 8017 § 7.2](https://datatracker.ietf.org/doc/html/rfc8017#section-7.2).
671+
///
672+
/// This padding exists only for legacy compatibility and is known to be
673+
/// weak and insecure. This algorithm is vulnerable to chosen-ciphertext
674+
/// attacks outlined in http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf.
675+
///
676+
/// When you have a choice, you should always favor OAEP over this.
677+
public static let _WEAK_AND_INSECURE_PKCS_V1_5 = Self(._weakAndInsecure_pkcs1v1_5)
678+
667679
/// PKCS#1 OAEP padding
668680
///
669681
/// As defined by [RFC 8017 § 7.1](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
@@ -711,6 +723,9 @@ extension _RSA.Encryption.PublicKey {
711723
/// 4096|PKCS-OAEP|470 bytes
712724
public func maximumEncryptSize(with padding: _RSA.Encryption.Padding) -> Int {
713725
switch padding.backing {
726+
case ._weakAndInsecure_pkcs1v1_5:
727+
// https://www.rfc-editor.org/rfc/rfc8017#section-7.2
728+
return (self.keySizeInBits / 8) - 11
714729
case let .pkcs1_oaep(Digest):
715730
// https://datatracker.ietf.org/doc/html/rfc8017#section-7.1.1
716731
return (self.keySizeInBits / 8) - (2 * Digest.hashBitLength / 8) - 2

Sources/CryptoExtras/RSA/RSA_boring.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,8 @@ extension BoringSSLRSAPublicKey {
418418
CCryptoBoringSSL_EVP_PKEY_encrypt_init(ctx)
419419

420420
switch padding.backing {
421+
case ._weakAndInsecure_pkcs1v1_5:
422+
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)
421423
case let .pkcs1_oaep(digest):
422424
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING)
423425
switch digest {
@@ -883,6 +885,8 @@ extension BoringSSLRSAPrivateKey {
883885

884886
CCryptoBoringSSL_EVP_PKEY_decrypt_init(ctx)
885887
switch padding.backing {
888+
case ._weakAndInsecure_pkcs1v1_5:
889+
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)
886890
case let .pkcs1_oaep(digest):
887891
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING)
888892
switch digest {

Tests/CryptoExtrasTests/TestRSAEncryption.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,66 @@ final class TestRSAEncryption: XCTestCase {
169169
XCTAssertEqual(214, pubKey2048.maximumEncryptSize(with: .PKCS1_OAEP))
170170
XCTAssertEqual(190, pubKey2048.maximumEncryptSize(with: .PKCS1_OAEP_SHA256))
171171
}
172+
173+
func testPKCS1() throws {
174+
let pubKeyPEM = """
175+
-----BEGIN RSA PUBLIC KEY-----
176+
MIIBCgKCAQEAv6ElnElHGQO1BC5wsU/S01tHK8GbCnDLkxkS1259kOU250pEjOJa
177+
ceOGFnhYzE36KXmKTrGw3o1m5vgbQz88j7/tNjymAX990I3YdWTnGQYcypp8c4TD
178+
wHIj5Q3OHYXAC0KUHRBSKBeS+QJybrMI6SAQbFpHh9C3Q9W3WTtSAVqs8VveS4Jc
179+
j4a3K21MNeHgNfyxwn3KTrrNs/c0yOvWlwyfxYTdWLFVVp2hn6YVQUfo7twM4BCE
180+
Xz/6gR03NpqjVqKeyBmmMtDIy82+BzG4vd3jm02zwNvahsBy9b2NCOjq3y2ud72b
181+
Q4bYU9/r/ccApts5BIW8ASwmYSGSmE6MzwIDAQAB
182+
-----END RSA PUBLIC KEY-----
183+
"""
184+
let privKeyPEM = """
185+
-----BEGIN PRIVATE KEY-----
186+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/oSWcSUcZA7UE
187+
LnCxT9LTW0crwZsKcMuTGRLXbn2Q5TbnSkSM4lpx44YWeFjMTfopeYpOsbDejWbm
188+
+BtDPzyPv+02PKYBf33Qjdh1ZOcZBhzKmnxzhMPAciPlDc4dhcALQpQdEFIoF5L5
189+
AnJuswjpIBBsWkeH0LdD1bdZO1IBWqzxW95LglyPhrcrbUw14eA1/LHCfcpOus2z
190+
9zTI69aXDJ/FhN1YsVVWnaGfphVBR+ju3AzgEIRfP/qBHTc2mqNWop7IGaYy0MjL
191+
zb4HMbi93eObTbPA29qGwHL1vY0I6OrfLa53vZtDhthT3+v9xwCm2zkEhbwBLCZh
192+
IZKYTozPAgMBAAECggEAGncL9bCdFBRR/JjZUXOfvzbc9msPmXqIcvFEi+Ijj05I
193+
rdqw6vAb45yzmQjX4qdmRDIX6tRZg/LtYjqjsT7bg1LTVOk9V/mei537ZgMgc3FH
194+
qqd5Ro7wZfSdhnXIoIUnR6bTQ8xMPGM9FgzDdwxcz61w9zXkqRonJUQvxTAPHEaH
195+
SiNhRP8LUjzB0Y2ZYVXMWbs0nPPrSE+xuzjcGRX3lvz7nNOM1N4EyWto1RVJIlry
196+
4EV8RFczo3BjPXZFbtval76AGPmurDVqBdHpDN6IBZdhz4ZX/0fq8NR2p8/6S5VZ
197+
4Ylcth1S3HErcnG2UqT8rl/P3m9idTv4EZOg6HziyQKBgQDsmxaQCFTJnDwxSQR+
198+
4j9WsgDpSxvCdnUtMX9w77aw3EIdcHkhnX99jTvNkt3uwGAsVsx4x7ilj1eaZOfl
199+
soMIX1WBBx11yN4GOw173VmzC0LtaBGTh/2ollxuNoEqYxkuKLNxWxTGW0uc5TVA
200+
0hK2c6cF4eZ5sH07aIU6HIIknQKBgQDPVkeyxhF6lvgobLxFQOChOyLVcb1EymnU
201+
W1zF27HciA+0FuaiWTj69bKoR8d+ZIFtIzVvjo7MfoFRJvEZmDGy5+I8HhpSW6JQ
202+
NLdaRI5RGYxbEGmmC48icknXioZJ8JOXhbVuMyT4uLaN5D1M47ZYaq75dPM83fqZ
203+
BDc+izDdWwKBgQCJw5d0j9VGeni1va0nb/avNP/A1qG4LZ72jH6GtJysB+NbHtT4
204+
1KqZ4PU0MlKUpGCbEIMHxEpn47l/RUec/765zkCL2ye1IBreh93HBFApJuJ2NwUc
205+
4K66TapN5eB5XLAZp0ssMns7L4csOG00a9zHbTmP/ENlEXUpdSc1ecnxJQKBgFsJ
206+
n2G35mTVdREK7X/bBMbGmHzv/BMAbYd4tjuKQ4Z5l6uTgqE2W/aVe2S4X7f3mXy6
207+
QPRCvCC+Szm+x45dbTUI7CVJcnVHFvXwr7FK+NJTTXWOt1TZLngJhrLFeEFvCN83
208+
Lnq8qjcro7yZwvDH64DXFw0hdMv9C9O0Li2gIEyRAoGBAK+C7Stfm3vViV2YfByt
209+
MI73t2rN+t3ffnKsXZtGzWW1kxv4cueiAdeM7QwE2AaN7yKzsSMfsSXe+/r69wUR
210+
UPB8NcGLKWE/gJuIcitQx1HCbQZ3AplRK6xhjDVXG1A5SszQVx09hhq76JVBm0sJ
211+
DDYta1f+sEfAS750XLJ7A1h0
212+
-----END PRIVATE KEY-----
213+
"""
214+
let pubKey = try _RSA.Encryption.PublicKey(pemRepresentation: pubKeyPEM)
215+
let privKey = try _RSA.Encryption.PrivateKey(pemRepresentation: privKeyPEM)
216+
let msgs = [
217+
// empty
218+
"",
219+
// short
220+
"467A8AFB-9165-484A-8377-B66BCACD774A",
221+
// example text
222+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla rutrum odio ut sem luctus, non finibus diam congue. Suspendisse nisl enim, placerat consectetur dolor non, mattis sollicitudin augue.",
223+
// max length
224+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla rutrum odio ut sem luctus, non finibus diam congue. Suspendisse nisl enim, placerat consectetur dolor non, mattis sollicitudin augue. Pellentesque a est eget enim efficitur volutpat ",
225+
]
226+
for msg in msgs {
227+
let msgEnc = try pubKey.encrypt(msg.data(using: .utf8)!, padding: ._WEAK_AND_INSECURE_PKCS_V1_5)
228+
let msgDec = String(data: try privKey.decrypt(msgEnc, padding: ._WEAK_AND_INSECURE_PKCS_V1_5), encoding: .utf8)!
229+
XCTAssertEqual(msg, msgDec)
230+
}
231+
}
172232
}
173233

174234
struct RSAEncryptionOAEPTestGroup: Codable {

0 commit comments

Comments
 (0)