montana/macOS/MontanaPresence/test_crypto.swift

285 lines
13 KiB
Swift
Raw Permalink Normal View History

import Foundation
//
// Montana Protocol Layer 1 Cryptography: 4 Checks + 16 Tests
// ML-DSA-65 (FIPS 204) Post-Quantum Signatures
//
// Check 1: Generation (seed deterministic keys)
// Check 2: Addressing (pubkey mt... with checksum)
// Check 3: Sign & Verify (deterministic signatures)
// Check 4: Isolation (private key never exposed)
//
// Servers: Amsterdam (72.56.102.240), Almaty (91.200.148.93)
//
@main
struct CryptoTest {
static var passed = 0
static var failed = 0
static var total = 16
static func test(_ num: Int, _ name: String, _ check: () -> Bool) {
let result = check()
if result {
print(" [\(num)/\(total)] PASS: \(name)")
passed += 1
} else {
print(" [\(num)/\(total)] FAIL: \(name)")
failed += 1
}
}
// HTTP helper for server tests
static func httpRequest(url: String, method: String = "GET", body: [String: Any]? = nil, headers: [String: String] = [:]) -> (data: Data?, statusCode: Int) {
guard let u = URL(string: url) else { return (nil, 0) }
var req = URLRequest(url: u, timeoutInterval: 10)
req.httpMethod = method
for (k, v) in headers {
req.setValue(v, forHTTPHeaderField: k)
}
if let body = body {
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try? JSONSerialization.data(withJSONObject: body)
}
let sem = DispatchSemaphore(value: 0)
var respData: Data?
var statusCode = 0
let task = URLSession.shared.dataTask(with: req) { data, response, _ in
respData = data
statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
sem.signal()
}
task.resume()
_ = sem.wait(timeout: .now() + 15)
return (respData, statusCode)
}
static func main() {
print("═══════════════════════════════════════════════════════")
print(" Montana Layer 1 — 4 Checks, 16 Mainnet Tests")
print(" Algorithm: ML-DSA-65 (FIPS 204)")
print(" Key sizes: priv=4032, pub=1952, sig=3309")
print("═══════════════════════════════════════════════════════")
//
// CHECK 1: GENERATION seed deterministic keys
//
print("\n══ CHECK 1: GENERATION ══")
// TEST 1: Generate 24-word mnemonic
print("\n[TEST 1] Generate Mnemonic")
guard let words = MontanaSeed.generateMnemonic() else {
print(" FATAL: generateMnemonic() returned nil")
Foundation.exit(1)
}
test(1, "Mnemonic: 24 words generated") {
words.count == 24
}
// TEST 2: Validate mnemonic
print("\n[TEST 2] Validate Mnemonic")
test(2, "Mnemonic checksum valid") {
MontanaSeed.validateMnemonic(words)
}
// TEST 3: Derive keypair from mnemonic
print("\n[TEST 3] Derive Keypair from Mnemonic")
guard let kp1 = MontanaSeed.keypairFromMnemonic(words) else {
print(" FATAL: keypairFromMnemonic() returned nil")
Foundation.exit(1)
}
test(3, "Keygen from seed: priv=4032, pub=1952") {
kp1.privateKey.count == 4032 && kp1.publicKey.count == 1952
}
// TEST 4: DETERMINISTIC same seed = same keys (CRITICAL)
print("\n[TEST 4] Deterministic Keys — Same Seed = Same Keys")
guard let kp2 = MontanaSeed.keypairFromMnemonic(words) else {
print(" FATAL: second keypairFromMnemonic() returned nil")
Foundation.exit(1)
}
test(4, "Same mnemonic → identical privateKey AND publicKey") {
kp1.privateKey == kp2.privateKey && kp1.publicKey == kp2.publicKey
}
//
// CHECK 2: ADDRESSING pubkey mt... with checksum
//
print("\n══ CHECK 2: ADDRESSING ══")
// TEST 5: Address format
print("\n[TEST 5] Address Format")
let addr = MLDSA65.generateAddress(from: kp1.publicKey)
test(5, "Address: mt prefix, 42 chars, lowercase hex") {
addr.hasPrefix("mt") &&
addr.count == 42 &&
String(addr.dropFirst(2)).allSatisfy { "0123456789abcdef".contains($0) }
}
// TEST 6: Deterministic address
print("\n[TEST 6] Deterministic Address")
let addr2 = MLDSA65.generateAddress(from: kp1.publicKey)
test(6, "Same pubkey → same address") {
addr == addr2
}
// TEST 7: Address checksum validation
print("\n[TEST 7] Address Checksum Validation")
test(7, "Valid address passes checksum validation") {
MLDSA65.validateAddress(addr)
}
// TEST 8: Tampered address fails validation
print("\n[TEST 8] Tampered Address Rejection")
var tamperedAddr = addr
// Change one character in the middle
let idx = tamperedAddr.index(tamperedAddr.startIndex, offsetBy: 10)
let oldChar = tamperedAddr[idx]
let newChar: Character = oldChar == "a" ? "b" : "a"
tamperedAddr.replaceSubrange(idx...idx, with: String(newChar))
test(8, "Changed 1 char → checksum validation fails") {
!MLDSA65.validateAddress(tamperedAddr)
}
// TEST 9: Cannot reverse address pubkey
print("\n[TEST 9] Address Irreversibility")
guard let kp3 = MLDSA65.generateKeypair() else {
print(" FATAL: random keygen failed")
Foundation.exit(1)
}
let addr3 = MLDSA65.generateAddress(from: kp3.publicKey)
test(9, "Different keys → different addresses, both valid") {
addr != addr3 && MLDSA65.validateAddress(addr3)
}
//
// CHECK 3: SIGN & VERIFY deterministic signatures
//
print("\n══ CHECK 3: SIGN & VERIFY ══")
// TEST 10: Sign + Verify roundtrip
print("\n[TEST 10] Sign + Verify Roundtrip")
let genesisMsg = "Montana Genesis".data(using: .utf8)!
guard let sig1 = MLDSA65.sign(message: genesisMsg, privateKey: kp1.privateKey) else {
print(" FATAL: sign() returned nil")
Foundation.exit(1)
}
test(10, "Sign(Montana Genesis) → Verify = true, sig=3309") {
MLDSA65.verify(message: genesisMsg, signature: sig1, publicKey: kp1.publicKey) &&
sig1.count == 3309
}
// TEST 11: DETERMINISTIC SIGNATURE same key + same message = identical signature
print("\n[TEST 11] Deterministic Signature — Same Key + Same Message = Identical")
guard let sig2 = MLDSA65.sign(message: genesisMsg, privateKey: kp1.privateKey) else {
print(" FATAL: second sign() returned nil")
Foundation.exit(1)
}
test(11, "Two signatures of same message are byte-identical") {
sig1 == sig2
}
// TEST 12: THE CANONICAL TEST
// Seed keys sign "delete" recover from same seed sign identical
print("\n[TEST 12] CANONICAL: Seed → Sign → Delete → Recover → Sign = Identical")
let sig1Hex = sig1.map { String(format: "%02x", $0) }.joined()
// "Delete" keys (forget them)
// Recover from same mnemonic
guard let kpRecovered = MontanaSeed.keypairFromMnemonic(words) else {
print(" FATAL: recovery keypairFromMnemonic() returned nil")
Foundation.exit(1)
}
guard let sigRecovered = MLDSA65.sign(message: genesisMsg, privateKey: kpRecovered.privateKey) else {
print(" FATAL: recovered sign() returned nil")
Foundation.exit(1)
}
let sigRecoveredHex = sigRecovered.map { String(format: "%02x", $0) }.joined()
test(12, "Recovered signature is byte-identical to original") {
sig1Hex == sigRecoveredHex &&
kpRecovered.publicKey == kp1.publicKey &&
kpRecovered.privateKey == kp1.privateKey
}
// TEST 13: Wrong key rejection
print("\n[TEST 13] Wrong Key Rejection")
test(13, "Verify with wrong pubkey = false") {
!MLDSA65.verify(message: genesisMsg, signature: sig1, publicKey: kp3.publicKey)
}
// TEST 14: Tampered message rejection
print("\n[TEST 14] Tampered Message Rejection")
let tampered = "Montana Genesi5".data(using: .utf8)!
test(14, "Verify tampered message = false") {
!MLDSA65.verify(message: tampered, signature: sig1, publicKey: kp1.publicKey)
}
//
// CHECK 4: ISOLATION + SERVER VERIFICATION
//
print("\n══ CHECK 4: ISOLATION + SERVERS ══")
// TEST 15: Server Health Amsterdam & Almaty
print("\n[TEST 15] Server Health")
let (_, amsStatus) = httpRequest(url: "http://72.56.102.240/api/health")
let (_, almStatus) = httpRequest(url: "http://91.200.148.93/api/health")
test(15, "Amsterdam=200, Almaty=200") {
amsStatus == 200 && almStatus == 200
}
// TEST 16: Server PQ-Signed Balance Amsterdam
print("\n[TEST 16] Server PQ-Signed Balance — Amsterdam")
let ts = Int(Date().timeIntervalSince1970)
let balanceMsg = "BALANCE:\(addr):\(ts)"
guard let balanceData = balanceMsg.data(using: .utf8),
let balanceSig = MLDSA65.sign(message: balanceData, privateKey: kp1.privateKey) else {
print(" FATAL: balance sign failed")
Foundation.exit(1)
}
let pubHex = kp1.publicKey.map { String(format: "%02x", $0) }.joined()
let sigHex = balanceSig.map { String(format: "%02x", $0) }.joined()
let (balRespData, balRespStatus) = httpRequest(
url: "http://72.56.102.240/api/balance/\(addr)",
method: "GET",
headers: [
"X-Address": addr,
"X-Timestamp": "\(ts)",
"X-Signature": sigHex,
"X-Public-Key": pubHex
]
)
var pqVerified = false
if let data = balRespData,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
pqVerified = json["pq_verified"] as? Bool ?? false
let bal = json["balance"] as? Double ?? -1
print(" Response: balance=\(bal), pq_verified=\(pqVerified)")
}
test(16, "Amsterdam PQ balance: status=200, pq_verified=true") {
balRespStatus == 200 && pqVerified
}
//
// RESULTS
//
print("\n" + String(repeating: "", count: 55))
print(" Results: \(passed)/\(total) passed, \(failed) failed")
print(String(repeating: "", count: 55))
if failed == 0 {
print("\n ML-DSA-65 LAYER 1: ALL \(total) TESTS PASSED")
print(" Mnemonic: \(words.prefix(3).joined(separator: " "))... (\(words.count) words)")
print(" Address: \(addr)")
print(" Checksum: \(MLDSA65.validateAddress(addr) ? "VALID" : "INVALID")")
print(" Deterministic: sig1 == sigRecovered = \(sig1Hex == sigRecoveredHex)")
} else {
print("\n FAILED — \(failed) test(s)")
Foundation.exit(1)
}
}
}