// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved. import Foundation import Sodium import Clibsodium import SessionUtilitiesKit // MARK: - Generic Hash public extension Crypto.Action { static func hash(message: Bytes, key: Bytes? = nil) -> Crypto.Action { return Crypto.Action(id: "hash", args: [message, key]) { sodium in sodium.genericHash.hash(message: message, key: key) } } } // MARK: - Sign public extension Crypto.Action { static func signature(message: Bytes, secretKey: Bytes) -> Crypto.Action { return Crypto.Action(id: "signature", args: [message, secretKey]) { sodium in sodium.sign.signature(message: message, secretKey: secretKey) } } } public extension Crypto.Verification { static func signature(message: Bytes, publicKey: Bytes, signature: Bytes) -> Crypto.Verification { return Crypto.Verification(id: "signature", args: [message, publicKey, signature]) { sodium in sodium.sign.verify(message: message, publicKey: publicKey, signature: signature) } } } // MARK: - AeadXChaCha20Poly1305Ietf public extension Crypto.Size { static let aeadXChaCha20KeyBytes: Crypto.Size = Crypto.Size(id: "aeadXChaCha20KeyBytes") { sodium in sodium.aead.xchacha20poly1305ietf.KeyBytes } static let aeadXChaCha20ABytes: Crypto.Size = Crypto.Size(id: "aeadXChaCha20ABytes") { sodium in sodium.aead.xchacha20poly1305ietf.ABytes } } public extension Crypto.Action { /// This method is the same as the standard AeadXChaCha20Poly1305Ietf `encrypt` method except it allows the /// specification of a nonce which allows for deterministic behaviour with unit testing static func encryptAeadXChaCha20( message: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes? = nil, using dependencies: Dependencies ) -> Crypto.Action { return Crypto.Action( id: "encryptAeadXChaCha20", args: [message, secretKey, nonce, additionalData] ) { guard secretKey.count == dependencies[singleton: .crypto].size(.aeadXChaCha20KeyBytes) else { return nil } var authenticatedCipherText = Bytes( repeating: 0, count: message.count + dependencies[singleton: .crypto].size(.aeadXChaCha20ABytes) ) var authenticatedCipherTextLen: UInt64 = 0 let result = crypto_aead_xchacha20poly1305_ietf_encrypt( &authenticatedCipherText, &authenticatedCipherTextLen, message, UInt64(message.count), additionalData, UInt64(additionalData?.count ?? 0), nil, nonce, secretKey ) guard result == 0 else { return nil } return authenticatedCipherText } } static func decryptAeadXChaCha20( authenticatedCipherText: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes? = nil ) -> Crypto.Action { return Crypto.Action( id: "decryptAeadXChaCha20", args: [authenticatedCipherText, secretKey, nonce, additionalData] ) { sodium in sodium.aead.xchacha20poly1305ietf.decrypt( authenticatedCipherText: authenticatedCipherText, secretKey: secretKey, nonce: nonce, additionalData: additionalData ) } } } // MARK: - Legacy Argon2-based encryption public extension Crypto.Size { static let legacyArgon2PWHashSaltBytes: Crypto.Size = Crypto.Size(id: "legacyArgon2PWHashSaltBytes") { $0.pwHash.SaltBytes } static let legacyArgon2SecretBoxNonceBytes: Crypto.Size = Crypto.Size(id: "legacyArgon2SecretBoxNonceBytes") { $0.secretBox.NonceBytes } } public extension Crypto.Action { static func legacyArgon2PWHash(passwd: Bytes, salt: Bytes) -> Crypto.Action { return Crypto.Action(id: "legacyArgon2PWHash", args: [passwd, salt]) { sodium in sodium.pwHash.hash( outputLength: sodium.secretBox.KeyBytes, passwd: passwd, salt: salt, opsLimit: sodium.pwHash.OpsLimitModerate, memLimit: sodium.pwHash.MemLimitModerate, alg: .Argon2ID13 ) } } static func legacyArgon2SecretBoxOpen( authenticatedCipherText: Bytes, secretKey: Bytes, nonce: Bytes ) -> Crypto.Action { return Crypto.Action(id: "legacyArgon2SecretBoxOpen", args: [authenticatedCipherText, secretKey, nonce]) { $0.secretBox.open( authenticatedCipherText: authenticatedCipherText, secretKey: secretKey, nonce: nonce ) } } }