extrakeys.go raw
1 package p256k1
2
3 import (
4 "errors"
5 "unsafe"
6 )
7
8 // XOnlyPubkey represents an x-only public key (32 bytes, just X coordinate)
9 // Following BIP-340 specification
10 type XOnlyPubkey struct {
11 data [32]byte
12 }
13
14 // KeyPair represents a keypair consisting of a secret key and public key
15 // Used for Schnorr signatures
16 type KeyPair struct {
17 seckey [32]byte
18 pubkey PublicKey
19 }
20
21 // XOnlyPubkeyParse parses a 32-byte sequence into an x-only public key
22 func XOnlyPubkeyParse(input32 []byte) (*XOnlyPubkey, error) {
23 if len(input32) != 32 {
24 return nil, errors.New("input must be 32 bytes")
25 }
26
27 // Create a point from X coordinate
28 var x FieldElement
29 if err := x.setB32(input32); err != nil {
30 return nil, errors.New("invalid X coordinate")
31 }
32
33 // Try to recover Y coordinate (check if point is on curve)
34 var point GroupElementAffine
35 if !point.setXOVar(&x, false) {
36 // Try with odd Y
37 if !point.setXOVar(&x, true) {
38 return nil, errors.New("X coordinate does not correspond to a valid point")
39 }
40 }
41
42 // Verify point is valid
43 if !point.isValid() {
44 return nil, errors.New("invalid point")
45 }
46
47 // Create x-only pubkey (just X coordinate)
48 var xonly XOnlyPubkey
49 copy(xonly.data[:], input32)
50 return &xonly, nil
51 }
52
53 // Serialize serializes an x-only public key to 32 bytes
54 func (xonly *XOnlyPubkey) Serialize() [32]byte {
55 return xonly.data
56 }
57
58 // XOnlyPubkeyFromPubkey converts a PublicKey to an XOnlyPubkey
59 // Returns the x-only pubkey and parity (1 if Y was odd, 0 if even)
60 func XOnlyPubkeyFromPubkey(pubkey *PublicKey) (*XOnlyPubkey, int, error) {
61 if pubkey == nil {
62 return nil, 0, errors.New("pubkey cannot be nil")
63 }
64
65 // Load public key
66 var pt GroupElementAffine
67 pt.fromBytes(pubkey.data[:])
68 if pt.isInfinity() {
69 return nil, 0, errors.New("invalid public key")
70 }
71
72 // Normalize Y coordinate
73 pt.y.normalize()
74
75 // Check parity
76 parity := 0
77 if pt.y.isOdd() {
78 parity = 1
79 // Negate point if Y is odd to get even Y
80 pt.negate(&pt)
81 }
82
83 // Extract X coordinate
84 var xonly XOnlyPubkey
85 pt.x.normalize()
86 pt.x.getB32(xonly.data[:])
87
88 return &xonly, parity, nil
89 }
90
91 // XOnlyPubkeyCmp compares two x-only public keys lexicographically
92 // Returns: <0 if xonly1 < xonly2, >0 if xonly1 > xonly2, 0 if equal
93 func XOnlyPubkeyCmp(xonly1, xonly2 *XOnlyPubkey) int {
94 if xonly1 == nil || xonly2 == nil {
95 panic("xonly pubkey cannot be nil")
96 }
97
98 for i := 31; i >= 0; i-- {
99 if xonly1.data[i] < xonly2.data[i] {
100 return -1
101 }
102 if xonly1.data[i] > xonly2.data[i] {
103 return 1
104 }
105 }
106 return 0
107 }
108
109 // KeyPairCreate creates a keypair from a secret key
110 func KeyPairCreate(seckey []byte) (*KeyPair, error) {
111 if len(seckey) != 32 {
112 return nil, errors.New("secret key must be 32 bytes")
113 }
114
115 if !ECSeckeyVerify(seckey) {
116 return nil, errors.New("invalid secret key")
117 }
118
119 // Create public key
120 var pubkey PublicKey
121 if err := ECPubkeyCreate(&pubkey, seckey); err != nil {
122 return nil, err
123 }
124
125 kp := &KeyPair{}
126 copy(kp.seckey[:], seckey)
127 kp.pubkey = pubkey
128
129 return kp, nil
130 }
131
132 // KeyPairGenerate generates a new random keypair
133 func KeyPairGenerate() (*KeyPair, error) {
134 seckey, pubkey, err := ECKeyPairGenerate()
135 if err != nil {
136 return nil, err
137 }
138
139 kp := &KeyPair{}
140 copy(kp.seckey[:], seckey)
141 kp.pubkey = *pubkey
142
143 return kp, nil
144 }
145
146 // Seckey returns the secret key
147 func (kp *KeyPair) Seckey() []byte {
148 return kp.seckey[:]
149 }
150
151 // Pubkey returns the public key
152 func (kp *KeyPair) Pubkey() *PublicKey {
153 return &kp.pubkey
154 }
155
156 // XOnlyPubkey returns the x-only public key
157 func (kp *KeyPair) XOnlyPubkey() (*XOnlyPubkey, error) {
158 xonly, _, err := XOnlyPubkeyFromPubkey(&kp.pubkey)
159 return xonly, err
160 }
161
162 // Clear clears the keypair to prevent leaking sensitive information
163 func (kp *KeyPair) Clear() {
164 memclear(unsafe.Pointer(&kp.seckey[0]), 32)
165 kp.pubkey.data = [64]byte{}
166 }
167