mirror of
https://github.com/emmansun/gmsm.git
synced 2025-05-12 03:56:17 +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 (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/sha512"
|
||||
_subtle "crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -203,11 +200,19 @@ var (
|
||||
)
|
||||
|
||||
// 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) {
|
||||
return Encrypt(random, pub, msg, ASN1EncrypterOpts)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
//A3, requirement is to check if h*P is infinite point, h is 1
|
||||
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()
|
||||
}
|
||||
|
||||
// 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) {
|
||||
randutil.MaybeReadByte(rand)
|
||||
|
||||
c := p256()
|
||||
k, Q, err := randomPoint(c, rand)
|
||||
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
|
||||
// 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,
|
||||
// then the hash will be treated as raw message.
|
||||
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)
|
||||
csprng, err := mixedCSPRNG(rand, &priv.PrivateKey, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch priv.Curve.Params() {
|
||||
case P256().Params():
|
||||
return signSM2EC(p256(), priv, csprng, hash)
|
||||
return signSM2EC(p256(), priv, rand, hash)
|
||||
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()
|
||||
hashToNat(c, e, hash)
|
||||
var (
|
||||
@ -546,7 +557,7 @@ func signSM2EC(c *sm2Curve, priv *PrivateKey, csprng io.Reader, hash []byte) (si
|
||||
|
||||
for {
|
||||
for {
|
||||
k, R, err = randomPoint(c, csprng)
|
||||
k, R, err = randomPoint(c, rand)
|
||||
if err != nil {
|
||||
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
|
||||
func IsSM2PublicKey(publicKey interface{}) bool {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
c := priv.PublicKey.Curve
|
||||
N := c.Params().N
|
||||
@ -92,7 +92,7 @@ func signLegacy(priv *PrivateKey, csprng io.Reader, hash []byte) (sig []byte, er
|
||||
e := hashToInt(hash, c)
|
||||
for {
|
||||
for {
|
||||
k, err = randFieldElement(c, csprng)
|
||||
k, err = randFieldElement(c, rand)
|
||||
if err != nil {
|
||||
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.
|
||||
func TestINDCCA(t *testing.T) {
|
||||
priv, err := GenerateKey(rand.Reader)
|
||||
|
26
sm9/sm9.go
26
sm9/sm9.go
@ -10,6 +10,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/bigmod"
|
||||
"github.com/emmansun/gmsm/internal/randutil"
|
||||
"github.com/emmansun/gmsm/internal/subtle"
|
||||
"github.com/emmansun/gmsm/kdf"
|
||||
"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)
|
||||
// using the user dsa key. It returns the signature as a pair of h and s.
|
||||
// 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) {
|
||||
sig, err := SignASN1(rand, priv, hash)
|
||||
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
|
||||
// is not currently used but, in keeping with the crypto.Signer interface.
|
||||
// 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) {
|
||||
return SignASN1(rand, priv, hash)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
var (
|
||||
hNat *bigmod.Nat
|
||||
s *bn256.G1
|
||||
)
|
||||
randutil.MaybeReadByte(rand)
|
||||
for {
|
||||
r, err := randomScalar(rand)
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
q := pub.GenerateUserPublicKey(uid, hid)
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
||||
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
|
||||
// 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) {
|
||||
key, cipher, err := WrapKey(rand, pub, uid, hid, kLen)
|
||||
if err != nil {
|
||||
|
@ -497,22 +497,21 @@ func testVerify(t *testing.T, test verifyTest, useSystemRoots bool) {
|
||||
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 {
|
||||
nChainMatched := 0
|
||||
var match bool
|
||||
for _, chain := range chains {
|
||||
if doesMatch(expectedChain, chain) {
|
||||
nChainMatched++
|
||||
match = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if nChainMatched != 1 {
|
||||
t.Errorf("Got %v matches instead of %v for expected chain %v", nChainMatched, 1, expectedChain)
|
||||
for _, chain := range chains {
|
||||
if doesMatch(expectedChain, chain) {
|
||||
t.Errorf("\t matched %v", chainToDebugString(chain))
|
||||
}
|
||||
}
|
||||
if !match {
|
||||
t.Errorf("No match found for %v", expectedChain)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user