spb/os-vault Public
Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.
Swift 96%
Shell 3.4%
Makefile 0.6%
1import Foundation2import TweetNacl34public enum Ed25519HDKey {5 public typealias Hex = String6 public typealias Path = String78 private static let ed25519Curve = "ed25519 seed"9 public static let hardenedOffset = 0x8000_00001011 public static func getMasterKeyFromSeed(_ seed: Hex) -> Result<Keys, Ed25519HDKeyError> {12 let hmacKey = ed25519Curve.bytes1314 guard let entropy = hmacSha512(message: Data(hex: seed), key: Data(hmacKey)) else {15 return .failure(.hmacCanNotAuthenticate)16 }17 let IL = Data(entropy[0 ..< 32])18 let IR = Data(entropy[32...])19 return .success(Keys(key: IL, chainCode: IR))20 }2122 private static func CKDPriv(keys: Keys, index: UInt32) -> Result<Keys, Ed25519HDKeyError> {23 var bytes = [UInt8]()24 bytes.append(UInt8(0))25 bytes += keys.key.bytes26 bytes += index.edBytes27 let data = Data(bytes)2829 guard let entropy = hmacSha512(message: data, key: keys.chainCode) else {30 return .failure(.hmacCanNotAuthenticate)31 }32 let IL = Data(entropy[0 ..< 32])33 let IR = Data(entropy[32...])34 return .success(Keys(key: IL, chainCode: IR))35 }3637 public static func getPublicKey(privateKey: Data, withZeroBytes: Bool = true) throws -> Data {38 let keyPair = try NaclSign.KeyPair.keyPair(fromSeed: privateKey)39 let signPk = keyPair.secretKey[32...]40 let zero = Data([UInt8(0)])41 return withZeroBytes ? Data(zero + signPk) : Data(signPk)42 }4344 public static func derivePath(_ path: Path, seed: Hex,45 offSet: Int = hardenedOffset) -> Result<Keys, Ed25519HDKeyError>46 {47 guard path.isValidDerivationPath else {48 return .failure(.invalidDerivationPath)49 }5051 return getMasterKeyFromSeed(seed).flatMap { keys in52 let segments = path.components(separatedBy: "/")[1...]53 .map(\.replacingDerive)54 .map { Int($0)! }55 return .success((keys: keys, segments: segments))56 }.flatMap { (keys: Keys, segments: [Int]) in57 do {58 let keys = try segments.reduce(keys) { try CKDPriv(keys: $0, index: UInt32($1 + offSet)).get() }59 return .success(keys)60 } catch {61 return .failure(.canNotGetMasterKeyFromSeed)62 }63 }64 }65}66