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