mirror of
https://github.com/golang/go
synced 2024-10-02 22:25:08 +00:00
crypto/elliptic: move P-521 group logic to internal/nistec
This abstracts the clunky and not constant time math/big elliptic.Curve compatibility layer away from the pure fiat-backed group logic. Change-Id: I3b7a7495034d0c569b21c442ae36958763b8b2d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/320074 Trust: Filippo Valsorda <filippo@golang.org> Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Julie Qiu <julie@golang.org>
This commit is contained in:
parent
d1dceafc29
commit
30b5d6385e
|
@ -21,9 +21,12 @@ import (
|
|||
|
||||
// A Curve represents a short-form Weierstrass curve with a=-3.
|
||||
//
|
||||
// Note that the point at infinity (0, 0) is not considered on the curve, and
|
||||
// although it can be returned by Add, Double, ScalarMult, or ScalarBaseMult, it
|
||||
// can't be marshaled or unmarshaled, and IsOnCurve will return false for it.
|
||||
// The output of Add, Double, and ScalarMult when the input is not a point on
|
||||
// the curve is undefined.
|
||||
//
|
||||
// Note that the conventional point at infinity (0, 0) is not considered on the
|
||||
// curve, although it can be returned by Add, Double, ScalarMult, or
|
||||
// ScalarBaseMult (but not Unmarshal or UnmarshalCompressed).
|
||||
type Curve interface {
|
||||
// Params returns the parameters for the curve.
|
||||
Params() *CurveParams
|
||||
|
|
|
@ -109,6 +109,15 @@ func testInfinity(t *testing.T, curve Curve) {
|
|||
if curve.IsOnCurve(x, y) {
|
||||
t.Errorf("IsOnCurve(∞) == true")
|
||||
}
|
||||
|
||||
if xx, yy := Unmarshal(curve, Marshal(curve, x, y)); xx != nil || yy != nil {
|
||||
t.Errorf("Unmarshal(Marshal(∞)) did not return an error")
|
||||
}
|
||||
// We don't test UnmarshalCompressed(MarshalCompressed(∞)) because there are
|
||||
// two valid points with x = 0.
|
||||
if xx, yy := Unmarshal(curve, []byte{0x00}); xx != nil || yy != nil {
|
||||
t.Errorf("Unmarshal(∞) did not return an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
|
@ -274,3 +283,29 @@ func BenchmarkScalarMult(b *testing.B) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkMarshalUnmarshal(b *testing.B) {
|
||||
benchmarkAllCurves(b, func(b *testing.B, curve Curve) {
|
||||
_, x, y, _ := GenerateKey(curve, rand.Reader)
|
||||
b.Run("Uncompressed", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := Marshal(curve, x, y)
|
||||
xx, yy := Unmarshal(curve, buf)
|
||||
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
|
||||
b.Error("Unmarshal output different from Marshal input")
|
||||
}
|
||||
}
|
||||
})
|
||||
b.Run("Compressed", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf := Marshal(curve, x, y)
|
||||
xx, yy := Unmarshal(curve, buf)
|
||||
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
|
||||
b.Error("Unmarshal output different from Marshal input")
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -53,28 +53,40 @@ func (e *P521Element) Set(t *P521Element) *P521Element {
|
|||
return e
|
||||
}
|
||||
|
||||
// Bytes returns the 66-byte little-endian encoding of e.
|
||||
// Bytes returns the 66-byte big-endian encoding of e.
|
||||
func (e *P521Element) Bytes() []byte {
|
||||
// This function must be inlined to move the allocation to the parent and
|
||||
// save it from escaping to the heap.
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [66]byte
|
||||
p521ToBytes(&out, &e.x)
|
||||
return e.bytes(&out)
|
||||
}
|
||||
|
||||
func (e *P521Element) bytes(out *[66]byte) []byte {
|
||||
p521ToBytes(out, &e.x)
|
||||
invertEndianness(out[:])
|
||||
return out[:]
|
||||
}
|
||||
|
||||
// SetBytes sets e = v, where v is a little-endian 66-byte encoding, and returns
|
||||
// SetBytes sets e = v, where v is a big-endian 66-byte encoding, and returns
|
||||
// e. If v is not 66 bytes or it encodes a value higher than 2^521 - 1, SetBytes
|
||||
// returns nil and an error, and e is unchanged.
|
||||
func (e *P521Element) SetBytes(v []byte) (*P521Element, error) {
|
||||
if len(v) != 66 || v[65] > 1 {
|
||||
if len(v) != 66 || v[0] > 1 {
|
||||
return nil, errors.New("invalid P-521 field encoding")
|
||||
}
|
||||
var in [66]byte
|
||||
copy(in[:], v)
|
||||
invertEndianness(in[:])
|
||||
p521FromBytes(&e.x, &in)
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func invertEndianness(v []byte) {
|
||||
for i := 0; i < len(v)/2; i++ {
|
||||
v[i], v[len(v)-1-i] = v[len(v)-1-i], v[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Add sets e = t1 + t2, and returns e.
|
||||
func (e *P521Element) Add(t1, t2 *P521Element) *P521Element {
|
||||
p521Add(&e.x, &t1.x, &t2.x)
|
||||
|
|
|
@ -15,7 +15,7 @@ func p521Random(t *testing.T) *fiat.P521Element {
|
|||
if _, err := rand.Read(buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buf[65] &= 1
|
||||
buf[0] &= 1
|
||||
e, err := new(fiat.P521Element).SetBytes(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
306
src/crypto/elliptic/internal/nistec/p521.go
Normal file
306
src/crypto/elliptic/internal/nistec/p521.go
Normal file
|
@ -0,0 +1,306 @@
|
|||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package nistec implements the NIST P elliptic curves from FIPS 186-4.
|
||||
//
|
||||
// This package uses fiat-crypto for its backend field arithmetic (not math/big)
|
||||
// and exposes constant-time, heap allocation-free, byte slice-based safe APIs.
|
||||
// Group operations use modern and safe complete addition formulas. The point at
|
||||
// infinity is handled and encoded according to SEC 1, Version 2.0, and invalid
|
||||
// curve points can't be represented.
|
||||
package nistec
|
||||
|
||||
import (
|
||||
"crypto/elliptic/internal/fiat"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var p521B, _ = new(fiat.P521Element).SetBytes([]byte{
|
||||
0x00, 0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c, 0x9a, 0x1f, 0x92, 0x9a,
|
||||
0x21, 0xa0, 0xb6, 0x85, 0x40, 0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3,
|
||||
0x15, 0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1, 0x09, 0xe1, 0x56, 0x19,
|
||||
0x39, 0x51, 0xec, 0x7e, 0x93, 0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1,
|
||||
0xbf, 0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c, 0x34, 0xf1, 0xef, 0x45,
|
||||
0x1f, 0xd4, 0x6b, 0x50, 0x3f, 0x00})
|
||||
|
||||
var p521G, _ = NewP521Point().SetBytes([]byte{0x04,
|
||||
0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04, 0xe9, 0xcd, 0x9e, 0x3e,
|
||||
0xcb, 0x66, 0x23, 0x95, 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
|
||||
0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d, 0x3d, 0xba, 0xa1, 0x4b,
|
||||
0x5e, 0x77, 0xef, 0xe7, 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
|
||||
0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a, 0x42, 0x9b, 0xf9, 0x7e,
|
||||
0x7e, 0x31, 0xc2, 0xe5, 0xbd, 0x66, 0x01, 0x18, 0x39, 0x29, 0x6a, 0x78,
|
||||
0x9a, 0x3b, 0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d, 0x1b, 0xd9,
|
||||
0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b, 0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17,
|
||||
0x27, 0x3e, 0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4, 0x26, 0x40,
|
||||
0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad, 0x07, 0x61, 0x35, 0x3c, 0x70, 0x86,
|
||||
0xa2, 0x72, 0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1, 0x66, 0x50})
|
||||
|
||||
const p521ElementLength = 66
|
||||
|
||||
// P521Point is a P-521 point. The zero value is NOT valid.
|
||||
type P521Point struct {
|
||||
// The point is represented in projective coordinates (X:Y:Z),
|
||||
// where x = X/Z and y = Y/Z.
|
||||
x, y, z *fiat.P521Element
|
||||
}
|
||||
|
||||
// NewP521Point returns a new P521Point representing the point at infinity point.
|
||||
func NewP521Point() *P521Point {
|
||||
return &P521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element).One(),
|
||||
z: new(fiat.P521Element),
|
||||
}
|
||||
}
|
||||
|
||||
// NewP521Generator returns a new P521Point set to the canonical generator.
|
||||
func NewP521Generator() *P521Point {
|
||||
return NewP521Point().Set(p521G)
|
||||
}
|
||||
|
||||
// Set sets p = q and returns p.
|
||||
func (p *P521Point) Set(q *P521Point) *P521Point {
|
||||
p.x.Set(q.x)
|
||||
p.y.Set(q.y)
|
||||
p.z.Set(q.z)
|
||||
return p
|
||||
}
|
||||
|
||||
// SetBytes sets p to the compressed, uncompressed, or infinity value encoded in
|
||||
// b, as specified in SEC 1, Version 2.0, Section 2.3.4. If the point is not on
|
||||
// the curve, it returns nil and an error, and the receiver is unchanged.
|
||||
// Otherwise, it returns p.
|
||||
func (p *P521Point) SetBytes(b []byte) (*P521Point, error) {
|
||||
switch {
|
||||
// Point at infinity.
|
||||
case len(b) == 1 && b[0] == 0:
|
||||
return p.Set(NewP521Point()), nil
|
||||
|
||||
// Uncompressed form.
|
||||
case len(b) == 1+2*p521ElementLength && b[0] == 4:
|
||||
x, err := new(fiat.P521Element).SetBytes(b[1 : 1+p521ElementLength])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
y, err := new(fiat.P521Element).SetBytes(b[1+p521ElementLength:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p521CheckOnCurve(x, y); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.x.Set(x)
|
||||
p.y.Set(y)
|
||||
p.z.One()
|
||||
return p, nil
|
||||
|
||||
// Compressed form
|
||||
case len(b) == 1+p521ElementLength && b[0] == 0:
|
||||
return nil, errors.New("unimplemented") // TODO(filippo)
|
||||
|
||||
default:
|
||||
return nil, errors.New("invalid P521 point encoding")
|
||||
}
|
||||
}
|
||||
|
||||
func p521CheckOnCurve(x, y *fiat.P521Element) error {
|
||||
// x³ - 3x + b.
|
||||
x3 := new(fiat.P521Element).Square(x)
|
||||
x3.Mul(x3, x)
|
||||
|
||||
threeX := new(fiat.P521Element).Add(x, x)
|
||||
threeX.Add(threeX, x)
|
||||
|
||||
x3.Sub(x3, threeX)
|
||||
x3.Add(x3, p521B)
|
||||
|
||||
// y² = x³ - 3x + b
|
||||
y2 := new(fiat.P521Element).Square(y)
|
||||
|
||||
if x3.Equal(y2) != 1 {
|
||||
return errors.New("P521 point not on curve")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes returns the uncompressed or infinity encoding of p, as specified in
|
||||
// SEC 1, Version 2.0, Section 2.3.3. Note that the encoding of the point at
|
||||
// infinity is shorter than all other encodings.
|
||||
func (p *P521Point) Bytes() []byte {
|
||||
// This function is outlined to make the allocations inline in the caller
|
||||
// rather than happen on the heap.
|
||||
var out [133]byte
|
||||
return p.bytes(&out)
|
||||
}
|
||||
|
||||
func (p *P521Point) bytes(out *[133]byte) []byte {
|
||||
if p.z.IsZero() == 1 {
|
||||
return append(out[:0], 0)
|
||||
}
|
||||
|
||||
zinv := new(fiat.P521Element).Invert(p.z)
|
||||
xx := new(fiat.P521Element).Mul(p.x, zinv)
|
||||
yy := new(fiat.P521Element).Mul(p.y, zinv)
|
||||
|
||||
buf := append(out[:0], 4)
|
||||
buf = append(buf, xx.Bytes()...)
|
||||
buf = append(buf, yy.Bytes()...)
|
||||
return buf
|
||||
}
|
||||
|
||||
// Add sets q = p1 + p2, and returns q. The points may overlap.
|
||||
func (q *P521Point) Add(p1, p2 *P521Point) *P521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
t0 := new(fiat.P521Element).Mul(p1.x, p2.x) // t0 := X1 * X2
|
||||
t1 := new(fiat.P521Element).Mul(p1.y, p2.y) // t1 := Y1 * Y2
|
||||
t2 := new(fiat.P521Element).Mul(p1.z, p2.z) // t2 := Z1 * Z2
|
||||
t3 := new(fiat.P521Element).Add(p1.x, p1.y) // t3 := X1 + Y1
|
||||
t4 := new(fiat.P521Element).Add(p2.x, p2.y) // t4 := X2 + Y2
|
||||
t3.Mul(t3, t4) // t3 := t3 * t4
|
||||
t4.Add(t0, t1) // t4 := t0 + t1
|
||||
t3.Sub(t3, t4) // t3 := t3 - t4
|
||||
t4.Add(p1.y, p1.z) // t4 := Y1 + Z1
|
||||
x3 := new(fiat.P521Element).Add(p2.y, p2.z) // X3 := Y2 + Z2
|
||||
t4.Mul(t4, x3) // t4 := t4 * X3
|
||||
x3.Add(t1, t2) // X3 := t1 + t2
|
||||
t4.Sub(t4, x3) // t4 := t4 - X3
|
||||
x3.Add(p1.x, p1.z) // X3 := X1 + Z1
|
||||
y3 := new(fiat.P521Element).Add(p2.x, p2.z) // Y3 := X2 + Z2
|
||||
x3.Mul(x3, y3) // X3 := X3 * Y3
|
||||
y3.Add(t0, t2) // Y3 := t0 + t2
|
||||
y3.Sub(x3, y3) // Y3 := X3 - Y3
|
||||
z3 := new(fiat.P521Element).Mul(p521B, t2) // Z3 := b * t2
|
||||
x3.Sub(y3, z3) // X3 := Y3 - Z3
|
||||
z3.Add(x3, x3) // Z3 := X3 + X3
|
||||
x3.Add(x3, z3) // X3 := X3 + Z3
|
||||
z3.Sub(t1, x3) // Z3 := t1 - X3
|
||||
x3.Add(t1, x3) // X3 := t1 + X3
|
||||
y3.Mul(p521B, y3) // Y3 := b * Y3
|
||||
t1.Add(t2, t2) // t1 := t2 + t2
|
||||
t2.Add(t1, t2) // t2 := t1 + t2
|
||||
y3.Sub(y3, t2) // Y3 := Y3 - t2
|
||||
y3.Sub(y3, t0) // Y3 := Y3 - t0
|
||||
t1.Add(y3, y3) // t1 := Y3 + Y3
|
||||
y3.Add(t1, y3) // Y3 := t1 + Y3
|
||||
t1.Add(t0, t0) // t1 := t0 + t0
|
||||
t0.Add(t1, t0) // t0 := t1 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t1.Mul(t4, y3) // t1 := t4 * Y3
|
||||
t2.Mul(t0, y3) // t2 := t0 * Y3
|
||||
y3.Mul(x3, z3) // Y3 := X3 * Z3
|
||||
y3.Add(y3, t2) // Y3 := Y3 + t2
|
||||
x3.Mul(t3, x3) // X3 := t3 * X3
|
||||
x3.Sub(x3, t1) // X3 := X3 - t1
|
||||
z3.Mul(t4, z3) // Z3 := t4 * Z3
|
||||
t1.Mul(t3, t0) // t1 := t3 * t0
|
||||
z3.Add(z3, t1) // Z3 := Z3 + t1
|
||||
|
||||
q.x.Set(x3)
|
||||
q.y.Set(y3)
|
||||
q.z.Set(z3)
|
||||
return q
|
||||
}
|
||||
|
||||
// Double sets q = p + p, and returns q. The points may overlap.
|
||||
func (q *P521Point) Double(p *P521Point) *P521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
t0 := new(fiat.P521Element).Square(p.x) // t0 := X ^ 2
|
||||
t1 := new(fiat.P521Element).Square(p.y) // t1 := Y ^ 2
|
||||
t2 := new(fiat.P521Element).Square(p.z) // t2 := Z ^ 2
|
||||
t3 := new(fiat.P521Element).Mul(p.x, p.y) // t3 := X * Y
|
||||
t3.Add(t3, t3) // t3 := t3 + t3
|
||||
z3 := new(fiat.P521Element).Mul(p.x, p.z) // Z3 := X * Z
|
||||
z3.Add(z3, z3) // Z3 := Z3 + Z3
|
||||
y3 := new(fiat.P521Element).Mul(p521B, t2) // Y3 := b * t2
|
||||
y3.Sub(y3, z3) // Y3 := Y3 - Z3
|
||||
x3 := new(fiat.P521Element).Add(y3, y3) // X3 := Y3 + Y3
|
||||
y3.Add(x3, y3) // Y3 := X3 + Y3
|
||||
x3.Sub(t1, y3) // X3 := t1 - Y3
|
||||
y3.Add(t1, y3) // Y3 := t1 + Y3
|
||||
y3.Mul(x3, y3) // Y3 := X3 * Y3
|
||||
x3.Mul(x3, t3) // X3 := X3 * t3
|
||||
t3.Add(t2, t2) // t3 := t2 + t2
|
||||
t2.Add(t2, t3) // t2 := t2 + t3
|
||||
z3.Mul(p521B, z3) // Z3 := b * Z3
|
||||
z3.Sub(z3, t2) // Z3 := Z3 - t2
|
||||
z3.Sub(z3, t0) // Z3 := Z3 - t0
|
||||
t3.Add(z3, z3) // t3 := Z3 + Z3
|
||||
z3.Add(z3, t3) // Z3 := Z3 + t3
|
||||
t3.Add(t0, t0) // t3 := t0 + t0
|
||||
t0.Add(t3, t0) // t0 := t3 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t0.Mul(t0, z3) // t0 := t0 * Z3
|
||||
y3.Add(y3, t0) // Y3 := Y3 + t0
|
||||
t0.Mul(p.y, p.z) // t0 := Y * Z
|
||||
t0.Add(t0, t0) // t0 := t0 + t0
|
||||
z3.Mul(t0, z3) // Z3 := t0 * Z3
|
||||
x3.Sub(x3, z3) // X3 := X3 - Z3
|
||||
z3.Mul(t0, t1) // Z3 := t0 * t1
|
||||
z3.Add(z3, z3) // Z3 := Z3 + Z3
|
||||
z3.Add(z3, z3) // Z3 := Z3 + Z3
|
||||
|
||||
q.x.Set(x3)
|
||||
q.y.Set(y3)
|
||||
q.z.Set(z3)
|
||||
return q
|
||||
}
|
||||
|
||||
// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
|
||||
func (q *P521Point) Select(p1, p2 *P521Point, cond int) *P521Point {
|
||||
q.x.Select(p1.x, p2.x, cond)
|
||||
q.y.Select(p1.y, p2.y, cond)
|
||||
q.z.Select(p1.z, p2.z, cond)
|
||||
return q
|
||||
}
|
||||
|
||||
// ScalarMult sets p = scalar * q, and returns p.
|
||||
func (p *P521Point) ScalarMult(q *P521Point, scalar []byte) *P521Point {
|
||||
// table holds the first 16 multiples of q. The explicit newP521Point calls
|
||||
// get inlined, letting the allocations live on the stack.
|
||||
var table = [16]*P521Point{
|
||||
NewP521Point(), NewP521Point(), NewP521Point(), NewP521Point(),
|
||||
NewP521Point(), NewP521Point(), NewP521Point(), NewP521Point(),
|
||||
NewP521Point(), NewP521Point(), NewP521Point(), NewP521Point(),
|
||||
NewP521Point(), NewP521Point(), NewP521Point(), NewP521Point(),
|
||||
}
|
||||
for i := 1; i < 16; i++ {
|
||||
table[i].Add(table[i-1], q)
|
||||
}
|
||||
|
||||
// Instead of doing the classic double-and-add chain, we do it with a
|
||||
// four-bit window: we double four times, and then add [0-15]P.
|
||||
t := NewP521Point()
|
||||
p.Set(NewP521Point())
|
||||
for _, byte := range scalar {
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
cond := subtle.ConstantTimeByteEq(byte>>4, i)
|
||||
t.Select(table[i], t, cond)
|
||||
}
|
||||
p.Add(p, t)
|
||||
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
cond := subtle.ConstantTimeByteEq(byte&0b1111, i)
|
||||
t.Select(table[i], t, cond)
|
||||
}
|
||||
p.Add(p, t)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
44
src/crypto/elliptic/internal/nistec/p521_test.go
Normal file
44
src/crypto/elliptic/internal/nistec/p521_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package nistec_test
|
||||
|
||||
import (
|
||||
"crypto/elliptic/internal/nistec"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestP521Allocations(t *testing.T) {
|
||||
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") {
|
||||
t.Skip("skipping allocations test without relevant optimizations")
|
||||
}
|
||||
if allocs := testing.AllocsPerRun(100, func() {
|
||||
p := nistec.NewP521Generator()
|
||||
scalar := make([]byte, 66)
|
||||
rand.Read(scalar)
|
||||
p.ScalarMult(p, scalar)
|
||||
out := p.Bytes()
|
||||
if _, err := p.SetBytes(out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}); allocs > 0 {
|
||||
t.Errorf("expected zero allocations, got %0.1f", allocs)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScalarMult(b *testing.B) {
|
||||
b.Run("P521", func(b *testing.B) {
|
||||
scalar := make([]byte, 66)
|
||||
rand.Read(scalar)
|
||||
p := nistec.NewP521Generator()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.ScalarMult(p, scalar)
|
||||
}
|
||||
})
|
||||
}
|
|
@ -5,286 +5,152 @@
|
|||
package elliptic
|
||||
|
||||
import (
|
||||
"crypto/elliptic/internal/fiat"
|
||||
"crypto/subtle"
|
||||
"crypto/elliptic/internal/nistec"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// p521Curve is a Curve implementation based on nistec.P521Point.
|
||||
//
|
||||
// It's a wrapper that exposes the big.Int-based Curve interface and encodes the
|
||||
// legacy idiosyncrasies it requires, such as invalid and infinity point
|
||||
// handling.
|
||||
//
|
||||
// To interact with the nistec package, points are encoded into and decoded from
|
||||
// properly formatted byte slices. All big.Int use is limited to this package.
|
||||
// Encoding and decoding is 1/1000th of the runtime of a scalar multiplication,
|
||||
// so the overhead is acceptable.
|
||||
type p521Curve struct {
|
||||
*CurveParams
|
||||
b *fiat.P521Element
|
||||
params *CurveParams
|
||||
}
|
||||
|
||||
var p521 p521Curve
|
||||
var p521Params *CurveParams
|
||||
var _ Curve = p521
|
||||
|
||||
func initP521() {
|
||||
// See FIPS 186-3, section D.2.5
|
||||
p521.CurveParams = &CurveParams{Name: "P-521"}
|
||||
p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10)
|
||||
p521.N, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449", 10)
|
||||
p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16)
|
||||
p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16)
|
||||
p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16)
|
||||
p521.BitSize = 521
|
||||
p521.b = bigIntToFiatP521(p521.B)
|
||||
p521.params = &CurveParams{
|
||||
Name: "P-521",
|
||||
BitSize: 521,
|
||||
// FIPS 186-4, section D.1.2.5
|
||||
P: bigFromDecimal("68647976601306097149819007990813932172694353001433" +
|
||||
"0540939446345918554318339765605212255964066145455497729631139148" +
|
||||
"0858037121987999716643812574028291115057151"),
|
||||
N: bigFromDecimal("68647976601306097149819007990813932172694353001433" +
|
||||
"0540939446345918554318339765539424505774633321719753296399637136" +
|
||||
"3321113864768612440380340372808892707005449"),
|
||||
B: bigFromHex("0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8" +
|
||||
"b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef" +
|
||||
"451fd46b503f00"),
|
||||
Gx: bigFromHex("00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f8" +
|
||||
"28af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf9" +
|
||||
"7e7e31c2e5bd66"),
|
||||
Gy: bigFromHex("011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817" +
|
||||
"afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088" +
|
||||
"be94769fd16650"),
|
||||
}
|
||||
}
|
||||
|
||||
func (curve p521Curve) Params() *CurveParams {
|
||||
return curve.CurveParams
|
||||
return curve.params
|
||||
}
|
||||
|
||||
func (curve p521Curve) IsOnCurve(x, y *big.Int) bool {
|
||||
x1 := bigIntToFiatP521(x)
|
||||
y1 := bigIntToFiatP521(y)
|
||||
|
||||
// x³ - 3x + b.
|
||||
x3 := new(fiat.P521Element).Square(x1)
|
||||
x3.Mul(x3, x1)
|
||||
|
||||
threeX := new(fiat.P521Element).Add(x1, x1)
|
||||
threeX.Add(threeX, x1)
|
||||
|
||||
x3.Sub(x3, threeX)
|
||||
x3.Add(x3, curve.b)
|
||||
|
||||
// y² = x³ - 3x + b
|
||||
y2 := new(fiat.P521Element).Square(y1)
|
||||
|
||||
return x3.Equal(y2) == 1
|
||||
// IsOnCurve is documented to reject (0, 0), so we don't use
|
||||
// p521PointFromAffine, but let SetBytes reject the invalid Marshal output.
|
||||
_, err := nistec.NewP521Point().SetBytes(Marshal(curve, x, y))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// p521Point is a P-521 point in projective coordinates, where x = X/Z, y = Y/Z.
|
||||
type p521Point struct {
|
||||
x, y, z *fiat.P521Element
|
||||
}
|
||||
|
||||
// newP521Point returns a new p521Point representing the identity point.
|
||||
func newP521Point() *p521Point {
|
||||
return &p521Point{
|
||||
x: new(fiat.P521Element),
|
||||
y: new(fiat.P521Element).One(),
|
||||
z: new(fiat.P521Element),
|
||||
func p521PointFromAffine(x, y *big.Int) (p *nistec.P521Point, ok bool) {
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates. Marshal incorrectly encodes it as an uncompressed
|
||||
// point, which SetBytes correctly rejects. See Issue 37294.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return nistec.NewP521Point(), true
|
||||
}
|
||||
}
|
||||
|
||||
func fiatP521ToBigInt(x *fiat.P521Element) *big.Int {
|
||||
xBytes := x.Bytes()
|
||||
for i := range xBytes[:len(xBytes)/2] {
|
||||
xBytes[i], xBytes[len(xBytes)-i-1] = xBytes[len(xBytes)-i-1], xBytes[i]
|
||||
p, err := nistec.NewP521Point().SetBytes(Marshal(P521(), x, y))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return new(big.Int).SetBytes(xBytes)
|
||||
return p, true
|
||||
}
|
||||
|
||||
// Affine returns p in affine coordinates, with (0, 0) representing infinity by
|
||||
// convention. It also goes back to big.Int values to match the exposed API.
|
||||
func (p *p521Point) Affine() (x, y *big.Int) {
|
||||
if p.z.IsZero() == 1 {
|
||||
func p521PointToAffine(p *nistec.P521Point) (x, y *big.Int) {
|
||||
out := p.Bytes()
|
||||
if len(out) == 1 && out[0] == 0 {
|
||||
// This is the correct encoding of the point at infinity, which
|
||||
// Unmarshal does not support. See Issue 37294.
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
|
||||
zinv := new(fiat.P521Element).Invert(p.z)
|
||||
xx := new(fiat.P521Element).Mul(p.x, zinv)
|
||||
yy := new(fiat.P521Element).Mul(p.y, zinv)
|
||||
|
||||
return fiatP521ToBigInt(xx), fiatP521ToBigInt(yy)
|
||||
x, y = Unmarshal(P521(), out)
|
||||
if x == nil {
|
||||
panic("crypto/elliptic: internal error: Unmarshal rejected a valid point encoding")
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
func bigIntToFiatP521(x *big.Int) *fiat.P521Element {
|
||||
xBytes := new(big.Int).Mod(x, p521.P).FillBytes(make([]byte, 66))
|
||||
for i := range xBytes[:len(xBytes)/2] {
|
||||
xBytes[i], xBytes[len(xBytes)-i-1] = xBytes[len(xBytes)-i-1], xBytes[i]
|
||||
}
|
||||
x1, err := new(fiat.P521Element).SetBytes(xBytes)
|
||||
// p521RandomPoint returns a random point on the curve. It's used when Add,
|
||||
// Double, or ScalarMult are fed a point not on the curve, which is undefined
|
||||
// behavior. Originally, we used to do the math on it anyway (which allows
|
||||
// invalid curve attacks) and relied on the caller and Unmarshal to avoid this
|
||||
// happening in the first place. Now, we just can't construct a nistec.P521Point
|
||||
// for an invalid pair of coordinates, because that API is safer. If we panic,
|
||||
// we risk introducing a DoS. If we return nil, we risk a panic. If we return
|
||||
// the input, ecdsa.Verify might fail open. The safest course seems to be to
|
||||
// return a valid, random point, which hopefully won't help the attacker.
|
||||
func p521RandomPoint() (x, y *big.Int) {
|
||||
_, x, y, err := GenerateKey(P521(), rand.Reader)
|
||||
if err != nil {
|
||||
// The input is reduced modulo P and encoded in a fixed size bytes
|
||||
// slice, this should be impossible.
|
||||
panic("internal error: bigIntToFiatP521")
|
||||
}
|
||||
return x1
|
||||
}
|
||||
|
||||
// newP521PointFromAffine converts (x, y) affine coordinates into (X, Y, Z) projective
|
||||
// coordinates. It also converts from big.Int to fiat, which is necessarily a
|
||||
// messy and variable-time operation, which we can't avoid due to the exposed API.
|
||||
func newP521PointFromAffine(x, y *big.Int) *p521Point {
|
||||
// (0, 0) is by convention the point at infinity, which can't be represented
|
||||
// in affine coordinates, but is (0, 0, 0) in projective coordinates.
|
||||
if x.Sign() == 0 && y.Sign() == 0 {
|
||||
return newP521Point()
|
||||
}
|
||||
return &p521Point{
|
||||
x: bigIntToFiatP521(x),
|
||||
y: bigIntToFiatP521(y),
|
||||
z: new(fiat.P521Element).One(),
|
||||
panic("crypto/elliptic: failed to generate random point")
|
||||
}
|
||||
return x, y
|
||||
}
|
||||
|
||||
func (curve p521Curve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
|
||||
p1 := newP521PointFromAffine(x1, y1)
|
||||
p2 := newP521PointFromAffine(x2, y2)
|
||||
return p1.Add(p1, p2).Affine()
|
||||
}
|
||||
|
||||
// Add sets q = p1 + p2, and returns q. The points may overlap.
|
||||
func (q *p521Point) Add(p1, p2 *p521Point) *p521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
t0 := new(fiat.P521Element).Mul(p1.x, p2.x) // t0 := X1 * X2
|
||||
t1 := new(fiat.P521Element).Mul(p1.y, p2.y) // t1 := Y1 * Y2
|
||||
t2 := new(fiat.P521Element).Mul(p1.z, p2.z) // t2 := Z1 * Z2
|
||||
t3 := new(fiat.P521Element).Add(p1.x, p1.y) // t3 := X1 + Y1
|
||||
t4 := new(fiat.P521Element).Add(p2.x, p2.y) // t4 := X2 + Y2
|
||||
t3.Mul(t3, t4) // t3 := t3 * t4
|
||||
t4.Add(t0, t1) // t4 := t0 + t1
|
||||
t3.Sub(t3, t4) // t3 := t3 - t4
|
||||
t4.Add(p1.y, p1.z) // t4 := Y1 + Z1
|
||||
x := new(fiat.P521Element).Add(p2.y, p2.z) // X3 := Y2 + Z2
|
||||
t4.Mul(t4, x) // t4 := t4 * X3
|
||||
x.Add(t1, t2) // X3 := t1 + t2
|
||||
t4.Sub(t4, x) // t4 := t4 - X3
|
||||
x.Add(p1.x, p1.z) // X3 := X1 + Z1
|
||||
y := new(fiat.P521Element).Add(p2.x, p2.z) // Y3 := X2 + Z2
|
||||
x.Mul(x, y) // X3 := X3 * Y3
|
||||
y.Add(t0, t2) // Y3 := t0 + t2
|
||||
y.Sub(x, y) // Y3 := X3 - Y3
|
||||
z := new(fiat.P521Element).Mul(p521.b, t2) // Z3 := b * t2
|
||||
x.Sub(y, z) // X3 := Y3 - Z3
|
||||
z.Add(x, x) // Z3 := X3 + X3
|
||||
x.Add(x, z) // X3 := X3 + Z3
|
||||
z.Sub(t1, x) // Z3 := t1 - X3
|
||||
x.Add(t1, x) // X3 := t1 + X3
|
||||
y.Mul(p521.b, y) // Y3 := b * Y3
|
||||
t1.Add(t2, t2) // t1 := t2 + t2
|
||||
t2.Add(t1, t2) // t2 := t1 + t2
|
||||
y.Sub(y, t2) // Y3 := Y3 - t2
|
||||
y.Sub(y, t0) // Y3 := Y3 - t0
|
||||
t1.Add(y, y) // t1 := Y3 + Y3
|
||||
y.Add(t1, y) // Y3 := t1 + Y3
|
||||
t1.Add(t0, t0) // t1 := t0 + t0
|
||||
t0.Add(t1, t0) // t0 := t1 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t1.Mul(t4, y) // t1 := t4 * Y3
|
||||
t2.Mul(t0, y) // t2 := t0 * Y3
|
||||
y.Mul(x, z) // Y3 := X3 * Z3
|
||||
y.Add(y, t2) // Y3 := Y3 + t2
|
||||
x.Mul(t3, x) // X3 := t3 * X3
|
||||
x.Sub(x, t1) // X3 := X3 - t1
|
||||
z.Mul(t4, z) // Z3 := t4 * Z3
|
||||
t1.Mul(t3, t0) // t1 := t3 * t0
|
||||
z.Add(z, t1) // Z3 := Z3 + t1
|
||||
|
||||
q.x.Set(x)
|
||||
q.y.Set(y)
|
||||
q.z.Set(z)
|
||||
return q
|
||||
p1, ok := p521PointFromAffine(x1, y1)
|
||||
if !ok {
|
||||
return p521RandomPoint()
|
||||
}
|
||||
p2, ok := p521PointFromAffine(x2, y2)
|
||||
if !ok {
|
||||
return p521RandomPoint()
|
||||
}
|
||||
return p521PointToAffine(p1.Add(p1, p2))
|
||||
}
|
||||
|
||||
func (curve p521Curve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
|
||||
p := newP521PointFromAffine(x1, y1)
|
||||
return p.Double(p).Affine()
|
||||
}
|
||||
|
||||
// Double sets q = p + p, and returns q. The points may overlap.
|
||||
func (q *p521Point) Double(p *p521Point) *p521Point {
|
||||
// Complete addition formula for a = -3 from "Complete addition formulas for
|
||||
// prime order elliptic curves" (https://eprint.iacr.org/2015/1060), §A.2.
|
||||
|
||||
t0 := new(fiat.P521Element).Square(p.x) // t0 := X ^ 2
|
||||
t1 := new(fiat.P521Element).Square(p.y) // t1 := Y ^ 2
|
||||
t2 := new(fiat.P521Element).Square(p.z) // t2 := Z ^ 2
|
||||
t3 := new(fiat.P521Element).Mul(p.x, p.y) // t3 := X * Y
|
||||
t3.Add(t3, t3) // t3 := t3 + t3
|
||||
z := new(fiat.P521Element).Mul(p.x, p.z) // Z3 := X * Z
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
y := new(fiat.P521Element).Mul(p521.b, t2) // Y3 := b * t2
|
||||
y.Sub(y, z) // Y3 := Y3 - Z3
|
||||
x := new(fiat.P521Element).Add(y, y) // X3 := Y3 + Y3
|
||||
y.Add(x, y) // Y3 := X3 + Y3
|
||||
x.Sub(t1, y) // X3 := t1 - Y3
|
||||
y.Add(t1, y) // Y3 := t1 + Y3
|
||||
y.Mul(x, y) // Y3 := X3 * Y3
|
||||
x.Mul(x, t3) // X3 := X3 * t3
|
||||
t3.Add(t2, t2) // t3 := t2 + t2
|
||||
t2.Add(t2, t3) // t2 := t2 + t3
|
||||
z.Mul(p521.b, z) // Z3 := b * Z3
|
||||
z.Sub(z, t2) // Z3 := Z3 - t2
|
||||
z.Sub(z, t0) // Z3 := Z3 - t0
|
||||
t3.Add(z, z) // t3 := Z3 + Z3
|
||||
z.Add(z, t3) // Z3 := Z3 + t3
|
||||
t3.Add(t0, t0) // t3 := t0 + t0
|
||||
t0.Add(t3, t0) // t0 := t3 + t0
|
||||
t0.Sub(t0, t2) // t0 := t0 - t2
|
||||
t0.Mul(t0, z) // t0 := t0 * Z3
|
||||
y.Add(y, t0) // Y3 := Y3 + t0
|
||||
t0.Mul(p.y, p.z) // t0 := Y * Z
|
||||
t0.Add(t0, t0) // t0 := t0 + t0
|
||||
z.Mul(t0, z) // Z3 := t0 * Z3
|
||||
x.Sub(x, z) // X3 := X3 - Z3
|
||||
z.Mul(t0, t1) // Z3 := t0 * t1
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
z.Add(z, z) // Z3 := Z3 + Z3
|
||||
|
||||
q.x.Set(x)
|
||||
q.y.Set(y)
|
||||
q.z.Set(z)
|
||||
return q
|
||||
}
|
||||
|
||||
// Select sets q to p1 if cond == 1, and to p2 if cond == 0.
|
||||
func (q *p521Point) Select(p1, p2 *p521Point, cond int) *p521Point {
|
||||
q.x.Select(p1.x, p2.x, cond)
|
||||
q.y.Select(p1.y, p2.y, cond)
|
||||
q.z.Select(p1.z, p2.z, cond)
|
||||
return q
|
||||
p, ok := p521PointFromAffine(x1, y1)
|
||||
if !ok {
|
||||
return p521RandomPoint()
|
||||
}
|
||||
return p521PointToAffine(p.Double(p))
|
||||
}
|
||||
|
||||
func (curve p521Curve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) {
|
||||
B := newP521PointFromAffine(Bx, By)
|
||||
p, t := newP521Point(), newP521Point()
|
||||
|
||||
// table holds the first 16 multiples of q. The explicit newP521Point calls
|
||||
// get inlined, letting the allocations live on the stack.
|
||||
var table = [16]*p521Point{
|
||||
newP521Point(), newP521Point(), newP521Point(), newP521Point(),
|
||||
newP521Point(), newP521Point(), newP521Point(), newP521Point(),
|
||||
newP521Point(), newP521Point(), newP521Point(), newP521Point(),
|
||||
newP521Point(), newP521Point(), newP521Point(), newP521Point(),
|
||||
p, ok := p521PointFromAffine(Bx, By)
|
||||
if !ok {
|
||||
return p521RandomPoint()
|
||||
}
|
||||
for i := 1; i < 16; i++ {
|
||||
table[i].Add(table[i-1], B)
|
||||
}
|
||||
|
||||
// Instead of doing the classic double-and-add chain, we do it with a
|
||||
// four-bit window: we double four times, and then add [0-15]P.
|
||||
for _, byte := range scalar {
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
cond := subtle.ConstantTimeByteEq(byte>>4, i)
|
||||
t.Select(table[i], t, cond)
|
||||
}
|
||||
p.Add(p, t)
|
||||
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
p.Double(p)
|
||||
|
||||
for i := uint8(0); i < 16; i++ {
|
||||
cond := subtle.ConstantTimeByteEq(byte&0b1111, i)
|
||||
t.Select(table[i], t, cond)
|
||||
}
|
||||
p.Add(p, t)
|
||||
}
|
||||
|
||||
return p.Affine()
|
||||
return p521PointToAffine(p.ScalarMult(p, scalar))
|
||||
}
|
||||
|
||||
func (curve p521Curve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
||||
return curve.ScalarMult(curve.Gx, curve.Gy, k)
|
||||
func (curve p521Curve) ScalarBaseMult(scalar []byte) (*big.Int, *big.Int) {
|
||||
p := nistec.NewP521Generator()
|
||||
return p521PointToAffine(p.ScalarMult(p, scalar))
|
||||
}
|
||||
|
||||
func bigFromDecimal(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 10)
|
||||
if !ok {
|
||||
panic("invalid encoding")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func bigFromHex(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid encoding")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -396,6 +396,7 @@ var depsRules = `
|
|||
< crypto/subtle
|
||||
< crypto/internal/subtle
|
||||
< crypto/elliptic/internal/fiat
|
||||
< crypto/elliptic/internal/nistec
|
||||
< crypto/ed25519/internal/edwards25519/field, golang.org/x/crypto/curve25519/internal/field
|
||||
< crypto/ed25519/internal/edwards25519
|
||||
< crypto/cipher
|
||||
|
|
Loading…
Reference in a new issue