SPB Git

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%
2.4 KB · 66 lines swift
Raw Blame History
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