mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			110 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Swift
		
	
			
		
		
	
	
			110 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Swift
		
	
| // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
 | |
| 
 | |
| import Foundation
 | |
| import CryptoKit
 | |
| import Curve25519Kit
 | |
| 
 | |
| public extension Digest {
 | |
|     var bytes: [UInt8] { Array(makeIterator()) }
 | |
|     var data: Data { Data(bytes) }
 | |
|     
 | |
|     var hexString: String {
 | |
|         bytes.map { String(format: "%02X", $0) }.joined()
 | |
|     }
 | |
| }
 | |
| 
 | |
| // MARK: - AES.GCM
 | |
| 
 | |
| public extension AES.GCM {
 | |
|     static let ivSize: Int = 12
 | |
|     
 | |
|     struct EncryptionResult {
 | |
|         public let ciphertext: Data
 | |
|         public let symmetricKey: Data
 | |
|         public let ephemeralPublicKey: Data
 | |
|     }
 | |
| 
 | |
|     enum Error: LocalizedError {
 | |
|         case keyPairGenerationFailed
 | |
|         case sharedSecretGenerationFailed
 | |
| 
 | |
|         public var errorDescription: String? {
 | |
|             switch self {
 | |
|                 case .keyPairGenerationFailed: return "Couldn't generate a key pair."
 | |
|                 case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret."
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// - Note: Sync. Don't call from the main thread.
 | |
|     static func generateSymmetricKey(x25519PublicKey: Data, x25519PrivateKey: Data) throws -> Data {
 | |
|         if Thread.isMainThread {
 | |
|             #if DEBUG
 | |
|             preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.")
 | |
|             #endif
 | |
|         }
 | |
|         guard let sharedSecret: Data = try? Curve25519.generateSharedSecret(fromPublicKey: x25519PublicKey, privateKey: x25519PrivateKey) else {
 | |
|             throw Error.sharedSecretGenerationFailed
 | |
|         }
 | |
|         let salt = "LOKI"
 | |
|         
 | |
|         return Data(
 | |
|             HMAC<SHA256>.authenticationCode(
 | |
|                 for: sharedSecret,
 | |
|                 using: SymmetricKey(data: salt.bytes)
 | |
|             )
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     /// - Note: Sync. Don't call from the main thread.
 | |
|     static func decrypt(_ nonceAndCiphertext: Data, with symmetricKey: Data) throws -> Data {
 | |
|         if Thread.isMainThread {
 | |
|             #if DEBUG
 | |
|             preconditionFailure("It's illegal to call decrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.")
 | |
|             #endif
 | |
|         }
 | |
|         
 | |
|         return try AES.GCM.open(
 | |
|             try AES.GCM.SealedBox(combined: nonceAndCiphertext),
 | |
|             using: SymmetricKey(data: symmetricKey)
 | |
|         )
 | |
|     }
 | |
| 
 | |
|     /// - Note: Sync. Don't call from the main thread.
 | |
|     static func encrypt(_ plaintext: Data, with symmetricKey: Data) throws -> Data {
 | |
|         if Thread.isMainThread {
 | |
|             #if DEBUG
 | |
|             preconditionFailure("It's illegal to call encrypt(_:usingAESGCMWithSymmetricKey:) from the main thread.")
 | |
|             #endif
 | |
|         }
 | |
|         
 | |
|         let nonceData: Data = try Randomness.generateRandomBytes(numberBytes: ivSize)
 | |
|         let sealedData: AES.GCM.SealedBox = try AES.GCM.seal(
 | |
|             plaintext,
 | |
|             using: SymmetricKey(data: symmetricKey),
 | |
|             nonce: try AES.GCM.Nonce(data: nonceData)
 | |
|         )
 | |
|         
 | |
|         guard let cipherText: Data = sealedData.combined else {
 | |
|             throw GeneralError.keyGenerationFailed
 | |
|         }
 | |
|         
 | |
|         return cipherText
 | |
|     }
 | |
| 
 | |
|     /// - Note: Sync. Don't call from the main thread.
 | |
|     static func encrypt(_ plaintext: Data, for hexEncodedX25519PublicKey: String) throws -> EncryptionResult {
 | |
|         if Thread.isMainThread {
 | |
|             #if DEBUG
 | |
|             preconditionFailure("It's illegal to call encrypt(_:forSnode:) from the main thread.")
 | |
|             #endif
 | |
|         }
 | |
|         let x25519PublicKey = Data(hex: hexEncodedX25519PublicKey)
 | |
|         let ephemeralKeyPair = Curve25519.generateKeyPair()
 | |
|         let symmetricKey = try generateSymmetricKey(x25519PublicKey: x25519PublicKey, x25519PrivateKey: ephemeralKeyPair.privateKey)
 | |
|         let ciphertext = try encrypt(plaintext, with: Data(symmetricKey))
 | |
|         
 | |
|         return EncryptionResult(ciphertext: ciphertext, symmetricKey: Data(symmetricKey), ephemeralPublicKey: ephemeralKeyPair.publicKey)
 | |
|     }
 | |
| }
 |