-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCryptoBox.swift
More file actions
79 lines (69 loc) · 2.77 KB
/
Copy pathCryptoBox.swift
File metadata and controls
79 lines (69 loc) · 2.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//
// CryptoBox.swift
// privamesh
//
// Low-level cryptographic primitives used by the Double Ratchet layer.
//
import Foundation
import CryptoKit
// MARK: - Errors
enum CryptoError: Error, LocalizedError {
case invalidKey
case invalidSignature
case invalidPadding
case invalidData
case decryptionFailed
case missingChainKey
case tooManySkippedMessages
var errorDescription: String? {
switch self {
case .invalidKey: return "Invalid cryptographic key"
case .invalidSignature: return "Bundle signature verification failed"
case .invalidPadding: return "Message padding is corrupt"
case .invalidData: return "Malformed data"
case .decryptionFailed: return "Decryption failed (wrong key or tampered data)"
case .missingChainKey: return "Ratchet chain key not initialized"
case .tooManySkippedMessages: return "Too many consecutive skipped messages"
}
}
}
// MARK: - Primitives
enum CryptoBox {
/// X25519 Diffie-Hellman — returns raw 32-byte shared secret.
static func dh(
privateKey: Curve25519.KeyAgreement.PrivateKey,
publicKey: Curve25519.KeyAgreement.PublicKey
) throws -> Data {
let shared = try privateKey.sharedSecretFromKeyAgreement(with: publicKey)
return shared.withUnsafeBytes { Data($0) }
}
/// HKDF-SHA256.
static func hkdf(inputKeyMaterial: Data, salt: Data, info: Data, outputByteCount: Int) -> Data {
let result = HKDF<SHA256>.deriveKey(
inputKeyMaterial: SymmetricKey(data: inputKeyMaterial),
salt: salt,
info: info,
outputByteCount: outputByteCount
)
return result.withUnsafeBytes { Data($0) }
}
/// HMAC-SHA256.
static func hmac(key: Data, message: Data) -> Data {
Data(HMAC<SHA256>.authenticationCode(for: message, using: SymmetricKey(data: key)))
}
/// AES-256-GCM encrypt. Returns combined bytes: 12-byte nonce ‖ ciphertext ‖ 16-byte tag.
static func encrypt(plaintext: Data, key: Data, associatedData: Data = Data()) throws -> Data {
let box = try AES.GCM.seal(plaintext, using: SymmetricKey(data: key), authenticating: associatedData)
guard let combined = box.combined else { throw CryptoError.invalidData }
return combined
}
/// AES-256-GCM decrypt.
static func decrypt(combined: Data, key: Data, associatedData: Data = Data()) throws -> Data {
do {
let box = try AES.GCM.SealedBox(combined: combined)
return try AES.GCM.open(box, using: SymmetricKey(data: key), authenticating: associatedData)
} catch is CryptoKitError {
throw CryptoError.decryptionFailed
}
}
}