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