...

Source file src/github.com/cloudflare/circl/pke/kyber/kyber512/internal/cpapke.go

Documentation: github.com/cloudflare/circl/pke/kyber/kyber512/internal

     1  package internal
     2  
     3  import (
     4  	"github.com/cloudflare/circl/internal/sha3"
     5  	"github.com/cloudflare/circl/pke/kyber/internal/common"
     6  )
     7  
     8  // A Kyber.CPAPKE private key.
     9  type PrivateKey struct {
    10  	sh Vec // NTT(s), normalized
    11  }
    12  
    13  // A Kyber.CPAPKE public key.
    14  type PublicKey struct {
    15  	rho [32]byte // ρ, the seed for the matrix A
    16  	th  Vec      // NTT(t), normalized
    17  
    18  	// cached values
    19  	aT Mat // the matrix Aᵀ
    20  }
    21  
    22  // Packs the private key to buf.
    23  func (sk *PrivateKey) Pack(buf []byte) {
    24  	sk.sh.Pack(buf)
    25  }
    26  
    27  // Unpacks the private key from buf.
    28  func (sk *PrivateKey) Unpack(buf []byte) {
    29  	sk.sh.Unpack(buf)
    30  	sk.sh.Normalize()
    31  }
    32  
    33  // Packs the public key to buf.
    34  func (pk *PublicKey) Pack(buf []byte) {
    35  	pk.th.Pack(buf)
    36  	copy(buf[K*common.PolySize:], pk.rho[:])
    37  }
    38  
    39  // Unpacks the public key from buf.
    40  func (pk *PublicKey) Unpack(buf []byte) {
    41  	pk.th.Unpack(buf)
    42  	pk.th.Normalize()
    43  	copy(pk.rho[:], buf[K*common.PolySize:])
    44  	pk.aT.Derive(&pk.rho, true)
    45  }
    46  
    47  // Derives a new Kyber.CPAPKE keypair from the given seed.
    48  func NewKeyFromSeed(seed []byte) (*PublicKey, *PrivateKey) {
    49  	var pk PublicKey
    50  	var sk PrivateKey
    51  
    52  	var expandedSeed [64]byte
    53  
    54  	h := sha3.New512()
    55  	_, _ = h.Write(seed)
    56  
    57  	// This writes hash into expandedSeed.  Yes, this is idiomatic Go.
    58  	_, _ = h.Read(expandedSeed[:])
    59  
    60  	copy(pk.rho[:], expandedSeed[:32])
    61  	sigma := expandedSeed[32:] // σ, the noise seed
    62  
    63  	pk.aT.Derive(&pk.rho, false) // Expand ρ to matrix A; we'll transpose later
    64  
    65  	var eh Vec
    66  	sk.sh.DeriveNoise(sigma, 0, Eta1) // Sample secret vector s
    67  	sk.sh.NTT()
    68  	sk.sh.Normalize()
    69  
    70  	eh.DeriveNoise(sigma, K, Eta1) // Sample blind e
    71  	eh.NTT()
    72  
    73  	// Next, we compute t = A s + e.
    74  	for i := 0; i < K; i++ {
    75  		// Note that coefficients of s are bounded by q and those of A
    76  		// are bounded by 4.5q and so their product is bounded by 2¹⁵q
    77  		// as required for multiplication.
    78  		PolyDotHat(&pk.th[i], &pk.aT[i], &sk.sh)
    79  
    80  		// A and s were not in Montgomery form, so the Montgomery
    81  		// multiplications in the inner product added a factor R⁻¹ which
    82  		// we'll cancel out now.  This will also ensure the coefficients of
    83  		// t are bounded in absolute value by q.
    84  		pk.th[i].ToMont()
    85  	}
    86  
    87  	pk.th.Add(&pk.th, &eh) // bounded by 8q.
    88  	pk.th.Normalize()
    89  	pk.aT.Transpose()
    90  
    91  	return &pk, &sk
    92  }
    93  
    94  // Decrypts ciphertext ct meant for private key sk to plaintext pt.
    95  func (sk *PrivateKey) DecryptTo(pt, ct []byte) {
    96  	var u Vec
    97  	var v, m common.Poly
    98  
    99  	u.Decompress(ct, DU)
   100  	v.Decompress(ct[K*compressedPolySize(DU):], DV)
   101  
   102  	// Compute m = v - <s, u>
   103  	u.NTT()
   104  	PolyDotHat(&m, &sk.sh, &u)
   105  	m.BarrettReduce()
   106  	m.InvNTT()
   107  	m.Sub(&v, &m)
   108  	m.Normalize()
   109  
   110  	// Compress polynomial m to original message
   111  	m.CompressMessageTo(pt)
   112  }
   113  
   114  // Encrypts message pt for the public key to ciphertext ct using randomness
   115  // from seed.
   116  //
   117  // seed has to be of length SeedSize, pt of PlaintextSize and ct of
   118  // CiphertextSize.
   119  func (pk *PublicKey) EncryptTo(ct, pt, seed []byte) {
   120  	var rh, e1, u Vec
   121  	var e2, v, m common.Poly
   122  
   123  	// Sample r, e₁ and e₂ from B_η
   124  	rh.DeriveNoise(seed, 0, Eta1)
   125  	rh.NTT()
   126  	rh.BarrettReduce()
   127  
   128  	e1.DeriveNoise(seed, K, common.Eta2)
   129  	e2.DeriveNoise(seed, 2*K, common.Eta2)
   130  
   131  	// Next we compute u = Aᵀ r + e₁.  First Aᵀ.
   132  	for i := 0; i < K; i++ {
   133  		// Note that coefficients of r are bounded by q and those of Aᵀ
   134  		// are bounded by 4.5q and so their product is bounded by 2¹⁵q
   135  		// as required for multiplication.
   136  		PolyDotHat(&u[i], &pk.aT[i], &rh)
   137  	}
   138  
   139  	u.BarrettReduce()
   140  
   141  	// Aᵀ and r were not in Montgomery form, so the Montgomery
   142  	// multiplications in the inner product added a factor R⁻¹ which
   143  	// the InvNTT cancels out.
   144  	u.InvNTT()
   145  
   146  	u.Add(&u, &e1) // u = Aᵀ r + e₁
   147  
   148  	// Next compute v = <t, r> + e₂ + Decompress_q(m, 1).
   149  	PolyDotHat(&v, &pk.th, &rh)
   150  	v.BarrettReduce()
   151  	v.InvNTT()
   152  
   153  	m.DecompressMessage(pt)
   154  	v.Add(&v, &m)
   155  	v.Add(&v, &e2) // v = <t, r> + e₂ + Decompress_q(m, 1)
   156  
   157  	// Pack ciphertext
   158  	u.Normalize()
   159  	v.Normalize()
   160  
   161  	u.CompressTo(ct, DU)
   162  	v.CompressTo(ct[K*compressedPolySize(DU):], DV)
   163  }
   164  
   165  // Returns whether sk equals other.
   166  func (sk *PrivateKey) Equal(other *PrivateKey) bool {
   167  	ret := int16(0)
   168  	for i := 0; i < K; i++ {
   169  		for j := 0; j < common.N; j++ {
   170  			ret |= sk.sh[i][j] ^ other.sh[i][j]
   171  		}
   172  	}
   173  	return ret == 0
   174  }
   175  

View as plain text