Bech32.swift (7772B)
1 // 2 // Bech32.swift 3 // 4 // Modified by William Casarin in 2022 5 // Created by Evolution Group Ltd on 12.02.2018. 6 // Copyright © 2018 Evolution Group Ltd. All rights reserved. 7 // 8 // Base32 address format for native v0-16 witness outputs implementation 9 // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki 10 // Inspired by Pieter Wuille C++ implementation 11 import Foundation 12 13 /// Bech32 checksum implementation 14 fileprivate let gen: [UInt32] = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 15 /// Bech32 checksum delimiter 16 fileprivate let checksumMarker: String = "1" 17 /// Bech32 character set for encoding 18 fileprivate let encCharset: Data = "qpzry9x8gf2tvdw0s3jn54khce6mua7l".data(using: .utf8)! 19 /// Bech32 character set for decoding 20 fileprivate let decCharset: [Int8] = [ 21 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 22 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 23 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 24 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, 25 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 26 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, 27 -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 28 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 29 ] 30 31 /// Find the polynomial with value coefficients mod the generator as 30-bit. 32 public func bech32_polymod(_ values: Data) -> UInt32 { 33 var chk: UInt32 = 1 34 for v in values { 35 let top = (chk >> 25) 36 chk = (chk & 0x1ffffff) << 5 ^ UInt32(v) 37 for i: UInt8 in 0..<5 { 38 chk ^= ((top >> i) & 1) == 0 ? 0 : gen[Int(i)] 39 } 40 } 41 return chk 42 } 43 44 45 /// Expand a HRP for use in checksum computation. 46 func bech32_expand_hrp(_ s: String) -> Data { 47 var left: [UInt8] = [] 48 var right: [UInt8] = [] 49 for x in Array(s) { 50 let scalars = String(x).unicodeScalars 51 left.append(UInt8(scalars[scalars.startIndex].value) >> 5) 52 right.append(UInt8(scalars[scalars.startIndex].value) & 31) 53 } 54 return Data(left + [0] + right) 55 } 56 57 /// Verify checksum 58 public func bech32_verify(hrp: String, checksum: Data) -> Bool { 59 var data = bech32_expand_hrp(hrp) 60 data.append(checksum) 61 return bech32_polymod(data) == 1 62 } 63 64 /// Create checksum 65 public func bech32_create_checksum(hrp: String, values: Data) -> Data { 66 var enc = bech32_expand_hrp(hrp) 67 enc.append(values) 68 enc.append(Data(repeating: 0x00, count: 6)) 69 let mod: UInt32 = bech32_polymod(enc) ^ 1 70 var ret: Data = Data(repeating: 0x00, count: 6) 71 for i in 0..<6 { 72 ret[i] = UInt8((mod >> (5 * (5 - i))) & 31) 73 } 74 return ret 75 } 76 77 public func bech32_encode(hrp: String, _ input: [UInt8]) -> String { 78 let table: [Character] = Array("qpzry9x8gf2tvdw0s3jn54khce6mua7l") 79 let bits = eightToFiveBits(input) 80 let check_sum = bech32_checksum(hrp: hrp, data: bits) 81 let separator = "1" 82 return "\(hrp)" + separator + String((bits + check_sum).map { table[Int($0)] }) 83 } 84 85 func bech32_checksum(hrp: String, data: [UInt8]) -> [UInt8] { 86 let values = bech32_expand_hrp(hrp) + data 87 let polymod = bech32_polymod(values + [0,0,0,0,0,0]) ^ 1 88 var result: [UInt32] = [] 89 for i in (0..<6) { 90 result.append((polymod >> (5 * (5 - UInt32(i)))) & 31) 91 } 92 return result.map { UInt8($0) } 93 } 94 95 func bech32_convert_bits(outbits: Int, input: Data, inbits: Int, pad: Int) -> Data? { 96 let maxv: UInt32 = ((UInt32(1)) << outbits) - 1; 97 var val: UInt32 = 0 98 var bits: Int = 0 99 var out = Data() 100 101 for i in (0..<input.count) { 102 val = (val << inbits) | UInt32(input[i]) 103 bits += inbits; 104 while bits >= outbits { 105 bits -= outbits; 106 out.append(UInt8((val >> bits) & maxv)) 107 } 108 } 109 110 if pad != 0 { 111 if bits != 0 { 112 out.append(UInt8(val << (outbits - bits) & maxv)) 113 } 114 } else if 0 != ((val << (outbits - bits)) & maxv) || bits >= inbits { 115 return nil 116 } 117 118 return out 119 } 120 121 func eightToFiveBits(_ input: [UInt8]) -> [UInt8] { 122 guard !input.isEmpty else { return [] } 123 124 var outputSize = (input.count * 8) / 5 125 if ((input.count * 8) % 5) != 0 { 126 outputSize += 1 127 } 128 var outputArray: [UInt8] = [] 129 for i in (0..<outputSize) { 130 let devision = (i * 5) / 8 131 let reminder = (i * 5) % 8 132 var element = input[devision] << reminder 133 element >>= 3 134 135 if (reminder > 3) && (i + 1 < outputSize) { 136 element = element | (input[devision + 1] >> (8 - reminder + 3)) 137 } 138 139 outputArray.append(element) 140 } 141 142 return outputArray 143 } 144 145 /// Decode Bech32 string 146 @discardableResult 147 public func bech32_decode(_ str: String) throws -> (hrp: String, data: Data)? { 148 guard let strBytes = str.data(using: .utf8) else { 149 throw Bech32Error.nonUTF8String 150 } 151 guard strBytes.count <= 2024 else { 152 throw Bech32Error.stringLengthExceeded 153 } 154 var lower: Bool = false 155 var upper: Bool = false 156 for c in strBytes { 157 // printable range 158 if c < 33 || c > 126 { 159 throw Bech32Error.nonPrintableCharacter 160 } 161 // 'a' to 'z' 162 if c >= 97 && c <= 122 { 163 lower = true 164 } 165 // 'A' to 'Z' 166 if c >= 65 && c <= 90 { 167 upper = true 168 } 169 } 170 if lower && upper { 171 throw Bech32Error.invalidCase 172 } 173 guard let pos = str.range(of: checksumMarker, options: .backwards)?.lowerBound else { 174 throw Bech32Error.noChecksumMarker 175 } 176 let intPos: Int = str.distance(from: str.startIndex, to: pos) 177 guard intPos >= 1 else { 178 throw Bech32Error.incorrectHrpSize 179 } 180 guard intPos + 7 <= str.count else { 181 throw Bech32Error.incorrectChecksumSize 182 } 183 let vSize: Int = str.count - 1 - intPos 184 var values: Data = Data(repeating: 0x00, count: vSize) 185 for i in 0..<vSize { 186 let c = strBytes[i + intPos + 1] 187 let decInt = decCharset[Int(c)] 188 if decInt == -1 { 189 throw Bech32Error.invalidCharacter 190 } 191 values[i] = UInt8(decInt) 192 } 193 let hrp = String(str[..<pos]).lowercased() 194 guard bech32_verify(hrp: hrp, checksum: values) else { 195 throw Bech32Error.checksumMismatch 196 } 197 let out = Data(values[..<(vSize-6)]) 198 guard let converted = bech32_convert_bits(outbits: 8, input: out, inbits: 5, pad: 0) else { 199 return nil 200 } 201 return (hrp, converted) 202 } 203 204 public enum Bech32Error: LocalizedError { 205 case nonUTF8String 206 case nonPrintableCharacter 207 case invalidCase 208 case noChecksumMarker 209 case incorrectHrpSize 210 case incorrectChecksumSize 211 case stringLengthExceeded 212 213 case invalidCharacter 214 case checksumMismatch 215 216 public var errorDescription: String? { 217 switch self { 218 case .checksumMismatch: 219 return "Checksum doesn't match" 220 case .incorrectChecksumSize: 221 return "Checksum size too low" 222 case .incorrectHrpSize: 223 return "Human-readable-part is too small or empty" 224 case .invalidCase: 225 return "String contains mixed case characters" 226 case .invalidCharacter: 227 return "Invalid character met on decoding" 228 case .noChecksumMarker: 229 return "Checksum delimiter not found" 230 case .nonPrintableCharacter: 231 return "Non printable character in input string" 232 case .nonUTF8String: 233 return "String cannot be decoded by utf8 decoder" 234 case .stringLengthExceeded: 235 return "Input string is too long" 236 } 237 } 238 }