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 Foundation23public enum RIPEMD {4 public static func digest(_ input: Data, bitlength: Int = 160) -> Data {5 assert(bitlength == 160, "Only RIPEMD-160 is implemented")67 let paddedData = pad(input)89 var block = RIPEMD.Block()1011 for i in 0 ..< paddedData.count / 64 {12 let part = getWordsInSection(paddedData, i)13 block.compress(part)14 }1516 return encodeWords(block.hash)17 }1819 // Pads the input to a multiple 64 bytes. First it adds 0x80 followed by zeros.20 // It then needs 8 bytes at the end where it writes the length (in bits, little endian).21 // If this doesn't fit it will add another block of 64 bytes.2223 // FIXME: Make private once tests support it24 public static func pad(_ data: Data) -> Data {25 let paddedData = (data as NSData).mutableCopy() as! NSMutableData2627 // Put 0x80 after the last character:28 let stop: [UInt8] = [UInt8(0x80)] // 2^829 paddedData.append(stop, length: 1)3031 // Pad with zeros until there are 64 * k - 8 bytes.32 var numberOfZerosToPad: Int33 if paddedData.length % 64 == 56 {34 // No padding needed35 numberOfZerosToPad = 036 } else if paddedData.length % 64 < 56 {37 numberOfZerosToPad = 56 - (paddedData.length % 64)38 } else {39 // Add an extra round40 numberOfZerosToPad = 56 + (64 - paddedData.length % 64)41 }4243 let zeroBytes = [UInt8](repeating: 0, count: numberOfZerosToPad)44 paddedData.append(zeroBytes, length: numberOfZerosToPad)4546 // Append length of message:47 let length = UInt32(data.count) * 848 let lengthBytes: [UInt32] = [length, UInt32(0x0000_0000)]49 paddedData.append(lengthBytes, length: 8)5051 return paddedData as Data52 }5354 // Takes an NSData object of length k * 64 bytes and returns an array of UInt3255 // representing 1 word (4 bytes) each. Each word is in little endian,56 // so "abcdefgh" is now "dcbahgfe".57 // FIXME: Make private once tests support it58 public static func getWordsInSection(_ data: Data, _ section: Int) -> [UInt32] {59 let offset = section * 646061 assert(data.count >= Int(offset + 64), "Data too short")6263 var words: [UInt32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]64 (data as NSData).getBytes(&words, range: NSMakeRange(offset, 64))6566 return words67 }6869 // FIXME: Make private once tests support it70 public static func encodeWords(_ input: [UInt32]) -> Data {71 let data = NSMutableData(bytes: input, length: 20)72 return data as Data73 }7475 // Returns a string representation of a hexadecimal number76 public static func digest(_ input: Data, bitlength: Int = 160) -> String {77 digest(input, bitlength: bitlength).toHexString()78 }7980 // Takes a string representation of a hexadecimal number81 public static func hexStringDigest(_ input: String, bitlength: Int = 160) -> Data {82 let data = Data.dataFromHexString(hexString: input)83 return digest(data, bitlength: bitlength)84 }8586 // Takes a string representation of a hexadecimal number and returns a87 // string represenation of the resulting 160 bit hash.88 public static func hexStringDigest(_ input: String, bitlength: Int = 160) -> String {89 let digest: Data = hexStringDigest(input, bitlength: bitlength)90 return digest.toHexString()91 }9293 // Takes an ASCII string94 public static func asciiDigest(_ input: String, bitlength: Int = 160) -> Data {95 // Order of bytes is preserved; if the last character is dot, the last96 // byte is a dot.97 if let data: Data = input.data(using: String.Encoding.ascii) {98 return digest(data, bitlength: bitlength)99 } else {100 assertionFailure("Invalid input")101 return Data()102 }103 }104105// Takes an ASCII string and returns a hex string represenation of the106// resulting 160 bit hash.107 public static func asciiDigest(_ input: String, bitlength: Int = 160) -> String {108 asciiDigest(input, bitlength: bitlength).toHexString()109 }110}111