github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/crypto/ed25519/chainkd/expanded_key.go (about)

     1  // Package chainkd This is an extension to ed25519.Sign that is compatible with NaCl `crypto_sign`
     2  // function taking 64-byte expanded private key (where the left part is a pre-multiplied
     3  // scalar and the right part is "prefix" used for generating a nonce).
     4  //
     5  // Invariants:
     6  // 1) Expanded(PrivateKey).Sign() == PrivateKey.Sign()
     7  // 2) InnerSign(Expanded(PrivateKey)) == Sign(PrivateKey)
     8  package chainkd
     9  
    10  import (
    11  	"crypto"
    12  	"crypto/sha512"
    13  	"errors"
    14  	"io"
    15  	"strconv"
    16  
    17  	"github.com/bytom/bytom/crypto/ed25519"
    18  	"github.com/bytom/bytom/crypto/ed25519/internal/edwards25519"
    19  )
    20  
    21  const (
    22  	// ExpandedPrivateKeySize is the size, in bytes, of a "secret key" as defined in NaCl.
    23  	ExpandedPrivateKeySize = 64
    24  )
    25  
    26  // ExpandedPrivateKey is the type of NaCl secret keys. It implements crypto.Signer.
    27  type ExpandedPrivateKey []byte
    28  
    29  // Public returns the PublicKey corresponding to secret key.
    30  func (priv ExpandedPrivateKey) Public() crypto.PublicKey {
    31  	var A edwards25519.ExtendedGroupElement
    32  	var scalar [32]byte
    33  	copy(scalar[:], priv[:32])
    34  	edwards25519.GeScalarMultBase(&A, &scalar)
    35  	var publicKeyBytes [32]byte
    36  	A.ToBytes(&publicKeyBytes)
    37  	return ed25519.PublicKey(publicKeyBytes[:])
    38  }
    39  
    40  func expandEd25519PrivateKey(priv ed25519.PrivateKey) ExpandedPrivateKey {
    41  	digest := sha512.Sum512(priv[:32])
    42  	digest[0] &= 248
    43  	digest[31] &= 127
    44  	digest[31] |= 64
    45  	return ExpandedPrivateKey(digest[:])
    46  }
    47  
    48  // Sign signs the given message with expanded private key.
    49  // Ed25519 performs two passes over messages to be signed and therefore cannot
    50  // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
    51  // indicate the message hasn't been hashed. This can be achieved by passing
    52  // crypto.Hash(0) as the value for opts.
    53  func (priv ExpandedPrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
    54  	if opts.HashFunc() != crypto.Hash(0) {
    55  		return nil, errors.New("ed25519: cannot sign hashed message")
    56  	}
    57  
    58  	return Ed25519InnerSign(priv, message), nil
    59  }
    60  
    61  // Ed25519InnerSign signs the message with expanded private key and returns a signature.
    62  // It will panic if len(privateKey) is not ExpandedPrivateKeySize.
    63  func Ed25519InnerSign(privateKey ExpandedPrivateKey, message []byte) []byte {
    64  	if l := len(privateKey); l != ExpandedPrivateKeySize {
    65  		panic("ed25519: bad private key length: " + strconv.Itoa(l))
    66  	}
    67  
    68  	var messageDigest, hramDigest [64]byte
    69  
    70  	h := sha512.New()
    71  	h.Write(privateKey[32:])
    72  	h.Write(message)
    73  	h.Sum(messageDigest[:0])
    74  
    75  	var messageDigestReduced [32]byte
    76  	edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
    77  	var R edwards25519.ExtendedGroupElement
    78  	edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
    79  
    80  	var encodedR [32]byte
    81  	R.ToBytes(&encodedR)
    82  
    83  	publicKey := privateKey.Public().(ed25519.PublicKey)
    84  	h.Reset()
    85  	h.Write(encodedR[:])
    86  	h.Write(publicKey[:])
    87  	h.Write(message)
    88  	h.Sum(hramDigest[:0])
    89  	var hramDigestReduced [32]byte
    90  	edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
    91  
    92  	var sk [32]byte
    93  	copy(sk[:], privateKey[:32])
    94  	var s [32]byte
    95  	edwards25519.ScMulAdd(&s, &hramDigestReduced, &sk, &messageDigestReduced)
    96  
    97  	signature := make([]byte, ed25519.SignatureSize)
    98  	copy(signature[:], encodedR[:])
    99  	copy(signature[32:], s[:])
   100  
   101  	return signature
   102  }