Source file src/crypto/mldsa/mldsa_fips140v1.26.go

     1  // Copyright 2026 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build !fips140v1.0
     6  
     7  package mldsa
     8  
     9  import (
    10  	"crypto"
    11  	"crypto/internal/fips140/mldsa"
    12  	"errors"
    13  	"io"
    14  )
    15  
    16  // PrivateKey is an in-memory ML-DSA private key. It implements [crypto.Signer]
    17  // and the informal extended [crypto.PrivateKey] interface.
    18  //
    19  // A PrivateKey is safe for concurrent use.
    20  type PrivateKey struct {
    21  	k mldsa.PrivateKey
    22  }
    23  
    24  var errInvalidParameters = errors.New("mldsa: invalid parameters")
    25  
    26  // GenerateKey generates a new random ML-DSA private key.
    27  func GenerateKey(params Parameters) (*PrivateKey, error) {
    28  	switch params {
    29  	case MLDSA44():
    30  		return &PrivateKey{k: *mldsa.GenerateKey44()}, nil
    31  	case MLDSA65():
    32  		return &PrivateKey{k: *mldsa.GenerateKey65()}, nil
    33  	case MLDSA87():
    34  		return &PrivateKey{k: *mldsa.GenerateKey87()}, nil
    35  	default:
    36  		return nil, errInvalidParameters
    37  	}
    38  }
    39  
    40  // NewPrivateKey decodes an ML-DSA private key from the given seed.
    41  //
    42  // The seed must be exactly [PrivateKeySize] bytes long.
    43  func NewPrivateKey(params Parameters, seed []byte) (*PrivateKey, error) {
    44  	var err error
    45  	var k *mldsa.PrivateKey
    46  	switch params {
    47  	case MLDSA44():
    48  		k, err = mldsa.NewPrivateKey44(seed)
    49  	case MLDSA65():
    50  		k, err = mldsa.NewPrivateKey65(seed)
    51  	case MLDSA87():
    52  		k, err = mldsa.NewPrivateKey87(seed)
    53  	default:
    54  		return nil, errInvalidParameters
    55  	}
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	return &PrivateKey{k: *k}, nil
    60  }
    61  
    62  // Public returns the corresponding [PublicKey] for this private key.
    63  //
    64  // It implements the [crypto.Signer] interface.
    65  func (sk *PrivateKey) Public() crypto.PublicKey {
    66  	return sk.PublicKey()
    67  }
    68  
    69  // Equal reports whether sk and x are the same key (i.e. they are derived from
    70  // the same seed).
    71  //
    72  // If x is not a *PrivateKey, Equal returns false.
    73  func (sk *PrivateKey) Equal(x crypto.PrivateKey) bool {
    74  	other, ok := x.(*PrivateKey)
    75  	if !ok || other == nil {
    76  		return false
    77  	}
    78  	return sk.k.Equal(&other.k)
    79  }
    80  
    81  // PublicKey returns the corresponding [PublicKey] for this private key.
    82  func (sk *PrivateKey) PublicKey() *PublicKey {
    83  	// Making a copy severs the pointer relationship between the private and
    84  	// public keys, so that keeping the public key around doesn't keep the
    85  	// private key alive. This costs a copy and an allocation.
    86  	return &PublicKey{p: *sk.k.PublicKey()}
    87  }
    88  
    89  // Bytes returns the private key seed.
    90  func (sk *PrivateKey) Bytes() []byte {
    91  	return sk.k.Bytes()
    92  }
    93  
    94  var errInvalidSignerOpts = errors.New("mldsa: invalid SignerOpts")
    95  
    96  // Sign returns a signature of the given message using this private key.
    97  //
    98  // If opts is nil or opts.HashFunc returns zero, the message is signed directly.
    99  // If opts.HashFunc returns [crypto.MLDSAMu], the provided message must be a
   100  // [pre-hashed μ message representative]. opts can be of type *[Options] if a
   101  // context string is desired along with a directly-signed message. The io.Reader
   102  // argument is ignored.
   103  //
   104  // [pre-hashed μ message representative]: https://www.rfc-editor.org/rfc/rfc9881.html#externalmu
   105  func (sk *PrivateKey) Sign(_ io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
   106  	if sk.k == (mldsa.PrivateKey{}) {
   107  		return nil, errors.New("mldsa: zero private key")
   108  	}
   109  	if opts == nil {
   110  		opts = &Options{}
   111  	}
   112  	switch opts.HashFunc() {
   113  	case 0:
   114  		var context string
   115  		if opts, ok := opts.(*Options); ok && opts != nil {
   116  			context = opts.Context
   117  		}
   118  		return mldsa.Sign(&sk.k, message, context)
   119  	case crypto.MLDSAMu:
   120  		return mldsa.SignExternalMu(&sk.k, message)
   121  	default:
   122  		return nil, errInvalidSignerOpts
   123  	}
   124  }
   125  
   126  // SignDeterministic works like [PrivateKey.Sign], but the signature is
   127  // deterministic.
   128  func (sk *PrivateKey) SignDeterministic(message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
   129  	if sk.k == (mldsa.PrivateKey{}) {
   130  		return nil, errors.New("mldsa: zero private key")
   131  	}
   132  	if opts == nil {
   133  		opts = &Options{}
   134  	}
   135  	switch opts.HashFunc() {
   136  	case 0:
   137  		var context string
   138  		if opts, ok := opts.(*Options); ok && opts != nil {
   139  			context = opts.Context
   140  		}
   141  		return mldsa.SignDeterministic(&sk.k, message, context)
   142  	case crypto.MLDSAMu:
   143  		return mldsa.SignExternalMuDeterministic(&sk.k, message)
   144  	default:
   145  		return nil, errInvalidSignerOpts
   146  	}
   147  }
   148  
   149  // PublicKey is an ML-DSA public key. It implements the informal extended
   150  // [crypto.PublicKey] interface.
   151  //
   152  // A PublicKey is safe for concurrent use.
   153  type PublicKey struct {
   154  	p mldsa.PublicKey
   155  }
   156  
   157  // NewPublicKey creates a new ML-DSA public key from the given encoding.
   158  func NewPublicKey(params Parameters, encoding []byte) (*PublicKey, error) {
   159  	return newPublicKey(&PublicKey{}, params, encoding)
   160  }
   161  
   162  func newPublicKey(pub *PublicKey, params Parameters, encoding []byte) (*PublicKey, error) {
   163  	var err error
   164  	var pk *mldsa.PublicKey
   165  	switch params {
   166  	case MLDSA44():
   167  		pk, err = mldsa.NewPublicKey44(encoding)
   168  	case MLDSA65():
   169  		pk, err = mldsa.NewPublicKey65(encoding)
   170  	case MLDSA87():
   171  		pk, err = mldsa.NewPublicKey87(encoding)
   172  	default:
   173  		return nil, errInvalidParameters
   174  	}
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	pub.p = *pk
   179  	return pub, nil
   180  }
   181  
   182  // Bytes returns the public key encoding.
   183  func (pk *PublicKey) Bytes() []byte {
   184  	return pk.p.Bytes()
   185  }
   186  
   187  // Equal reports whether pk and x are the same key (i.e. they have the same
   188  // encoding).
   189  //
   190  // If x is not a *PublicKey, Equal returns false.
   191  func (pk *PublicKey) Equal(x crypto.PublicKey) bool {
   192  	other, ok := x.(*PublicKey)
   193  	if !ok || other == nil {
   194  		return false
   195  	}
   196  	return pk.p.Equal(&other.p)
   197  }
   198  
   199  // Parameters returns the parameters associated with this public key.
   200  func (pk *PublicKey) Parameters() Parameters {
   201  	switch pk.p.Parameters() {
   202  	case "ML-DSA-44":
   203  		return MLDSA44()
   204  	case "ML-DSA-65":
   205  		return MLDSA65()
   206  	case "ML-DSA-87":
   207  		return MLDSA87()
   208  	default:
   209  		panic("mldsa: invalid parameters in public key")
   210  	}
   211  }
   212  
   213  // Verify reports whether signature is a valid signature of message by pk.
   214  // If opts is nil, it's equivalent to the zero value of Options.
   215  func Verify(pk *PublicKey, message []byte, signature []byte, opts *Options) error {
   216  	if pk == nil {
   217  		return errors.New("mldsa: nil public key")
   218  	}
   219  	if pk.p == (mldsa.PublicKey{}) {
   220  		return errors.New("mldsa: zero public key")
   221  	}
   222  	if opts == nil {
   223  		opts = &Options{}
   224  	}
   225  	return mldsa.Verify(&pk.p, message, signature, opts.Context)
   226  }
   227  

View as plain text