// // CryptoChatModelMsg.swift // PayCash // // Created by Saveliy Stavitsky on 8/17/20. // Copyright © 2020 List. All rights reserved. // import Foundation import CryptoSwift extension CryptoChat.Model { struct Msg: Codable { let from: String let to: String let iv: String let ephemPublicKey: String let cipherText: String let mac: String private enum CodingKeys: String, CodingKey { case from case to case iv case ephemPublicKey = "ephem_key" case cipherText = "cipher_text" case mac } let curve = uECC_secp256k1() init(from: String, to: String, iv: String, ephemPublicKey: String, cipherText: String, mac: String) { self.from = from self.to = to self.iv = iv self.ephemPublicKey = ephemPublicKey self.cipherText = cipherText self.mac = mac } //Encrypt init(from: String, to: String, msg: String, receiverPublicKey: String) throws { self.from = from self.to = to //Read end decompress receiver public key into bytes array for future use in alg let receiverPublicKeyCompressed = try Data(eosioPublicKey: receiverPublicKey).bytes var receiverPublicKeyDecompressed = Array(repeating: UInt8(0), count: 64) uECC_decompress(receiverPublicKeyCompressed, &receiverPublicKeyDecompressed, curve) //Generate ephem public and private key pair into bytes array var ephemPublicKey = Array(repeating: UInt8(0), count: Int(uECC_curve_public_key_size(curve)) * 2) var ephemPrivateKey = Array(repeating: UInt8(0), count: Int(uECC_curve_private_key_size(curve))) uECC_make_key(&ephemPublicKey, &ephemPrivateKey, curve) ephemPublicKey = Data(bytes: ephemPublicKey, count: Int(uECC_curve_public_key_size(curve))).bytes //Calculate symetric shared key for encryption var sharedKey = Array(repeating: UInt8(0), count: 32) uECC_shared_secret(receiverPublicKeyDecompressed, ephemPrivateKey, &sharedKey, curve) //Calculate sharedKey sha512 (64 bytes) and split it to encryptionKey - first 32 bytes and macKey - second 32 bytes let hash = sharedKey.sha512() let encryptionKey = [UInt8](hash[..<32]) let macKey = [UInt8](hash[32...]) //Generate iv random 16 bytes let iv = (0..<16).map({ _ in UInt8.random(in: 0.. String { let privateKeyBytes = try Data(eosioPrivateKey: privateKey).bytes //Read end decompress ephem public key into bytes array for future use in alg let compressedEphemPublicKey = try Data(eosioPublicKey: self.ephemPublicKey).bytes var decompressedEphemPublicKey = Array(repeating: UInt8(0), count: 64) uECC_decompress(compressedEphemPublicKey, &decompressedEphemPublicKey, curve) //Calculate symetric shared key for encryption var sharedKey = Array(repeating: UInt8(0), count: 32) uECC_shared_secret(decompressedEphemPublicKey, privateKeyBytes, &sharedKey, curve) //Calculate sharedKey sha512 (64 bytes) and split it to encryptionKey - first 32 bytes and macKey - second 32 bytes let hash = sharedKey.sha512() let encryptionKey = [UInt8](hash[..<32]) let macKey = [UInt8](hash[32...]) //Calculate HMAC for msg validation if let ivBytes = Data(hexString: iv)?.bytes, let cipherTextBytes = Data(hexString: cipherText)?.bytes { let dataToMac = ivBytes + compressedEphemPublicKey + cipherTextBytes let realMac = try HMAC(key: macKey, variant: .sha256).authenticate(dataToMac) if (realMac.toHexString().uppercased() == mac.uppercased()) { let plaintext = try AES(key: encryptionKey, blockMode: CBC(iv: ivBytes), padding: .pkcs7).decrypt(cipherTextBytes) return String(bytes: plaintext, encoding: .utf8) ?? "*** Wrong message encryption format ***</plaintext>" } else { return "<plaintext>\(L10n.CryptoChat.Chats.decodeError)</plaintext>" } } return "<plaintext>*** Wrong iv or cipherText hex format ***</plaintext>" } } }