synor/sdk/swift/Sources/SynorCrypto/SynorCrypto.swift
Gulshan Yadav 08a55aa80e feat(sdk): Add Crypto SDK for all 12 languages
Implements quantum-resistant cryptographic primitives across all SDK languages:
- Hybrid Ed25519 + Dilithium3 signatures (classical + post-quantum)
- BIP-39 mnemonic support (12, 15, 18, 21, 24 words)
- BIP-44 hierarchical key derivation (coin type 0x5359)
- Post-quantum algorithms: Falcon (FIPS 206), SPHINCS+ (FIPS 205)
- Key derivation: HKDF-SHA3-256, PBKDF2
- Hash functions: SHA3-256, BLAKE3, Keccak-256

Languages: JavaScript/TypeScript, Python, Go, Rust, Flutter/Dart, Java,
Kotlin, Swift, C, C++, C#/.NET, Ruby
2026-01-28 13:47:55 +05:30

347 lines
13 KiB
Swift

import Foundation
/// Synor Crypto SDK for Swift
///
/// Quantum-resistant cryptographic primitives for the Synor blockchain.
///
/// ```swift
/// let config = CryptoConfig(apiKey: "your-api-key")
/// let crypto = SynorCrypto(config: config)
///
/// // Generate a mnemonic
/// let mnemonic = try await crypto.mnemonic.generate(wordCount: 24)
/// print("Backup words: \(mnemonic.phrase)")
///
/// // Create keypair from mnemonic
/// let keypair = try await crypto.keypairs.fromMnemonic(phrase: mnemonic.phrase, passphrase: "")
/// let address = keypair.address(for: .mainnet)
///
/// // Sign a message
/// let signature = try await crypto.signing.sign(keypair: keypair, message: "Hello!".data(using: .utf8)!)
/// ```
public class SynorCrypto {
private let config: CryptoConfig
private let session: URLSession
private let encoder: JSONEncoder
private let decoder: JSONDecoder
private var _closed = false
public let mnemonic: MnemonicClient
public let keypairs: KeypairClient
public let signing: SigningClient
public let falcon: FalconClient
public let sphincs: SphincsClient
public let kdf: KdfClient
public let hash: HashClient
public var defaultNetwork: Network { config.defaultNetwork }
public var isClosed: Bool { _closed }
public init(config: CryptoConfig) {
self.config = config
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = config.timeout
self.session = URLSession(configuration: sessionConfig)
self.encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
self.decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
self.mnemonic = MnemonicClient()
self.keypairs = KeypairClient()
self.signing = SigningClient()
self.falcon = FalconClient()
self.sphincs = SphincsClient()
self.kdf = KdfClient()
self.hash = HashClient()
// Set parent references
self.mnemonic.crypto = self
self.keypairs.crypto = self
self.signing.crypto = self
self.falcon.crypto = self
self.sphincs.crypto = self
self.kdf.crypto = self
self.hash.crypto = self
}
public func healthCheck() async -> Bool {
do {
let result: [String: String] = try await get(path: "/health")
return result["status"] == "healthy"
} catch {
return false
}
}
public func getInfo() async throws -> [String: Any] {
try await get(path: "/info")
}
public func close() {
_closed = true
session.invalidateAndCancel()
}
// MARK: - Internal HTTP Methods
func get<T: Decodable>(path: String) async throws -> T {
guard !_closed else {
throw CryptoError(message: "Client has been closed", code: "CLIENT_CLOSED")
}
guard let url = URL(string: config.endpoint + path) else {
throw CryptoError(message: "Invalid URL", code: "INVALID_URL")
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version")
let (data, response) = try await session.data(for: request)
return try handleResponse(data: data, response: response)
}
func post<T: Decodable>(path: String, body: [String: Any]? = nil) async throws -> T {
guard !_closed else {
throw CryptoError(message: "Client has been closed", code: "CLIENT_CLOSED")
}
guard let url = URL(string: config.endpoint + path) else {
throw CryptoError(message: "Invalid URL", code: "INVALID_URL")
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(config.apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("swift/0.1.0", forHTTPHeaderField: "X-SDK-Version")
if let body = body {
request.httpBody = try JSONSerialization.data(withJSONObject: body)
} else {
request.httpBody = "{}".data(using: .utf8)
}
let (data, response) = try await session.data(for: request)
return try handleResponse(data: data, response: response)
}
private func handleResponse<T: Decodable>(data: Data, response: URLResponse) throws -> T {
guard let httpResponse = response as? HTTPURLResponse else {
throw CryptoError(message: "Invalid response", code: "INVALID_RESPONSE")
}
if httpResponse.statusCode >= 400 {
if let errorDict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
let message = errorDict["message"] as? String ?? "HTTP \(httpResponse.statusCode)"
let code = errorDict["code"] as? String
throw CryptoError(message: message, code: code)
}
throw CryptoError(message: "HTTP \(httpResponse.statusCode)", code: nil)
}
return try decoder.decode(T.self, from: data)
}
// MARK: - Sub-Clients
public class MnemonicClient {
weak var crypto: SynorCrypto?
public func generate(wordCount: Int = 24) async throws -> Mnemonic {
try await crypto!.post(path: "/mnemonic/generate", body: ["word_count": wordCount])
}
public func fromPhrase(phrase: String) async throws -> Mnemonic {
try await crypto!.post(path: "/mnemonic/from-phrase", body: ["phrase": phrase])
}
public func validate(phrase: String) async throws -> MnemonicValidation {
try await crypto!.post(path: "/mnemonic/validate", body: ["phrase": phrase])
}
public func toSeed(phrase: String, passphrase: String = "") async throws -> Data {
struct SeedResponse: Decodable { let seed: String }
let result: SeedResponse = try await crypto!.post(
path: "/mnemonic/to-seed",
body: ["phrase": phrase, "passphrase": passphrase]
)
return Data(base64Encoded: result.seed) ?? Data()
}
public func suggestWords(partial: String, limit: Int = 5) async throws -> [String] {
struct SuggestResponse: Decodable { let suggestions: [String] }
let result: SuggestResponse = try await crypto!.post(
path: "/mnemonic/suggest",
body: ["partial": partial, "limit": limit]
)
return result.suggestions
}
}
public class KeypairClient {
weak var crypto: SynorCrypto?
public func generate() async throws -> HybridKeypair {
try await crypto!.post(path: "/keypair/generate")
}
public func fromMnemonic(phrase: String, passphrase: String = "") async throws -> HybridKeypair {
try await crypto!.post(
path: "/keypair/from-mnemonic",
body: ["phrase": phrase, "passphrase": passphrase]
)
}
public func fromSeed(seed: Data) async throws -> HybridKeypair {
try await crypto!.post(
path: "/keypair/from-seed",
body: ["seed": seed.base64EncodedString()]
)
}
public func getAddress(publicKey: HybridPublicKey, network: Network) async throws -> Address {
try await crypto!.post(
path: "/keypair/address",
body: ["public_key": publicKey.toDict(), "network": network.rawValue]
)
}
}
public class SigningClient {
weak var crypto: SynorCrypto?
public func sign(keypair: HybridKeypair, message: Data) async throws -> HybridSignature {
try await crypto!.post(path: "/sign/hybrid", body: [
"secret_key": keypair.secretKey.toDict(),
"message": message.base64EncodedString()
])
}
public func verify(publicKey: HybridPublicKey, message: Data, signature: HybridSignature) async throws -> Bool {
struct VerifyResponse: Decodable { let valid: Bool }
let result: VerifyResponse = try await crypto!.post(path: "/sign/verify", body: [
"public_key": publicKey.toDict(),
"message": message.base64EncodedString(),
"signature": signature.toDict()
])
return result.valid
}
public func signEd25519(secretKey: Data, message: Data) async throws -> Data {
struct SignResponse: Decodable { let signature: String }
let result: SignResponse = try await crypto!.post(path: "/sign/ed25519", body: [
"secret_key": secretKey.base64EncodedString(),
"message": message.base64EncodedString()
])
return Data(base64Encoded: result.signature) ?? Data()
}
}
public class FalconClient {
weak var crypto: SynorCrypto?
public func generate(variant: FalconVariant = .falcon512) async throws -> FalconKeypair {
try await crypto!.post(path: "/falcon/generate", body: ["variant": variant.rawValue])
}
public func sign(keypair: FalconKeypair, message: Data) async throws -> FalconSignature {
try await crypto!.post(path: "/falcon/sign", body: [
"variant": keypair.variantEnum.rawValue,
"secret_key": keypair.secretKey.keyBytes.base64EncodedString(),
"message": message.base64EncodedString()
])
}
public func verify(publicKey: Data, message: Data, signature: FalconSignature) async throws -> Bool {
struct VerifyResponse: Decodable { let valid: Bool }
let result: VerifyResponse = try await crypto!.post(path: "/falcon/verify", body: [
"variant": signature.variantEnum.rawValue,
"public_key": publicKey.base64EncodedString(),
"message": message.base64EncodedString(),
"signature": signature.signatureBytes.base64EncodedString()
])
return result.valid
}
}
public class SphincsClient {
weak var crypto: SynorCrypto?
public func generate(variant: SphincsVariant = .shake128s) async throws -> SphincsKeypair {
try await crypto!.post(path: "/sphincs/generate", body: ["variant": variant.rawValue])
}
public func sign(keypair: SphincsKeypair, message: Data) async throws -> SphincsSignature {
try await crypto!.post(path: "/sphincs/sign", body: [
"variant": keypair.variantEnum.rawValue,
"secret_key": keypair.secretKey.keyBytes.base64EncodedString(),
"message": message.base64EncodedString()
])
}
public func verify(publicKey: Data, message: Data, signature: SphincsSignature) async throws -> Bool {
struct VerifyResponse: Decodable { let valid: Bool }
let result: VerifyResponse = try await crypto!.post(path: "/sphincs/verify", body: [
"variant": signature.variantEnum.rawValue,
"public_key": publicKey.base64EncodedString(),
"message": message.base64EncodedString(),
"signature": signature.signatureBytes.base64EncodedString()
])
return result.valid
}
}
public class KdfClient {
weak var crypto: SynorCrypto?
public func deriveKey(seed: Data, config: DerivationConfig = DerivationConfig()) async throws -> Data {
var body: [String: Any] = [
"seed": seed.base64EncodedString(),
"output_length": config.outputLength
]
if let salt = config.salt {
body["salt"] = salt.base64EncodedString()
}
if let info = config.info {
body["info"] = info.base64EncodedString()
}
struct KeyResponse: Decodable { let key: String }
let result: KeyResponse = try await crypto!.post(path: "/kdf/hkdf", body: body)
return Data(base64Encoded: result.key) ?? Data()
}
public func deriveFromPassword(password: Data, config: PasswordDerivationConfig) async throws -> Data {
struct KeyResponse: Decodable { let key: String }
let result: KeyResponse = try await crypto!.post(path: "/kdf/pbkdf2", body: [
"password": password.base64EncodedString(),
"salt": config.salt.base64EncodedString(),
"iterations": config.iterations,
"output_length": config.outputLength
])
return Data(base64Encoded: result.key) ?? Data()
}
}
public class HashClient {
weak var crypto: SynorCrypto?
public func sha3_256(data: Data) async throws -> Hash256 {
try await crypto!.post(path: "/hash/sha3-256", body: ["data": data.base64EncodedString()])
}
public func blake3(data: Data) async throws -> Hash256 {
try await crypto!.post(path: "/hash/blake3", body: ["data": data.base64EncodedString()])
}
public func keccak256(data: Data) async throws -> Hash256 {
try await crypto!.post(path: "/hash/keccak256", body: ["data": data.base64EncodedString()])
}
}
}