fix nip19 bech32 encoding and decoding.

This commit is contained in:
fiatjaf 2022-11-15 16:29:37 -03:00
parent dd0571229b
commit 2ec7957409
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
3 changed files with 38 additions and 16 deletions

View File

@ -9,14 +9,13 @@ const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3}
// Decode decodes a bech32 encoded string, returning the human-readable
// decode decodes a bech32 encoded string, returning the human-readable
// part and the data part excluding the checksum.
func decode(bech string) (string, []byte, error) {
// Only ASCII characters between 33 and 126 are allowed.
for i := 0; i < len(bech); i++ {
if bech[i] < 33 || bech[i] > 126 {
return "", nil, fmt.Errorf("invalid character in "+
"string: '%c'", bech[i])
return "", nil, fmt.Errorf("invalid character in string: '%c'", bech[i])
}
}
@ -24,8 +23,7 @@ func decode(bech string) (string, []byte, error) {
lower := strings.ToLower(bech)
upper := strings.ToUpper(bech)
if bech != lower && bech != upper {
return "", nil, fmt.Errorf("string not all lowercase or all " +
"uppercase")
return "", nil, fmt.Errorf("string not all lowercase or all uppercase")
}
// We'll work with the lowercase string from now on.
@ -48,8 +46,7 @@ func decode(bech string) (string, []byte, error) {
// 'charset'.
decoded, err := toBytes(data)
if err != nil {
return "", nil, fmt.Errorf("failed converting data to bytes: "+
"%v", err)
return "", nil, fmt.Errorf("failed converting data to bytes: %s", err.Error())
}
if !bech32VerifyChecksum(hrp, decoded) {
@ -58,8 +55,7 @@ func decode(bech string) (string, []byte, error) {
expected, err := toChars(bech32Checksum(hrp,
decoded[:len(decoded)-6]))
if err == nil {
moreInfo = fmt.Sprintf("Expected %v, got %v.",
expected, checksum)
moreInfo = fmt.Sprintf("Expected %v, got %v.", expected, checksum)
}
return "", nil, fmt.Errorf("checksum failed. " + moreInfo)
}
@ -81,8 +77,7 @@ func encode(hrp string, data []byte) (string, error) {
// represented using the specified charset.
dataChars, err := toChars(combined)
if err != nil {
return "", fmt.Errorf("unable to convert data bytes to chars: "+
"%v", err)
return "", fmt.Errorf("unable to convert data bytes to chars: %s", err.Error())
}
return hrp + "1" + dataChars, nil
}
@ -115,9 +110,9 @@ func toChars(data []byte) (string, error) {
return string(result), nil
}
// ConvertBits converts a byte slice where each byte is encoding fromBits bits,
// convertBits converts a byte slice where each byte is encoding fromBits bits,
// to a byte slice where each byte is encoding toBits bits.
func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) {
func convertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) {
if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 {
return nil, fmt.Errorf("only bit groups between 1 and 8 allowed")
}

View File

@ -18,7 +18,7 @@ func EncodePrivateKey(privateKeyHex string) (string, error) {
func EncodePublicKey(publicKeyHex string, masterRelay string) (string, error) {
b, err := hex.DecodeString(publicKeyHex)
if err != nil {
return "", err
return "", fmt.Errorf("failed to decode public key hex: %w", err)
}
tlv := make([]byte, 0, 64)
@ -35,7 +35,12 @@ func EncodePublicKey(publicKeyHex string, masterRelay string) (string, error) {
}
b = append(b, tlv...)
return encode("nsec", b)
bits5, err := convertBits(b, 8, 5, true)
if err != nil {
return "", err
}
return encode("npub", bits5)
}
func EncodeNote(eventIdHex string) (string, error) {
@ -53,9 +58,14 @@ func Decode(bech32string string) ([]byte, string, error) {
return nil, "", err
}
bits8, err := convertBits(data, 5, 8, false)
if err != nil {
return nil, "", fmt.Errorf("failed translating data into 8 bits: %s", err.Error())
}
if len(data) < 32 {
return nil, "", fmt.Errorf("data is less than 32 bytes (%d)", len(data))
}
return data[0:32], prefix, nil
return bits8[0:32], prefix, nil
}

17
nip19/utils.go Normal file
View File

@ -0,0 +1,17 @@
package nip19
import (
"encoding/hex"
"strings"
)
// TranslatePublicKey turns a hex or bech32 public key into always hex
func TranslatePublicKey(bech32orHexKey string) string {
if strings.HasPrefix(bech32orHexKey, "npub1") {
data, _, _ := Decode(bech32orHexKey)
return hex.EncodeToString(data)
}
// just return what we got
return bech32orHexKey
}