Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for encryption #12

Merged
merged 5 commits into from
Jan 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
167 changes: 161 additions & 6 deletions Sources/CryptorRSA/CryptorRSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
}

// TODO: hash type option is not being used right now.
let ( _, enc, padding) = algorithm.alogrithmForEncryption

let rsaEncryptCtx = UnsafeMutablePointer<EVP_CIPHER_CTX>.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<UInt8>?
var ek: UInt8Ptr
ek = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(EVP_PKEY_size(evp_key)))
let ekPtr = UnsafeMutablePointer<UInt8Ptr>.allocate(capacity: MemoryLayout<UInt8Ptr>.size)
ekPtr.pointee = ek

// Assign size of the corresponding cipher's IV
let IVLength = EVP_CIPHER_iv_length(enc)
let iv = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(IVLength))

let encrypted = UnsafeMutablePointer<UInt8>.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 (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)

// 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<UInt8>) -> 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<CFError>? = nil
Expand Down Expand Up @@ -269,8 +346,85 @@ 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.")
}

// 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))
// 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..<encKeyLength)
let encryptedData = self.data.subdata(in: encKeyLength..<encKeyLength+encryptedDataLength)
let encryptedIV = self.data.subdata(in: encKeyLength+encryptedDataLength..<self.data.count)

let rsaDecryptCtx = UnsafeMutablePointer<EVP_CIPHER_CTX>.allocate(capacity: 1)

defer {
EVP_CIPHER_CTX_cleanup(rsaDecryptCtx)
EVP_PKEY_free(evp_key)
}

EVP_CIPHER_CTX_set_padding(rsaDecryptCtx, padding)

// processedLen 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<UInt8>.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<UInt8>) -> Int32 in
return encryptedIV.withUnsafeBytes({ (iv: UnsafePointer<UInt8>) -> 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:
_ = encryptedData.withUnsafeBytes({ (enc: UnsafePointer<UInt8>) -> Int32 in
return EVP_DecryptUpdate(rsaDecryptCtx, decrypted, &processedLen, enc, Int32(encryptedData.count))
})
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<CFError>? = nil
Expand Down Expand Up @@ -439,7 +593,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)
Expand Down Expand Up @@ -606,3 +760,4 @@ public class CryptorRSA {
}

}

25 changes: 25 additions & 0 deletions Sources/CryptorRSA/CryptorRSADigest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public extension Data {
}

#if os(Linux)

// Hash, padding type
public var algorithmForSignature: (UnsafePointer<EVP_MD>, Int32) {

switch self {
Expand All @@ -127,6 +129,29 @@ public extension Data {
}
}

// HMAC type, symmetric encryption, padding type
public var alogrithmForEncryption: (UnsafePointer<EVP_MD>, UnsafePointer<EVP_CIPHER>, 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_128_gcm(), RSA_PKCS1_OAEP_PADDING)

}
}

#else

@available(macOS 10.12, iOS 10.0, *)
Expand Down
6 changes: 3 additions & 3 deletions Tests/CryptorRSATests/CryptorRSATests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]
Expand Down