pubkey.go raw
1 package p256k1
2
3 import (
4 "errors"
5 )
6
7 // PublicKey represents a secp256k1 public key
8 type PublicKey struct {
9 data [64]byte // Internal representation
10 }
11
12 // Compression flags for public key serialization
13 const (
14 ECCompressed = 0x02
15 ECUncompressed = 0x04
16 )
17
18 // ECPubkeyParse parses a public key from bytes
19 func ECPubkeyParse(pubkey *PublicKey, input []byte) error {
20 if len(input) == 0 {
21 return errors.New("input cannot be empty")
22 }
23
24 var point GroupElementAffine
25
26 switch len(input) {
27 case 33:
28 // Compressed format
29 if input[0] != 0x02 && input[0] != 0x03 {
30 return errors.New("invalid compressed public key prefix")
31 }
32
33 // Extract X coordinate
34 var x FieldElement
35 if err := x.setB32(input[1:33]); err != nil {
36 return err
37 }
38
39 // Determine Y coordinate from X and parity
40 odd := input[0] == 0x03
41 if !point.setXOVar(&x, odd) {
42 return errors.New("invalid public key")
43 }
44
45 case 65:
46 // Uncompressed format
47 if input[0] != 0x04 {
48 return errors.New("invalid uncompressed public key prefix")
49 }
50
51 // Extract X and Y coordinates
52 var x, y FieldElement
53 if err := x.setB32(input[1:33]); err != nil {
54 return err
55 }
56 if err := y.setB32(input[33:65]); err != nil {
57 return err
58 }
59
60 point.setXY(&x, &y)
61
62 default:
63 return errors.New("invalid public key length")
64 }
65
66 // Validate the point is on the curve
67 if !point.isValid() {
68 return errors.New("public key not on curve")
69 }
70
71 // Store in internal format
72 point.toBytes(pubkey.data[:])
73
74 return nil
75 }
76
77 // ECPubkeySerialize serializes a public key to bytes
78 func ECPubkeySerialize(output []byte, pubkey *PublicKey, flags uint) int {
79 // Load the public key
80 var point GroupElementAffine
81 point.fromBytes(pubkey.data[:])
82
83 if point.isInfinity() {
84 return 0 // Invalid public key
85 }
86
87 // Normalize coordinates
88 point.x.normalize()
89 point.y.normalize()
90
91 if flags == ECCompressed {
92 if len(output) < 33 {
93 return 0 // Buffer too small
94 }
95
96 // Compressed format: 0x02/0x03 + X coordinate
97 if point.y.isOdd() {
98 output[0] = 0x03
99 } else {
100 output[0] = 0x02
101 }
102 point.x.getB32(output[1:33])
103 return 33
104
105 } else if flags == ECUncompressed {
106 if len(output) < 65 {
107 return 0 // Buffer too small
108 }
109
110 // Uncompressed format: 0x04 + X + Y coordinates
111 output[0] = 0x04
112 point.x.getB32(output[1:33])
113 point.y.getB32(output[33:65])
114 return 65
115
116 } else {
117 return 0 // Invalid flags
118 }
119 }
120
121 // ECPubkeyCmp compares two public keys
122 func ECPubkeyCmp(pubkey1, pubkey2 *PublicKey) int {
123 // Load both public keys
124 var point1, point2 GroupElementAffine
125 point1.fromBytes(pubkey1.data[:])
126 point2.fromBytes(pubkey2.data[:])
127
128 if point1.equal(&point2) {
129 return 0
130 }
131
132 // For ordering, compare the serialized forms
133 var buf1, buf2 [33]byte
134 ECPubkeySerialize(buf1[:], pubkey1, ECCompressed)
135 ECPubkeySerialize(buf2[:], pubkey2, ECCompressed)
136
137 for i := 0; i < 33; i++ {
138 if buf1[i] < buf2[i] {
139 return -1
140 }
141 if buf1[i] > buf2[i] {
142 return 1
143 }
144 }
145
146 return 0
147 }
148
149 // ECPubkeyCreate creates a public key from a private key
150 func ECPubkeyCreate(pubkey *PublicKey, seckey []byte) error {
151 if len(seckey) != 32 {
152 return errors.New("private key must be 32 bytes")
153 }
154
155 // Parse the private key as a scalar
156 var scalar Scalar
157 if !scalar.setB32Seckey(seckey) {
158 return errors.New("invalid private key")
159 }
160
161 // Compute pubkey = scalar * G
162 var point GroupElementJacobian
163 EcmultGen(&point, &scalar)
164
165 // Convert to affine and store directly - optimize by avoiding intermediate copy
166 var affine GroupElementAffine
167 affine.setGEJ(&point)
168
169 // Normalize in-place and write directly to pubkey.data to avoid copy allocation
170 affine.x.normalize()
171 affine.y.normalize()
172 affine.x.getB32(pubkey.data[:32])
173 affine.y.getB32(pubkey.data[32:64])
174
175 // Clear sensitive data
176 scalar.clear()
177 point.clear()
178 affine.clear()
179
180 return nil
181 }
182
183 // pubkeyLoad loads a public key from internal format (helper function)
184 func pubkeyLoad(point *GroupElementAffine, pubkey *PublicKey) {
185 point.fromBytes(pubkey.data[:])
186 }
187
188 // pubkeySave saves a public key to internal format (helper function)
189 func pubkeySave(pubkey *PublicKey, point *GroupElementAffine) {
190 point.toBytes(pubkey.data[:])
191 }
192