// // Bech32.swift // // Created by Evolution Group Ltd on 12.02.2018. // Copyright © 2018 Evolution Group Ltd. All rights reserved. // // Base32 address format for native v0-16 witness outputs implementation // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki // Inspired by Pieter Wuille C++ implementation import Foundation /// Bech32 checksum implementation public class Bech32 { private let gen: [UInt32] = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] /// Bech32 checksum delimiter private let checksumMarker: String = "1" /// Bech32 character set for encoding private let encCharset: Data = "qpzry9x8gf2tvdw0s3jn54khce6mua7l".data(using: .utf8)! /// Bech32 character set for decoding private let decCharset: [Int8] = [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 ] /// Find the polynomial with value coefficients mod the generator as 30-bit. private func polymod(_ values: Data) -> UInt32 { var chk: UInt32 = 1 for v in values { let top = (chk >> 25) chk = (chk & 0x1ffffff) << 5 ^ UInt32(v) for i: UInt8 in 0..<5 { chk ^= ((top >> i) & 1) == 0 ? 0 : gen[Int(i)] } } return chk } /// Expand a HRP for use in checksum computation. private func expandHrp(_ hrp: String) -> Data { guard let hrpBytes = hrp.data(using: .utf8) else { return Data() } var result = Data(repeating: 0x00, count: hrpBytes.count*2+1) for (i, c) in hrpBytes.enumerated() { result[i] = c >> 5 result[i + hrpBytes.count + 1] = c & 0x1f } result[hrp.count] = 0 return result } /// Verify checksum private func verifyChecksum(hrp: String, checksum: Data) -> Bool { var data = expandHrp(hrp) data.append(checksum) return polymod(data) == 1 } /// Create checksum private func createChecksum(hrp: String, values: Data) -> Data { var enc = expandHrp(hrp) enc.append(values) enc.append(Data(repeating: 0x00, count: 6)) let mod: UInt32 = polymod(enc) ^ 1 var ret: Data = Data(repeating: 0x00, count: 6) for i in 0..<6 { ret[i] = UInt8((mod >> (5 * (5 - i))) & 31) } return ret } /// Encode Bech32 string public func encode(_ hrp: String, values: Data) -> String { let checksum = createChecksum(hrp: hrp, values: values) var combined = values combined.append(checksum) guard let hrpBytes = hrp.data(using: .utf8) else { return "" } var ret = hrpBytes ret.append("1".data(using: .utf8)!) for i in combined { ret.append(encCharset[Int(i)]) } return String(data: ret, encoding: .utf8) ?? "" } /// Decode Bech32 string public func decode(_ str: String) throws -> (hrp: String, checksum: Data) { guard let strBytes = str.data(using: .utf8) else { throw DecodingError.nonUTF8String } guard strBytes.count <= 90 else { throw DecodingError.stringLengthExceeded } var lower: Bool = false var upper: Bool = false for c in strBytes { // printable range if c < 33 || c > 126 { throw DecodingError.nonPrintableCharacter } // 'a' to 'z' if c >= 97 && c <= 122 { lower = true } // 'A' to 'Z' if c >= 65 && c <= 90 { upper = true } } if lower && upper { throw DecodingError.invalidCase } guard let pos = str.range(of: checksumMarker, options: .backwards)?.lowerBound else { throw DecodingError.noChecksumMarker } let intPos: Int = str.distance(from: str.startIndex, to: pos) guard intPos >= 1 else { throw DecodingError.incorrectHrpSize } guard intPos + 7 <= str.count else { throw DecodingError.incorrectChecksumSize } let vSize: Int = str.count - 1 - intPos var values: Data = Data(repeating: 0x00, count: vSize) for i in 0..