github.com/zntrio/harp/v2@v2.0.9/pkg/sdk/security/crypto/bech32/bech32.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 // Copyright (c) 2017 Takatoshi Nakagawa 19 // Copyright (c) 2019 Google LLC 20 // 21 // Permission is hereby granted, free of charge, to any person obtaining a copy 22 // of this software and associated documentation files (the "Software"), to deal 23 // in the Software without restriction, including without limitation the rights 24 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 // copies of the Software, and to permit persons to whom the Software is 26 // furnished to do so, subject to the following conditions: 27 // 28 // The above copyright notice and this permission notice shall be included in 29 // all copies or substantial portions of the Software. 30 // 31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 37 // THE SOFTWARE. 38 39 // Package bech32 is a modified version of the reference implementation of BIP173. 40 package bech32 41 42 import ( 43 "fmt" 44 "strings" 45 ) 46 47 var charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 48 49 var generator = []uint32{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} 50 51 func polymod(values []byte) uint32 { 52 chk := uint32(1) 53 for _, v := range values { 54 top := chk >> 25 55 chk = (chk & 0x1ffffff) << 5 56 chk ^= uint32(v) 57 for i := 0; i < 5; i++ { 58 bit := top >> i & 1 59 if bit == 1 { 60 chk ^= generator[i] 61 } 62 } 63 } 64 return chk 65 } 66 67 func hrpExpand(hrp string) []byte { 68 h := []byte(strings.ToLower(hrp)) 69 ret := []byte{} 70 for _, c := range h { 71 ret = append(ret, c>>5) 72 } 73 ret = append(ret, 0) 74 for _, c := range h { 75 ret = append(ret, c&31) 76 } 77 return ret 78 } 79 80 func verifyChecksum(hrp string, data []byte) bool { 81 return polymod(append(hrpExpand(hrp), data...)) == 1 82 } 83 84 func createChecksum(hrp string, data []byte) []byte { 85 values := append(hrpExpand(hrp), data...) 86 values = append(values, []byte{0, 0, 0, 0, 0, 0}...) 87 mod := polymod(values) ^ 1 88 ret := make([]byte, 6) 89 for p := range ret { 90 shift := 5 * (5 - p) 91 ret[p] = byte(mod>>shift) & 31 92 } 93 return ret 94 } 95 96 func convertBits(data []byte, frombits, tobits byte, pad bool) ([]byte, error) { 97 var ret []byte 98 acc := uint32(0) 99 bits := byte(0) 100 maxv := byte(1<<tobits - 1) 101 for idx, value := range data { 102 if value>>frombits != 0 { 103 return nil, fmt.Errorf("invalid data range: data[%d]=%d (frombits=%d)", idx, value, frombits) 104 } 105 acc = acc<<frombits | uint32(value) 106 bits += frombits 107 for bits >= tobits { 108 bits -= tobits 109 ret = append(ret, byte(acc>>bits)&maxv) 110 } 111 } 112 switch { 113 case pad: 114 if bits > 0 { 115 ret = append(ret, byte(acc<<(tobits-bits))&maxv) 116 } 117 case bits >= frombits: 118 return nil, fmt.Errorf("illegal zero padding") 119 case byte(acc<<(tobits-bits))&maxv != 0: 120 return nil, fmt.Errorf("non-zero padding") 121 } 122 123 return ret, nil 124 } 125 126 // Encode encodes the HRP and a bytes slice to Bech32. If the HRP is uppercase, 127 // the output will be uppercase. 128 func Encode(hrp string, data []byte) (string, error) { 129 values, err := convertBits(data, 8, 5, true) 130 if err != nil { 131 return "", err 132 } 133 if len(hrp)+len(values)+7 > 90 { 134 return "", fmt.Errorf("too long: hrp length=%d, data length=%d", len(hrp), len(values)) 135 } 136 if len(hrp) < 1 { 137 return "", fmt.Errorf("invalid HRP: %q", hrp) 138 } 139 for p, c := range hrp { 140 if c < 33 || c > 126 { 141 return "", fmt.Errorf("invalid HRP character: hrp[%d]=%d", p, c) 142 } 143 } 144 if strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp { 145 return "", fmt.Errorf("mixed case HRP: %q", hrp) 146 } 147 lower := strings.ToLower(hrp) == hrp 148 hrp = strings.ToLower(hrp) 149 var ret strings.Builder 150 ret.WriteString(hrp) 151 ret.WriteString("1") 152 for _, p := range values { 153 ret.WriteByte(charset[p]) 154 } 155 for _, p := range createChecksum(hrp, values) { 156 ret.WriteByte(charset[p]) 157 } 158 if lower { 159 return ret.String(), nil 160 } 161 return strings.ToUpper(ret.String()), nil 162 } 163 164 // Decode decodes a Bech32 string. If the string is uppercase, the HRP will be uppercase. 165 func Decode(s string) (hrp string, data []byte, err error) { 166 if len(s) > 90 { 167 return "", nil, fmt.Errorf("too long: len=%d", len(s)) 168 } 169 if strings.ToLower(s) != s && strings.ToUpper(s) != s { 170 return "", nil, fmt.Errorf("mixed case") 171 } 172 pos := strings.LastIndex(s, "1") 173 if pos < 1 || pos+7 > len(s) { 174 return "", nil, fmt.Errorf("separator '1' at invalid position: pos=%d, len=%d", pos, len(s)) 175 } 176 hrp = s[:pos] 177 for p, c := range hrp { 178 if c < 33 || c > 126 { 179 return "", nil, fmt.Errorf("invalid character human-readable part: s[%d]=%d", p, c) 180 } 181 } 182 s = strings.ToLower(s) 183 for p, c := range s[pos+1:] { 184 d := strings.IndexRune(charset, c) 185 if d == -1 { 186 return "", nil, fmt.Errorf("invalid character data part: s[%d]=%v", p, c) 187 } 188 data = append(data, byte(d)) 189 } 190 if !verifyChecksum(hrp, data) { 191 return "", nil, fmt.Errorf("invalid checksum") 192 } 193 data, err = convertBits(data[:len(data)-6], 5, 8, false) 194 if err != nil { 195 return "", nil, err 196 } 197 return hrp, data, nil 198 }