Skip to content

Commit

Permalink
crypto/elliptic: port P-224 and P-384 to fiat-crypto
Browse files Browse the repository at this point in the history
Also, adopt addchain code generation for field inversion, and switch
P-521 to Montgomery multiplication, which is significantly slower but
allows us to reuse the P-224/P-256/P-384 wrapper code. No one uses P-521
anyway, and it's still faster than it was in Go 1.16.

Removed a portion of tests that ran the P-224 vectors against P-256,
for some reason.

Sadly, fiat-crypto is not fast enough to replace the generic 32-bit
P-256 implementation (just yet?).

A change in visible behavior is that we literally can't internally
operate on invalid curve points anymore (yay!) but the crypto/elliptic
API locked us into accepting any pair of integers for
Add/Double/ScalarMult and return no error (sigh), although of course
that's undefined behavior. Panics are always regretted. Returning nil
leads to panics. A fixed point might be exploited. The most reasonable
solution felt to return a made up random point, which is not that
different from an off-curve point but leaks less.

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P224-8                    573µs ± 0%     146µs ± 0%   -74.56%  (p=0.000 n=7+9)
ScalarMult/P224-8                        574µs ± 0%     152µs ± 5%   -73.58%  (p=0.000 n=7+10)
MarshalUnmarshal/P224/Uncompressed-8     664ns ± 0%     481ns ± 1%   -27.64%  (p=0.000 n=8+10)
MarshalUnmarshal/P224/Compressed-8       666ns ± 1%     480ns ± 0%   -27.92%  (p=0.000 n=10+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P224-8                              597µs ± 0%     169µs ± 2%   -71.71%  (p=0.000 n=10+9)
Verify/P224-8                           1.18ms ± 1%    0.32ms ± 5%   -72.81%  (p=0.000 n=10+10)
GenerateKey/P224-8                       577µs ± 0%     147µs ± 0%   -74.51%  (p=0.000 n=8+8)

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P384-8                   2.01ms ± 2%    0.50ms ± 0%  -75.00%  (p=0.000 n=10+8)
ScalarMult/P384-8                       2.02ms ± 3%    0.51ms ± 3%  -74.64%  (p=0.000 n=10+10)
MarshalUnmarshal/P384/Uncompressed-8    1.09µs ± 1%    0.76µs ± 0%  -30.27%  (p=0.000 n=10+9)
MarshalUnmarshal/P384/Compressed-8      1.08µs ± 0%    0.76µs ± 1%  -29.86%  (p=0.000 n=8+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P384-8                             2.06ms ± 1%    0.56ms ± 2%  -72.76%  (p=0.000 n=10+10)
Verify/P384-8                           4.06ms ± 2%    1.08ms ± 0%  -73.49%  (p=0.000 n=10+8)
GenerateKey/P384-8                      2.01ms ± 1%    0.51ms ± 3%  -74.65%  (p=0.000 n=10+10)

name                                  old time/op    new time/op    delta
pkg:crypto/elliptic goos:darwin goarch:arm64
ScalarBaseMult/P521-8                    715µs ± 6%    1525µs ± 4%  +113.39%  (p=0.000 n=10+10)
ScalarMult/P521-8                        698µs ± 1%    1543µs ± 1%  +120.99%  (p=0.000 n=9+9)
MarshalUnmarshal/P521/Uncompressed-8     797ns ± 0%    1296ns ± 0%   +62.65%  (p=0.000 n=10+9)
MarshalUnmarshal/P521/Compressed-8       798ns ± 0%    1299ns ± 1%   +62.82%  (p=0.000 n=8+10)
pkg:crypto/ecdsa goos:darwin goarch:arm64
Sign/P521-8                              810µs ± 3%    1645µs ± 0%  +103.03%  (p=0.000 n=10+10)
Verify/P521-8                           1.42ms ± 1%    3.19ms ± 1%  +125.28%  (p=0.000 n=10+8)
GenerateKey/P521-8                       698µs ± 1%    1549µs ± 0%  +121.87%  (p=0.000 n=10+7)

Updates #40171

Change-Id: I34edf5002b5e9fad0ebb6c1e2119fb123ea6d18f
Reviewed-on: https://go-review.googlesource.com/c/go/+/360014
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Trust: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
  • Loading branch information
FiloSottile committed Nov 5, 2021
1 parent 53bab19 commit 93bab8a
Show file tree
Hide file tree
Showing 28 changed files with 11,284 additions and 2,505 deletions.
3 changes: 3 additions & 0 deletions src/cmd/compile/internal/ssa/stmtlines_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func TestStmtLines(t *testing.T) {
if pkgname == "runtime" {
continue
}
if pkgname == "crypto/elliptic/internal/fiat" {
continue // golang.org/issue/49372
}
if e.Val(dwarf.AttrStmtList) == nil {
continue
}
Expand Down
32 changes: 16 additions & 16 deletions src/crypto/elliptic/elliptic.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func (curve *CurveParams) polynomial(x *big.Int) *big.Int {
func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.IsOnCurve(x, y)
}

Expand Down Expand Up @@ -128,7 +128,7 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.
func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.Add(x1, y1, x2, y2)
}

Expand Down Expand Up @@ -218,7 +218,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p384, p521); ok {
return specific.Double(x1, y1)
}

Expand Down Expand Up @@ -290,7 +290,7 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p256, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p256, p384, p521); ok {
return specific.ScalarMult(Bx, By, k)
}

Expand All @@ -313,7 +313,7 @@ func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.
func (curve *CurveParams) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
// If there is a dedicated constant-time implementation for this curve operation,
// use that instead of the generic one.
if specific, ok := matchesSpecificCurve(curve, p224, p256, p521); ok {
if specific, ok := matchesSpecificCurve(curve, p224, p256, p384, p521); ok {
return specific.ScalarBaseMult(k)
}

Expand Down Expand Up @@ -431,7 +431,6 @@ func UnmarshalCompressed(curve Curve, data []byte) (x, y *big.Int) {
}

var initonce sync.Once
var p384 *CurveParams

func initAll() {
initP224()
Expand All @@ -440,15 +439,16 @@ func initAll() {
initP521()
}

func initP384() {
// See FIPS 186-3, section D.2.4
p384 = &CurveParams{Name: "P-384"}
p384.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10)
p384.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10)
p384.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16)
p384.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16)
p384.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16)
p384.BitSize = 384
// P224 returns a Curve which implements NIST P-224 (FIPS 186-3, section D.2.2),
// also known as secp224r1. The CurveParams.Name of this Curve is "P-224".
//
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations are implemented using constant-time algorithms.
func P224() Curve {
initonce.Do(initAll)
return p224
}

// P256 returns a Curve which implements NIST P-256 (FIPS 186-3, section D.2.3),
Expand All @@ -470,7 +470,7 @@ func P256() Curve {
// Multiple invocations of this function will return the same value, so it can
// be used for equality checks and switch statements.
//
// The cryptographic operations do not use constant-time algorithms.
// The cryptographic operations are implemented using constant-time algorithms.
func P384() Curve {
initonce.Do(initAll)
return p384
Expand Down
5 changes: 2 additions & 3 deletions src/crypto/elliptic/elliptic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import (

// genericParamsForCurve returns the dereferenced CurveParams for
// the specified curve. This is used to avoid the logic for
// upgrading a curve to it's specific implementation, forcing
// usage of the generic implementation. This is only relevant
// for the P224, P256, and P521 curves.
// upgrading a curve to its specific implementation, forcing
// usage of the generic implementation.
func genericParamsForCurve(c Curve) *CurveParams {
d := *(c.Params())
return &d
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/elliptic/internal/fiat/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

FROM coqorg/coq:8.13.2

RUN git clone https://github.com/mit-plv/fiat-crypto
RUN cd fiat-crypto && git checkout c076f3550bea2bb7f4cb5766a32594b9e67694f2
RUN cd fiat-crypto && git submodule update --init --recursive
RUN git clone https://github.com/mit-plv/fiat-crypto && cd fiat-crypto && \
git checkout 23d2dbc4ab897d14bde4404f70cd6991635f9c01 && \
git submodule update --init --recursive
RUN cd fiat-crypto && eval $(opam env) && make -j4 standalone-ocaml SKIP_BEDROCK2=1

ENTRYPOINT ["fiat-crypto/src/ExtractionOCaml/unsaturated_solinas"]
ENV PATH /home/coq/fiat-crypto/src/ExtractionOCaml:$PATH
21 changes: 8 additions & 13 deletions src/crypto/elliptic/internal/fiat/README
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
The code in this package was autogenerated by the fiat-crypto project
at commit c076f3550 from a formally verified model.

docker build -t fiat-crypto:c076f3550 .
docker run fiat-crypto:c076f3550 --lang Go --no-wide-int --cmovznz-by-mul \
--internal-static --public-function-case camelCase --public-type-case camelCase \
--private-function-case camelCase --private-type-case camelCase \
--no-prefix-fiat --package-name fiat --doc-text-before-function-name '' \
--doc-prepend-header 'Code generated by Fiat Cryptography. DO NOT EDIT.' \
--doc-newline-before-package-declaration p521 64 9 '2^521 - 1' \
carry_mul carry_square carry add sub to_bytes from_bytes selectznz \
> p521_fiat64.go

It comes under the following license.
at version v0.0.9 from a formally verified model, and by the addchain
project at a recent tip version.

docker build -t fiat-crypto:v0.0.9 .
go install github.com/mmcloughlin/addchain/cmd/addchain@v0.3.1-0.20211027081849-6a7d3decbe08
../../../../../bin/go run generate.go

fiat-crypto code comes under the following license.

Copyright (c) 2015-2020 The fiat-crypto Authors. All rights reserved.

Expand Down
64 changes: 64 additions & 0 deletions src/crypto/elliptic/internal/fiat/fiat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 fiat_test

import (
"crypto/elliptic/internal/fiat"
"testing"
)

func BenchmarkMul(b *testing.B) {
b.Run("P224", func(b *testing.B) {
v := new(fiat.P224Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
b.Run("P384", func(b *testing.B) {
v := new(fiat.P384Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
b.Run("P521", func(b *testing.B) {
v := new(fiat.P521Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Mul(v, v)
}
})
}

func BenchmarkSquare(b *testing.B) {
b.Run("P224", func(b *testing.B) {
v := new(fiat.P224Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
b.Run("P384", func(b *testing.B) {
v := new(fiat.P384Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
b.Run("P521", func(b *testing.B) {
v := new(fiat.P521Element).One()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v.Square(v)
}
})
}
Loading

0 comments on commit 93bab8a

Please sign in to comment.