Files
koins/apps/ios/Koins/Support/RIPEMD160.swift
T

114 lines
4.0 KiB
Swift

import Foundation
/// RIPEMD-160, needed for XRP account id derivation (there is no CryptoKit /
/// CommonCrypto implementation of this digest on iOS).
enum RIPEMD160 {
private static let roundLeft: [Int] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8,
3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12,
1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2,
4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13,
]
private static let roundRight: [Int] = [
5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12,
6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2,
15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13,
8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14,
12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11,
]
private static let shiftsLeft: [UInt32] = [
11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8,
7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12,
11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5,
11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12,
9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6,
]
private static let shiftsRight: [UInt32] = [
8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6,
9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11,
9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5,
15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8,
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
]
private static let constantsLeft: [UInt32] = [0x0000_0000, 0x5A82_7999, 0x6ED9_EBA1, 0x8F1B_BCDC, 0xA953_FD4E]
private static let constantsRight: [UInt32] = [0x50A2_8BE6, 0x5C4D_D124, 0x6D70_3EF3, 0x7A6D_76E9, 0x0000_0000]
static func hash(_ message: [UInt8]) -> [UInt8] {
var padded = message
let bitLength = UInt64(message.count) * 8
padded.append(0x80)
while padded.count % 64 != 56 { padded.append(0) }
for shift in stride(from: 0, through: 56, by: 8) {
padded.append(UInt8((bitLength >> UInt64(shift)) & 0xff))
}
var h0: UInt32 = 0x6745_2301
var h1: UInt32 = 0xEFCD_AB89
var h2: UInt32 = 0x98BA_DCFE
var h3: UInt32 = 0x1032_5476
var h4: UInt32 = 0xC3D2_E1F0
var offset = 0
while offset < padded.count {
var words = [UInt32](repeating: 0, count: 16)
for index in 0..<16 {
let base = offset + index * 4
words[index] = UInt32(padded[base])
| (UInt32(padded[base + 1]) << 8)
| (UInt32(padded[base + 2]) << 16)
| (UInt32(padded[base + 3]) << 24)
}
var al = h0, bl = h1, cl = h2, dl = h3, el = h4
var ar = h0, br = h1, cr = h2, dr = h3, er = h4
for step in 0..<80 {
let round = step / 16
var leftTemp = rotate(al &+ f(round, bl, cl, dl) &+ words[roundLeft[step]] &+ constantsLeft[round], by: shiftsLeft[step]) &+ el
al = el; el = dl; dl = rotate(cl, by: 10); cl = bl; bl = leftTemp
let rightRound = 4 - round
leftTemp = rotate(ar &+ f(rightRound, br, cr, dr) &+ words[roundRight[step]] &+ constantsRight[round], by: shiftsRight[step]) &+ er
ar = er; er = dr; dr = rotate(cr, by: 10); cr = br; br = leftTemp
}
let temp = h1 &+ cl &+ dr
h1 = h2 &+ dl &+ er
h2 = h3 &+ el &+ ar
h3 = h4 &+ al &+ br
h4 = h0 &+ bl &+ cr
h0 = temp
offset += 64
}
var digest: [UInt8] = []
for word in [h0, h1, h2, h3, h4] {
digest.append(UInt8(word & 0xff))
digest.append(UInt8((word >> 8) & 0xff))
digest.append(UInt8((word >> 16) & 0xff))
digest.append(UInt8((word >> 24) & 0xff))
}
return digest
}
private static func f(_ round: Int, _ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
switch round {
case 0: return x ^ y ^ z
case 1: return (x & y) | (~x & z)
case 2: return (x | ~y) ^ z
case 3: return (x & z) | (y & ~z)
default: return x ^ (y | ~z)
}
}
private static func rotate(_ value: UInt32, by amount: UInt32) -> UInt32 {
(value << amount) | (value >> (32 - amount))
}
}