github.com/cloudflare/circl@v1.5.0/sign/ed25519/ed25519.go (about)

     1  // Package ed25519 implements Ed25519 signature scheme as described in RFC-8032.
     2  //
     3  // This package provides optimized implementations of the three signature
     4  // variants and maintaining closer compatibility with crypto/ed25519.
     5  //
     6  //	| Scheme Name | Sign Function     | Verification  | Context           |
     7  //	|-------------|-------------------|---------------|-------------------|
     8  //	| Ed25519     | Sign              | Verify        | None              |
     9  //	| Ed25519Ph   | SignPh            | VerifyPh      | Yes, can be empty |
    10  //	| Ed25519Ctx  | SignWithCtx       | VerifyWithCtx | Yes, non-empty    |
    11  //	| All above   | (PrivateKey).Sign | VerifyAny     | As above          |
    12  //
    13  // Specific functions for sign and verify are defined. A generic signing
    14  // function for all schemes is available through the crypto.Signer interface,
    15  // which is implemented by the PrivateKey type. A correspond all-in-one
    16  // verification method is provided by the VerifyAny function.
    17  //
    18  // Signing with Ed25519Ph or Ed25519Ctx requires a context string for domain
    19  // separation. This parameter is passed using a SignerOptions struct defined
    20  // in this package. While Ed25519Ph accepts an empty context, Ed25519Ctx
    21  // enforces non-empty context strings.
    22  //
    23  // # Compatibility with crypto.ed25519
    24  //
    25  // These functions are compatible with the “Ed25519” function defined in
    26  // RFC-8032. However, unlike RFC 8032's formulation, this package's private
    27  // key representation includes a public key suffix to make multiple signing
    28  // operations with the same key more efficient. This package refers to the
    29  // RFC-8032 private key as the “seed”.
    30  //
    31  // References
    32  //
    33  //   - RFC-8032: https://rfc-editor.org/rfc/rfc8032.txt
    34  //   - Ed25519: https://ed25519.cr.yp.to/
    35  //   - EdDSA: High-speed high-security signatures. https://doi.org/10.1007/s13389-012-0027-1
    36  package ed25519
    37  
    38  import (
    39  	"bytes"
    40  	"crypto"
    41  	cryptoRand "crypto/rand"
    42  	"crypto/sha512"
    43  	"crypto/subtle"
    44  	"errors"
    45  	"fmt"
    46  	"io"
    47  	"strconv"
    48  
    49  	"github.com/cloudflare/circl/sign"
    50  )
    51  
    52  const (
    53  	// ContextMaxSize is the maximum length (in bytes) allowed for context.
    54  	ContextMaxSize = 255
    55  	// PublicKeySize is the size, in bytes, of public keys as used in this package.
    56  	PublicKeySize = 32
    57  	// PrivateKeySize is the size, in bytes, of private keys as used in this package.
    58  	PrivateKeySize = 64
    59  	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
    60  	SignatureSize = 64
    61  	// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
    62  	SeedSize = 32
    63  )
    64  
    65  const (
    66  	paramB = 256 / 8 // Size of keys in bytes.
    67  )
    68  
    69  // SignerOptions implements crypto.SignerOpts and augments with parameters
    70  // that are specific to the Ed25519 signature schemes.
    71  type SignerOptions struct {
    72  	// Hash must be crypto.Hash(0) for Ed25519/Ed25519ctx, or crypto.SHA512
    73  	// for Ed25519ph.
    74  	crypto.Hash
    75  
    76  	// Context is an optional domain separation string for Ed25519ph and a
    77  	// must for Ed25519ctx. Its length must be less or equal than 255 bytes.
    78  	Context string
    79  
    80  	// Scheme is an identifier for choosing a signature scheme. The zero value
    81  	// is ED25519.
    82  	Scheme SchemeID
    83  }
    84  
    85  // SchemeID is an identifier for each signature scheme.
    86  type SchemeID uint
    87  
    88  const (
    89  	ED25519 SchemeID = iota
    90  	ED25519Ph
    91  	ED25519Ctx
    92  )
    93  
    94  // PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
    95  type PrivateKey []byte
    96  
    97  // Equal reports whether priv and x have the same value.
    98  func (priv PrivateKey) Equal(x crypto.PrivateKey) bool {
    99  	xx, ok := x.(PrivateKey)
   100  	return ok && subtle.ConstantTimeCompare(priv, xx) == 1
   101  }
   102  
   103  // Public returns the PublicKey corresponding to priv.
   104  func (priv PrivateKey) Public() crypto.PublicKey {
   105  	publicKey := make(PublicKey, PublicKeySize)
   106  	copy(publicKey, priv[SeedSize:])
   107  	return publicKey
   108  }
   109  
   110  // Seed returns the private key seed corresponding to priv. It is provided for
   111  // interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
   112  // in this package.
   113  func (priv PrivateKey) Seed() []byte {
   114  	seed := make([]byte, SeedSize)
   115  	copy(seed, priv[:SeedSize])
   116  	return seed
   117  }
   118  
   119  func (priv PrivateKey) Scheme() sign.Scheme { return sch }
   120  
   121  func (pub PublicKey) Scheme() sign.Scheme { return sch }
   122  
   123  func (priv PrivateKey) MarshalBinary() (data []byte, err error) {
   124  	privateKey := make(PrivateKey, PrivateKeySize)
   125  	copy(privateKey, priv)
   126  	return privateKey, nil
   127  }
   128  
   129  func (pub PublicKey) MarshalBinary() (data []byte, err error) {
   130  	publicKey := make(PublicKey, PublicKeySize)
   131  	copy(publicKey, pub)
   132  	return publicKey, nil
   133  }
   134  
   135  // Equal reports whether pub and x have the same value.
   136  func (pub PublicKey) Equal(x crypto.PublicKey) bool {
   137  	xx, ok := x.(PublicKey)
   138  	return ok && bytes.Equal(pub, xx)
   139  }
   140  
   141  // Sign creates a signature of a message with priv key.
   142  // This function is compatible with crypto.ed25519 and also supports the
   143  // three signature variants defined in RFC-8032, namely Ed25519 (or pure
   144  // EdDSA), Ed25519Ph, and Ed25519Ctx.
   145  // The opts.HashFunc() must return zero to specify either Ed25519 or Ed25519Ctx
   146  // variant. This can be achieved by passing crypto.Hash(0) as the value for
   147  // opts.
   148  // The opts.HashFunc() must return SHA512 to specify the Ed25519Ph variant.
   149  // This can be achieved by passing crypto.SHA512 as the value for opts.
   150  // Use a SignerOptions struct (defined in this package) to pass a context
   151  // string for signing.
   152  func (priv PrivateKey) Sign(
   153  	rand io.Reader,
   154  	message []byte,
   155  	opts crypto.SignerOpts,
   156  ) (signature []byte, err error) {
   157  	var ctx string
   158  	var scheme SchemeID
   159  	if o, ok := opts.(SignerOptions); ok {
   160  		ctx = o.Context
   161  		scheme = o.Scheme
   162  	}
   163  
   164  	switch true {
   165  	case scheme == ED25519 && opts.HashFunc() == crypto.Hash(0):
   166  		return Sign(priv, message), nil
   167  	case scheme == ED25519Ph && opts.HashFunc() == crypto.SHA512:
   168  		return SignPh(priv, message, ctx), nil
   169  	case scheme == ED25519Ctx && opts.HashFunc() == crypto.Hash(0) && len(ctx) > 0:
   170  		return SignWithCtx(priv, message, ctx), nil
   171  	default:
   172  		return nil, errors.New("ed25519: bad hash algorithm")
   173  	}
   174  }
   175  
   176  // GenerateKey generates a public/private key pair using entropy from rand.
   177  // If rand is nil, crypto/rand.Reader will be used.
   178  func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
   179  	if rand == nil {
   180  		rand = cryptoRand.Reader
   181  	}
   182  
   183  	seed := make([]byte, SeedSize)
   184  	if _, err := io.ReadFull(rand, seed); err != nil {
   185  		return nil, nil, err
   186  	}
   187  
   188  	privateKey := NewKeyFromSeed(seed)
   189  	publicKey := make(PublicKey, PublicKeySize)
   190  	copy(publicKey, privateKey[SeedSize:])
   191  
   192  	return publicKey, privateKey, nil
   193  }
   194  
   195  // NewKeyFromSeed calculates a private key from a seed. It will panic if
   196  // len(seed) is not SeedSize. This function is provided for interoperability
   197  // with RFC 8032. RFC 8032's private keys correspond to seeds in this
   198  // package.
   199  func NewKeyFromSeed(seed []byte) PrivateKey {
   200  	privateKey := make(PrivateKey, PrivateKeySize)
   201  	newKeyFromSeed(privateKey, seed)
   202  	return privateKey
   203  }
   204  
   205  func newKeyFromSeed(privateKey, seed []byte) {
   206  	if l := len(seed); l != SeedSize {
   207  		panic("ed25519: bad seed length: " + strconv.Itoa(l))
   208  	}
   209  	var P pointR1
   210  	k := sha512.Sum512(seed)
   211  	clamp(k[:])
   212  	reduceModOrder(k[:paramB], false)
   213  	P.fixedMult(k[:paramB])
   214  	copy(privateKey[:SeedSize], seed)
   215  	_ = P.ToBytes(privateKey[SeedSize:])
   216  }
   217  
   218  func signAll(signature []byte, privateKey PrivateKey, message, ctx []byte, preHash bool) {
   219  	if l := len(privateKey); l != PrivateKeySize {
   220  		panic("ed25519: bad private key length: " + strconv.Itoa(l))
   221  	}
   222  
   223  	H := sha512.New()
   224  	var PHM []byte
   225  
   226  	if preHash {
   227  		_, _ = H.Write(message)
   228  		PHM = H.Sum(nil)
   229  		H.Reset()
   230  	} else {
   231  		PHM = message
   232  	}
   233  
   234  	// 1.  Hash the 32-byte private key using SHA-512.
   235  	_, _ = H.Write(privateKey[:SeedSize])
   236  	h := H.Sum(nil)
   237  	clamp(h[:])
   238  	prefix, s := h[paramB:], h[:paramB]
   239  
   240  	// 2.  Compute SHA-512(dom2(F, C) || prefix || PH(M))
   241  	H.Reset()
   242  
   243  	writeDom(H, ctx, preHash)
   244  
   245  	_, _ = H.Write(prefix)
   246  	_, _ = H.Write(PHM)
   247  	r := H.Sum(nil)
   248  	reduceModOrder(r[:], true)
   249  
   250  	// 3.  Compute the point [r]B.
   251  	var P pointR1
   252  	P.fixedMult(r[:paramB])
   253  	R := (&[paramB]byte{})[:]
   254  	if err := P.ToBytes(R); err != nil {
   255  		panic(err)
   256  	}
   257  
   258  	// 4.  Compute SHA512(dom2(F, C) || R || A || PH(M)).
   259  	H.Reset()
   260  
   261  	writeDom(H, ctx, preHash)
   262  
   263  	_, _ = H.Write(R)
   264  	_, _ = H.Write(privateKey[SeedSize:])
   265  	_, _ = H.Write(PHM)
   266  	hRAM := H.Sum(nil)
   267  
   268  	reduceModOrder(hRAM[:], true)
   269  
   270  	// 5.  Compute S = (r + k * s) mod order.
   271  	S := (&[paramB]byte{})[:]
   272  	calculateS(S, r[:paramB], hRAM[:paramB], s)
   273  
   274  	// 6.  The signature is the concatenation of R and S.
   275  	copy(signature[:paramB], R[:])
   276  	copy(signature[paramB:], S[:])
   277  }
   278  
   279  // Sign signs the message with privateKey and returns a signature.
   280  // This function supports the signature variant defined in RFC-8032: Ed25519,
   281  // also known as the pure version of EdDSA.
   282  // It will panic if len(privateKey) is not PrivateKeySize.
   283  func Sign(privateKey PrivateKey, message []byte) []byte {
   284  	signature := make([]byte, SignatureSize)
   285  	signAll(signature, privateKey, message, []byte(""), false)
   286  	return signature
   287  }
   288  
   289  // SignPh creates a signature of a message with private key and context.
   290  // This function supports the signature variant defined in RFC-8032: Ed25519ph,
   291  // meaning it internally hashes the message using SHA-512, and optionally
   292  // accepts a context string.
   293  // It will panic if len(privateKey) is not PrivateKeySize.
   294  // Context could be passed to this function, which length should be no more than
   295  // ContextMaxSize=255. It can be empty.
   296  func SignPh(privateKey PrivateKey, message []byte, ctx string) []byte {
   297  	if len(ctx) > ContextMaxSize {
   298  		panic(fmt.Errorf("ed25519: bad context length: %v", len(ctx)))
   299  	}
   300  
   301  	signature := make([]byte, SignatureSize)
   302  	signAll(signature, privateKey, message, []byte(ctx), true)
   303  	return signature
   304  }
   305  
   306  // SignWithCtx creates a signature of a message with private key and context.
   307  // This function supports the signature variant defined in RFC-8032: Ed25519ctx,
   308  // meaning it accepts a non-empty context string.
   309  // It will panic if len(privateKey) is not PrivateKeySize.
   310  // Context must be passed to this function, which length should be no more than
   311  // ContextMaxSize=255 and cannot be empty.
   312  func SignWithCtx(privateKey PrivateKey, message []byte, ctx string) []byte {
   313  	if len(ctx) == 0 || len(ctx) > ContextMaxSize {
   314  		panic(fmt.Errorf("ed25519: bad context length: %v > %v", len(ctx), ContextMaxSize))
   315  	}
   316  
   317  	signature := make([]byte, SignatureSize)
   318  	signAll(signature, privateKey, message, []byte(ctx), false)
   319  	return signature
   320  }
   321  
   322  func verify(public PublicKey, message, signature, ctx []byte, preHash bool) bool {
   323  	if len(public) != PublicKeySize ||
   324  		len(signature) != SignatureSize ||
   325  		!isLessThanOrder(signature[paramB:]) {
   326  		return false
   327  	}
   328  
   329  	var P pointR1
   330  	if ok := P.FromBytes(public); !ok {
   331  		return false
   332  	}
   333  
   334  	H := sha512.New()
   335  	var PHM []byte
   336  
   337  	if preHash {
   338  		_, _ = H.Write(message)
   339  		PHM = H.Sum(nil)
   340  		H.Reset()
   341  	} else {
   342  		PHM = message
   343  	}
   344  
   345  	R := signature[:paramB]
   346  
   347  	writeDom(H, ctx, preHash)
   348  
   349  	_, _ = H.Write(R)
   350  	_, _ = H.Write(public)
   351  	_, _ = H.Write(PHM)
   352  	hRAM := H.Sum(nil)
   353  	reduceModOrder(hRAM[:], true)
   354  
   355  	var Q pointR1
   356  	encR := (&[paramB]byte{})[:]
   357  	P.neg()
   358  	Q.doubleMult(&P, signature[paramB:], hRAM[:paramB])
   359  	_ = Q.ToBytes(encR)
   360  	return bytes.Equal(R, encR)
   361  }
   362  
   363  // VerifyAny returns true if the signature is valid. Failure cases are invalid
   364  // signature, or when the public key cannot be decoded.
   365  // This function supports all the three signature variants defined in RFC-8032,
   366  // namely Ed25519 (or pure EdDSA), Ed25519Ph, and Ed25519Ctx.
   367  // The opts.HashFunc() must return zero to specify either Ed25519 or Ed25519Ctx
   368  // variant. This can be achieved by passing crypto.Hash(0) as the value for opts.
   369  // The opts.HashFunc() must return SHA512 to specify the Ed25519Ph variant.
   370  // This can be achieved by passing crypto.SHA512 as the value for opts.
   371  // Use a SignerOptions struct to pass a context string for signing.
   372  func VerifyAny(public PublicKey, message, signature []byte, opts crypto.SignerOpts) bool {
   373  	var ctx string
   374  	var scheme SchemeID
   375  	if o, ok := opts.(SignerOptions); ok {
   376  		ctx = o.Context
   377  		scheme = o.Scheme
   378  	}
   379  
   380  	switch true {
   381  	case scheme == ED25519 && opts.HashFunc() == crypto.Hash(0):
   382  		return Verify(public, message, signature)
   383  	case scheme == ED25519Ph && opts.HashFunc() == crypto.SHA512:
   384  		return VerifyPh(public, message, signature, ctx)
   385  	case scheme == ED25519Ctx && opts.HashFunc() == crypto.Hash(0) && len(ctx) > 0:
   386  		return VerifyWithCtx(public, message, signature, ctx)
   387  	default:
   388  		return false
   389  	}
   390  }
   391  
   392  // Verify returns true if the signature is valid. Failure cases are invalid
   393  // signature, or when the public key cannot be decoded.
   394  // This function supports the signature variant defined in RFC-8032: Ed25519,
   395  // also known as the pure version of EdDSA.
   396  func Verify(public PublicKey, message, signature []byte) bool {
   397  	return verify(public, message, signature, []byte(""), false)
   398  }
   399  
   400  // VerifyPh returns true if the signature is valid. Failure cases are invalid
   401  // signature, or when the public key cannot be decoded.
   402  // This function supports the signature variant defined in RFC-8032: Ed25519ph,
   403  // meaning it internally hashes the message using SHA-512.
   404  // Context could be passed to this function, which length should be no more than
   405  // 255. It can be empty.
   406  func VerifyPh(public PublicKey, message, signature []byte, ctx string) bool {
   407  	return verify(public, message, signature, []byte(ctx), true)
   408  }
   409  
   410  // VerifyWithCtx returns true if the signature is valid. Failure cases are invalid
   411  // signature, or when the public key cannot be decoded, or when context is
   412  // not provided.
   413  // This function supports the signature variant defined in RFC-8032: Ed25519ctx,
   414  // meaning it does not handle prehashed messages. Non-empty context string must be
   415  // provided, and must not be more than 255 of length.
   416  func VerifyWithCtx(public PublicKey, message, signature []byte, ctx string) bool {
   417  	if len(ctx) == 0 || len(ctx) > ContextMaxSize {
   418  		return false
   419  	}
   420  
   421  	return verify(public, message, signature, []byte(ctx), false)
   422  }
   423  
   424  func clamp(k []byte) {
   425  	k[0] &= 248
   426  	k[paramB-1] = (k[paramB-1] & 127) | 64
   427  }
   428  
   429  // isLessThanOrder returns true if 0 <= x < order.
   430  func isLessThanOrder(x []byte) bool {
   431  	i := len(order) - 1
   432  	for i > 0 && x[i] == order[i] {
   433  		i--
   434  	}
   435  	return x[i] < order[i]
   436  }
   437  
   438  func writeDom(h io.Writer, ctx []byte, preHash bool) {
   439  	dom2 := "SigEd25519 no Ed25519 collisions"
   440  
   441  	if len(ctx) > 0 {
   442  		_, _ = h.Write([]byte(dom2))
   443  		if preHash {
   444  			_, _ = h.Write([]byte{byte(0x01), byte(len(ctx))})
   445  		} else {
   446  			_, _ = h.Write([]byte{byte(0x00), byte(len(ctx))})
   447  		}
   448  		_, _ = h.Write(ctx)
   449  	} else if preHash {
   450  		_, _ = h.Write([]byte(dom2))
   451  		_, _ = h.Write([]byte{0x01, 0x00})
   452  	}
   453  }