xref: /aosp_15_r20/external/tink/go/hybrid/subtle/elliptic_curves.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang// Copyright 2020 Google LLC
2*e7b1675dSTing-Kang Chang//
3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang//
7*e7b1675dSTing-Kang Chang//      http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang//
9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang// limitations under the License.
14*e7b1675dSTing-Kang Chang//
15*e7b1675dSTing-Kang Chang////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Changpackage subtle
18*e7b1675dSTing-Kang Chang
19*e7b1675dSTing-Kang Changimport (
20*e7b1675dSTing-Kang Chang	"bytes"
21*e7b1675dSTing-Kang Chang	"crypto/elliptic"
22*e7b1675dSTing-Kang Chang	"crypto/rand"
23*e7b1675dSTing-Kang Chang	"errors"
24*e7b1675dSTing-Kang Chang	"fmt"
25*e7b1675dSTing-Kang Chang	"math/big"
26*e7b1675dSTing-Kang Chang)
27*e7b1675dSTing-Kang Chang
28*e7b1675dSTing-Kang Chang// ECPublicKey represents a elliptic curve public key.
29*e7b1675dSTing-Kang Changtype ECPublicKey struct {
30*e7b1675dSTing-Kang Chang	elliptic.Curve
31*e7b1675dSTing-Kang Chang	Point ECPoint
32*e7b1675dSTing-Kang Chang}
33*e7b1675dSTing-Kang Chang
34*e7b1675dSTing-Kang Chang// ECPrivateKey represents a elliptic curve private key.
35*e7b1675dSTing-Kang Changtype ECPrivateKey struct {
36*e7b1675dSTing-Kang Chang	PublicKey ECPublicKey
37*e7b1675dSTing-Kang Chang	D         *big.Int
38*e7b1675dSTing-Kang Chang}
39*e7b1675dSTing-Kang Chang
40*e7b1675dSTing-Kang Chang// GetECPrivateKey converts a stored private key to ECPrivateKey.
41*e7b1675dSTing-Kang Changfunc GetECPrivateKey(c elliptic.Curve, b []byte) *ECPrivateKey {
42*e7b1675dSTing-Kang Chang	d := new(big.Int)
43*e7b1675dSTing-Kang Chang	d.SetBytes(b)
44*e7b1675dSTing-Kang Chang
45*e7b1675dSTing-Kang Chang	x, y := c.Params().ScalarBaseMult(b)
46*e7b1675dSTing-Kang Chang	pub := ECPublicKey{
47*e7b1675dSTing-Kang Chang		Curve: c,
48*e7b1675dSTing-Kang Chang		Point: ECPoint{
49*e7b1675dSTing-Kang Chang			X: x,
50*e7b1675dSTing-Kang Chang			Y: y,
51*e7b1675dSTing-Kang Chang		},
52*e7b1675dSTing-Kang Chang	}
53*e7b1675dSTing-Kang Chang	return &ECPrivateKey{
54*e7b1675dSTing-Kang Chang		PublicKey: pub,
55*e7b1675dSTing-Kang Chang		D:         d,
56*e7b1675dSTing-Kang Chang	}
57*e7b1675dSTing-Kang Chang
58*e7b1675dSTing-Kang Chang}
59*e7b1675dSTing-Kang Chang
60*e7b1675dSTing-Kang Chang// ECPoint represents a point on the elliptic curve.
61*e7b1675dSTing-Kang Changtype ECPoint struct {
62*e7b1675dSTing-Kang Chang	X, Y *big.Int
63*e7b1675dSTing-Kang Chang}
64*e7b1675dSTing-Kang Chang
65*e7b1675dSTing-Kang Changfunc (p *ECPrivateKey) getParams() *elliptic.CurveParams {
66*e7b1675dSTing-Kang Chang	return p.PublicKey.Curve.Params()
67*e7b1675dSTing-Kang Chang}
68*e7b1675dSTing-Kang Chang
69*e7b1675dSTing-Kang Changfunc getModulus(c elliptic.Curve) *big.Int {
70*e7b1675dSTing-Kang Chang	return c.Params().P
71*e7b1675dSTing-Kang Chang}
72*e7b1675dSTing-Kang Chang
73*e7b1675dSTing-Kang Changfunc fieldSizeInBits(c elliptic.Curve) int {
74*e7b1675dSTing-Kang Chang	t := big.NewInt(1)
75*e7b1675dSTing-Kang Chang	r := t.Sub(getModulus(c), t)
76*e7b1675dSTing-Kang Chang	return r.BitLen()
77*e7b1675dSTing-Kang Chang}
78*e7b1675dSTing-Kang Chang
79*e7b1675dSTing-Kang Changfunc fieldSizeInBytes(c elliptic.Curve) int {
80*e7b1675dSTing-Kang Chang	return (fieldSizeInBits(c) + 7) / 8
81*e7b1675dSTing-Kang Chang}
82*e7b1675dSTing-Kang Chang
83*e7b1675dSTing-Kang Changfunc encodingSizeInBytes(c elliptic.Curve, p string) (int, error) {
84*e7b1675dSTing-Kang Chang	cSize := fieldSizeInBytes(c)
85*e7b1675dSTing-Kang Chang	switch p {
86*e7b1675dSTing-Kang Chang	case "UNCOMPRESSED":
87*e7b1675dSTing-Kang Chang		return 2*cSize + 1, nil
88*e7b1675dSTing-Kang Chang	case "DO_NOT_USE_CRUNCHY_UNCOMPRESSED":
89*e7b1675dSTing-Kang Chang		return 2 * cSize, nil
90*e7b1675dSTing-Kang Chang	case "COMPRESSED":
91*e7b1675dSTing-Kang Chang		return cSize + 1, nil
92*e7b1675dSTing-Kang Chang	}
93*e7b1675dSTing-Kang Chang	return 0, fmt.Errorf("invalid point format :%s", p)
94*e7b1675dSTing-Kang Chang
95*e7b1675dSTing-Kang Chang}
96*e7b1675dSTing-Kang Chang
97*e7b1675dSTing-Kang Chang// PointEncode encodes a point into the format specified.
98*e7b1675dSTing-Kang Changfunc PointEncode(c elliptic.Curve, pFormat string, pt ECPoint) ([]byte, error) {
99*e7b1675dSTing-Kang Chang	if !c.IsOnCurve(pt.X, pt.Y) {
100*e7b1675dSTing-Kang Chang		return nil, errors.New("curve check failed")
101*e7b1675dSTing-Kang Chang	}
102*e7b1675dSTing-Kang Chang	cSize := fieldSizeInBytes(c)
103*e7b1675dSTing-Kang Chang	y := pt.Y.Bytes()
104*e7b1675dSTing-Kang Chang	x := pt.X.Bytes()
105*e7b1675dSTing-Kang Chang	switch pFormat {
106*e7b1675dSTing-Kang Chang	case "UNCOMPRESSED":
107*e7b1675dSTing-Kang Chang		encoded := make([]byte, 2*cSize+1)
108*e7b1675dSTing-Kang Chang		copy(encoded[1+2*cSize-len(y):], y)
109*e7b1675dSTing-Kang Chang		copy(encoded[1+cSize-len(x):], x)
110*e7b1675dSTing-Kang Chang		encoded[0] = 4
111*e7b1675dSTing-Kang Chang		return encoded, nil
112*e7b1675dSTing-Kang Chang	case "DO_NOT_USE_CRUNCHY_UNCOMPRESSED":
113*e7b1675dSTing-Kang Chang		encoded := make([]byte, 2*cSize)
114*e7b1675dSTing-Kang Chang		if len(x) > cSize {
115*e7b1675dSTing-Kang Chang			x = bytes.Replace(x, []byte("\x00"), []byte{}, -1)
116*e7b1675dSTing-Kang Chang		}
117*e7b1675dSTing-Kang Chang		if len(y) > cSize {
118*e7b1675dSTing-Kang Chang			y = bytes.Replace(y, []byte("\x00"), []byte{}, -1)
119*e7b1675dSTing-Kang Chang		}
120*e7b1675dSTing-Kang Chang		copy(encoded[2*cSize-len(y):], y)
121*e7b1675dSTing-Kang Chang		copy(encoded[cSize-len(x):], x)
122*e7b1675dSTing-Kang Chang		return encoded, nil
123*e7b1675dSTing-Kang Chang	case "COMPRESSED":
124*e7b1675dSTing-Kang Chang		encoded := make([]byte, cSize+1)
125*e7b1675dSTing-Kang Chang		copy(encoded[1+cSize-len(x):], x)
126*e7b1675dSTing-Kang Chang		encoded[0] = 2
127*e7b1675dSTing-Kang Chang		if pt.Y.Bit(0) > 0 {
128*e7b1675dSTing-Kang Chang			encoded[0] = 3
129*e7b1675dSTing-Kang Chang		}
130*e7b1675dSTing-Kang Chang		return encoded, nil
131*e7b1675dSTing-Kang Chang	}
132*e7b1675dSTing-Kang Chang	return nil, errors.New("invalid point format")
133*e7b1675dSTing-Kang Chang
134*e7b1675dSTing-Kang Chang}
135*e7b1675dSTing-Kang Chang
136*e7b1675dSTing-Kang Chang// PointDecode decodes a encoded point to return an ECPoint
137*e7b1675dSTing-Kang Changfunc PointDecode(c elliptic.Curve, pFormat string, e []byte) (*ECPoint, error) {
138*e7b1675dSTing-Kang Chang	cSize := fieldSizeInBytes(c)
139*e7b1675dSTing-Kang Chang	x, y := new(big.Int), new(big.Int)
140*e7b1675dSTing-Kang Chang	switch pFormat {
141*e7b1675dSTing-Kang Chang	case "UNCOMPRESSED":
142*e7b1675dSTing-Kang Chang		if len(e) != (2*cSize + 1) {
143*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid point size")
144*e7b1675dSTing-Kang Chang		}
145*e7b1675dSTing-Kang Chang		if e[0] != 4 {
146*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid point format")
147*e7b1675dSTing-Kang Chang		}
148*e7b1675dSTing-Kang Chang		x.SetBytes(e[1 : cSize+1])
149*e7b1675dSTing-Kang Chang		y.SetBytes(e[cSize+1:])
150*e7b1675dSTing-Kang Chang		if !c.IsOnCurve(x, y) {
151*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid point")
152*e7b1675dSTing-Kang Chang		}
153*e7b1675dSTing-Kang Chang		return &ECPoint{
154*e7b1675dSTing-Kang Chang			X: x,
155*e7b1675dSTing-Kang Chang			Y: y,
156*e7b1675dSTing-Kang Chang		}, nil
157*e7b1675dSTing-Kang Chang	case "DO_NOT_USE_CRUNCHY_UNCOMPRESSED":
158*e7b1675dSTing-Kang Chang		if len(e) != 2*cSize {
159*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid point size")
160*e7b1675dSTing-Kang Chang		}
161*e7b1675dSTing-Kang Chang		x.SetBytes(e[:cSize])
162*e7b1675dSTing-Kang Chang		y.SetBytes(e[cSize:])
163*e7b1675dSTing-Kang Chang		if !c.IsOnCurve(x, y) {
164*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid point")
165*e7b1675dSTing-Kang Chang		}
166*e7b1675dSTing-Kang Chang		return &ECPoint{
167*e7b1675dSTing-Kang Chang			X: x,
168*e7b1675dSTing-Kang Chang			Y: y,
169*e7b1675dSTing-Kang Chang		}, nil
170*e7b1675dSTing-Kang Chang	case "COMPRESSED":
171*e7b1675dSTing-Kang Chang		if len(e) != cSize+1 {
172*e7b1675dSTing-Kang Chang			return nil, errors.New("compressed point has wrong length")
173*e7b1675dSTing-Kang Chang		}
174*e7b1675dSTing-Kang Chang		lsb := false
175*e7b1675dSTing-Kang Chang		if e[0] == 2 {
176*e7b1675dSTing-Kang Chang			lsb = false
177*e7b1675dSTing-Kang Chang		} else if e[0] == 3 {
178*e7b1675dSTing-Kang Chang			lsb = true
179*e7b1675dSTing-Kang Chang		} else {
180*e7b1675dSTing-Kang Chang			return nil, errors.New("invalid format")
181*e7b1675dSTing-Kang Chang		}
182*e7b1675dSTing-Kang Chang		x := new(big.Int)
183*e7b1675dSTing-Kang Chang		x.SetBytes(e[1:])
184*e7b1675dSTing-Kang Chang		if (x.Sign() == -1) || (x.Cmp(c.Params().P) != -1) {
185*e7b1675dSTing-Kang Chang			return nil, errors.New("x is out of range")
186*e7b1675dSTing-Kang Chang		}
187*e7b1675dSTing-Kang Chang		y := getY(x, lsb, c)
188*e7b1675dSTing-Kang Chang		return &ECPoint{
189*e7b1675dSTing-Kang Chang			X: x,
190*e7b1675dSTing-Kang Chang			Y: y,
191*e7b1675dSTing-Kang Chang		}, nil
192*e7b1675dSTing-Kang Chang	}
193*e7b1675dSTing-Kang Chang	return nil, fmt.Errorf("invalid format: %s", pFormat)
194*e7b1675dSTing-Kang Chang}
195*e7b1675dSTing-Kang Chang
196*e7b1675dSTing-Kang Changfunc getY(x *big.Int, lsb bool, c elliptic.Curve) *big.Int {
197*e7b1675dSTing-Kang Chang	// y² = x³ - 3x + b
198*e7b1675dSTing-Kang Chang	x3 := new(big.Int).Mul(x, x)
199*e7b1675dSTing-Kang Chang	x3.Mul(x3, x)
200*e7b1675dSTing-Kang Chang
201*e7b1675dSTing-Kang Chang	threeX := new(big.Int).Lsh(x, 1)
202*e7b1675dSTing-Kang Chang	threeX.Add(threeX, x)
203*e7b1675dSTing-Kang Chang	b := c.Params().B
204*e7b1675dSTing-Kang Chang	p := c.Params().P
205*e7b1675dSTing-Kang Chang
206*e7b1675dSTing-Kang Chang	x3.Sub(x3, threeX)
207*e7b1675dSTing-Kang Chang	x3.Add(x3, b)
208*e7b1675dSTing-Kang Chang	x3.ModSqrt(x3, p)
209*e7b1675dSTing-Kang Chang	e := uint(1)
210*e7b1675dSTing-Kang Chang	if lsb {
211*e7b1675dSTing-Kang Chang		e = 0
212*e7b1675dSTing-Kang Chang	}
213*e7b1675dSTing-Kang Chang	if e == x3.Bit(0) {
214*e7b1675dSTing-Kang Chang		x3 := x3.Sub(p, x3)
215*e7b1675dSTing-Kang Chang		x3.Mod(x3, p)
216*e7b1675dSTing-Kang Chang	}
217*e7b1675dSTing-Kang Chang	return x3
218*e7b1675dSTing-Kang Chang}
219*e7b1675dSTing-Kang Chang
220*e7b1675dSTing-Kang Changfunc validatePublicPoint(pub *ECPoint, priv *ECPrivateKey) error {
221*e7b1675dSTing-Kang Chang	if priv.PublicKey.Curve.IsOnCurve(pub.X, pub.Y) {
222*e7b1675dSTing-Kang Chang		return nil
223*e7b1675dSTing-Kang Chang	}
224*e7b1675dSTing-Kang Chang	return errors.New("invalid public key")
225*e7b1675dSTing-Kang Chang}
226*e7b1675dSTing-Kang Chang
227*e7b1675dSTing-Kang Chang// ComputeSharedSecret is used to compute a shared secret using given private key and peer public key.
228*e7b1675dSTing-Kang Changfunc ComputeSharedSecret(pub *ECPoint, priv *ECPrivateKey) ([]byte, error) {
229*e7b1675dSTing-Kang Chang	if err := validatePublicPoint(pub, priv); err != nil {
230*e7b1675dSTing-Kang Chang		return nil, err
231*e7b1675dSTing-Kang Chang	}
232*e7b1675dSTing-Kang Chang
233*e7b1675dSTing-Kang Chang	x, y := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
234*e7b1675dSTing-Kang Chang
235*e7b1675dSTing-Kang Chang	if x == nil {
236*e7b1675dSTing-Kang Chang		return nil, errors.New("shared key compute error")
237*e7b1675dSTing-Kang Chang	}
238*e7b1675dSTing-Kang Chang	// check if x,y are on the curve
239*e7b1675dSTing-Kang Chang	if err := validatePublicPoint(&ECPoint{X: x, Y: y}, priv); err != nil {
240*e7b1675dSTing-Kang Chang		return nil, errors.New("invalid shared key")
241*e7b1675dSTing-Kang Chang	}
242*e7b1675dSTing-Kang Chang
243*e7b1675dSTing-Kang Chang	sharedSecret := make([]byte, maxSharedKeyLength(priv.PublicKey))
244*e7b1675dSTing-Kang Chang	return x.FillBytes(sharedSecret), nil
245*e7b1675dSTing-Kang Chang}
246*e7b1675dSTing-Kang Chang
247*e7b1675dSTing-Kang Changfunc maxSharedKeyLength(pub ECPublicKey) int {
248*e7b1675dSTing-Kang Chang	return (pub.Curve.Params().BitSize + 7) / 8
249*e7b1675dSTing-Kang Chang}
250*e7b1675dSTing-Kang Chang
251*e7b1675dSTing-Kang Chang// GenerateECDHKeyPair will create a new private key for a given curve.
252*e7b1675dSTing-Kang Changfunc GenerateECDHKeyPair(c elliptic.Curve) (*ECPrivateKey, error) {
253*e7b1675dSTing-Kang Chang	p, x, y, err := elliptic.GenerateKey(c, rand.Reader)
254*e7b1675dSTing-Kang Chang	if err != nil {
255*e7b1675dSTing-Kang Chang		return nil, err
256*e7b1675dSTing-Kang Chang	}
257*e7b1675dSTing-Kang Chang	return &ECPrivateKey{
258*e7b1675dSTing-Kang Chang		PublicKey: ECPublicKey{
259*e7b1675dSTing-Kang Chang			Curve: c,
260*e7b1675dSTing-Kang Chang			Point: ECPoint{
261*e7b1675dSTing-Kang Chang				X: x,
262*e7b1675dSTing-Kang Chang				Y: y,
263*e7b1675dSTing-Kang Chang			},
264*e7b1675dSTing-Kang Chang		},
265*e7b1675dSTing-Kang Chang		D: new(big.Int).SetBytes(p),
266*e7b1675dSTing-Kang Chang	}, nil
267*e7b1675dSTing-Kang Chang
268*e7b1675dSTing-Kang Chang}
269*e7b1675dSTing-Kang Chang
270*e7b1675dSTing-Kang Chang// GetCurve returns the elliptic.Curve for a given standard curve name.
271*e7b1675dSTing-Kang Changfunc GetCurve(c string) (elliptic.Curve, error) {
272*e7b1675dSTing-Kang Chang	switch c {
273*e7b1675dSTing-Kang Chang	case "secp224r1", "NIST_P224", "P-224":
274*e7b1675dSTing-Kang Chang		return elliptic.P224(), nil
275*e7b1675dSTing-Kang Chang	case "secp256r1", "NIST_P256", "P-256", "EllipticCurveType_NIST_P256":
276*e7b1675dSTing-Kang Chang		return elliptic.P256(), nil
277*e7b1675dSTing-Kang Chang	case "secp384r1", "NIST_P384", "P-384", "EllipticCurveType_NIST_P384":
278*e7b1675dSTing-Kang Chang		return elliptic.P384(), nil
279*e7b1675dSTing-Kang Chang	case "secp521r1", "NIST_P521", "P-521", "EllipticCurveType_NIST_P521":
280*e7b1675dSTing-Kang Chang		return elliptic.P521(), nil
281*e7b1675dSTing-Kang Chang	default:
282*e7b1675dSTing-Kang Chang		return nil, errors.New("unsupported curve")
283*e7b1675dSTing-Kang Chang	}
284*e7b1675dSTing-Kang Chang}
285