/
keys.go
149 lines (131 loc) · 3.63 KB
/
keys.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package bech32encoding
import (
"fmt"
"github.com/Hubmakerlabs/replicatr/pkg/hex"
"mleku.online/git/bech32"
"mleku.online/git/ec"
"mleku.online/git/ec/schnorr"
"mleku.online/git/ec/secp256k1"
"mleku.online/git/slog"
)
var log = slog.GetStd()
const (
// MinKeyStringLen is 56 because Bech32 needs 52 characters plus 4 for the HRP,
// any string shorter than this cannot be a nostr key.
MinKeyStringLen = 56
HexKeyLen = 64
Bech32HRPLen = 4
SecHRP = "nsec"
PubHRP = "npub"
)
// ConvertForBech32 performs the bit expansion required for encoding into
// Bech32.
func ConvertForBech32(b8 []byte) (b5 []byte, err error) {
return bech32.ConvertBits(b8, 8, 5, true)
}
// ConvertFromBech32 collapses together the bit expanded 5 bit numbers encoded
// in bech32.
func ConvertFromBech32(b5 []byte) (b8 []byte, err error) {
return bech32.ConvertBits(b5, 5, 8, true)
}
// SecretKeyToNsec encodes an secp256k1 secret key as a Bech32 string (nsec).
func SecretKeyToNsec(sk *secp256k1.SecretKey) (encoded string, err error) {
var b5 []byte
if b5, err = ConvertForBech32(sk.Serialize()); err != nil {
return
}
return bech32.Encode(SecHRP, b5)
}
// PublicKeyToNpub encodes a public kxey as a bech32 string (npub).
func PublicKeyToNpub(pk *secp256k1.PublicKey) (encoded string, err error) {
var bits5 []byte
if bits5, err = ConvertForBech32(schnorr.SerializePubKey(pk)); err != nil {
return
}
return bech32.Encode(PubHRP, bits5)
}
// NsecToSecretKey decodes a nostr secret key (nsec) and returns the secp256k1
// secret key.
func NsecToSecretKey(encoded string) (sk *secp256k1.SecretKey, err error) {
var b5, b8 []byte
var hrp string
hrp, b5, err = bech32.Decode(encoded)
if err != nil {
return
}
if hrp != SecHRP {
err = fmt.Errorf("wrong human readable part, got '%s' want '%s'",
hrp, SecHRP)
return
}
b8, err = ConvertFromBech32(b5)
if err != nil {
return
}
sk = secp256k1.SecKeyFromBytes(b8)
return
}
// NpubToPublicKey decodes an nostr public key (npub) and returns an secp256k1
// public key.
func NpubToPublicKey(encoded string) (pk *secp256k1.PublicKey, err error) {
var b5, b8 []byte
var hrp string
hrp, b5, err = bech32.Decode(encoded)
if err != nil {
err = fmt.Errorf("ERROR: '%s'", err)
return
}
if hrp != PubHRP {
err = fmt.Errorf("wrong human readable part, got '%s' want '%s'",
hrp, PubHRP)
return
}
b8, err = ConvertFromBech32(b5)
if err != nil {
return
}
return schnorr.ParsePubKey(b8[:32])
}
// HexToPublicKey decodes a string that should be a 64 character long hex
// encoded public key into a ec.PublicKey that can be used to verify a
// signature or encode to Bech32.
func HexToPublicKey(pk string) (p *ec.PublicKey, err error) {
if len(pk) != HexKeyLen {
err = fmt.Errorf("seckey is %d bytes, must be %d", len(pk), HexKeyLen)
return
}
var pb []byte
if pb, err = hex.Dec(pk); log.Fail(err) {
return
}
if p, err = schnorr.ParsePubKey(pb); log.Fail(err) {
return
}
return
}
// HexToSecretKey decodes a string that should be a 64 character long hex
// encoded public key into a ec.PublicKey that can be used to verify a
// signature or encode to Bech32.
func HexToSecretKey(sk string) (s *ec.SecretKey, err error) {
if len(sk) != HexKeyLen {
err = fmt.Errorf("seckey is %d bytes, must be %d", len(sk), HexKeyLen)
return
}
var pb []byte
if pb, err = hex.Dec(sk); log.Fail(err) {
return
}
if s = secp256k1.SecKeyFromBytes(pb); log.Fail(err) {
return
}
return
}
func GetPublicKey(sk string) (s string, err error) {
var b []byte
b, err = hex.Dec(sk)
if err != nil {
return "", err
}
_, pk := ec.PrivKeyFromBytes(b)
return hex.Enc(schnorr.SerializePubKey(pk)), nil
}