Files
Kecalek_python/ios_client 0.8.5/Kecalek/Crypto/X25519Crypto.swift
2026-03-14 12:43:56 +01:00

78 lines
3.1 KiB
Swift

import Foundation
import CryptoKit
/// X25519 Diffie-Hellman key agreement
enum X25519Crypto {
// MARK: - Key Generation
/// Generate X25519 keypair
static func generateKeypair() -> (privateKey: Curve25519.KeyAgreement.PrivateKey, publicKey: Curve25519.KeyAgreement.PublicKey) {
let privateKey = Curve25519.KeyAgreement.PrivateKey()
return (privateKey, privateKey.publicKey)
}
// MARK: - Serialization
/// Serialize X25519 private key to 32 raw bytes
static func serializePrivate(_ key: Curve25519.KeyAgreement.PrivateKey) -> Data {
key.rawData // 32 bytes
}
/// Serialize X25519 public key to 32 raw bytes
static func serializePublic(_ key: Curve25519.KeyAgreement.PublicKey) -> Data {
key.rawData // 32 bytes
}
/// Load X25519 private key from 32 raw bytes
static func loadPrivate(_ data: Data) throws -> Curve25519.KeyAgreement.PrivateKey {
guard data.count == 32 else {
throw CryptoError.invalidKeyData("X25519 private key must be 32 bytes")
}
return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: data)
}
/// Load X25519 public key from 32 raw bytes
static func loadPublic(_ data: Data) throws -> Curve25519.KeyAgreement.PublicKey {
guard data.count == 32 else {
throw CryptoError.invalidKeyData("X25519 public key must be 32 bytes")
}
return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: data)
}
// MARK: - Diffie-Hellman
/// Perform X25519 DH key agreement. Returns 32-byte shared secret.
/// Matches Python: x25519_dh(private_key, public_key)
static func dh(_ privateKey: Curve25519.KeyAgreement.PrivateKey, _ publicKey: Curve25519.KeyAgreement.PublicKey) throws -> Data {
let sharedSecret = try privateKey.sharedSecretFromKeyAgreement(with: publicKey)
// Extract raw bytes from SharedSecret
return sharedSecret.withUnsafeBytes { Data($0) }
}
// MARK: - Ed25519 X25519 Key Conversion
/// Convert Ed25519 private key to X25519 private key.
/// SHA-512(seed) take first 32 bytes clamp per RFC 7748
/// Matches Python: ed25519_private_to_x25519(ed_private)
static func fromEd25519Private(_ edPrivate: Curve25519.Signing.PrivateKey) throws -> Curve25519.KeyAgreement.PrivateKey {
let raw = edPrivate.rawData // 32 bytes seed
// SHA-512 of the seed
let hash = SHA512.hash(data: raw)
var clamped = Data(hash.prefix(32))
// Clamp per RFC 7748
clamped[0] &= 248
clamped[31] &= 127
clamped[31] |= 64
return try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: clamped)
}
/// Convert Ed25519 public key to X25519 public key.
/// Uses Montgomery birational map: u = (1+y)/(1-y) mod p
/// Matches Python: ed25519_public_to_x25519(ed_public)
static func fromEd25519Public(_ edPublic: Curve25519.Signing.PublicKey) throws -> Curve25519.KeyAgreement.PublicKey {
let x25519Bytes = FieldArithmetic.ed25519PublicToX25519(edPublic.rawData)
return try Curve25519.KeyAgreement.PublicKey(rawRepresentation: x25519Bytes)
}
}