Files
raspberry/iOS/Wallet/Sources/CryptoChat/Model/CryptoChatModelMsg.swift
2022-05-27 20:21:01 +03:00

120 lines
5.2 KiB
Swift

//
// 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..<UInt8.max) })
//Encrypt msg with encryptionKey and iv
let ciphertext = try AES(key: encryptionKey, blockMode: CBC(iv: iv), padding: .pkcs7).encrypt(msg.bytes)
//Calculate HMAC for msg validation
let compressedEphemPublicKey = ([4] + ephemPublicKey).toData().toCompressedPublicKey ?? Data()
let dataToMac = iv + compressedEphemPublicKey.bytes + ciphertext
let mac = try HMAC(key: macKey, variant: .sha256).authenticate(dataToMac)
self.iv = iv.toHexString()
self.ephemPublicKey = compressedEphemPublicKey.toEosioK1PublicKey
self.cipherText = ciphertext.toHexString()
self.mac = mac.toHexString()
}
func decrypt(privateKey: String) throws -> 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)
?? "<plaintext>*** Wrong message encryption format ***</plaintext>"
} else {
return "<plaintext>\(L10n.CryptoChat.Chats.decodeError)</plaintext>"
}
}
return "<plaintext>*** Wrong iv or cipherText hex format ***</plaintext>"
}
}
}