package ecdsa
import (
"crypto"
"crypto/ecdh"
"crypto/elliptic"
"crypto/internal/boring"
"crypto/internal/boring/bbig"
"crypto/internal/fips140/ecdsa"
"crypto/internal/fips140only"
"crypto/internal/randutil"
"crypto/sha512"
"crypto/subtle"
"errors"
"io"
"math/big"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)
type PublicKey struct {
elliptic .Curve
X, Y *big .Int
}
func (k *PublicKey ) ECDH () (*ecdh .PublicKey , error ) {
c := curveToECDH (k .Curve )
if c == nil {
return nil , errors .New ("ecdsa: unsupported curve by crypto/ecdh" )
}
if !k .Curve .IsOnCurve (k .X , k .Y ) {
return nil , errors .New ("ecdsa: invalid public key" )
}
return c .NewPublicKey (elliptic .Marshal (k .Curve , k .X , k .Y ))
}
func (pub *PublicKey ) Equal (x crypto .PublicKey ) bool {
xx , ok := x .(*PublicKey )
if !ok {
return false
}
return bigIntEqual (pub .X , xx .X ) && bigIntEqual (pub .Y , xx .Y ) &&
pub .Curve == xx .Curve
}
type PrivateKey struct {
PublicKey
D *big .Int
}
func (k *PrivateKey ) ECDH () (*ecdh .PrivateKey , error ) {
c := curveToECDH (k .Curve )
if c == nil {
return nil , errors .New ("ecdsa: unsupported curve by crypto/ecdh" )
}
size := (k .Curve .Params ().N .BitLen () + 7 ) / 8
if k .D .BitLen () > size *8 {
return nil , errors .New ("ecdsa: invalid private key" )
}
return c .NewPrivateKey (k .D .FillBytes (make ([]byte , size )))
}
func curveToECDH(c elliptic .Curve ) ecdh .Curve {
switch c {
case elliptic .P256 ():
return ecdh .P256 ()
case elliptic .P384 ():
return ecdh .P384 ()
case elliptic .P521 ():
return ecdh .P521 ()
default :
return nil
}
}
func (priv *PrivateKey ) Public () crypto .PublicKey {
return &priv .PublicKey
}
func (priv *PrivateKey ) Equal (x crypto .PrivateKey ) bool {
xx , ok := x .(*PrivateKey )
if !ok {
return false
}
return priv .PublicKey .Equal (&xx .PublicKey ) && bigIntEqual (priv .D , xx .D )
}
func bigIntEqual(a , b *big .Int ) bool {
return subtle .ConstantTimeCompare (a .Bytes (), b .Bytes ()) == 1
}
func (priv *PrivateKey ) Sign (rand io .Reader , digest []byte , opts crypto .SignerOpts ) ([]byte , error ) {
if rand == nil {
return signRFC6979 (priv , digest , opts )
}
return SignASN1 (rand , priv , digest )
}
func GenerateKey (c elliptic .Curve , rand io .Reader ) (*PrivateKey , error ) {
randutil .MaybeReadByte (rand )
if boring .Enabled && rand == boring .RandReader {
x , y , d , err := boring .GenerateKeyECDSA (c .Params ().Name )
if err != nil {
return nil , err
}
return &PrivateKey {PublicKey : PublicKey {Curve : c , X : bbig .Dec (x ), Y : bbig .Dec (y )}, D : bbig .Dec (d )}, nil
}
boring .UnreachableExceptTests ()
switch c .Params () {
case elliptic .P224 ().Params ():
return generateFIPS (c , ecdsa .P224 (), rand )
case elliptic .P256 ().Params ():
return generateFIPS (c , ecdsa .P256 (), rand )
case elliptic .P384 ().Params ():
return generateFIPS (c , ecdsa .P384 (), rand )
case elliptic .P521 ().Params ():
return generateFIPS (c , ecdsa .P521 (), rand )
default :
return generateLegacy (c , rand )
}
}
func generateFIPS[P ecdsa .Point [P ]](curve elliptic .Curve , c *ecdsa .Curve [P ], rand io .Reader ) (*PrivateKey , error ) {
if fips140only .Enabled && fips140only .ApprovedRandomReader (rand ) {
return nil , errors .New ("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode" )
}
privateKey , err := ecdsa .GenerateKey (c , rand )
if err != nil {
return nil , err
}
return privateKeyFromFIPS (curve , privateKey )
}
var errNoAsm = errors .New ("no assembly implementation available" )
func SignASN1 (rand io .Reader , priv *PrivateKey , hash []byte ) ([]byte , error ) {
randutil .MaybeReadByte (rand )
if boring .Enabled && rand == boring .RandReader {
b , err := boringPrivateKey (priv )
if err != nil {
return nil , err
}
return boring .SignMarshalECDSA (b , hash )
}
boring .UnreachableExceptTests ()
switch priv .Curve .Params () {
case elliptic .P224 ().Params ():
return signFIPS (ecdsa .P224 (), priv , rand , hash )
case elliptic .P256 ().Params ():
return signFIPS (ecdsa .P256 (), priv , rand , hash )
case elliptic .P384 ().Params ():
return signFIPS (ecdsa .P384 (), priv , rand , hash )
case elliptic .P521 ().Params ():
return signFIPS (ecdsa .P521 (), priv , rand , hash )
default :
return signLegacy (priv , rand , hash )
}
}
func signFIPS[P ecdsa .Point [P ]](c *ecdsa .Curve [P ], priv *PrivateKey , rand io .Reader , hash []byte ) ([]byte , error ) {
if fips140only .Enabled && !fips140only .ApprovedRandomReader (rand ) {
return nil , errors .New ("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode" )
}
k , err := privateKeyToFIPS (c , priv )
if err != nil {
return nil , err
}
sig , err := ecdsa .Sign (c , sha512 .New , k , rand , hash )
if err != nil {
return nil , err
}
return encodeSignature (sig .R , sig .S )
}
func signRFC6979(priv *PrivateKey , hash []byte , opts crypto .SignerOpts ) ([]byte , error ) {
if opts == nil {
return nil , errors .New ("ecdsa: Sign called with nil opts" )
}
h := opts .HashFunc ()
if h .Size () != len (hash ) {
return nil , errors .New ("ecdsa: hash length does not match hash function" )
}
switch priv .Curve .Params () {
case elliptic .P224 ().Params ():
return signFIPSDeterministic (ecdsa .P224 (), h , priv , hash )
case elliptic .P256 ().Params ():
return signFIPSDeterministic (ecdsa .P256 (), h , priv , hash )
case elliptic .P384 ().Params ():
return signFIPSDeterministic (ecdsa .P384 (), h , priv , hash )
case elliptic .P521 ().Params ():
return signFIPSDeterministic (ecdsa .P521 (), h , priv , hash )
default :
return nil , errors .New ("ecdsa: curve not supported by deterministic signatures" )
}
}
func signFIPSDeterministic[P ecdsa .Point [P ]](c *ecdsa .Curve [P ], hashFunc crypto .Hash , priv *PrivateKey , hash []byte ) ([]byte , error ) {
k , err := privateKeyToFIPS (c , priv )
if err != nil {
return nil , err
}
sig , err := ecdsa .SignDeterministic (c , hashFunc .New , k , hash )
if err != nil {
return nil , err
}
return encodeSignature (sig .R , sig .S )
}
func encodeSignature(r , s []byte ) ([]byte , error ) {
var b cryptobyte .Builder
b .AddASN1 (asn1 .SEQUENCE , func (b *cryptobyte .Builder ) {
addASN1IntBytes (b , r )
addASN1IntBytes (b , s )
})
return b .Bytes ()
}
func addASN1IntBytes(b *cryptobyte .Builder , bytes []byte ) {
for len (bytes ) > 0 && bytes [0 ] == 0 {
bytes = bytes [1 :]
}
if len (bytes ) == 0 {
b .SetError (errors .New ("invalid integer" ))
return
}
b .AddASN1 (asn1 .INTEGER , func (c *cryptobyte .Builder ) {
if bytes [0 ]&0x80 != 0 {
c .AddUint8 (0 )
}
c .AddBytes (bytes )
})
}
func VerifyASN1 (pub *PublicKey , hash , sig []byte ) bool {
if boring .Enabled {
key , err := boringPublicKey (pub )
if err != nil {
return false
}
return boring .VerifyECDSA (key , hash , sig )
}
boring .UnreachableExceptTests ()
switch pub .Curve .Params () {
case elliptic .P224 ().Params ():
return verifyFIPS (ecdsa .P224 (), pub , hash , sig )
case elliptic .P256 ().Params ():
return verifyFIPS (ecdsa .P256 (), pub , hash , sig )
case elliptic .P384 ().Params ():
return verifyFIPS (ecdsa .P384 (), pub , hash , sig )
case elliptic .P521 ().Params ():
return verifyFIPS (ecdsa .P521 (), pub , hash , sig )
default :
return verifyLegacy (pub , hash , sig )
}
}
func verifyFIPS[P ecdsa .Point [P ]](c *ecdsa .Curve [P ], pub *PublicKey , hash , sig []byte ) bool {
r , s , err := parseSignature (sig )
if err != nil {
return false
}
k , err := publicKeyToFIPS (c , pub )
if err != nil {
return false
}
if err := ecdsa .Verify (c , k , hash , &ecdsa .Signature {R : r , S : s }); err != nil {
return false
}
return true
}
func parseSignature(sig []byte ) (r , s []byte , err error ) {
var inner cryptobyte .String
input := cryptobyte .String (sig )
if !input .ReadASN1 (&inner , asn1 .SEQUENCE ) ||
!input .Empty () ||
!inner .ReadASN1Integer (&r ) ||
!inner .ReadASN1Integer (&s ) ||
!inner .Empty () {
return nil , nil , errors .New ("invalid ASN.1" )
}
return r , s , nil
}
func publicKeyFromFIPS(curve elliptic .Curve , pub *ecdsa .PublicKey ) (*PublicKey , error ) {
x , y , err := pointToAffine (curve , pub .Bytes ())
if err != nil {
return nil , err
}
return &PublicKey {Curve : curve , X : x , Y : y }, nil
}
func privateKeyFromFIPS(curve elliptic .Curve , priv *ecdsa .PrivateKey ) (*PrivateKey , error ) {
pub , err := publicKeyFromFIPS (curve , priv .PublicKey ())
if err != nil {
return nil , err
}
return &PrivateKey {PublicKey : *pub , D : new (big .Int ).SetBytes (priv .Bytes ())}, nil
}
func publicKeyToFIPS[P ecdsa .Point [P ]](c *ecdsa .Curve [P ], pub *PublicKey ) (*ecdsa .PublicKey , error ) {
Q , err := pointFromAffine (pub .Curve , pub .X , pub .Y )
if err != nil {
return nil , err
}
return ecdsa .NewPublicKey (c , Q )
}
func privateKeyToFIPS[P ecdsa .Point [P ]](c *ecdsa .Curve [P ], priv *PrivateKey ) (*ecdsa .PrivateKey , error ) {
Q , err := pointFromAffine (priv .Curve , priv .X , priv .Y )
if err != nil {
return nil , err
}
return ecdsa .NewPrivateKey (c , priv .D .Bytes (), Q )
}
func pointFromAffine(curve elliptic .Curve , x , y *big .Int ) ([]byte , error ) {
bitSize := curve .Params ().BitSize
if x .Sign () < 0 || y .Sign () < 0 {
return nil , errors .New ("negative coordinate" )
}
if x .BitLen () > bitSize || y .BitLen () > bitSize {
return nil , errors .New ("overflowing coordinate" )
}
byteLen := (bitSize + 7 ) / 8
buf := make ([]byte , 1 +2 *byteLen )
buf [0 ] = 4
x .FillBytes (buf [1 : 1 +byteLen ])
y .FillBytes (buf [1 +byteLen : 1 +2 *byteLen ])
return buf , nil
}
func pointToAffine(curve elliptic .Curve , p []byte ) (x , y *big .Int , err error ) {
if len (p ) == 1 && p [0 ] == 0 {
return nil , nil , errors .New ("ecdsa: public key point is the infinity" )
}
byteLen := (curve .Params ().BitSize + 7 ) / 8
x = new (big .Int ).SetBytes (p [1 : 1 +byteLen ])
y = new (big .Int ).SetBytes (p [1 +byteLen :])
return x , y , nil
}
The pages are generated with Golds v0.7.3 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .