58 lines
2.0 KiB
Swift
58 lines
2.0 KiB
Swift
|
|
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)
|
||
|
|
}
|
||
|
|
}
|