diff --git a/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h b/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h index 4ef86401..ac26435d 100644 --- a/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h +++ b/Sources/CCryptoBoringSSLShims/include/CCryptoBoringSSLShims.h @@ -124,6 +124,14 @@ int CCryptoBoringSSLShims_RSA_public_encrypt(int flen, const void *from, void *t int CCryptoBoringSSLShims_RSA_private_decrypt(int flen, const void *from, void *to, RSA *rsa, int padding); +int CCryptoBoringSSLShims_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, void *out, + size_t *out_len, const void *in, + size_t in_len); + +int CCryptoBoringSSLShims_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, void *out, + size_t *out_len, const void *in, + size_t in_len); + #if defined(__cplusplus) } #endif // defined(__cplusplus) diff --git a/Sources/CCryptoBoringSSLShims/shims.c b/Sources/CCryptoBoringSSLShims/shims.c index 80c82f1d..051fe100 100644 --- a/Sources/CCryptoBoringSSLShims/shims.c +++ b/Sources/CCryptoBoringSSLShims/shims.c @@ -152,3 +152,15 @@ int CCryptoBoringSSLShims_RSA_private_decrypt(int flen, const void *from, void * RSA *rsa, int padding) { return CCryptoBoringSSL_RSA_private_decrypt(flen, from, to, rsa, padding); } + +int CCryptoBoringSSLShims_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, void *out, + size_t *out_len, const void *in, + size_t in_len) { + return CCryptoBoringSSL_EVP_PKEY_encrypt(ctx, out, out_len, in, in_len); +} + +int CCryptoBoringSSLShims_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, void *out, + size_t *out_len, const void *in, + size_t in_len) { + return CCryptoBoringSSL_EVP_PKEY_decrypt(ctx, out, out_len, in, in_len); +} diff --git a/Sources/_CryptoExtras/RSA/RSA_boring.swift b/Sources/_CryptoExtras/RSA/RSA_boring.swift index f0169ca4..f8fb37ad 100644 --- a/Sources/_CryptoExtras/RSA/RSA_boring.swift +++ b/Sources/_CryptoExtras/RSA/RSA_boring.swift @@ -122,17 +122,20 @@ extension BoringSSLRSAPublicKey { } fileprivate init(copying other: Backing) { - self.pointer = CCryptoBoringSSL_RSAPublicKey_dup(other.pointer) + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() + let rsaPublicKey = CCryptoBoringSSL_RSAPublicKey_dup(CCryptoBoringSSL_EVP_PKEY_get0_RSA(other.pointer)) + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPublicKey) } fileprivate init(pemRepresentation: String) throws { var pemRepresentation = pemRepresentation + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() // There are two encodings for RSA public keys: PKCS#1 and the SPKI form. // The SPKI form is what we support for EC keys, so we try that first, then we // fall back to the PKCS#1 form if that parse fails. do { - self.pointer = try pemRepresentation.withUTF8 { utf8Ptr in + let rsaPublicKey = try pemRepresentation.withUTF8 { utf8Ptr in return try BIOHelper.withReadOnlyMemoryBIO(wrapping: utf8Ptr) { bio in guard let key = CCryptoBoringSSL_PEM_read_bio_RSA_PUBKEY(bio, nil, nil, nil) else { throw CryptoKitError.internalBoringSSLError() @@ -140,14 +143,21 @@ extension BoringSSLRSAPublicKey { return key } } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPublicKey) } catch { - self.pointer = try pemRepresentation.withUTF8 { utf8Ptr in - return try BIOHelper.withReadOnlyMemoryBIO(wrapping: utf8Ptr) { bio in - guard let key = CCryptoBoringSSL_PEM_read_bio_RSAPublicKey(bio, nil, nil, nil) else { - throw CryptoKitError.internalBoringSSLError() + do { + let rsaPublicKey = try pemRepresentation.withUTF8 { utf8Ptr in + return try BIOHelper.withReadOnlyMemoryBIO(wrapping: utf8Ptr) { bio in + guard let key = CCryptoBoringSSL_PEM_read_bio_RSAPublicKey(bio, nil, nil, nil) else { + throw CryptoKitError.internalBoringSSLError() + } + return key } - return key } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPublicKey) + } catch { + CCryptoBoringSSL_EVP_PKEY_free(self.pointer) + throw error } } } @@ -162,11 +172,12 @@ extension BoringSSLRSAPublicKey { } private init(contiguousDerRepresentation: Bytes) throws { + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() // There are two encodings for RSA public keys: PKCS#1 and the SPKI form. // The SPKI form is what we support for EC keys, so we try that first, then we // fall back to the PKCS#1 form if that parse fails. do { - self.pointer = try contiguousDerRepresentation.withUnsafeBytes { derPtr in + let rsaPublicKey = try contiguousDerRepresentation.withUnsafeBytes { derPtr in return try BIOHelper.withReadOnlyMemoryBIO(wrapping: derPtr) { bio in guard let key = CCryptoBoringSSL_d2i_RSA_PUBKEY_bio(bio, nil) else { throw CryptoKitError.internalBoringSSLError() @@ -174,21 +185,29 @@ extension BoringSSLRSAPublicKey { return key } } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPublicKey) } catch { - self.pointer = try contiguousDerRepresentation.withUnsafeBytes { derPtr in - return try BIOHelper.withReadOnlyMemoryBIO(wrapping: derPtr) { bio in - guard let key = CCryptoBoringSSL_d2i_RSAPublicKey_bio(bio, nil) else { - throw CryptoKitError.internalBoringSSLError() + do { + let rsaPublicKey = try contiguousDerRepresentation.withUnsafeBytes { derPtr in + return try BIOHelper.withReadOnlyMemoryBIO(wrapping: derPtr) { bio in + guard let key = CCryptoBoringSSL_d2i_RSAPublicKey_bio(bio, nil) else { + throw CryptoKitError.internalBoringSSLError() + } + return key } - return key } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPublicKey) + } catch { + CCryptoBoringSSL_EVP_PKEY_free(self.pointer) + throw error } } } fileprivate var pkcs1DERRepresentation: Data { return BIOHelper.withWritableMemoryBIO { bio in - let rc = CCryptoBoringSSL_i2d_RSAPublicKey_bio(bio, self.pointer) + let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let rc = CCryptoBoringSSL_i2d_RSAPublicKey_bio(bio, rsaPublicKey) precondition(rc == 1) return try! Data(copyingMemoryBIO: bio) @@ -201,7 +220,8 @@ extension BoringSSLRSAPublicKey { fileprivate var derRepresentation: Data { return BIOHelper.withWritableMemoryBIO { bio in - let rc = CCryptoBoringSSL_i2d_RSA_PUBKEY_bio(bio, self.pointer) + let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let rc = CCryptoBoringSSL_i2d_RSA_PUBKEY_bio(bio, rsaPublicKey) precondition(rc == 1) return try! Data(copyingMemoryBIO: bio) @@ -213,11 +233,13 @@ extension BoringSSLRSAPublicKey { } fileprivate var keySizeInBits: Int { - return Int(CCryptoBoringSSL_RSA_size(self.pointer)) * 8 + let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + return Int(CCryptoBoringSSL_RSA_size(rsaPublicKey)) * 8 } fileprivate func isValidSignature(_ signature: _RSA.Signing.RSASignature, for digest: D, padding: _RSA.Signing.Padding) -> Bool { let hashDigestType = try! DigestType(forDigestType: D.self) + let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) return signature.withUnsafeBytes { signaturePtr in let rc: CInt = digest.withUnsafeBytes { digestPtr in @@ -229,11 +251,11 @@ extension BoringSSLRSAPublicKey { digestPtr.count, signaturePtr.baseAddress, signaturePtr.count, - self.pointer + rsaPublicKey ) case .pss: return CCryptoBoringSSLShims_RSA_verify_pss_mgf1( - self.pointer, + rsaPublicKey, digestPtr.baseAddress, digestPtr.count, hashDigestType.dispatchTable, @@ -249,34 +271,47 @@ extension BoringSSLRSAPublicKey { } fileprivate func encrypt(_ data: D, padding: _RSA.Encryption.Padding) throws -> Data { - let outputSize = Int(CCryptoBoringSSL_RSA_size(self.pointer)) + let rsaPublicKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let outputSize = Int(CCryptoBoringSSL_RSA_size(rsaPublicKey)) var output = Data(count: outputSize) let contiguousData: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - let rc: CInt = output.withUnsafeMutableBytes { bufferPtr in - contiguousData.withUnsafeBytes { dataPtr in + 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 rc = CCryptoBoringSSLShims_RSA_public_encrypt( - CInt(dataPtr.count), - dataPtr.baseAddress, + + // `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) + + var writtenLength = bufferPtr.count + let rc = CCryptoBoringSSLShims_EVP_PKEY_encrypt( + ctx, bufferPtr.baseAddress, - self.pointer, - rawPadding + &writtenLength, + dataPtr.baseAddress, + dataPtr.count ) - return rc + precondition(writtenLength == bufferPtr.count, "PKEY encrypt actual written length should match RSA key size.") + + guard rc == 1 else { + throw CryptoKitError.internalBoringSSLError() + } } } - if rc == -1 { - throw CryptoKitError.internalBoringSSLError() - } return output } deinit { - CCryptoBoringSSL_RSA_free(self.pointer) + CCryptoBoringSSL_EVP_PKEY_free(self.pointer) } } } @@ -286,13 +321,16 @@ extension BoringSSLRSAPrivateKey { private let pointer: OpaquePointer fileprivate init(copying other: Backing) { - self.pointer = CCryptoBoringSSL_RSAPrivateKey_dup(other.pointer) + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() + let rsaPrivateKey = CCryptoBoringSSL_RSAPrivateKey_dup(CCryptoBoringSSL_EVP_PKEY_get0_RSA(other.pointer)) + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPrivateKey) } fileprivate init(pemRepresentation: String) throws { var pemRepresentation = pemRepresentation + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() - self.pointer = try pemRepresentation.withUTF8 { utf8Ptr in + let rsaPrivateKey = try pemRepresentation.withUTF8 { utf8Ptr in return try BIOHelper.withReadOnlyMemoryBIO(wrapping: utf8Ptr) { bio in guard let key = CCryptoBoringSSL_PEM_read_bio_RSAPrivateKey(bio, nil, nil, nil) else { throw CryptoKitError.internalBoringSSLError() @@ -301,6 +339,7 @@ extension BoringSSLRSAPrivateKey { return key } } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPrivateKey) } fileprivate convenience init(derRepresentation: Bytes) throws { @@ -313,13 +352,16 @@ extension BoringSSLRSAPrivateKey { } private init(contiguousDerRepresentation: Bytes) throws { + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() + let rsaPrivateKey: OpaquePointer if let pointer = Backing.pkcs8DERPrivateKey(contiguousDerRepresentation) { - self.pointer = pointer + rsaPrivateKey = pointer } else if let pointer = Backing.pkcs1DERPrivateKey(contiguousDerRepresentation) { - self.pointer = pointer + rsaPrivateKey = pointer } else { throw CryptoKitError.internalBoringSSLError() } + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, rsaPrivateKey) } private static func pkcs8DERPrivateKey(_ derRepresentation: Bytes) -> OpaquePointer? { @@ -366,7 +408,8 @@ extension BoringSSLRSAPrivateKey { throw CryptoKitError.internalBoringSSLError() } - self.pointer = pointer + self.pointer = CCryptoBoringSSL_EVP_PKEY_new() + CCryptoBoringSSL_EVP_PKEY_assign_RSA(self.pointer, pointer) } catch { CCryptoBoringSSL_RSA_free(pointer) throw error @@ -375,7 +418,8 @@ extension BoringSSLRSAPrivateKey { fileprivate var derRepresentation: Data { return BIOHelper.withWritableMemoryBIO { bio in - let rc = CCryptoBoringSSL_i2d_RSAPrivateKey_bio(bio, self.pointer) + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let rc = CCryptoBoringSSL_i2d_RSAPrivateKey_bio(bio, rsaPrivateKey) precondition(rc == 1) return try! Data(copyingMemoryBIO: bio) @@ -384,7 +428,8 @@ extension BoringSSLRSAPrivateKey { fileprivate var pemRepresentation: String { return BIOHelper.withWritableMemoryBIO { bio in - let rc = CCryptoBoringSSL_PEM_write_bio_RSAPrivateKey(bio, self.pointer, nil, nil, 0, nil, nil) + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let rc = CCryptoBoringSSL_PEM_write_bio_RSAPrivateKey(bio, rsaPrivateKey, nil, nil, 0, nil, nil) precondition(rc == 1) return try! String(copyingUTF8MemoryBIO: bio) @@ -397,7 +442,8 @@ extension BoringSSLRSAPrivateKey { defer { CCryptoBoringSSL_EVP_PKEY_free(evp) } - CCryptoBoringSSL_EVP_PKEY_set1_RSA(evp, self.pointer) + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + CCryptoBoringSSL_EVP_PKEY_set1_RSA(evp, rsaPrivateKey) let rc = CCryptoBoringSSL_PEM_write_bio_PKCS8PrivateKey(bio, evp, nil, nil, 0, nil, nil) precondition(rc == 1) @@ -406,19 +452,24 @@ extension BoringSSLRSAPrivateKey { } fileprivate var keySizeInBits: Int { - return Int(CCryptoBoringSSL_RSA_size(self.pointer)) * 8 + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + return Int(CCryptoBoringSSL_RSA_size(rsaPrivateKey)) * 8 } fileprivate var publicKey: BoringSSLRSAPublicKey { + let pkey = CCryptoBoringSSL_EVP_PKEY_new()! + let rsaPublicKey = CCryptoBoringSSL_RSAPublicKey_dup(CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer)) + CCryptoBoringSSL_EVP_PKEY_assign_RSA(pkey, rsaPublicKey) let backing = BoringSSLRSAPublicKey.Backing( - takingOwnershipOf: CCryptoBoringSSL_RSAPublicKey_dup(self.pointer) + takingOwnershipOf: pkey ) return BoringSSLRSAPublicKey(backing) } fileprivate func signature(for digest: D, padding: _RSA.Signing.Padding) throws -> _RSA.Signing.RSASignature { + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) let hashDigestType = try DigestType(forDigestType: D.self) - let outputSize = Int(CCryptoBoringSSL_RSA_size(self.pointer)) + let outputSize = Int(CCryptoBoringSSL_RSA_size(rsaPrivateKey)) let output = try Array(unsafeUninitializedCapacity: outputSize) { bufferPtr, length in var outputLength = 0 @@ -433,13 +484,13 @@ extension BoringSSLRSAPrivateKey { CUnsignedInt(digestPtr.count), bufferPtr.baseAddress, &writtenLength, - self.pointer + rsaPrivateKey ) outputLength = Int(writtenLength) return rc case .pss: return CCryptoBoringSSLShims_RSA_sign_pss_mgf1( - self.pointer, + rsaPrivateKey, &outputLength, bufferPtr.baseAddress, bufferPtr.count, @@ -461,35 +512,50 @@ extension BoringSSLRSAPrivateKey { } fileprivate func decrypt(_ data: D, padding: _RSA.Encryption.Padding) throws -> Data { - let outputSize = Int(CCryptoBoringSSL_RSA_size(self.pointer)) + let rsaPrivateKey = CCryptoBoringSSL_EVP_PKEY_get0_RSA(self.pointer) + let outputSize = Int(CCryptoBoringSSL_RSA_size(rsaPrivateKey)) var output = Data(count: outputSize) let contiguousData: ContiguousBytes = data.regions.count == 1 ? data.regions.first! : Array(data) - let rc: CInt = output.withUnsafeMutableBytes { bufferPtr in - contiguousData.withUnsafeBytes { dataPtr in + 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 rc = CCryptoBoringSSLShims_RSA_private_decrypt( - CInt(dataPtr.count), - dataPtr.baseAddress, + + 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) + + var writtenLength = bufferPtr.count + + let rc = CCryptoBoringSSLShims_EVP_PKEY_decrypt( + ctx, bufferPtr.baseAddress, - self.pointer, - rawPadding + &writtenLength, + dataPtr.baseAddress, + dataPtr.count ) - return rc + + guard rc == 1 else { + throw CryptoKitError.internalBoringSSLError() + } + + return CInt(writtenLength) } } - if rc == -1 { - throw CryptoKitError.internalBoringSSLError() - } - output.removeSubrange(output.index(output.startIndex, offsetBy: Int(rc)) ..< output.endIndex) + + output.removeSubrange(output.index(output.startIndex, offsetBy: Int(writtenLength)) ..< output.endIndex) return output } deinit { - CCryptoBoringSSL_RSA_free(self.pointer) + CCryptoBoringSSL_EVP_PKEY_free(self.pointer) } } }