Skip to content

Commit

Permalink
Add support for pkcs1 oaep sha256 (#206)
Browse files Browse the repository at this point in the history
Motivation

Add RSA OAEP-sha256 support in addition to the increasingly aged SHA-1
implementation.

Modifications

* Add a `Digest` enum to `_RSA.Encryption.Padding`
* Add a digest associated type to the `.pkcs1_oaep` padding enum case
  to allow us to distinguish different digest hash functions.
* Add a `PKCS1_OAEP_SHA256` public static let to allow users to use the
  new hash function.
* Enable SHA-256 RSA encryption tests

Result

* Support for RSA OAEP-sha256
  • Loading branch information
rnro authored Nov 6, 2023
1 parent 3df7fa0 commit f4b21db
Show file tree
Hide file tree
Showing 5 changed files with 446 additions and 25 deletions.
14 changes: 10 additions & 4 deletions Sources/_CryptoExtras/RSA/RSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -401,19 +401,25 @@ extension _RSA.Encryption {
extension _RSA.Encryption {
public struct Padding {
internal enum Backing {
case pkcs1_oaep
case pkcs1_oaep(Digest)
}

internal var backing: Backing

private init(_ backing: Backing) {
self.backing = backing
}

/// PKCS#1 OAEP padding
///
/// As defined by [RFC 8017 § 7.1](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
public static let PKCS1_OAEP = Self(.pkcs1_oaep)
public static let PKCS1_OAEP = Self(.pkcs1_oaep(.sha1))
public static let PKCS1_OAEP_SHA256 = Self(.pkcs1_oaep(.sha256))
}

internal enum Digest {
case sha1
case sha256
}
}

Expand Down
34 changes: 22 additions & 12 deletions Sources/_CryptoExtras/RSA/RSA_boring.swift
Original file line number Diff line number Diff line change
Expand Up @@ -278,19 +278,24 @@ extension BoringSSLRSAPublicKey {
let contiguousData: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
try output.withUnsafeMutableBytes { bufferPtr in
try contiguousData.withUnsafeBytes { dataPtr in
let rawPadding: CInt
switch padding.backing {
case .pkcs1_oaep: rawPadding = RSA_PKCS1_OAEP_PADDING
}

// `nil` 'engine' defaults to the standard implementation with no hooks
let ctx = CCryptoBoringSSL_EVP_PKEY_CTX_new(self.pointer, nil)
defer {
CCryptoBoringSSL_EVP_PKEY_CTX_free(ctx)
}

CCryptoBoringSSL_EVP_PKEY_encrypt_init(ctx)
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, rawPadding)

switch padding.backing {
case let .pkcs1_oaep(digest):
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING)
switch digest {
case .sha1:
break // default case, nothing to set
case .sha256:
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, CCryptoBoringSSL_EVP_sha256())
}
}

var writtenLength = bufferPtr.count
let rc = CCryptoBoringSSLShims_EVP_PKEY_encrypt(
Expand Down Expand Up @@ -519,18 +524,23 @@ extension BoringSSLRSAPrivateKey {
let contiguousData: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data)
let writtenLength: CInt = try output.withUnsafeMutableBytes { bufferPtr in
try contiguousData.withUnsafeBytes { dataPtr in
let rawPadding: CInt
switch padding.backing {
case .pkcs1_oaep: rawPadding = RSA_PKCS1_OAEP_PADDING
}

let ctx = CCryptoBoringSSL_EVP_PKEY_CTX_new(self.pointer, nil)
defer {
CCryptoBoringSSL_EVP_PKEY_CTX_free(ctx)
}

CCryptoBoringSSL_EVP_PKEY_decrypt_init(ctx)
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, rawPadding)
switch padding.backing {
case let .pkcs1_oaep(digest):
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING)
switch digest {
case .sha1:
break // default case, nothing to set
case .sha256:
CCryptoBoringSSL_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, CCryptoBoringSSL_EVP_sha256())
}
}


var writtenLength = bufferPtr.count

Expand Down
9 changes: 7 additions & 2 deletions Sources/_CryptoExtras/RSA/RSA_security.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,13 @@ extension SecKeyAlgorithm {

fileprivate init(padding: _RSA.Encryption.Padding) throws {
switch padding.backing {
case .pkcs1_oaep:
self = .rsaEncryptionOAEPSHA1
case .pkcs1_oaep(let digest):
switch digest {
case .sha1:
self = .rsaEncryptionOAEPSHA1
case .sha256:
self = .rsaEncryptionOAEPSHA256
}
}
}
}
Expand Down
22 changes: 15 additions & 7 deletions Tests/_CryptoExtrasTests/TestRSAEncryption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ final class TestRSAEncryption: XCTestCase {
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 wycheproofTest(
jsonName: "rsa_oaep_misc_test",
testFunction: self.testOAEPGroup)
Expand All @@ -36,8 +39,13 @@ final class TestRSAEncryption: XCTestCase {

let derPubKey = derPrivKey.publicKey

guard group.sha == "SHA-1", group.mgfSha == "SHA-1" else {
// We currently only support SHA-1 OAEP, which is very legacy but oh well.
let padding: _RSA.Encryption.Padding
if group.sha == "SHA-1", group.mgfSha == "SHA-1" {
padding = .PKCS1_OAEP
} else if group.sha == "SHA-256", group.mgfSha == "SHA-256" {
padding = .PKCS1_OAEP_SHA256
} else {
// We currently only support SHA-1, SHA-256.
return
}

Expand All @@ -47,12 +55,12 @@ final class TestRSAEncryption: XCTestCase {
continue
}
let valid: Bool

do {
let decryptResult = try derPrivKey.decrypt(test.ciphertextBytes, padding: .PKCS1_OAEP)
let encryptResult = try derPubKey.encrypt(test.messageBytes, padding: .PKCS1_OAEP)
let decryptResult2 = try derPrivKey.decrypt(encryptResult, padding: .PKCS1_OAEP)
let decryptResult = try derPrivKey.decrypt(test.ciphertextBytes, padding: padding)
let encryptResult = try derPubKey.encrypt(test.messageBytes, padding: padding)
let decryptResult2 = try derPrivKey.decrypt(encryptResult, padding: padding)

valid = (test.messageBytes == decryptResult && decryptResult2 == decryptResult)
} catch {
valid = false
Expand Down
Loading

0 comments on commit f4b21db

Please sign in to comment.