Files
Nearfield_Friends/Proximity/Services/Security/CryptoManager.swift

58 lines
2.0 KiB
Swift
Raw Permalink Normal View History

import Foundation
import CryptoKit
/// End-to-end encryption for revealed conversations.
///
/// Uses X25519 key agreement + ChaChaPoly AEAD, the same primitives family
/// used by Signal. The server only ever sees ciphertext and opaque key IDs
/// it cannot read message contents.
struct CryptoManager {
// MARK: - Key pair
func generateKeyPair() -> (privateKey: Curve25519.KeyAgreement.PrivateKey,
publicKey: Curve25519.KeyAgreement.PublicKey) {
let privateKey = Curve25519.KeyAgreement.PrivateKey()
return (privateKey, privateKey.publicKey)
}
// MARK: - Session establishment
/// Derive a shared symmetric session key from two key pairs.
func deriveSessionKey(
myPrivate: Curve25519.KeyAgreement.PrivateKey,
theirPublic: Curve25519.KeyAgreement.PublicKey
) throws -> SymmetricKey {
let shared = try myPrivate.sharedSecretFromKeyAgreement(with: theirPublic)
// Salt with a fixed app domain string to keep keys per-app.
return shared.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: Data("proximity.e2e.v1".utf8),
sharedInfo: Data(),
outputByteCount: 32
)
}
// MARK: - Message encryption
func encrypt(_ plaintext: Data, using key: SymmetricKey) throws -> Data {
let sealed = try ChaChaPoly.seal(plaintext, using: key)
return sealed.combined
}
func decrypt(_ combined: Data, using key: SymmetricKey) throws -> Data {
let box = try ChaChaPoly.SealedBox(combined: combined)
return try ChaChaPoly.open(box, using: key)
}
// MARK: - Signatures (for token authenticity)
func sign(_ data: Data, with key: Curve25519.Signing.PrivateKey) throws -> Data {
try key.signature(for: data)
}
func verify(_ data: Data, signature: Data, with key: Curve25519.Signing.PublicKey) -> Bool {
key.isValidSignature(signature, for: data)
}
}