1// Keccak.swift2// SophaxChatCore3//4// Minimal Keccak-256 (SHA3-256) implementation.5// Used solely for Tor v3 .onion address checksum derivation.6// Based on the NIST SHA-3 standard (FIPS 202).78import Foundation910enum Keccak {1112// MARK: - Public API1314/// Computes the Keccak-256 (SHA3-256) digest of the given data.15 static func hash256(_ input: Data)-> Data {16 var state = State()17 absorb(&state, input: input, rate: rate256, suffix: domainSuffix256)18 return squeeze(&state, outputLength: 32, rate: rate256)19}2021// MARK: - Constants2223 private static let rate256 = 136 // bytes (1088 bits), r = 1600 - 2*25624 private static let domainSuffix256: UInt8 = 0x06 // SHA3 domain separation2526// MARK: - State2728 private struct State {29 var lanes:[UInt64]= Array(repeating: 0, count: 25)30}3132// MARK: - Absorb3334 private static func absorb(_ state: inout State, input: Data, rate: Int, suffix: UInt8){35 var buf = Array(input)36// Pad with SHA3 multi-rate padding: append suffix, then zeros, then 0x8037 buf.append(suffix)38 while buf.count % rate != 0 {39 buf.append(0x00)40}41 buf[buf.count - 1]|= 0x804243 var offset = 044 while offset < buf.count {45 for i in 0 ..<(rate / 8){46 let lo = offset + i * 847 let slice = buf[lo ..< lo + 8]48 let lane = slice.withUnsafeBytes { $0.load(as: UInt64.self)}// little-endian49 state.lanes[i]^= lane50}51 keccakF(&state)52 offset += rate53}54}5556// MARK: - Squeeze5758 private static func squeeze(_ state: inout State, outputLength: Int, rate: Int)-> Data {59 var out = Data()60 var remaining = outputLength61 while remaining > 0 {62 let take = min(remaining, rate)63 for i in 0 ..<(take + 7)/ 8 {64 var lane = state.lanes[i]65 withUnsafeBytes(of:&lane){ out.append(contentsOf: $0)}66}67 remaining -= take68 if remaining > 0 { keccakF(&state)}69}70 return out.prefix(outputLength)71}7273// MARK: - Keccak-f[1600] permutation7475 private static func keccakF(_ state: inout State){76 let roundConstants:[UInt64]=[77 0x0000000000000001, 0x0000000000008082,78 0x800000000000808a, 0x8000000080008000,79 0x000000000000808b, 0x0000000080000001,80 0x8000000080008081, 0x8000000000008009,81 0x000000000000008a, 0x0000000000000088,82 0x0000000080008009, 0x000000008000000a,83 0x000000008000808b, 0x800000000000008b,84 0x8000000000008089, 0x8000000000008003,85 0x8000000000008002, 0x8000000000000080,86 0x000000000000800a, 0x800000008000000a,87 0x8000000080008081, 0x8000000000008080,88 0x0000000080000001, 0x8000000080008008,89]90// rho rotation amounts, indexed by source lane position (lanes[1]..lanes[24]).91// Each entry rho[i] is the rotation for lane i+1.92// Derived from FIPS 202 §3.2.2: rho offsets (x,y) -> (t+1)(t+2)/2 mod 64 along93// the rho/pi traversal path, then re-indexed by sequential lane order.94 let rho:[Int]=[95 1, 62, 28, 27, 36, 44, 6, 55, 20, 3,96 10, 43, 25, 39, 41, 45, 15, 21, 8, 18,97 2, 61, 56, 14,98]99// pi destination indices (+1), indexed by source lane position (lanes[1]..lanes[24]).100// b[pi[i]-1] = rho-rotated(lanes[i+1]) implements the FIPS 202 §3.2.3 pi step.101 let pi:[Int]=[102 11, 21, 6, 16, 17,103 2, 12, 22, 7, 8,104 18, 3, 13, 23, 24,105 9, 19, 4, 14, 15,106 25, 10, 20, 5,107]108109 var lanes = state.lanes110 for rc in roundConstants {111// θ112 var c =[UInt64](repeating: 0, count: 5)113 for x in 0 ..< 5 {114 c[x]= lanes[x]^ lanes[x+5]^ lanes[x+10]^ lanes[x+15]^ lanes[x+20]115}116 var d =[UInt64](repeating: 0, count: 5)117 for x in 0 ..< 5 {118 d[x]= c[(x+4)%5]^ rotl(c[(x+1)%5], by: 1)119}120 for i in 0 ..< 25 { lanes[i]^= d[i % 5]}121122// ρ and π123 var b =[UInt64](repeating: 0, count: 25)124 b[0]= lanes[0]125 for (i, lane) in lanes[1...].enumerated(){126 let rotated = rotl(lane, by: rho[i])127 b[pi[i]- 1]= rotated128}129130// χ131 for y in stride(from: 0, to: 25, by: 5){132 for x in 0 ..< 5 {133 lanes[y+x]= b[y+x]^(~b[y +(x+1)%5]& b[y +(x+2)%5])134}135}136137// ι138 lanes[0]^= rc139}140 state.lanes = lanes141}142143@inline(__always)144 private static func rotl(_ x: UInt64, by n: Int)-> UInt64 {145(x << n)|(x >>(64 - n))146}147}