github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/bech32/bech32.go (about)

     1  package bech32
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  )
     7  
     8  // charset is the sequence of ascii characters that make up the bech32
     9  // alphabet.  Each character represents a 5-bit squashed byte.
    10  // q = 0b00000, p = 0b00001, z = 0b00010, and so on.
    11  const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
    12  
    13  // inverseCharset is a mapping of 8-bit ascii characters to the charset
    14  // positions.  Both uppercase and lowercase ascii are mapped to the 5-bit
    15  // position values.
    16  var inverseCharset = [256]int8{
    17  	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    18  	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    19  	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    20  	15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
    21  	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
    22  	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
    23  	-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
    24  	1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}
    25  
    26  // Bytes8to5 extends a byte slice into a longer, padded byte slice of 5-bit elements
    27  // where the high 3 bits are all 0.
    28  func Bytes8to5(input []byte) []byte {
    29  	// no way to triger an error going from 8 to 5
    30  	output, _ := ByteSquasher(input, 8, 5)
    31  	return output
    32  }
    33  
    34  // Bytes5to8 goes from squashed bytes to full height bytes
    35  func Bytes5to8(input []byte) ([]byte, error) {
    36  	return ByteSquasher(input, 5, 8)
    37  }
    38  
    39  // ByteSquasher squashes full-width (8-bit) bytes into "squashed" 5-bit bytes,
    40  // and vice versa.  It can operate on other widths but in this package only
    41  // goes 5 to 8 and back again.  It can return an error if the squashed input
    42  // you give it isn't actually squashed, or if there is padding (trailing q characters)
    43  // when going from 5 to 8
    44  func ByteSquasher(input []byte, inputWidth, outputWidth uint32) ([]byte, error) {
    45  	var bitstash, accumulator uint32
    46  	var output []byte
    47  	maxOutputValue := uint32((1 << outputWidth) - 1)
    48  	for i, c := range input {
    49  		if c>>inputWidth != 0 {
    50  			return nil, fmt.Errorf("byte %d (%x) high bits set", i, c)
    51  		}
    52  		accumulator = (accumulator << inputWidth) | uint32(c)
    53  		bitstash += inputWidth
    54  		for bitstash >= outputWidth {
    55  			bitstash -= outputWidth
    56  			output = append(output,
    57  				byte((accumulator>>bitstash)&maxOutputValue))
    58  		}
    59  	}
    60  	// pad if going from 8 to 5
    61  	if inputWidth == 8 && outputWidth == 5 {
    62  		if bitstash != 0 {
    63  			output = append(output,
    64  				byte((accumulator << (outputWidth - bitstash) & maxOutputValue)))
    65  		}
    66  	} else if bitstash >= inputWidth ||
    67  		((accumulator<<(outputWidth-bitstash))&maxOutputValue) != 0 {
    68  		// no pad from 5 to 8 allowed
    69  		return nil, fmt.Errorf(
    70  			"invalid padding from %d to %d bits", inputWidth, outputWidth)
    71  	}
    72  	return output, nil
    73  }
    74  
    75  // SquashedBytesToString swaps 5-bit bytes with a string of the corresponding letters
    76  func SquashedBytesToString(input []byte) (string, error) {
    77  	var s string
    78  	for i, c := range input {
    79  		if c&0xe0 != 0 {
    80  			return "", fmt.Errorf("high bits set at position %d: %x", i, c)
    81  		}
    82  		s += string(charset[c])
    83  	}
    84  	return s, nil
    85  }
    86  
    87  // StringToSquashedBytes uses the inverseCharset to switch from the characters
    88  // back to 5-bit squashed bytes.
    89  func StringToSquashedBytes(input string) ([]byte, error) {
    90  	b := make([]byte, len(input))
    91  	for i, c := range input {
    92  		if inverseCharset[c] == -1 {
    93  			return nil, fmt.Errorf("contains invalid character %s", string(c))
    94  		}
    95  		b[i] = byte(inverseCharset[c])
    96  	}
    97  	return b, nil
    98  }
    99  
   100  // PolyMod takes a byte slice and returns the 32-bit BCH checksum.
   101  // Note that the input bytes to PolyMod need to be squashed to 5-bits tall
   102  // before being used in this function.  And this function will not error,
   103  // but instead return an unusable checksum, if you give it full-height bytes.
   104  func PolyMod(values []byte) uint32 {
   105  
   106  	// magic generator uint32s
   107  	gen := []uint32{
   108  		0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3,
   109  	}
   110  
   111  	// start with 1
   112  	chk := uint32(1)
   113  
   114  	for _, v := range values {
   115  		top := chk >> 25
   116  		chk = (chk&0x1ffffff)<<5 ^ uint32(v)
   117  		for i, g := range gen {
   118  			if (top>>uint8(i))&1 == 1 {
   119  				chk ^= g
   120  			}
   121  		}
   122  	}
   123  
   124  	return chk
   125  }
   126  
   127  // HRPExpand turns the human redable part into 5bit-bytes for later processing
   128  func HRPExpand(input string) []byte {
   129  	output := make([]byte, (len(input)*2)+1)
   130  
   131  	// first half is the input string shifted down 5 bits.
   132  	// not much is going on there in terms of data / entropy
   133  	for i, c := range input {
   134  		output[i] = uint8(c) >> 5
   135  	}
   136  	// then there's a 0 byte separator
   137  	// don't need to set 0 byte in the middle, as it starts out that way
   138  
   139  	// second half is the input string, with the top 3 bits zeroed.
   140  	// most of the data / entropy will live here.
   141  	for i, c := range input {
   142  		output[i+len(input)+1] = uint8(c) & 0x1f
   143  	}
   144  	return output
   145  }
   146  
   147  // create checksum makes a 6-shortbyte checksum from the HRP and data parts
   148  func CreateChecksum(hrp string, data []byte) []byte {
   149  	values := append(HRPExpand(hrp), data...)
   150  	// put 6 zero bytes on at the end
   151  	values = append(values, make([]byte, 6)...)
   152  	//get checksum for whole slice
   153  
   154  	// flip the LSB of the checksum data after creating it
   155  	checksum := PolyMod(values) ^ 1
   156  
   157  	for i := 0; i < 6; i++ {
   158  		// note that this is NOT the same as converting 8 to 5
   159  		// this is it's own expansion to 6 bytes from 4, chopping
   160  		// off the MSBs.
   161  		values[(len(values)-6)+i] = byte(checksum>>(5*(5-uint32(i)))) & 0x1f
   162  	}
   163  
   164  	return values[len(values)-6:]
   165  }
   166  
   167  func VerifyChecksum(hrp string, data []byte) bool {
   168  	values := append(HRPExpand(hrp), data...)
   169  	checksum := PolyMod(values)
   170  	// make sure it's 1 (from the LSB flip in CreateChecksum
   171  	return checksum == 1
   172  }
   173  
   174  // Encode takes regular bytes of data, and an hrp prefix, and returns the
   175  // bech32 encoded string.  It doesn't do any segwit specific encoding.
   176  func Encode(hrp string, data []byte) string {
   177  	fiveData := Bytes8to5(data)
   178  	return EncodeSquashed(hrp, fiveData)
   179  }
   180  
   181  // EncodeSquashed takes the hrp prefix, as well as byte data that has already
   182  // been squashed to 5-bits high, and returns the bech32 encoded string.
   183  // It does not return an error; if you give it non-squashed data it will return
   184  // an empty string.
   185  func EncodeSquashed(hrp string, data []byte) string {
   186  	combined := append(data, CreateChecksum(hrp, data)...)
   187  
   188  	// Should be squashed, return empty string if it's not.
   189  	dataString, err := SquashedBytesToString(combined)
   190  	if err != nil {
   191  		return ""
   192  	}
   193  	return hrp + "1" + dataString
   194  }
   195  
   196  // Decode takes a bech32 encoded string and returns the hrp and the full-height
   197  // data.  Can error out for various reasons, mostly problems in the string given.
   198  // Doesn't do anything segwit specific.
   199  func Decode(adr string) (string, []byte, error) {
   200  	hrp, squashedData, err := DecodeSquashed(adr)
   201  	if err != nil {
   202  		return hrp, nil, err
   203  	}
   204  	data, err := Bytes5to8(squashedData)
   205  	if err != nil {
   206  		return hrp, nil, err
   207  	}
   208  	return hrp, data, nil
   209  }
   210  
   211  // DecodeSquashed is the same as Decode, but will return squashed 5-bit high
   212  // data.
   213  func DecodeSquashed(adr string) (string, []byte, error) {
   214  
   215  	// make an all lowercase and all uppercase version of the input string
   216  	lowAdr := strings.ToLower(adr)
   217  	highAdr := strings.ToUpper(adr)
   218  
   219  	// if there's mixed case, that's not OK
   220  	if adr != lowAdr && adr != highAdr {
   221  		return "", nil, fmt.Errorf("mixed case address")
   222  	}
   223  
   224  	// default to lowercase
   225  	adr = lowAdr
   226  
   227  	// find the last "1" and split there
   228  	splitLoc := strings.LastIndex(adr, "1")
   229  	if splitLoc == -1 {
   230  		return "", nil, fmt.Errorf("1 separator not present in address")
   231  	}
   232  
   233  	// hrp comes before the split
   234  	hrp := adr[0:splitLoc]
   235  
   236  	// get squashed data
   237  	data, err := StringToSquashedBytes(adr[splitLoc+1:])
   238  	if err != nil {
   239  		return hrp, nil, err
   240  	}
   241  
   242  	// make sure checksum works
   243  	sumOK := VerifyChecksum(hrp, data)
   244  	if !sumOK {
   245  		return hrp, nil, fmt.Errorf("Checksum invalid")
   246  	}
   247  
   248  	// chop off checksum to return only payload
   249  	data = data[:len(data)-6]
   250  
   251  	return hrp, data, nil
   252  }
   253  
   254  // Segwit addresses can't be used in Encode and Decode directly, because the
   255  // witness version is special and doesn't get squashed.  GetHRP gets the
   256  // HRP without checking any validity.
   257  func GetHRP(adr string) (string, error) {
   258  	splitLoc := strings.LastIndex(adr, "1")
   259  	if splitLoc == -1 {
   260  		return "", fmt.Errorf("1 separator not present in address")
   261  	}
   262  	return adr[0:splitLoc], nil
   263  }
   264  
   265  // SegWitAddressEncode takes an hrp and data and gives back a segwit address.
   266  // The data that goes in should be the full pkscript from the txout, including the
   267  // version byte and the pushdata byte.
   268  func SegWitAddressEncode(hrp string, data []byte) (string, error) {
   269  
   270  	if len(data) < 4 {
   271  		return "", fmt.Errorf("data too short (%d bytes)", len(data))
   272  	}
   273  	// first byte is the version number.  that shouldn't be more than
   274  	// 16, so only 4 bits, doesn't need to be squashed
   275  	version := data[0]
   276  	// the next byte is the length.  make sure it's right
   277  	length := data[1]
   278  
   279  	// the rest of the data is real data and needs to be squashed
   280  	data = data[2:]
   281  
   282  	if int(length) != len(data) {
   283  		return "", fmt.Errorf(
   284  			"push byte / payload length mismatch: %d, %d", length, len(data))
   285  	}
   286  
   287  	// allow alts
   288  	//	if hrp != "bc" && hrp != "tb" {
   289  	//		return "", fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
   290  	//	}
   291  	// 1 byte programs are not ok.  Also 40 bytes should be enough for anyone.
   292  	if len(data) < 2 || len(data) > 40 {
   293  		return "", fmt.Errorf("Data length %d out of bounds", len(data))
   294  	}
   295  	// Better get all your features in soon; only 16 possible script versions.
   296  	if version > 16 {
   297  		return "", fmt.Errorf("Invalid witness program version %d", data[0])
   298  	}
   299  	// version 0 scripts can only be 20 bytes (p2wpkh) or 32 bytes (p2wsh)
   300  	if version == 0 && len(data) != 20 && len(data) != 32 {
   301  		return "", fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
   302  	}
   303  
   304  	// squash payload data
   305  	squashedData := Bytes8to5(data)
   306  	// prepend version byte
   307  	squashedData = append([]byte{version}, squashedData...)
   308  
   309  	address := EncodeSquashed(hrp, squashedData)
   310  
   311  	return address, nil
   312  }
   313  
   314  // SegWitAddressDecode takes a segwit address and returns the pkscript that
   315  // can go directly into the txout.  (includes version byte and data push byte)
   316  func SegWitAddressDecode(adr string) ([]byte, error) {
   317  	_, squashedData, err := DecodeSquashed(adr)
   318  	if err != nil {
   319  		return nil, err
   320  	}
   321  	// the segwit version byte is directly put into a 5bit squashed byte
   322  	// since it maxes out at 16, wasting ~1 byte instead of 4.
   323  
   324  	version := squashedData[0]
   325  	data, err := Bytes5to8(squashedData[1:])
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	// Allow alts
   330  	//	if hrp != "bc" && hrp != "tb" {
   331  	//		return nil, fmt.Errorf("prefix %s is not bitcoin or testnet", hrp)
   332  	//	}
   333  	if len(data) < 2 || len(data) > 40 {
   334  		return nil, fmt.Errorf("Data length %d out of bounds", len(data))
   335  	}
   336  
   337  	if version > 16 {
   338  		return nil, fmt.Errorf("Invalid witness program version %d", data[0])
   339  	}
   340  	if version == 0 && len(data) != 20 && len(data) != 32 {
   341  		return nil, fmt.Errorf("expect 20 or 32 byte v0 witprog, got %d", len(data))
   342  	}
   343  
   344  	// first give version byte, then push length
   345  	if version > 0 {
   346  		version |= 0x80
   347  	}
   348  	outputScript := append([]byte{version}, byte(len(data)))
   349  	outputScript = append(outputScript, data...)
   350  
   351  	return outputScript, nil
   352  }
   353  
   354  // SegWitV0Encode takes an hrp prefix string and a 20 or 32 byte witness program
   355  // hash, and turns it into a version 0 address.  (it puts the 0 and pushdata in
   356  // for you.
   357  func SegWitV0Encode(hrp string, data []byte) (string, error) {
   358  	if len(data) != 20 && len(data) != 32 {
   359  		return "", fmt.Errorf("Invalid data length %d, expect 20 or 32", len(data))
   360  	}
   361  	script := []byte{0, byte(len(data))}
   362  	script = append(script, data...)
   363  	return SegWitAddressEncode(hrp, script)
   364  }