0

From a server, I get an encrypted and base 64 encoded string, these are criteria used to encrypt it.

  • aes 256 cbc encryption system with hexadecimal digest
  • the key is hash sha 512 of a phone number
  • the first 32 characters are the key, the last 16 characters are the ivs
  • I get the result in base 64 of the AES encryption

I tried many solutions, these are two of many versions of the code. It runs, but I'm not getting the right decryption.

updated version, still not running

import UIKit
import CryptoKit
import Security
import CommonCrypto

class ViewController: UIViewController {

    let encryptedString = "myEncryptedAndEncodedString"
    let phoneNumber = "myPhoneNumber"

    override func viewDidLoad() {
        super.viewDidLoad()

        var sha512HashFromPhoneNumber = ""

        // 1. Get the hash from the number
        if let tempSha512Hash = sha512(phoneNumber) {
            print("✅ SHA-512 hash: \(tempSha512Hash)")
            sha512HashFromPhoneNumber = tempSha512Hash
        } else {
            print("❌ error calculating hash")
        }

        var key = Data()
        var iv = Data()

        // 2. Extract key and IV
        if let (tempKey, tempIv) = extractKeyAndIV(from: sha512HashFromPhoneNumber) {
            key = tempKey
            iv = tempIv
        } else {
            print("❌ Error extracting key and IV")
        }

        print("✅ Key NSData: \(key as NSData)")
        print("✅ IV NSData: \(iv as NSData)")

        var decodedData = Data()
        // 3. Decode the Base64 string
        if let tempDecodedData = decodeBase64(encryptedString) {
            decodedData = tempDecodedData
        } else {
            print("❌ Error decoding base64 string.")
        }

        print("✅ Decoded Data: \(decodedData as NSData)")

        // 4. Decrypt the encrypted data
        if let decryptedString = decryptAES256CBC(encryptedData: decodedData, key: key, iv: iv) {
            print("✅ Decrypted string: \(decryptedString)")
        } else {
            print("❌ error decrypting String.")
        }
    }


    // 1. Get the hash from the number
    func sha512(_ string: String) -> String? {
        guard let data = string.data(using: .utf8) else { return nil }

        var hash = [UInt8](repeating: 0, count: Int(CC_SHA512_DIGEST_LENGTH))
        data.withUnsafeBytes {
            _ = CC_SHA512($0.baseAddress, CC_LONG(data.count), &hash)
        }

        return hash.map { String(format: "%02x", $0) }.joined()
    }

    // 2. Extract key and IV
    func extractKeyAndIV(from hash: String) -> (key: Data, iv: Data)? {
        let keyString = String(hash.prefix(32))
        let ivString = String(hash.dropFirst(32).suffix(16))
        print("keyString \(keyString) count \(keyString.count)")
        print("ivString  \(ivString)  count \(ivString.count)")
        // This part handles the hexadecimal digest
        guard let keyData = Data(hexString: keyString), let ivData = Data(hexString: ivString) else { return nil }
        return (key: keyData, iv: ivData)
    }

    // 3. Decode the Base64 string
    func decodeBase64(_ base64String: String) -> Data? {
        return Data(base64Encoded: base64String)
    }

    // 4. Decrypt the encrypted data
    func decryptAES256CBC(encryptedData: Data, key: Data, iv: Data) -> String? {
        let bufferSize = encryptedData.count
        var buffer = Data(count: bufferSize)

        let options = CCOptions(kCCOptionPKCS7Padding)
        var numBytesDecrypted: size_t = 0
        // This part handles the AES-256-CBC decryption
        let cryptStatus = key.withUnsafeBytes { keyBytes in
            iv.withUnsafeBytes { ivBytes in
                encryptedData.withUnsafeBytes { dataBytes in
                    buffer.withUnsafeMutableBytes { mutableBytes in
                        CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), options,
                                keyBytes.baseAddress, key.count,
                                ivBytes.baseAddress,
                                dataBytes.baseAddress, bufferSize,
                                mutableBytes.baseAddress, bufferSize,
                                &numBytesDecrypted)
                    }
                }
            }
        }

        if cryptStatus == kCCSuccess {
            buffer.count = numBytesDecrypted
            return String(data: buffer, encoding: .utf8)
        } else {
            return nil
        }
    }

}

// Extension to handle hex string conversion
extension Data {
    init?(hexString: String) {
        let length = hexString.count / 2
        var data = Data(capacity: length)
        for i in 0..<length {
            let j = hexString.index(hexString.startIndex, offsetBy: i*2)
            let k = hexString.index(j, offsetBy: 2)
            //print("j: \(hexString.distance(from: hexString.startIndex, to: j))")
            //print("k: \(hexString.distance(from: hexString.startIndex, to: k))")

            let bytes = hexString[j..<k]
            if var num = UInt8(bytes, radix: 16) {
                data.append(&num, count: 1)
            } else {
                return nil
            }
        }
        self = data
    }
}






import UIKit
import CryptoKit
import CommonCrypto

class ViewController: UIViewController {

    let encryptedString = "myencryptedSring"
        let phoneNumber = "mygivengNumber"
        

    override func viewDidLoad() {
        super.viewDidLoad()

        if let decryptedString = decryptAES256CBC(encryptedData: encryptedString, key: phoneNumber) {
            print("original email: \(decryptedString)")
        } else {
            print("error during decoding")
        }
    }

    func decryptAES256CBC(encryptedData: String, key: String) -> String? {
        guard let data = Data(base64Encoded: encryptedData) else { return nil }

        let keyData = SHA512.hash(data: key.data(using: .utf8)!)
        let keyBytes = Data(keyData.prefix(32)).bytes
        let ivBytes = Data(keyData.suffix(16)).bytes

        var numBytesDecrypted = 0
        var decryptedData = Data(count: data.count + kCCBlockSizeAES128)

        let status = decryptedData.withUnsafeMutableBytes { (decryptedBytes: UnsafeMutableRawBufferPointer) -> CCCryptorStatus in
            let decryptedDataCopy = Data(decryptedData)
            return decryptedDataCopy.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> CCCryptorStatus in
                data.withUnsafeBytes { (dataBytes: UnsafeRawBufferPointer) -> CCCryptorStatus in
                    CCCrypt(CCOperation(kCCDecrypt), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), keyBytes, kCCKeySizeAES256, ivBytes, dataBytes.baseAddress, data.count, decryptedBytes.baseAddress, decryptedDataCopy.count, &numBytesDecrypted)
                }
            }
        }

        if status == kCCSuccess {
            decryptedData = Data(decryptedData.prefix(numBytesDecrypted))
            return String(data: decryptedData, encoding: .utf8)
        } else {
            return nil
        }
    }
}

extension Data {
    var bytes: [UInt8] {
        return [UInt8](self)
    }
}
biggreentree
  • 1,633
  • 3
  • 20
  • 35
  • Did you check if the data received is equal to the uploaded data? – Leo Dabus May 07 '23 at 16:44
  • I never upload anything, due to security issues, I send a certain code, and get back my encrypted and encoded string. I then try to decode then decrypt using a number, hear the phone number. but yes, we both use the same number to encrypt decrypt. – biggreentree May 07 '23 at 18:08
  • This is a very unusual way to construct the key and IV. You'd need to post the server code if anyone is going to be able to work it out (though implementing ad hoc encryption protocols don't get a ton of answers on SO; it's too custom for it to help anyone else). To debug it, you'll need to step through each step and make sure precisely the same bytes go into each step. Run the server code and see precisely what the final key and IV are. Make sure those are precisely the same as keyBytes and ivBytes. Make sure that dataBytes is precisely what came out of the encryptor before Base64-encoding. – Rob Napier May 08 '23 at 13:30
  • If I just had to guess at the mistake, I would guess that the key is supposed to be the first 32-bytes of the hash, rather than the first 32 characters of the hex-encoding of the hash (which only represent 16 "real" bytes). But it's hard to tell without a complete spec or working code to compare. This isn't a normal way to build a cryptosystem. It's also extremely insecure, but it's very difficult to build a secure encryption scheme where the only secret is a phone number, so that's probably not fixable. – Rob Napier May 08 '23 at 13:33

0 Answers0