From 3283cae2d314424a1219dd09c3b5a98565db1ba7 Mon Sep 17 00:00:00 2001 From: Gelareh Taban Date: Fri, 22 Dec 2017 13:45:19 -0600 Subject: [PATCH 1/5] Correct copy-paste error. --- Sources/CryptorRSA/CryptorRSA.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/CryptorRSA/CryptorRSA.swift b/Sources/CryptorRSA/CryptorRSA.swift index 633488c..5e40ac1 100644 --- a/Sources/CryptorRSA/CryptorRSA.swift +++ b/Sources/CryptorRSA/CryptorRSA.swift @@ -439,7 +439,7 @@ public class CryptorRSA { return EVP_DigestUpdate(md_ctx, message, self.data.count) }) guard rc == 1 else { - let source = "Couldn't create key reference from key data" + let source = "Signature verification failed." if let reason = CryptorRSA.getLastError(source: source) { throw Error(code: ERR_VERIFICATION_FAILED, reason: reason) From 85a2fe71baa34a7b9a7fe26f0d94df1025310f7f Mon Sep 17 00:00:00 2001 From: Gelareh Taban Date: Sun, 24 Dec 2017 10:00:18 -0600 Subject: [PATCH 2/5] Encryption support --- README.md | 2 + Sources/CryptorRSA/CryptorRSA.swift | 172 +++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e67198a..69c0b83 100644 --- a/README.md +++ b/README.md @@ -216,6 +216,8 @@ Once the `EncryptedData` object is created, there is an instance function that c - `decrypted(with key: PrivateKey, algorithm: Data.Algorithm) throws -> DecryptedData?` - This function allows you to decrypt containing data using the public `key` and `algorithm` specified. This function returns an optional `DecryptedData` object containing the encryped data. +BlueRSA currently supports `OAEP` padding, which is the recommended padding algorithm. + **Example** - *Decryption*: **Note**: Exception handling omitted for brevity. diff --git a/Sources/CryptorRSA/CryptorRSA.swift b/Sources/CryptorRSA/CryptorRSA.swift index 5e40ac1..4af36ec 100644 --- a/Sources/CryptorRSA/CryptorRSA.swift +++ b/Sources/CryptorRSA/CryptorRSA.swift @@ -222,9 +222,86 @@ public class CryptorRSA { } #if os(Linux) - - throw Error(code: ERR_NOT_IMPLEMENTED, reason: "Not implemented yet.") - + + // convert RSA key to EVP + var evp_key = EVP_PKEY_new() + var rc = EVP_PKEY_set1_RSA(evp_key, key.reference) + guard rc == 1 else { + let source = "Couldn't create key reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + + let (md, enc, padding) = algorithm.alogrithmForEncryption + + let rsaEncryptCtx = UnsafeMutablePointer.allocate(capacity: 1) + + defer { + EVP_CIPHER_CTX_cleanup(rsaEncryptCtx) + EVP_PKEY_free(evp_key) + } + + EVP_CIPHER_CTX_set_padding(rsaEncryptCtx, padding) + + // Initialize the AES encryption key array (of size 1) + typealias UInt8Ptr = UnsafeMutablePointer? + var ek: UInt8Ptr + ek = UnsafeMutablePointer.allocate(capacity: Int(EVP_PKEY_size(evp_key))) + let ekPtr = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size) + ekPtr.pointee = ek + + // Assign size of the corresponding cipher's IV + let IVLength = EVP_CIPHER_iv_length(enc) + let iv = UnsafeMutablePointer.allocate(capacity: Int(IVLength)) + + let encrypted = UnsafeMutablePointer.allocate(capacity: self.data.count + Int(IVLength)) + var encKeyLength: Int32 = 0 + var processedLength: Int32 = 0 + var encLength: Int32 = 0 + + // Initializes a cipher context ctx for encryption with cipher type using a random secret key and IV. + // The secret key is encrypted using the public key (can be a set of public keys) + // Here we are using just 1 public key + var status = EVP_SealInit(rsaEncryptCtx, enc, ekPtr, &encKeyLength, iv, &evp_key, 1) + + // SealInit should return the number of public keys that were input, here it is only 1 + guard status == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") + } + + // EVP_SealUpdate is a complex macros and therefore the compiler doesnt + // convert it directly to swift. From /usr/local/opt/openssl/include/openssl/evp.h: + _ = self.data.withUnsafeBytes({ (plaintext: UnsafePointer) -> Int32 in + return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext, Int32(self.data.count)) + + }) + encLength = processedLength + + status = EVP_SealFinal(rsaEncryptCtx, encrypted.advanced(by: Int(encLength)), &processedLength) + guard status == 1 else { + let source = "Encryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ENCRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_ENCRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") + } + encLength = encLength + processedLength + + let cipher = Data(bytes: encrypted, count: Int(encLength)) + let ekFinal = Data(bytes: ek!, count: Int(encKeyLength)) + let ivFinal = Data(bytes: iv, count: Int(IVLength)) + + return EncryptedData(with: ekFinal + cipher + ivFinal) + #else var response: Unmanaged? = nil @@ -269,8 +346,92 @@ public class CryptorRSA { #if os(Linux) - throw Error(code: ERR_NOT_IMPLEMENTED, reason: "Not implemented yet.") - + // convert RSA key to EVP + var evp_key = EVP_PKEY_new() + var status = EVP_PKEY_set1_RSA(evp_key, key.reference) + guard status == 1 else { + let source = "Couldn't create key reference from key data" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_ADD_KEY, reason: reason) + } + throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") + } + + let (md, encType, padding) = algorithm.alogrithmForEncryption + + // Size of symmetric encryption + let encKeyLength = Int(EVP_PKEY_size(evp_key)) + // Size of the corresponding cipher's IV + let encIVLength = Int(EVP_CIPHER_iv_length(encType)) + // Size of encryptedKey + let encryptedDataLength = Int(self.data.count) - encKeyLength - encIVLength + + // extract encryptedKey, encryptedData, encryptedIV from data + // self.data = encryptedKey + encryptedData + encryptedIV + let encryptedKey = self.data.subdata(in: 0...allocate(capacity: 1) + + defer { + EVP_CIPHER_CTX_cleanup(rsaDecryptCtx) + EVP_PKEY_free(evp_key) + } + + EVP_CIPHER_CTX_set_padding(rsaDecryptCtx, padding) + + // This is the number of bytes that each EVP_DecryptUpdate/EVP_DecryptFinal decrypts. + // The sum of processedLen is the total size of the decrypted message (decMsgLen) + var processedLen: Int32 = 0 + var decMsgLen: Int32 = 0 + + let decrypted = UnsafeMutablePointer.allocate(capacity: Int(encryptedData.count + encryptedIV.count)) + + // EVP_OpenInit returns 0 on error or the recovered secret key size if successful + status = encryptedKey.withUnsafeBytes({ (ek: UnsafePointer) -> Int32 in + return encryptedIV.withUnsafeBytes({ (iv: UnsafePointer) -> Int32 in + return EVP_OpenInit(rsaDecryptCtx, encType, ek, Int32(encryptedKey.count), iv, evp_key) + }) + }) + guard status != EVP_CIPHER_key_length(encType) else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") + } + + // EVP_OpenUpdate is a complex macros and therefore the compiler doesnt + // convert it directly to swift. From /usr/local/opt/openssl/include/openssl/evp.h: + status = encryptedData.withUnsafeBytes({ (enc: UnsafePointer) -> Int32 in + return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc, Int32(encryptedData.count)) + }) + guard status != 0 else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") + } + decMsgLen = processedLen; + + status = EVP_OpenFinal(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) + guard status != 0 else { + let source = "Decryption failed" + if let reason = CryptorRSA.getLastError(source: source) { + + throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) + } + throw Error(code: ERR_DECRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") + } + decMsgLen = decMsgLen + processedLen + + return PlaintextData(with: Data(bytes: decrypted, count: Int(decMsgLen))) + #else var response: Unmanaged? = nil @@ -606,3 +767,4 @@ public class CryptorRSA { } } + From b7e01b8d4a112734f18648e9b8df3018208cd6c2 Mon Sep 17 00:00:00 2001 From: Gelareh Taban Date: Wed, 3 Jan 2018 15:39:36 -0600 Subject: [PATCH 3/5] Add in the remaining changes for encryption support and enable the encryption tests on Linux. --- Sources/CryptorRSA/CryptorRSADigest.swift | 25 +++++++++++++++++++++ Tests/CryptorRSATests/CryptorRSATests.swift | 6 ++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Sources/CryptorRSA/CryptorRSADigest.swift b/Sources/CryptorRSA/CryptorRSADigest.swift index d78c731..201ca40 100644 --- a/Sources/CryptorRSA/CryptorRSADigest.swift +++ b/Sources/CryptorRSA/CryptorRSADigest.swift @@ -105,6 +105,8 @@ public extension Data { } #if os(Linux) + + // Hash, padding type public var algorithmForSignature: (UnsafePointer, Int32) { switch self { @@ -127,6 +129,29 @@ public extension Data { } } + // Hash, symmetric encryption, padding type + public var alogrithmForEncryption: (UnsafePointer, UnsafePointer, Int32) { + + switch self { + + case .sha1: + return (EVP_sha1(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + + case .sha224: + return (EVP_sha224(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + + case .sha256: + return (EVP_sha256(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + + case .sha384: + return (EVP_sha384(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + + case .sha512: + return (EVP_sha512(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + + } + } + #else @available(macOS 10.12, iOS 10.0, *) diff --git a/Tests/CryptorRSATests/CryptorRSATests.swift b/Tests/CryptorRSATests/CryptorRSATests.swift index b3c3028..050d794 100644 --- a/Tests/CryptorRSATests/CryptorRSATests.swift +++ b/Tests/CryptorRSATests/CryptorRSATests.swift @@ -507,9 +507,9 @@ class CryptorRSATests: XCTestCase { // ("test_private_initWithPEMStringHeaderless", test_private_initWithPEMStringHeaderless), ("test_private_initWithPEMName", test_private_initWithPEMName), ("test_private_initWithDERName", test_private_initWithDERName), -// ("test_simpleEncryption", test_simpleEncryption), -// ("test_longStringEncryption", test_longStringEncryption), -// ("test_randomByteEncryption", test_randomByteEncryption), + ("test_simpleEncryption", test_simpleEncryption), + ("test_longStringEncryption", test_longStringEncryption), + ("test_randomByteEncryption", test_randomByteEncryption), ("test_signVerifyAllDigestTypes", test_signVerifyAllDigestTypes), ("test_signVerifyBase64", test_signVerifyBase64), ] From efa8d9819843db44a3dd14cf18caab68235bd3ff Mon Sep 17 00:00:00 2001 From: Gelareh Taban Date: Fri, 5 Jan 2018 11:21:23 -0600 Subject: [PATCH 4/5] replacing EVP_aes_256_cbc with EVP_aes_128_gcm for Linux. --- Sources/CryptorRSA/CryptorRSA.swift | 2 +- Sources/CryptorRSA/CryptorRSADigest.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/CryptorRSA/CryptorRSA.swift b/Sources/CryptorRSA/CryptorRSA.swift index 4af36ec..4b2f015 100644 --- a/Sources/CryptorRSA/CryptorRSA.swift +++ b/Sources/CryptorRSA/CryptorRSA.swift @@ -263,7 +263,7 @@ public class CryptorRSA { var encLength: Int32 = 0 // Initializes a cipher context ctx for encryption with cipher type using a random secret key and IV. - // The secret key is encrypted using the public key (can be a set of public keys) + // The secret key is encrypted using the public key (evp_key can be an array of public keys) // Here we are using just 1 public key var status = EVP_SealInit(rsaEncryptCtx, enc, ekPtr, &encKeyLength, iv, &evp_key, 1) diff --git a/Sources/CryptorRSA/CryptorRSADigest.swift b/Sources/CryptorRSA/CryptorRSADigest.swift index 201ca40..7f105e5 100644 --- a/Sources/CryptorRSA/CryptorRSADigest.swift +++ b/Sources/CryptorRSA/CryptorRSADigest.swift @@ -129,25 +129,25 @@ public extension Data { } } - // Hash, symmetric encryption, padding type + // HMAC type, symmetric encryption, padding type public var alogrithmForEncryption: (UnsafePointer, UnsafePointer, Int32) { switch self { case .sha1: - return (EVP_sha1(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha1(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) case .sha224: - return (EVP_sha224(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha224(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) case .sha256: - return (EVP_sha256(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha256(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) case .sha384: - return (EVP_sha384(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha384(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) case .sha512: - return (EVP_sha512(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha512(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) } } From aa65327ebebbcb39ef3e22fce34d1ef72b47569b Mon Sep 17 00:00:00 2001 From: Gelareh Taban Date: Thu, 11 Jan 2018 08:59:52 -0600 Subject: [PATCH 5/5] - Go back to using EVP_aes_256_cbc() instead of EVP_aes_128_gcm() because of test errors. - clean up code - add TODO for hash type option for encryption since it is not being set right now --- Sources/CryptorRSA/CryptorRSA.swift | 29 +++++++++-------------- Sources/CryptorRSA/CryptorRSADigest.swift | 8 +++---- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/Sources/CryptorRSA/CryptorRSA.swift b/Sources/CryptorRSA/CryptorRSA.swift index 4b2f015..2a70942 100644 --- a/Sources/CryptorRSA/CryptorRSA.swift +++ b/Sources/CryptorRSA/CryptorRSA.swift @@ -223,7 +223,7 @@ public class CryptorRSA { #if os(Linux) - // convert RSA key to EVP + // Convert RSA key to EVP var evp_key = EVP_PKEY_new() var rc = EVP_PKEY_set1_RSA(evp_key, key.reference) guard rc == 1 else { @@ -235,7 +235,8 @@ public class CryptorRSA { throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") } - let (md, enc, padding) = algorithm.alogrithmForEncryption + // TODO: hash type option is not being used right now. + let ( _, enc, padding) = algorithm.alogrithmForEncryption let rsaEncryptCtx = UnsafeMutablePointer.allocate(capacity: 1) @@ -281,7 +282,6 @@ public class CryptorRSA { // convert it directly to swift. From /usr/local/opt/openssl/include/openssl/evp.h: _ = self.data.withUnsafeBytes({ (plaintext: UnsafePointer) -> Int32 in return EVP_EncryptUpdate(rsaEncryptCtx, encrypted, &processedLength, plaintext, Int32(self.data.count)) - }) encLength = processedLength @@ -346,7 +346,7 @@ public class CryptorRSA { #if os(Linux) - // convert RSA key to EVP + // Convert RSA key to EVP var evp_key = EVP_PKEY_new() var status = EVP_PKEY_set1_RSA(evp_key, key.reference) guard status == 1 else { @@ -358,7 +358,8 @@ public class CryptorRSA { throw Error(code: ERR_ADD_KEY, reason: source + ": No OpenSSL error reported.") } - let (md, encType, padding) = algorithm.alogrithmForEncryption + // TODO: hash type option is not being used right now. + let ( _, encType, padding) = algorithm.alogrithmForEncryption // Size of symmetric encryption let encKeyLength = Int(EVP_PKEY_size(evp_key)) @@ -367,7 +368,7 @@ public class CryptorRSA { // Size of encryptedKey let encryptedDataLength = Int(self.data.count) - encKeyLength - encIVLength - // extract encryptedKey, encryptedData, encryptedIV from data + // Extract encryptedKey, encryptedData, encryptedIV from data // self.data = encryptedKey + encryptedData + encryptedIV let encryptedKey = self.data.subdata(in: 0..) -> Int32 in + // convert it directly to Swift. From /usr/local/opt/openssl/include/openssl/evp.h: + _ = encryptedData.withUnsafeBytes({ (enc: UnsafePointer) -> Int32 in return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc, Int32(encryptedData.count)) }) - guard status != 0 else { - let source = "Decryption failed" - if let reason = CryptorRSA.getLastError(source: source) { - - throw Error(code: ERR_DECRYPTION_FAILED, reason: reason) - } - throw Error(code: ERR_DECRYPTION_FAILED , reason: source + ": No OpenSSL error reported.") - } - decMsgLen = processedLen; + decMsgLen = processedLen status = EVP_OpenFinal(rsaDecryptCtx, decrypted.advanced(by: Int(decMsgLen)), &processedLen) guard status != 0 else { diff --git a/Sources/CryptorRSA/CryptorRSADigest.swift b/Sources/CryptorRSA/CryptorRSADigest.swift index 7f105e5..5cbe04c 100644 --- a/Sources/CryptorRSA/CryptorRSADigest.swift +++ b/Sources/CryptorRSA/CryptorRSADigest.swift @@ -135,16 +135,16 @@ public extension Data { switch self { case .sha1: - return (EVP_sha1(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha1(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) case .sha224: - return (EVP_sha224(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha224(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) case .sha256: - return (EVP_sha256(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha256(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) case .sha384: - return (EVP_sha384(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING) + return (EVP_sha384(), EVP_aes_256_cbc(), RSA_PKCS1_OAEP_PADDING) case .sha512: return (EVP_sha512(), EVP_aes_128_gcm(), RSA_PKCS1_OAEP_PADDING)