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

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