mirror of
https://github.com/emmansun/gmsm.git
synced 2025-05-14 04:56:21 +08:00
sm2: remove CSPRNG usage
This commit is contained in:
parent
c1ea628282
commit
8041c5e310
92
sm2/sm2.go
92
sm2/sm2.go
@ -11,11 +11,8 @@ package sm2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/sha512"
|
|
||||||
_subtle "crypto/subtle"
|
_subtle "crypto/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -203,11 +200,19 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// EncryptASN1 sm2 encrypt and output ASN.1 result, compliance with GB/T 32918.4-2016.
|
// EncryptASN1 sm2 encrypt and output ASN.1 result, compliance with GB/T 32918.4-2016.
|
||||||
|
//
|
||||||
|
// The random parameter is used as a source of entropy to ensure that
|
||||||
|
// encrypting the same message twice doesn't result in the same ciphertext.
|
||||||
|
// Most applications should use [crypto/rand.Reader] as random.
|
||||||
func EncryptASN1(random io.Reader, pub *ecdsa.PublicKey, msg []byte) ([]byte, error) {
|
func EncryptASN1(random io.Reader, pub *ecdsa.PublicKey, msg []byte) ([]byte, error) {
|
||||||
return Encrypt(random, pub, msg, ASN1EncrypterOpts)
|
return Encrypt(random, pub, msg, ASN1EncrypterOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt sm2 encrypt implementation, compliance with GB/T 32918.4-2016.
|
// Encrypt sm2 encrypt implementation, compliance with GB/T 32918.4-2016.
|
||||||
|
//
|
||||||
|
// The random parameter is used as a source of entropy to ensure that
|
||||||
|
// encrypting the same message twice doesn't result in the same ciphertext.
|
||||||
|
// Most applications should use [crypto/rand.Reader] as random.
|
||||||
func Encrypt(random io.Reader, pub *ecdsa.PublicKey, msg []byte, opts *EncrypterOpts) ([]byte, error) {
|
func Encrypt(random io.Reader, pub *ecdsa.PublicKey, msg []byte, opts *EncrypterOpts) ([]byte, error) {
|
||||||
//A3, requirement is to check if h*P is infinite point, h is 1
|
//A3, requirement is to check if h*P is infinite point, h is 1
|
||||||
if pub.X.Sign() == 0 && pub.Y.Sign() == 0 {
|
if pub.X.Sign() == 0 && pub.Y.Sign() == 0 {
|
||||||
@ -297,8 +302,14 @@ func encodingCiphertextASN1(C1 *_sm2ec.SM2P256Point, c2, c3 []byte) ([]byte, err
|
|||||||
return b.Bytes()
|
return b.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateKey generates a public and private key pair.
|
// GenerateKey generates a new SM2 private key.
|
||||||
|
//
|
||||||
|
// Most applications should use [crypto/rand.Reader] as rand. Note that the
|
||||||
|
// returned key does not depend deterministically on the bytes read from rand,
|
||||||
|
// and may change between calls and/or between versions.
|
||||||
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
func GenerateKey(rand io.Reader) (*PrivateKey, error) {
|
||||||
|
randutil.MaybeReadByte(rand)
|
||||||
|
|
||||||
c := p256()
|
c := p256()
|
||||||
k, Q, err := randomPoint(c, rand)
|
k, Q, err := randomPoint(c, rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -493,6 +504,10 @@ func calculateSM2Hash(pub *ecdsa.PublicKey, data, uid []byte) ([]byte, error) {
|
|||||||
// private key's curve order, the hash will be truncated to that length. It
|
// private key's curve order, the hash will be truncated to that length. It
|
||||||
// returns the ASN.1 encoded signature.
|
// returns the ASN.1 encoded signature.
|
||||||
//
|
//
|
||||||
|
// The signature is randomized. Most applications should use [crypto/rand.Reader]
|
||||||
|
// as rand. Note that the returned signature does not depend deterministically on
|
||||||
|
// the bytes read from rand, and may change between calls and/or between versions.
|
||||||
|
//
|
||||||
// If the opts argument is instance of [*SM2SignerOption], and its ForceGMSign is true,
|
// If the opts argument is instance of [*SM2SignerOption], and its ForceGMSign is true,
|
||||||
// then the hash will be treated as raw message.
|
// then the hash will be treated as raw message.
|
||||||
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
|
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||||
@ -505,20 +520,16 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte, opts crypto.SignerO
|
|||||||
}
|
}
|
||||||
|
|
||||||
randutil.MaybeReadByte(rand)
|
randutil.MaybeReadByte(rand)
|
||||||
csprng, err := mixedCSPRNG(rand, &priv.PrivateKey, hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch priv.Curve.Params() {
|
switch priv.Curve.Params() {
|
||||||
case P256().Params():
|
case P256().Params():
|
||||||
return signSM2EC(p256(), priv, csprng, hash)
|
return signSM2EC(p256(), priv, rand, hash)
|
||||||
default:
|
default:
|
||||||
return signLegacy(priv, csprng, hash)
|
return signLegacy(priv, rand, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func signSM2EC(c *sm2Curve, priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
|
func signSM2EC(c *sm2Curve, priv *PrivateKey, rand io.Reader, hash []byte) (sig []byte, err error) {
|
||||||
e := bigmod.NewNat()
|
e := bigmod.NewNat()
|
||||||
hashToNat(c, e, hash)
|
hashToNat(c, e, hash)
|
||||||
var (
|
var (
|
||||||
@ -546,7 +557,7 @@ func signSM2EC(c *sm2Curve, priv *PrivateKey, csprng io.Reader, hash []byte) (si
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
for {
|
for {
|
||||||
k, R, err = randomPoint(c, csprng)
|
k, R, err = randomPoint(c, rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -726,63 +737,6 @@ func hashToNat(c *sm2Curve, e *bigmod.Nat, hash []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mixedCSPRNG returns a CSPRNG that mixes entropy from rand with the message
|
|
||||||
// and the private key, to protect the key in case rand fails. This is
|
|
||||||
// equivalent in security to RFC 6979 deterministic nonce generation, but still
|
|
||||||
// produces randomized signatures.
|
|
||||||
func mixedCSPRNG(rand io.Reader, priv *ecdsa.PrivateKey, hash []byte) (io.Reader, error) {
|
|
||||||
// This implementation derives the nonce from an AES-CTR CSPRNG keyed by:
|
|
||||||
//
|
|
||||||
// SHA2-512(priv.D || entropy || hash)[:32]
|
|
||||||
//
|
|
||||||
// The CSPRNG key is indifferentiable from a random oracle as shown in
|
|
||||||
// [Coron], the AES-CTR stream is indifferentiable from a random oracle
|
|
||||||
// under standard cryptographic assumptions (see [Larsson] for examples).
|
|
||||||
//
|
|
||||||
// [Coron]: https://cs.nyu.edu/~dodis/ps/merkle.pdf
|
|
||||||
// [Larsson]: https://web.archive.org/web/20040719170906/https://www.nada.kth.se/kurser/kth/2D1441/semteo03/lecturenotes/assump.pdf
|
|
||||||
|
|
||||||
// Get 256 bits of entropy from rand.
|
|
||||||
entropy := make([]byte, 32)
|
|
||||||
if _, err := io.ReadFull(rand, entropy); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize an SHA-512 hash context; digest...
|
|
||||||
md := sha512.New()
|
|
||||||
md.Write(priv.D.Bytes()) // the private key,
|
|
||||||
md.Write(entropy) // the entropy,
|
|
||||||
md.Write(hash) // and the input hash;
|
|
||||||
key := md.Sum(nil)[:32] // and compute ChopMD-256(SHA-512),
|
|
||||||
// which is an indifferentiable MAC.
|
|
||||||
|
|
||||||
// Create an AES-CTR instance to use as a CSPRNG.
|
|
||||||
block, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a CSPRNG that xors a stream of zeros with
|
|
||||||
// the output of the AES-CTR instance.
|
|
||||||
const aesIV = "IV for ECDSA CTR"
|
|
||||||
return &cipher.StreamReader{
|
|
||||||
R: zeroReader,
|
|
||||||
S: cipher.NewCTR(block, []byte(aesIV)),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type zr struct{}
|
|
||||||
|
|
||||||
var zeroReader = &zr{}
|
|
||||||
|
|
||||||
// Read replaces the contents of dst with zeros.
|
|
||||||
func (zr) Read(dst []byte) (n int, err error) {
|
|
||||||
for i := range dst {
|
|
||||||
dst[i] = 0
|
|
||||||
}
|
|
||||||
return len(dst), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSM2PublicKey check if given public key is a SM2 public key or not
|
// IsSM2PublicKey check if given public key is a SM2 public key or not
|
||||||
func IsSM2PublicKey(publicKey interface{}) bool {
|
func IsSM2PublicKey(publicKey interface{}) bool {
|
||||||
pub, ok := publicKey.(*ecdsa.PublicKey)
|
pub, ok := publicKey.(*ecdsa.PublicKey)
|
||||||
|
@ -81,7 +81,7 @@ func Sign(rand io.Reader, priv *ecdsa.PrivateKey, hash []byte) (r, s *big.Int, e
|
|||||||
return r, s, nil
|
return r, s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func signLegacy(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, err error) {
|
func signLegacy(priv *PrivateKey, rand io.Reader, hash []byte) (sig []byte, err error) {
|
||||||
// See [NSA] 3.4.1
|
// See [NSA] 3.4.1
|
||||||
c := priv.PublicKey.Curve
|
c := priv.PublicKey.Curve
|
||||||
N := c.Params().N
|
N := c.Params().N
|
||||||
@ -92,7 +92,7 @@ func signLegacy(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, er
|
|||||||
e := hashToInt(hash, c)
|
e := hashToInt(hash, c)
|
||||||
for {
|
for {
|
||||||
for {
|
for {
|
||||||
k, err = randFieldElement(c, csprng)
|
k, err = randFieldElement(c, rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -395,37 +395,6 @@ func TestSignVerifyLegacy(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that signatures are safe even with a broken entropy source.
|
|
||||||
func TestNonceSafety(t *testing.T) {
|
|
||||||
priv, err := GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to generate key")
|
|
||||||
}
|
|
||||||
|
|
||||||
hashed := []byte("testing")
|
|
||||||
r0, s0, err := Sign(zeroReader, &priv.PrivateKey, hashed)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("SM2: error signing: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
hashed = []byte("testing...")
|
|
||||||
r1, s1, err := Sign(zeroReader, &priv.PrivateKey, hashed)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("SM2: error signing: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s0.Cmp(s1) == 0 {
|
|
||||||
// This should never happen.
|
|
||||||
t.Error("SM2: the signatures on two different messages were the same")
|
|
||||||
}
|
|
||||||
|
|
||||||
if r0.Cmp(r1) == 0 {
|
|
||||||
t.Error("SM2: the nonce used for two different messages was the same")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that signatures remain non-deterministic with a functional entropy source.
|
// Check that signatures remain non-deterministic with a functional entropy source.
|
||||||
func TestINDCCA(t *testing.T) {
|
func TestINDCCA(t *testing.T) {
|
||||||
priv, err := GenerateKey(rand.Reader)
|
priv, err := GenerateKey(rand.Reader)
|
||||||
|
26
sm9/sm9.go
26
sm9/sm9.go
@ -10,6 +10,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/bigmod"
|
"github.com/emmansun/gmsm/internal/bigmod"
|
||||||
|
"github.com/emmansun/gmsm/internal/randutil"
|
||||||
"github.com/emmansun/gmsm/internal/subtle"
|
"github.com/emmansun/gmsm/internal/subtle"
|
||||||
"github.com/emmansun/gmsm/kdf"
|
"github.com/emmansun/gmsm/kdf"
|
||||||
"github.com/emmansun/gmsm/sm3"
|
"github.com/emmansun/gmsm/sm3"
|
||||||
@ -120,6 +121,10 @@ func randomScalar(rand io.Reader) (k *bigmod.Nat, err error) {
|
|||||||
// Sign signs a hash (which should be the result of hashing a larger message)
|
// Sign signs a hash (which should be the result of hashing a larger message)
|
||||||
// using the user dsa key. It returns the signature as a pair of h and s.
|
// using the user dsa key. It returns the signature as a pair of h and s.
|
||||||
// Please use SignASN1 instead.
|
// Please use SignASN1 instead.
|
||||||
|
//
|
||||||
|
// The signature is randomized. Most applications should use [crypto/rand.Reader]
|
||||||
|
// as rand. Note that the returned signature does not depend deterministically on
|
||||||
|
// the bytes read from rand, and may change between calls and/or between versions.
|
||||||
func Sign(rand io.Reader, priv *SignPrivateKey, hash []byte) (h *big.Int, s *bn256.G1, err error) {
|
func Sign(rand io.Reader, priv *SignPrivateKey, hash []byte) (h *big.Int, s *bn256.G1, err error) {
|
||||||
sig, err := SignASN1(rand, priv, hash)
|
sig, err := SignASN1(rand, priv, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -131,17 +136,26 @@ func Sign(rand io.Reader, priv *SignPrivateKey, hash []byte) (h *big.Int, s *bn2
|
|||||||
// Sign signs digest with user's DSA key, reading randomness from rand. The opts argument
|
// Sign signs digest with user's DSA key, reading randomness from rand. The opts argument
|
||||||
// is not currently used but, in keeping with the crypto.Signer interface.
|
// is not currently used but, in keeping with the crypto.Signer interface.
|
||||||
// The result is SM9Signature ASN.1 format.
|
// The result is SM9Signature ASN.1 format.
|
||||||
|
//
|
||||||
|
// The signature is randomized. Most applications should use [crypto/rand.Reader]
|
||||||
|
// as rand. Note that the returned signature does not depend deterministically on
|
||||||
|
// the bytes read from rand, and may change between calls and/or between versions.
|
||||||
func (priv *SignPrivateKey) Sign(rand io.Reader, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
|
func (priv *SignPrivateKey) Sign(rand io.Reader, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||||
return SignASN1(rand, priv, hash)
|
return SignASN1(rand, priv, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignASN1 signs a hash (which should be the result of hashing a larger message)
|
// SignASN1 signs a hash (which should be the result of hashing a larger message)
|
||||||
// using the private key, priv. It returns the ASN.1 encoded signature of type SM9Signature.
|
// using the private key, priv. It returns the ASN.1 encoded signature of type SM9Signature.
|
||||||
|
//
|
||||||
|
// The signature is randomized. Most applications should use [crypto/rand.Reader]
|
||||||
|
// as rand. Note that the returned signature does not depend deterministically on
|
||||||
|
// the bytes read from rand, and may change between calls and/or between versions.
|
||||||
func SignASN1(rand io.Reader, priv *SignPrivateKey, hash []byte) ([]byte, error) {
|
func SignASN1(rand io.Reader, priv *SignPrivateKey, hash []byte) ([]byte, error) {
|
||||||
var (
|
var (
|
||||||
hNat *bigmod.Nat
|
hNat *bigmod.Nat
|
||||||
s *bn256.G1
|
s *bn256.G1
|
||||||
)
|
)
|
||||||
|
randutil.MaybeReadByte(rand)
|
||||||
for {
|
for {
|
||||||
r, err := randomScalar(rand)
|
r, err := randomScalar(rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -272,6 +286,10 @@ func (pub *SignMasterPublicKey) Verify(uid []byte, hid byte, hash, sig []byte) b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WrapKey generates and wraps key with reciever's uid and system hid, returns generated key and cipher.
|
// WrapKey generates and wraps key with reciever's uid and system hid, returns generated key and cipher.
|
||||||
|
//
|
||||||
|
// The rand parameter is used as a source of entropy to ensure that
|
||||||
|
// calls this function twice doesn't result in the same key.
|
||||||
|
// Most applications should use [crypto/rand.Reader] as random.
|
||||||
func WrapKey(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte, kLen int) (key []byte, cipher *bn256.G1, err error) {
|
func WrapKey(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte, kLen int) (key []byte, cipher *bn256.G1, err error) {
|
||||||
q := pub.GenerateUserPublicKey(uid, hid)
|
q := pub.GenerateUserPublicKey(uid, hid)
|
||||||
var (
|
var (
|
||||||
@ -308,6 +326,10 @@ func WrapKey(rand io.Reader, pub *EncryptMasterPublicKey, uid []byte, hid byte,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WrapKey wraps key and converts the cipher as ASN1 format, SM9PublicKey1 definition.
|
// WrapKey wraps key and converts the cipher as ASN1 format, SM9PublicKey1 definition.
|
||||||
|
//
|
||||||
|
// The rand parameter is used as a source of entropy to ensure that
|
||||||
|
// calls this function twice doesn't result in the same key.
|
||||||
|
// Most applications should use [crypto/rand.Reader] as random.
|
||||||
func (pub *EncryptMasterPublicKey) WrapKey(rand io.Reader, uid []byte, hid byte, kLen int) ([]byte, []byte, error) {
|
func (pub *EncryptMasterPublicKey) WrapKey(rand io.Reader, uid []byte, hid byte, kLen int) ([]byte, []byte, error) {
|
||||||
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -322,6 +344,10 @@ func (pub *EncryptMasterPublicKey) WrapKey(rand io.Reader, uid []byte, hid byte,
|
|||||||
|
|
||||||
// WrapKeyASN1 wraps key and converts the result of SM9KeyPackage as ASN1 format. according
|
// WrapKeyASN1 wraps key and converts the result of SM9KeyPackage as ASN1 format. according
|
||||||
// SM9 cryptographic algorithm application specification, SM9KeyPackage defnition.
|
// SM9 cryptographic algorithm application specification, SM9KeyPackage defnition.
|
||||||
|
//
|
||||||
|
// The rand parameter is used as a source of entropy to ensure that
|
||||||
|
// calls this function twice doesn't result in the same key.
|
||||||
|
// Most applications should use [crypto/rand.Reader] as random.
|
||||||
func (pub *EncryptMasterPublicKey) WrapKeyASN1(rand io.Reader, uid []byte, hid byte, kLen int) ([]byte, error) {
|
func (pub *EncryptMasterPublicKey) WrapKeyASN1(rand io.Reader, uid []byte, hid byte, kLen int) ([]byte, error) {
|
||||||
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -497,22 +497,21 @@ func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every expected chain should match 1 returned chain
|
// Every expected chain should match one (or more) returned chain. We tolerate multiple
|
||||||
|
// matches, as due to root store semantics it is plausible that (at least on the system
|
||||||
|
// verifiers) multiple identical (looking) chains may be returned when two roots with the
|
||||||
|
// same subject are present.
|
||||||
for _, expectedChain := range test.expectedChains {
|
for _, expectedChain := range test.expectedChains {
|
||||||
nChainMatched := 0
|
var match bool
|
||||||
for _, chain := range chains {
|
for _, chain := range chains {
|
||||||
if doesMatch(expectedChain, chain) {
|
if doesMatch(expectedChain, chain) {
|
||||||
nChainMatched++
|
match = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nChainMatched != 1 {
|
if !match {
|
||||||
t.Errorf("Got %v matches instead of %v for expected chain %v", nChainMatched, 1, expectedChain)
|
t.Errorf("No match found for %v", expectedChain)
|
||||||
for _, chain := range chains {
|
|
||||||
if doesMatch(expectedChain, chain) {
|
|
||||||
t.Errorf("\t matched %v", chainToDebugString(chain))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user