github.com/cloudflare/circl@v1.5.0/kem/frodo/frodo640shake/frodo.go (about)

     1  // Package frodo640shake implements the variant FrodoKEM-640 with SHAKE.
     2  package frodo640shake
     3  
     4  import (
     5  	"bytes"
     6  	cryptoRand "crypto/rand"
     7  	"crypto/subtle"
     8  	"io"
     9  
    10  	"github.com/cloudflare/circl/internal/sha3"
    11  	"github.com/cloudflare/circl/kem"
    12  )
    13  
    14  const (
    15  	paramN = 640
    16  
    17  	// Denoted by 'mbar' in the FrodoKEM spec.
    18  	paramNbar = 8
    19  
    20  	logQ       = 15
    21  	logQMask   = ((1 << logQ) - 1)
    22  	seedASize  = 16
    23  	pkHashSize = 16
    24  
    25  	// Denoted by 'B' in the FrodoKEM spec.
    26  	extractedBits = 2
    27  
    28  	messageSize        = 16
    29  	matrixBpPackedSize = (logQ * (paramN * paramNbar)) / 8
    30  )
    31  
    32  const (
    33  	// Size of seed for NewKeyFromSeed.
    34  	// = len(s) + len(seedSE) + len(z).
    35  	KeySeedSize = SharedKeySize + SharedKeySize + 16
    36  
    37  	// Size of seed for EncapsulateTo.
    38  	EncapsulationSeedSize = 16
    39  
    40  	// Size of the established shared key.
    41  	SharedKeySize = 16
    42  
    43  	// Size of the encapsulated shared key.
    44  	CiphertextSize = 9720
    45  
    46  	// Size of a packed public key.
    47  	PublicKeySize = 9616
    48  
    49  	// Size of a packed private key.
    50  	PrivateKeySize = 19888
    51  )
    52  
    53  // Multi-dimensional arrays are stored in 1-dimensional arrays in
    54  // row-major order.
    55  type (
    56  	nByNU16       [paramN * paramN]uint16
    57  	nByNbarU16    [paramN * paramNbar]uint16
    58  	nbarByNU16    [paramNbar * paramN]uint16
    59  	nbarByNbarU16 [paramNbar * paramNbar]uint16
    60  )
    61  
    62  // Type of a FrodoKEM-640-SHAKE public key
    63  type PublicKey struct {
    64  	seedA   [seedASize]byte
    65  	matrixB nByNbarU16
    66  }
    67  
    68  // Type of a FrodoKEM-640-SHAKE private key
    69  type PrivateKey struct {
    70  	hashInputIfDecapsFail [SharedKeySize]byte
    71  	pk                    *PublicKey
    72  
    73  	// matrixS stores transpose(S)
    74  	matrixS nByNbarU16
    75  
    76  	// H(packed(pk))
    77  	hpk [pkHashSize]byte
    78  }
    79  
    80  // NewKeyFromSeed derives a public/private keypair deterministically
    81  // from the given seed.
    82  //
    83  // Panics if seed is not of length KeySeedSize.
    84  func newKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) {
    85  	if len(seed) != KeySeedSize {
    86  		panic("seed must be of length KeySeedSize")
    87  	}
    88  
    89  	var sk PrivateKey
    90  	var pk PublicKey
    91  
    92  	var E nByNbarU16
    93  	var byteSE [2 * (len(sk.matrixS) + len(E))]byte
    94  
    95  	var A nByNU16
    96  
    97  	// Generate the secret value s, and the seed for S, E, and A. Add seedA to the public key
    98  	shake128 := sha3.NewShake128()
    99  	_, _ = shake128.Write(seed[2*SharedKeySize:])
   100  	_, _ = shake128.Read(pk.seedA[:])
   101  
   102  	shake128.Reset()
   103  	_, _ = shake128.Write([]byte{0x5F})
   104  	_, _ = shake128.Write(seed[SharedKeySize : 2*SharedKeySize])
   105  	_, _ = shake128.Read(byteSE[:])
   106  
   107  	i := 0
   108  	for i < len(sk.matrixS) {
   109  		sk.matrixS[i] = uint16(byteSE[i*2]) | (uint16(byteSE[(i*2)+1]) << 8)
   110  		i++
   111  	}
   112  	sample(sk.matrixS[:])
   113  
   114  	for j := range E {
   115  		E[j] = uint16(byteSE[i*2]) | (uint16(byteSE[(i*2)+1]) << 8)
   116  		i++
   117  	}
   118  	sample(E[:])
   119  
   120  	expandSeedIntoA(&A, &pk.seedA, &shake128)
   121  	mulAddASPlusE(&pk.matrixB, &A, &sk.matrixS, &E)
   122  
   123  	// Populate the private key
   124  	copy(sk.hashInputIfDecapsFail[:], seed[0:SharedKeySize])
   125  	sk.pk = &pk
   126  
   127  	// Add H(pk) to the private key
   128  	shake128.Reset()
   129  	var ppk [PublicKeySize]byte
   130  	pk.Pack(ppk[:])
   131  	_, _ = shake128.Write(ppk[:])
   132  	_, _ = shake128.Read(sk.hpk[:])
   133  
   134  	return &pk, &sk
   135  }
   136  
   137  // GenerateKeyPair generates public and private keys using entropy from rand.
   138  // If rand is nil, crypto/rand.Reader will be used.
   139  func generateKeyPair(rand io.Reader) (*PublicKey, *PrivateKey, error) {
   140  	var seed [KeySeedSize]byte
   141  	if rand == nil {
   142  		rand = cryptoRand.Reader
   143  	}
   144  	_, err := io.ReadFull(rand, seed[:])
   145  	if err != nil {
   146  		return nil, nil, err
   147  	}
   148  	pk, sk := newKeyFromSeed(seed[:])
   149  	return pk, sk, err
   150  }
   151  
   152  // EncapsulateTo generates a shared key and a ciphertext containing said key
   153  // from the public key and the randomness from seed and writes the shared key
   154  // to ss and ciphertext to ct.
   155  //
   156  // Panics if ss, ct, or seed are not of length SharedKeySize, CiphertextSize
   157  // and EncapsulationSeedSize respectively.
   158  //
   159  // seed may be nil, in which case crypto/rand.Reader is used to generate one.
   160  func (pk *PublicKey) EncapsulateTo(ct []byte, ss []byte, seed []byte) {
   161  	if seed == nil {
   162  		seed = make([]byte, EncapsulationSeedSize)
   163  		if _, err := cryptoRand.Read(seed[:]); err != nil {
   164  			panic(err)
   165  		}
   166  	}
   167  	if len(seed) != EncapsulationSeedSize {
   168  		panic("seed must be of length EncapsulationSeedSize")
   169  	}
   170  	if len(ct) != CiphertextSize {
   171  		panic("ct must be of length CiphertextSize")
   172  	}
   173  	if len(ss) != SharedKeySize {
   174  		panic("ss must be of length SharedKeySize")
   175  	}
   176  
   177  	var G2out [2 * SharedKeySize]byte
   178  
   179  	var SpEpEpp [(paramN * paramNbar) + (paramN * paramNbar) + (paramNbar * paramNbar)]uint16
   180  	var byteSpEpEpp [2 * len(SpEpEpp)]byte
   181  	Sp := SpEpEpp[:paramN*paramNbar]
   182  	Ep := SpEpEpp[paramN*paramNbar : 2*paramN*paramNbar]
   183  	Epp := SpEpEpp[2*paramN*paramNbar:]
   184  
   185  	var Bp nbarByNU16
   186  
   187  	var V nbarByNbarU16
   188  	var C nbarByNbarU16
   189  
   190  	var A nByNU16
   191  
   192  	var hpk [pkHashSize]byte
   193  
   194  	var mu [messageSize]byte
   195  	copy(mu[:], seed[:messageSize])
   196  
   197  	// compute hpk = G_1(packed(pk))
   198  	shake128 := sha3.NewShake128()
   199  	var ppk [PublicKeySize]byte
   200  	pk.Pack(ppk[:])
   201  	_, _ = shake128.Write(ppk[:])
   202  	_, _ = shake128.Read(hpk[:])
   203  
   204  	// compute (seedSE || k) = G_2(hpk || mu)
   205  	shake128.Reset()
   206  	_, _ = shake128.Write(hpk[:])
   207  	_, _ = shake128.Write(mu[:])
   208  	_, _ = shake128.Read(G2out[:])
   209  
   210  	// Generate Sp, Ep, Epp, and A, and compute:
   211  	// Bp = Sp*A + Ep
   212  	// V = Sp*B + Epp
   213  	shake128.Reset()
   214  	_, _ = shake128.Write([]byte{0x96})
   215  	_, _ = shake128.Write(G2out[:SharedKeySize])
   216  	_, _ = shake128.Read(byteSpEpEpp[:])
   217  	for i := range SpEpEpp {
   218  		SpEpEpp[i] = uint16(byteSpEpEpp[i*2]) | (uint16(byteSpEpEpp[(i*2)+1]) << 8)
   219  	}
   220  	sample(SpEpEpp[:])
   221  
   222  	expandSeedIntoA(&A, &pk.seedA, &shake128)
   223  	mulAddSAPlusE(&Bp, Sp, &A, Ep)
   224  
   225  	mulAddSBPlusE(&V, Sp, &pk.matrixB, Epp)
   226  
   227  	// Encode mu, and compute C = V + enc(mu) (mod q)
   228  	encodeMessage(&C, &mu)
   229  	add(&C, &V, &C)
   230  
   231  	// Prepare the ciphertext
   232  	pack(ct[:matrixBpPackedSize], Bp[:])
   233  	pack(ct[matrixBpPackedSize:], C[:])
   234  
   235  	// Compute ss = F(ct||k)
   236  	shake128.Reset()
   237  	_, _ = shake128.Write(ct[:])
   238  	_, _ = shake128.Write(G2out[SharedKeySize:])
   239  	_, _ = shake128.Read(ss[:])
   240  }
   241  
   242  // DecapsulateTo computes the shared key that is encapsulated in ct
   243  // from the private key.
   244  //
   245  // Panics if ct or ss are not of length CiphertextSize and SharedKeySize
   246  // respectively.
   247  func (sk *PrivateKey) DecapsulateTo(ss, ct []byte) {
   248  	if len(ct) != CiphertextSize {
   249  		panic("ct must be of length CiphertextSize")
   250  	}
   251  	if len(ss) != SharedKeySize {
   252  		panic("ss must be of length SharedKeySize")
   253  	}
   254  
   255  	var Bp nbarByNU16
   256  	var C nbarByNbarU16
   257  
   258  	var W nbarByNbarU16
   259  	var CC nbarByNbarU16
   260  	var BBp nbarByNU16
   261  
   262  	var SpEpEpp [(paramN * paramNbar) + (paramN * paramNbar) + (paramNbar * paramNbar)]uint16
   263  	var byteSpEpEpp [2 * len(SpEpEpp)]byte
   264  	Sp := SpEpEpp[:paramN*paramNbar]
   265  	Ep := SpEpEpp[paramN*paramNbar : 2*paramN*paramNbar]
   266  	Epp := SpEpEpp[2*paramN*paramNbar:]
   267  
   268  	var A nByNU16
   269  
   270  	var muprime [messageSize]byte
   271  	var G2out [2 * SharedKeySize]byte
   272  
   273  	kprime := G2out[SharedKeySize:]
   274  
   275  	// Compute W = C - Bp*S (mod q), and decode the randomness mu
   276  	unpack(Bp[:], ct[0:matrixBpPackedSize])
   277  	unpack(C[:], ct[matrixBpPackedSize:])
   278  	mulBS(&W, &Bp, &sk.matrixS)
   279  	sub(&W, &C, &W)
   280  
   281  	decodeMessage(&muprime, &W)
   282  
   283  	// Generate (seedSE' || k') = G_2(hpk || mu')
   284  	shake128 := sha3.NewShake128()
   285  	_, _ = shake128.Write(sk.hpk[:])
   286  	_, _ = shake128.Write(muprime[:])
   287  	_, _ = shake128.Read(G2out[:])
   288  
   289  	// Generate Sp, Ep, Epp, A, and compute BBp = Sp*A + Ep.
   290  	shake128.Reset()
   291  	_, _ = shake128.Write([]byte{0x96})
   292  	_, _ = shake128.Write(G2out[:SharedKeySize])
   293  	_, _ = shake128.Read(byteSpEpEpp[:])
   294  	for i := range SpEpEpp {
   295  		SpEpEpp[i] = uint16(byteSpEpEpp[i*2]) | (uint16(byteSpEpEpp[(i*2)+1]) << 8)
   296  	}
   297  
   298  	sample(SpEpEpp[:])
   299  
   300  	expandSeedIntoA(&A, &sk.pk.seedA, &shake128)
   301  	mulAddSAPlusE(&BBp, Sp[:], &A, Ep[:])
   302  
   303  	// Reduce BBp modulo q
   304  	for i := range BBp {
   305  		BBp[i] = BBp[i] & logQMask
   306  	}
   307  
   308  	// compute W = Sp*B + Epp
   309  	mulAddSBPlusE(&W, Sp, &sk.pk.matrixB, Epp)
   310  
   311  	// Encode mu, and compute CC = W + enc(mu') (mod q)
   312  	encodeMessage(&CC, &muprime)
   313  	add(&CC, &W, &CC)
   314  
   315  	// Prepare input to F
   316  
   317  	// If (Bp == BBp & C == CC) then ss = F(ct || k'), else ss = F(ct || s)
   318  	// Needs to avoid branching on secret data as per:
   319  	//     Qian Guo, Thomas Johansson, Alexander Nilsson. A key-recovery timing attack on post-quantum
   320  	//     primitives using the Fujisaki-Okamoto transformation and its application on FrodoKEM. In CRYPTO 2020.
   321  	selector := ctCompareU16(Bp[:], BBp[:]) | ctCompareU16(C[:], CC[:])
   322  	// If (selector == 0) then load k' to do ss = F(ct || k'), else if (selector == 1) load s to do ss = F(ct || s)
   323  	subtle.ConstantTimeCopy(selector, kprime[:], sk.hashInputIfDecapsFail[:])
   324  
   325  	shake128.Reset()
   326  	_, _ = shake128.Write(ct[:])
   327  	_, _ = shake128.Write(kprime[:])
   328  	_, _ = shake128.Read(ss[:])
   329  }
   330  
   331  // Packs sk to buf.
   332  //
   333  // Panics if buf is not of size PrivateKeySize.
   334  func (sk *PrivateKey) Pack(buf []byte) {
   335  	if len(buf) != PrivateKeySize {
   336  		panic("buf must be of length PrivateKeySize")
   337  	}
   338  
   339  	copy(buf[:SharedKeySize], sk.hashInputIfDecapsFail[:])
   340  	buf = buf[SharedKeySize:]
   341  
   342  	sk.pk.Pack(buf[:PublicKeySize])
   343  	buf = buf[PublicKeySize:]
   344  
   345  	j := 0
   346  	for i := range sk.matrixS {
   347  		buf[j] = byte(sk.matrixS[i])
   348  		buf[j+1] = byte(sk.matrixS[i] >> 8)
   349  		j += 2
   350  	}
   351  	buf = buf[j:]
   352  
   353  	copy(buf[:], sk.hpk[:])
   354  }
   355  
   356  // Unpacks sk from buf.
   357  //
   358  // Panics if buf is not of size PrivateKeySize.
   359  func (sk *PrivateKey) Unpack(buf []byte) {
   360  	if len(buf) != PrivateKeySize {
   361  		panic("buf must be of length PrivateKeySize")
   362  	}
   363  
   364  	copy(sk.hashInputIfDecapsFail[:], buf[:SharedKeySize])
   365  	buf = buf[SharedKeySize:]
   366  
   367  	sk.pk = new(PublicKey)
   368  	sk.pk.Unpack(buf[:PublicKeySize])
   369  	buf = buf[PublicKeySize:]
   370  
   371  	for i := range sk.matrixS {
   372  		sk.matrixS[i] = uint16(buf[i*2]) | (uint16(buf[(i*2)+1]) << 8)
   373  	}
   374  	buf = buf[len(sk.matrixS)*2:]
   375  
   376  	copy(sk.hpk[:], buf[:])
   377  }
   378  
   379  // Packs pk to buf.
   380  //
   381  // Panics if buf is not of size PublicKeySize.
   382  func (pk *PublicKey) Pack(buf []byte) {
   383  	if len(buf) != PublicKeySize {
   384  		panic("buf must be of length PublicKeySize")
   385  	}
   386  
   387  	copy(buf[:seedASize], pk.seedA[:])
   388  	pack(buf[seedASize:], pk.matrixB[:])
   389  }
   390  
   391  // TODO: Unpacks pk from buf.
   392  //
   393  // Panics if buf is not of size PublicKeySize.
   394  func (pk *PublicKey) Unpack(buf []byte) {
   395  	if len(buf) != PublicKeySize {
   396  		panic("buf must be of length PublicKeySize")
   397  	}
   398  
   399  	copy(pk.seedA[:], buf[:seedASize])
   400  	unpack(pk.matrixB[:], buf[seedASize:])
   401  }
   402  
   403  // Boilerplate down below for the KEM scheme API.
   404  
   405  type scheme struct{}
   406  
   407  var sch kem.Scheme = &scheme{}
   408  
   409  // Scheme returns a KEM interface.
   410  func Scheme() kem.Scheme { return sch }
   411  
   412  func (scheme) Name() string                { return "FrodoKEM-640-SHAKE" }
   413  func (*scheme) PublicKeySize() int         { return PublicKeySize }
   414  func (*scheme) PrivateKeySize() int        { return PrivateKeySize }
   415  func (*scheme) SeedSize() int              { return KeySeedSize }
   416  func (*scheme) SharedKeySize() int         { return SharedKeySize }
   417  func (*scheme) CiphertextSize() int        { return CiphertextSize }
   418  func (*scheme) EncapsulationSeedSize() int { return EncapsulationSeedSize }
   419  
   420  func (sk *PrivateKey) Scheme() kem.Scheme { return sch }
   421  func (pk *PublicKey) Scheme() kem.Scheme  { return sch }
   422  
   423  func (sk *PrivateKey) MarshalBinary() ([]byte, error) {
   424  	var ret [PrivateKeySize]byte
   425  	sk.Pack(ret[:])
   426  	return ret[:], nil
   427  }
   428  
   429  func (sk *PrivateKey) Equal(other kem.PrivateKey) bool {
   430  	oth, ok := other.(*PrivateKey)
   431  	if !ok {
   432  		return false
   433  	}
   434  	if sk.pk == nil && oth.pk == nil {
   435  		return true
   436  	}
   437  	if sk.pk == nil || oth.pk == nil {
   438  		return false
   439  	}
   440  	return ctCompareU16(sk.matrixS[:], oth.matrixS[:]) == 0 &&
   441  		subtle.ConstantTimeCompare(sk.hashInputIfDecapsFail[:], oth.hashInputIfDecapsFail[:]) == 1 &&
   442  		sk.pk.Equal(oth.pk) &&
   443  		bytes.Equal(sk.hpk[:], oth.hpk[:])
   444  }
   445  
   446  func (pk *PublicKey) Equal(other kem.PublicKey) bool {
   447  	oth, ok := other.(*PublicKey)
   448  	if !ok {
   449  		return false
   450  	}
   451  	if pk == nil && oth == nil {
   452  		return true
   453  	}
   454  	if pk == nil || oth == nil {
   455  		return false
   456  	}
   457  
   458  	for i := range pk.matrixB {
   459  		if (pk.matrixB[i] & logQMask) != (oth.matrixB[i] & logQMask) {
   460  			return false
   461  		}
   462  	}
   463  	return bytes.Equal(pk.seedA[:], oth.seedA[:])
   464  }
   465  
   466  func (sk *PrivateKey) Public() kem.PublicKey {
   467  	return sk.pk
   468  }
   469  
   470  func (pk *PublicKey) MarshalBinary() ([]byte, error) {
   471  	var ret [PublicKeySize]byte
   472  	pk.Pack(ret[:])
   473  	return ret[:], nil
   474  }
   475  
   476  func (*scheme) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) {
   477  	return generateKeyPair(cryptoRand.Reader)
   478  }
   479  
   480  func (*scheme) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) {
   481  	if len(seed) != KeySeedSize {
   482  		panic(kem.ErrSeedSize)
   483  	}
   484  	return newKeyFromSeed(seed[:])
   485  }
   486  
   487  func (*scheme) Encapsulate(pk kem.PublicKey) (ct, ss []byte, err error) {
   488  	ct = make([]byte, CiphertextSize)
   489  	ss = make([]byte, SharedKeySize)
   490  
   491  	pub, ok := pk.(*PublicKey)
   492  	if !ok {
   493  		return nil, nil, kem.ErrTypeMismatch
   494  	}
   495  	pub.EncapsulateTo(ct, ss, nil)
   496  	return
   497  }
   498  
   499  func (*scheme) EncapsulateDeterministically(
   500  	pk kem.PublicKey, seed []byte,
   501  ) (ct, ss []byte, err error) {
   502  	if len(seed) != EncapsulationSeedSize {
   503  		return nil, nil, kem.ErrSeedSize
   504  	}
   505  
   506  	ct = make([]byte, CiphertextSize)
   507  	ss = make([]byte, SharedKeySize)
   508  
   509  	pub, ok := pk.(*PublicKey)
   510  	if !ok {
   511  		return nil, nil, kem.ErrTypeMismatch
   512  	}
   513  	pub.EncapsulateTo(ct, ss, seed)
   514  	return
   515  }
   516  
   517  func (*scheme) Decapsulate(sk kem.PrivateKey, ct []byte) ([]byte, error) {
   518  	if len(ct) != CiphertextSize {
   519  		return nil, kem.ErrCiphertextSize
   520  	}
   521  
   522  	priv, ok := sk.(*PrivateKey)
   523  	if !ok {
   524  		return nil, kem.ErrTypeMismatch
   525  	}
   526  	ss := make([]byte, SharedKeySize)
   527  	priv.DecapsulateTo(ss, ct)
   528  	return ss, nil
   529  }
   530  
   531  func (*scheme) UnmarshalBinaryPublicKey(buf []byte) (kem.PublicKey, error) {
   532  	if len(buf) != PublicKeySize {
   533  		return nil, kem.ErrPubKeySize
   534  	}
   535  	var ret PublicKey
   536  	ret.Unpack(buf)
   537  	return &ret, nil
   538  }
   539  
   540  func (*scheme) UnmarshalBinaryPrivateKey(buf []byte) (kem.PrivateKey, error) {
   541  	if len(buf) != PrivateKeySize {
   542  		return nil, kem.ErrPrivKeySize
   543  	}
   544  	var ret PrivateKey
   545  	ret.Unpack(buf)
   546  	return &ret, nil
   547  }