github.com/cloudflare/circl@v1.5.0/dh/sidh/sike.go (about)

     1  package sidh
     2  
     3  import (
     4  	"crypto/subtle"
     5  	"errors"
     6  	"io"
     7  
     8  	"github.com/cloudflare/circl/dh/sidh/internal/common"
     9  	"github.com/cloudflare/circl/internal/sha3"
    10  )
    11  
    12  // SIKE KEM interface.
    13  //
    14  // Deprecated: not cryptographically secure.
    15  type KEM struct {
    16  	allocated   bool
    17  	rng         io.Reader
    18  	msg         []byte
    19  	secretBytes []byte
    20  	params      *common.SidhParams
    21  	shake       sha3.State
    22  }
    23  
    24  // NewSike434 instantiates SIKE/p434 KEM.
    25  //
    26  // Deprecated: not cryptographically secure.
    27  func NewSike434(rng io.Reader) *KEM {
    28  	var c KEM
    29  	c.Allocate(Fp434, rng)
    30  	return &c
    31  }
    32  
    33  // NewSike503 instantiates SIKE/p503 KEM.
    34  //
    35  // Deprecated: not cryptographically secure.
    36  func NewSike503(rng io.Reader) *KEM {
    37  	var c KEM
    38  	c.Allocate(Fp503, rng)
    39  	return &c
    40  }
    41  
    42  // NewSike751 instantiates SIKE/p751 KEM.
    43  //
    44  // Deprecated: not cryptographically secure.
    45  func NewSike751(rng io.Reader) *KEM {
    46  	var c KEM
    47  	c.Allocate(Fp751, rng)
    48  	return &c
    49  }
    50  
    51  // Allocate allocates KEM object for multiple SIKE operations. The rng
    52  // must be cryptographically secure PRNG.
    53  func (c *KEM) Allocate(id uint8, rng io.Reader) {
    54  	c.rng = rng
    55  	c.params = common.Params(id)
    56  	c.msg = make([]byte, c.params.MsgLen)
    57  	c.secretBytes = make([]byte, c.params.A.SecretByteLen)
    58  	c.shake = sha3.NewShake256()
    59  	c.allocated = true
    60  }
    61  
    62  // Encapsulate receives the public key and generates SIKE ciphertext and shared secret.
    63  // The generated ciphertext is used for authentication.
    64  // Error is returned in case PRNG fails. Function panics in case wrongly formatted
    65  // input was provided.
    66  func (c *KEM) Encapsulate(ciphertext, secret []byte, pub *PublicKey) error {
    67  	if !c.allocated {
    68  		panic("KEM unallocated")
    69  	}
    70  
    71  	if KeyVariantSike != pub.keyVariant {
    72  		panic("Wrong type of public key")
    73  	}
    74  
    75  	if len(secret) < c.SharedSecretSize() {
    76  		panic("shared secret buffer to small")
    77  	}
    78  
    79  	if len(ciphertext) < c.CiphertextSize() {
    80  		panic("ciphertext buffer to small")
    81  	}
    82  
    83  	// Generate ephemeral value
    84  	_, err := io.ReadFull(c.rng, c.msg[:])
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	var buf [3 * common.MaxSharedSecretBsz]byte
    90  	skA := PrivateKey{
    91  		key: key{
    92  			params:     c.params,
    93  			keyVariant: KeyVariantSidhA,
    94  		},
    95  		Scalar: c.secretBytes,
    96  	}
    97  	pkA := NewPublicKey(c.params.ID, KeyVariantSidhA)
    98  
    99  	pub.Export(buf[:])
   100  	c.shake.Reset()
   101  	_, _ = c.shake.Write(c.msg)
   102  	_, _ = c.shake.Write(buf[:3*c.params.SharedSecretSize])
   103  	_, _ = c.shake.Read(skA.Scalar)
   104  
   105  	// Ensure bitlength is not bigger then to 2^e2-1
   106  	skA.Scalar[len(skA.Scalar)-1] &= (1 << (c.params.A.SecretBitLen % 8)) - 1
   107  	skA.GeneratePublicKey(pkA)
   108  	c.generateCiphertext(ciphertext, &skA, pkA, pub, c.msg[:])
   109  
   110  	// K = H(msg||(c0||c1))
   111  	c.shake.Reset()
   112  	_, _ = c.shake.Write(c.msg)
   113  	_, _ = c.shake.Write(ciphertext)
   114  	_, _ = c.shake.Read(secret[:c.SharedSecretSize()])
   115  	return nil
   116  }
   117  
   118  // Decapsulate given the keypair and ciphertext as inputs, Decapsulate outputs a shared
   119  // secret if plaintext verifies correctly, otherwise function outputs random value.
   120  // Decapsulation may panic in case input is wrongly formatted, in particular, size of
   121  // the 'ciphertext' must be exactly equal to c.CiphertextSize().
   122  func (c *KEM) Decapsulate(secret []byte, prv *PrivateKey, pub *PublicKey, ciphertext []byte) error {
   123  	if !c.allocated {
   124  		panic("KEM unallocated")
   125  	}
   126  
   127  	if KeyVariantSike != pub.keyVariant {
   128  		panic("Wrong type of public key")
   129  	}
   130  
   131  	if pub.keyVariant != prv.keyVariant {
   132  		panic("Public and private key are of different type")
   133  	}
   134  
   135  	if len(secret) < c.SharedSecretSize() {
   136  		panic("shared secret buffer to small")
   137  	}
   138  
   139  	if len(ciphertext) != c.CiphertextSize() {
   140  		panic("ciphertext buffer to small")
   141  	}
   142  
   143  	var m [common.MaxMsgBsz]byte
   144  	var r [common.MaxSidhPrivateKeyBsz]byte
   145  	var pkBytes [3 * common.MaxSharedSecretBsz]byte
   146  	skA := PrivateKey{
   147  		key: key{
   148  			params:     c.params,
   149  			keyVariant: KeyVariantSidhA,
   150  		},
   151  		Scalar: c.secretBytes,
   152  	}
   153  	pkA := NewPublicKey(c.params.ID, KeyVariantSidhA)
   154  	c1Len, err := c.decrypt(m[:], prv, ciphertext)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	// r' = G(m'||pub)
   160  	pub.Export(pkBytes[:])
   161  	c.shake.Reset()
   162  	_, _ = c.shake.Write(m[:c1Len])
   163  	_, _ = c.shake.Write(pkBytes[:3*c.params.SharedSecretSize])
   164  	_, _ = c.shake.Read(r[:c.params.A.SecretByteLen])
   165  	// Ensure bitlength is not bigger than 2^e2-1
   166  	r[c.params.A.SecretByteLen-1] &= (1 << (c.params.A.SecretBitLen % 8)) - 1
   167  
   168  	err = skA.Import(r[:c.params.A.SecretByteLen])
   169  	if err != nil {
   170  		return err
   171  	}
   172  	skA.GeneratePublicKey(pkA)
   173  	pkA.Export(pkBytes[:])
   174  
   175  	// S is chosen at random when generating a key and unknown to other party. It is
   176  	// important that S is unpredictable to the other party.  Without this check, would
   177  	// be possible to recover a secret, by providing series of invalid ciphertexts.
   178  	//
   179  	// See more details in "On the security of supersingular isogeny cryptosystems"
   180  	// (S. Galbraith, et al., 2016, ePrint #859).
   181  	mask := subtle.ConstantTimeCompare(pkBytes[:c.params.PublicKeySize], ciphertext[:pub.params.PublicKeySize])
   182  	common.Cpick(mask, m[:c1Len], m[:c1Len], prv.S)
   183  	c.shake.Reset()
   184  	_, _ = c.shake.Write(m[:c1Len])
   185  	_, _ = c.shake.Write(ciphertext)
   186  	_, _ = c.shake.Read(secret[:c.SharedSecretSize()])
   187  	return nil
   188  }
   189  
   190  // Resets internal state of KEM. Function should be used
   191  // after Allocate and between subsequent calls to Encapsulate
   192  // and/or Decapsulate.
   193  func (c *KEM) Reset() {
   194  	for i := range c.msg {
   195  		c.msg[i] = 0
   196  	}
   197  
   198  	for i := range c.secretBytes {
   199  		c.secretBytes[i] = 0
   200  	}
   201  }
   202  
   203  // Returns size of resulting ciphertext.
   204  func (c *KEM) CiphertextSize() int {
   205  	return c.params.CiphertextSize
   206  }
   207  
   208  // Returns size of resulting shared secret.
   209  func (c *KEM) SharedSecretSize() int {
   210  	return c.params.KemSize
   211  }
   212  
   213  // PublicKeySize returns size of the public key in bytes.
   214  func (c *KEM) PublicKeySize() int {
   215  	return c.params.PublicKeySize
   216  }
   217  
   218  // Size returns size of the private key in bytes.
   219  func (c *KEM) PrivateKeySize() int {
   220  	return int(c.params.B.SecretByteLen) + c.params.MsgLen
   221  }
   222  
   223  func (c *KEM) generateCiphertext(ctext []byte, skA *PrivateKey, pkA, pkB *PublicKey, ptext []byte) {
   224  	var n [common.MaxMsgBsz]byte
   225  	var j [common.MaxSharedSecretBsz]byte
   226  	ptextLen := skA.params.MsgLen
   227  
   228  	skA.DeriveSecret(j[:], pkB)
   229  	c.shake.Reset()
   230  	_, _ = c.shake.Write(j[:skA.params.SharedSecretSize])
   231  	_, _ = c.shake.Read(n[:ptextLen])
   232  	for i := range ptext {
   233  		n[i] ^= ptext[i]
   234  	}
   235  
   236  	pkA.Export(ctext)
   237  	copy(ctext[pkA.Size():], n[:ptextLen])
   238  }
   239  
   240  // encrypt uses SIKE public key to encrypt plaintext. Requires cryptographically secure
   241  // PRNG. Returns ciphertext in case encryption succeeds. Returns error in case PRNG fails
   242  // or wrongly formated input was provided.
   243  func (c *KEM) encrypt(ctext []byte, rng io.Reader, pub *PublicKey, ptext []byte) error {
   244  	ptextLen := len(ptext)
   245  	// c1 must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3)
   246  	if ptextLen != pub.params.KemSize {
   247  		return errors.New("unsupported message length")
   248  	}
   249  
   250  	skA := NewPrivateKey(pub.params.ID, KeyVariantSidhA)
   251  	pkA := NewPublicKey(pub.params.ID, KeyVariantSidhA)
   252  	err := skA.Generate(rng)
   253  	if err != nil {
   254  		return err
   255  	}
   256  
   257  	skA.GeneratePublicKey(pkA)
   258  	c.generateCiphertext(ctext, skA, pkA, pub, ptext)
   259  	return nil
   260  }
   261  
   262  // decrypt uses SIKE private key to decrypt ciphertext. Returns plaintext in case
   263  // decryption succeeds or error in case unexpected input was provided.
   264  // Constant time.
   265  func (c *KEM) decrypt(n []byte, prv *PrivateKey, ctext []byte) (int, error) {
   266  	var c1Len int
   267  	var j [common.MaxSharedSecretBsz]byte
   268  	pkLen := prv.params.PublicKeySize
   269  
   270  	// ctext is a concatenation of (ciphertext = pubkey_A || c1)
   271  	// it must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3)
   272  	// Lengths has been already checked by Decapsulate()
   273  	c1Len = len(ctext) - pkLen
   274  	c0 := NewPublicKey(prv.params.ID, KeyVariantSidhA)
   275  	err := c0.Import(ctext[:pkLen])
   276  	prv.DeriveSecret(j[:], c0)
   277  	c.shake.Reset()
   278  	_, _ = c.shake.Write(j[:prv.params.SharedSecretSize])
   279  	_, _ = c.shake.Read(n[:c1Len])
   280  	for i := range n[:c1Len] {
   281  		n[i] ^= ctext[pkLen+i]
   282  	}
   283  	return c1Len, err
   284  }