├── assets ├── kyber-k2so.png └── mlkem │ ├── ML-KEM-512.txt │ └── ML-KEM-768.txt ├── .gitignore ├── go.mod ├── go.sum ├── .github └── workflows │ └── go.yml ├── .golangci.yml ├── LICENSE ├── byteops.go ├── params.go ├── ntt.go ├── kem_test.go ├── README.md ├── indcpa.go ├── kem.go ├── poly.go └── mlkem_test.go /assets/kyber-k2so.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/symbolicsoft/kyber-k2so/HEAD/assets/kyber-k2so.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.upx 8 | *.test 9 | 10 | # macOS 11 | .DS_Store 12 | 13 | # Visual Studio Code 14 | .vscode 15 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | // SPDX-License-Identifier: MIT 3 | 4 | module github.com/symbolicsoft/kyber-k2so 5 | 6 | go 1.25.0 7 | 8 | require golang.org/x/crypto v0.46.0 9 | 10 | require golang.org/x/sys v0.39.0 // indirect 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= 2 | golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= 3 | golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= 4 | golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= 5 | golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= 6 | golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 7 | golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= 8 | golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 9 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Kyber-K2SO 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | name: Build 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v5 18 | with: 19 | go-version: '1.25' 20 | id: go 21 | 22 | - name: Check out code 23 | uses: actions/checkout@v4 24 | 25 | - name: Cache Go modules 26 | uses: actions/cache@v4 27 | with: 28 | path: ~/go/pkg/mod 29 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 30 | restore-keys: | 31 | ${{ runner.os }}-go- 32 | 33 | - name: Get dependencies 34 | run: go mod download 35 | 36 | - name: Build 37 | run: go build -v . 38 | 39 | - name: Test 40 | run: go test -v . 41 | 42 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | enable: 4 | - dogsled 5 | - funlen 6 | - gochecknoinits 7 | - gocritic 8 | - gocyclo 9 | - godox 10 | - gosec 11 | - lll 12 | - misspell 13 | - nakedret 14 | - prealloc 15 | - unconvert 16 | - unparam 17 | settings: 18 | funlen: 19 | lines: 128 20 | statements: 64 21 | goconst: 22 | min-len: 12 23 | govet: 24 | enable-all: true 25 | lll: 26 | line-length: 120 27 | exclusions: 28 | generated: lax 29 | presets: 30 | - comments 31 | - common-false-positives 32 | - legacy 33 | - std-error-handling 34 | paths: 35 | - third_party$ 36 | - builtin$ 37 | - examples$ 38 | rules: 39 | - linters: 40 | - gosec 41 | text: "G115:" 42 | - linters: 43 | - gosec 44 | text: "G602:" 45 | formatters: 46 | enable: 47 | - gofmt 48 | exclusions: 49 | generated: lax 50 | paths: 51 | - third_party$ 52 | - builtin$ 53 | - examples$ 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Nadim Kobeissi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /byteops.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | // byteopsLoad32 returns a 32-bit unsigned integer loaded from byte x. 7 | func byteopsLoad32(x []byte) uint32 { 8 | var r uint32 9 | r = uint32(x[0]) 10 | r |= (uint32(x[1]) << 8) 11 | r |= (uint32(x[2]) << 16) 12 | r |= (uint32(x[3]) << 24) 13 | return r 14 | } 15 | 16 | // byteopsLoad24 returns a 32-bit unsigned integer loaded from byte x. 17 | func byteopsLoad24(x []byte) uint32 { 18 | var r uint32 19 | r = uint32(x[0]) 20 | r |= (uint32(x[1]) << 8) 21 | r |= (uint32(x[2]) << 16) 22 | return r 23 | } 24 | 25 | // byteopsCbd computers a polynomial with coefficients distributed 26 | // according to a centered binomial distribution with parameter eta, 27 | // given an array of uniformly random bytes. 28 | func byteopsCbd(buf []byte, paramsK int) poly { 29 | var t, d uint32 30 | var a, b int16 31 | var r poly 32 | switch paramsK { 33 | case 2: 34 | for i := 0; i < paramsN/4; i++ { 35 | t = byteopsLoad24(buf[3*i:]) 36 | d = t & 0x00249249 37 | d += ((t >> 1) & 0x00249249) 38 | d += ((t >> 2) & 0x00249249) 39 | for j := 0; j < 4; j++ { 40 | a = int16((d >> (6*j + 0)) & 0x7) 41 | b = int16((d >> (6*j + paramsETAK512)) & 0x7) 42 | r[4*i+j] = a - b 43 | } 44 | } 45 | default: 46 | for i := 0; i < paramsN/8; i++ { 47 | t = byteopsLoad32(buf[4*i:]) 48 | d = t & 0x55555555 49 | d += ((t >> 1) & 0x55555555) 50 | for j := 0; j < 8; j++ { 51 | a = int16((d >> (4*j + 0)) & 0x3) 52 | b = int16((d >> (4*j + paramsETAK768K1024)) & 0x3) 53 | r[8*i+j] = a - b 54 | } 55 | } 56 | } 57 | return r 58 | } 59 | 60 | // byteopsMontgomeryReduce computes a Montgomery reduction; given 61 | // a 32-bit integer `a`, returns `a * R^-1 mod Q` where `R=2^16`. 62 | func byteopsMontgomeryReduce(a int32) int16 { 63 | return int16((a - int32(int16(a*paramsQInv))*int32(paramsQ)) >> 16) 64 | } 65 | 66 | // byteopsBarrettReduce computes a Barrett reduction; given 67 | // a 16-bit integer `a`, returns a 16-bit integer congruent to 68 | // `a mod Q` in {0,...,Q}. 69 | func byteopsBarrettReduce(a int16) int16 { 70 | t := int16((int32(paramsBarrettV) * int32(a)) >> 26) 71 | t *= int16(paramsQ) 72 | return a - t 73 | } 74 | 75 | // byteopsCSubQ conditionally subtracts Q from a. 76 | func byteopsCSubQ(a int16) int16 { 77 | a -= int16(paramsQ) 78 | a += ((a >> 15) & int16(paramsQ)) 79 | return a 80 | } 81 | 82 | // byteopsZeroBytes zeroes a byte slice to clear sensitive data from memory. 83 | func byteopsZeroBytes(b []byte) { 84 | for i := range b { 85 | b[i] = 0 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /params.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | const paramsN int = 256 7 | const paramsQ int = 3329 8 | const paramsQDivBy2Ceil uint32 = 1665 9 | const params2Pow28DivByQ uint32 = 80635 10 | const params2Pow27DivByQ uint32 = 40318 11 | const params2Pow31DivByQ uint64 = 645084 12 | const params2Pow32DivByQ uint64 = 1290167 13 | const paramsQInv int32 = 62209 14 | const paramsBarrettV int16 = 20159 15 | const paramsMontFactor int16 = 1353 16 | const paramsSymBytes int = 32 17 | const paramsPolyBytes int = 384 18 | const paramsETAK512 int = 3 19 | const paramsETAK768K1024 int = 2 20 | const paramsPolyvecBytesK512 int = 2 * paramsPolyBytes 21 | const paramsPolyvecBytesK768 int = 3 * paramsPolyBytes 22 | const paramsPolyvecBytesK1024 int = 4 * paramsPolyBytes 23 | const paramsPolyCompressedBytesK512 int = 128 24 | const paramsPolyCompressedBytesK768 int = 128 25 | const paramsPolyCompressedBytesK1024 int = 160 26 | const paramsPolyvecCompressedBytesK512 int = 2 * 320 27 | const paramsPolyvecCompressedBytesK768 int = 3 * 320 28 | const paramsPolyvecCompressedBytesK1024 int = 4 * 352 29 | const paramsIndcpaPublicKeyBytesK512 int = paramsPolyvecBytesK512 + paramsSymBytes 30 | const paramsIndcpaPublicKeyBytesK768 int = paramsPolyvecBytesK768 + paramsSymBytes 31 | const paramsIndcpaPublicKeyBytesK1024 int = paramsPolyvecBytesK1024 + paramsSymBytes 32 | const paramsIndcpaSecretKeyBytesK512 int = 2 * paramsPolyBytes 33 | const paramsIndcpaSecretKeyBytesK768 int = 3 * paramsPolyBytes 34 | const paramsIndcpaSecretKeyBytesK1024 int = 4 * paramsPolyBytes 35 | 36 | // Kyber512SKBytes is a constant representing the byte length of private keys in Kyber-512. 37 | const Kyber512SKBytes int = paramsPolyvecBytesK512 + ((paramsPolyvecBytesK512 + paramsSymBytes) + 2*paramsSymBytes) 38 | 39 | // Kyber768SKBytes is a constant representing the byte length of private keys in Kyber-768. 40 | const Kyber768SKBytes int = paramsPolyvecBytesK768 + ((paramsPolyvecBytesK768 + paramsSymBytes) + 2*paramsSymBytes) 41 | 42 | // Kyber1024SKBytes is a constant representing the byte length of private keys in Kyber-1024. 43 | const Kyber1024SKBytes int = paramsPolyvecBytesK1024 + ((paramsPolyvecBytesK1024 + paramsSymBytes) + 2*paramsSymBytes) 44 | 45 | // Kyber512PKBytes is a constant representing the byte length of public keys in Kyber-512. 46 | const Kyber512PKBytes int = paramsPolyvecBytesK512 + paramsSymBytes 47 | 48 | // Kyber768PKBytes is a constant representing the byte length of public keys in Kyber-768. 49 | const Kyber768PKBytes int = paramsPolyvecBytesK768 + paramsSymBytes 50 | 51 | // Kyber1024PKBytes is a constant representing the byte length of public keys in Kyber-1024. 52 | const Kyber1024PKBytes int = paramsPolyvecBytesK1024 + paramsSymBytes 53 | 54 | // Kyber512CTBytes is a constant representing the byte length of ciphertexts in Kyber-512. 55 | const Kyber512CTBytes int = paramsPolyvecCompressedBytesK512 + paramsPolyCompressedBytesK512 56 | 57 | // Kyber768CTBytes is a constant representing the byte length of ciphertexts in Kyber-768. 58 | const Kyber768CTBytes int = paramsPolyvecCompressedBytesK768 + paramsPolyCompressedBytesK768 59 | 60 | // Kyber1024CTBytes is a constant representing the byte length of ciphertexts in Kyber-1024. 61 | const Kyber1024CTBytes int = paramsPolyvecCompressedBytesK1024 + paramsPolyCompressedBytesK1024 62 | 63 | // KyberSSBytes is a constant representing the byte length of shared secrets in Kyber. 64 | const KyberSSBytes int = 32 65 | -------------------------------------------------------------------------------- /ntt.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | var nttZetas [128]int16 = [128]int16{ 7 | 2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962, 8 | 2127, 1855, 1468, 573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017, 9 | 732, 608, 1787, 411, 3124, 1758, 1223, 652, 2777, 1015, 2036, 1491, 3047, 10 | 1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469, 2476, 3239, 3058, 830, 11 | 107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054, 2226, 12 | 430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574, 13 | 1653, 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349, 14 | 418, 329, 3173, 3254, 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193, 15 | 1218, 1994, 2455, 220, 2142, 1670, 2144, 1799, 2051, 794, 1819, 2475, 2459, 16 | 478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628, 17 | } 18 | 19 | var nttZetasInv [128]int16 = [128]int16{ 20 | 1701, 1807, 1460, 2371, 2338, 2333, 308, 108, 2851, 870, 854, 1510, 2535, 21 | 1278, 1530, 1185, 1659, 1187, 3109, 874, 1335, 2111, 136, 1215, 2945, 1465, 22 | 1285, 2007, 2719, 2726, 2232, 2512, 75, 156, 3000, 2911, 2980, 872, 2685, 23 | 1590, 2210, 602, 1846, 777, 147, 2170, 2551, 246, 1676, 1755, 460, 291, 235, 24 | 3152, 2742, 2907, 3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103, 1275, 2652, 25 | 1065, 2881, 725, 1508, 2368, 398, 951, 247, 1421, 3222, 2499, 271, 90, 853, 26 | 1860, 3203, 1162, 1618, 666, 320, 8, 2813, 1544, 282, 1838, 1293, 2314, 552, 27 | 2677, 2106, 1571, 205, 2918, 1542, 2721, 2597, 2312, 681, 130, 1602, 1871, 28 | 829, 2946, 3065, 1325, 2756, 1861, 1474, 1202, 2367, 3147, 1752, 2707, 171, 29 | 3127, 3042, 1907, 1836, 1517, 359, 758, 1441, 30 | } 31 | 32 | // nttFqMul performs multiplication followed by Montgomery reduction 33 | // and returns a 16-bit integer congruent to `a*b*R^{-1} mod Q`. 34 | func nttFqMul(a int16, b int16) int16 { 35 | return byteopsMontgomeryReduce(int32(a) * int32(b)) 36 | } 37 | 38 | // ntt performs an inplace number-theoretic transform (NTT) in `Rq`. 39 | // The input is in standard order, the output is in bit-reversed order. 40 | func ntt(r poly) poly { 41 | j := 0 42 | k := 1 43 | for l := 128; l >= 2; l >>= 1 { 44 | for start := 0; start < 256; start = j + l { 45 | zeta := nttZetas[k] 46 | k++ 47 | for j = start; j < start+l; j++ { 48 | t := nttFqMul(zeta, r[j+l]) 49 | r[j+l] = r[j] - t 50 | r[j] += t 51 | } 52 | } 53 | } 54 | return r 55 | } 56 | 57 | // nttInv performs an inplace inverse number-theoretic transform (NTT) 58 | // in `Rq` and multiplication by Montgomery factor 2^16. 59 | // The input is in bit-reversed order, the output is in standard order. 60 | func nttInv(r poly) poly { 61 | j := 0 62 | k := 0 63 | for l := 2; l <= 128; l <<= 1 { 64 | for start := 0; start < 256; start = j + l { 65 | zeta := nttZetasInv[k] 66 | k++ 67 | for j = start; j < start+l; j++ { 68 | t := r[j] 69 | r[j] = byteopsBarrettReduce(t + r[j+l]) 70 | r[j+l] = t - r[j+l] 71 | r[j+l] = nttFqMul(zeta, r[j+l]) 72 | } 73 | } 74 | } 75 | for j := 0; j < 256; j++ { 76 | r[j] = nttFqMul(r[j], nttZetasInv[127]) 77 | } 78 | return r 79 | } 80 | 81 | // nttBaseMul performs the multiplication of polynomials 82 | // in `Zq[X]/(X^2-zeta)`. Used for multiplication of elements 83 | // in `Rq` in the number-theoretic transformation domain. 84 | func nttBaseMul( 85 | a0 int16, a1 int16, 86 | b0 int16, b1 int16, 87 | zeta int16, 88 | ) (int16, int16) { 89 | return nttFqMul(nttFqMul(a1, b1), zeta) + nttFqMul(a0, b0), 90 | nttFqMul(a0, b1) + nttFqMul(a1, b0) 91 | } 92 | -------------------------------------------------------------------------------- /kem_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | import ( 7 | "crypto/subtle" 8 | "testing" 9 | ) 10 | 11 | func TestSelf512(t *testing.T) { 12 | for i := 0; i < 1000; i++ { 13 | privateKey, publicKey, err := KemKeypair512() 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | ciphertext, ssA, err := KemEncrypt512(publicKey) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | ssB, err := KemDecrypt512(ciphertext, privateKey) 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | if subtle.ConstantTimeCompare(ssA[:], ssB[:]) == 0 { 26 | t.Errorf("ML-KEM-512 self-test failed at iteration %d", i) 27 | } 28 | } 29 | } 30 | 31 | func TestSelf768(t *testing.T) { 32 | for i := 0; i < 1000; i++ { 33 | privateKey, publicKey, err := KemKeypair768() 34 | if err != nil { 35 | t.Error(err) 36 | } 37 | ciphertext, ssA, err := KemEncrypt768(publicKey) 38 | if err != nil { 39 | t.Error(err) 40 | } 41 | ssB, err := KemDecrypt768(ciphertext, privateKey) 42 | if err != nil { 43 | t.Error(err) 44 | } 45 | if subtle.ConstantTimeCompare(ssA[:], ssB[:]) == 0 { 46 | t.Errorf("ML-KEM-768 self-test failed at iteration %d", i) 47 | } 48 | } 49 | } 50 | 51 | func TestSelf1024(t *testing.T) { 52 | for i := 0; i < 1000; i++ { 53 | privateKey, publicKey, err := KemKeypair1024() 54 | if err != nil { 55 | t.Error(err) 56 | } 57 | ciphertext, ssA, err := KemEncrypt1024(publicKey) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | ssB, err := KemDecrypt1024(ciphertext, privateKey) 62 | if err != nil { 63 | t.Error(err) 64 | } 65 | if subtle.ConstantTimeCompare(ssA[:], ssB[:]) == 0 { 66 | t.Errorf("ML-KEM-1024 self-test failed at iteration %d", i) 67 | } 68 | } 69 | } 70 | 71 | // Benchmark test keys - generated once at startup 72 | var benchKey512sk, benchKey512pk, _ = KemKeypair512() 73 | var benchKey768sk, benchKey768pk, _ = KemKeypair768() 74 | var benchKey1024sk, benchKey1024pk, _ = KemKeypair1024() 75 | var benchCt512, _, _ = KemEncrypt512(benchKey512pk) 76 | var benchCt768, _, _ = KemEncrypt768(benchKey768pk) 77 | var benchCt1024, _, _ = KemEncrypt1024(benchKey1024pk) 78 | 79 | func BenchmarkKemKeypair512(b *testing.B) { 80 | for n := 0; n < b.N; n++ { 81 | _, _, err := KemKeypair512() 82 | if err != nil { 83 | b.Error(err) 84 | } 85 | } 86 | } 87 | 88 | func BenchmarkKemKeypair768(b *testing.B) { 89 | for n := 0; n < b.N; n++ { 90 | _, _, err := KemKeypair768() 91 | if err != nil { 92 | b.Error(err) 93 | } 94 | } 95 | } 96 | 97 | func BenchmarkKemKeypair1024(b *testing.B) { 98 | for n := 0; n < b.N; n++ { 99 | _, _, err := KemKeypair1024() 100 | if err != nil { 101 | b.Error(err) 102 | } 103 | } 104 | } 105 | 106 | func BenchmarkKemEncrypt512(b *testing.B) { 107 | for n := 0; n < b.N; n++ { 108 | _, _, _ = KemEncrypt512(benchKey512pk) 109 | } 110 | } 111 | 112 | func BenchmarkKemEncrypt768(b *testing.B) { 113 | for n := 0; n < b.N; n++ { 114 | _, _, _ = KemEncrypt768(benchKey768pk) 115 | } 116 | } 117 | 118 | func BenchmarkKemEncrypt1024(b *testing.B) { 119 | for n := 0; n < b.N; n++ { 120 | _, _, _ = KemEncrypt1024(benchKey1024pk) 121 | } 122 | } 123 | 124 | func BenchmarkKemDecrypt512(b *testing.B) { 125 | for n := 0; n < b.N; n++ { 126 | _, _ = KemDecrypt512(benchCt512, benchKey512sk) 127 | } 128 | } 129 | 130 | func BenchmarkKemDecrypt768(b *testing.B) { 131 | for n := 0; n < b.N; n++ { 132 | _, _ = KemDecrypt768(benchCt768, benchKey768sk) 133 | } 134 | } 135 | 136 | func BenchmarkKemDecrypt1024(b *testing.B) { 137 | for n := 0; n < b.N; n++ { 138 | _, _ = KemDecrypt1024(benchCt1024, benchKey1024sk) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kyber-K2SO 2 | 3 | 4 | 5 | [![Kyber-K2SO](https://github.com/symbolicsoft/kyber-k2so/workflows/Kyber-K2SO/badge.svg)](https://github.com/symbolicsoft/kyber-k2so/actions) 6 | [![GoDoc](https://godoc.org/github.com/symbolicsoft/kyber-k2so?status.svg)](https://pkg.go.dev/github.com/symbolicsoft/kyber-k2so?tab=overview) 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/symbolicsoft/kyber-k2so)](https://goreportcard.com/report/github.com/symbolicsoft/kyber-k2so) 8 | ![GitHub](https://img.shields.io/github/license/symbolicsoft/kyber-k2so) 9 | 10 | **[Kyber-K2SO](https://github.com/symbolicsoft/kyber-k2so)** is Symbolic Software's clean implementation of [ML-KEM](https://csrc.nist.gov/pubs/fips/203/final) (FIPS 203), the Module-Lattice-Based Key-Encapsulation Mechanism standardized by NIST. ML-KEM is an IND-CCA2-secure key encapsulation mechanism (KEM) whose security is based on the hardness of solving the learning-with-errors (LWE) problem over module lattices. 11 | 12 | ## Security Disclaimer 13 | 14 | 🚨 Extensive effort has been undertaken in order to ensure the correctness, interoperability, safety and reliability of this library. Furthermore, it is unlikely that the API will change in the future. While this library is likely ready for production use, it is offered as-is, and without a guarantee. 15 | 16 | ## Features & Usage 17 | 18 | Keeping in mind the Security Disclaimer above, Kyber-K2SO appears to be appropriate for use in any environment supported by Go: client-side application, server-side applications and more. All operations take no more than a few milliseconds on regular computing hardware. 19 | 20 | ### Features 21 | 22 | * **Small, easy to read code.** Kyber-K2SO keeps it simple with the goal of improving maintainability and being a good pedagogical resource. 23 | * **Simple API.** `KemKeypair768()` to generate a private key and a public key, `KemEncrypt768(publicKey)` generate and encrypt a shared secret, and `KemDecrypt768(ciphertext, privateKey)` to decrypt the shared secret. Aside from ML-KEM-768, ML-KEM-512 and ML-KEM-1024 are also offered. 24 | * **Good performance.** Kyber-K2SO is more than fast enough for regular usage in any environment supported by the Go programming language. 25 | * **Constant time (probably).** As far as we can tell, decryption appears to perform in constant time. Further analysis is encouraged. 26 | 27 | ### Using Kyber-K2SO 28 | 29 | ```bash 30 | go get -u github.com/symbolicsoft/kyber-k2so 31 | ``` 32 | 33 | ```go 34 | package main 35 | 36 | import ( 37 | kyberk2so "github.com/symbolicsoft/kyber-k2so" 38 | ) 39 | 40 | func main() { 41 | privateKey, publicKey, _ := kyberk2so.KemKeypair768() 42 | ciphertext, ssA, _ := kyberk2so.KemEncrypt768(publicKey) 43 | ssB, _ := kyberk2so.KemDecrypt768(ciphertext, privateKey) 44 | } 45 | ``` 46 | 47 | Replace `768` with `512` or `1024` in the above function names in order to call ML-KEM-512 or ML-KEM-1024 instead of ML-KEM-768. 48 | 49 | ### Running Tests 50 | 51 | ```bash 52 | > go test -v 53 | 54 | === RUN TestSelf512 55 | --- PASS: TestSelf512 (0.09s) 56 | === RUN TestSelf768 57 | --- PASS: TestSelf768 (0.14s) 58 | === RUN TestSelf1024 59 | --- PASS: TestSelf1024 (0.21s) 60 | === RUN TestMLKEM512Vector 61 | --- PASS: TestMLKEM512Vector (0.00s) 62 | === RUN TestMLKEM768Vector 63 | --- PASS: TestMLKEM768Vector (0.00s) 64 | === RUN TestMLKEM1024Vector 65 | --- PASS: TestMLKEM1024Vector (0.00s) 66 | PASS 67 | ok github.com/symbolicsoft/kyber-k2so 0.431s 68 | ``` 69 | 70 | ### Running Benchmarks 71 | 72 | ```bash 73 | > go test -bench=. 74 | 75 | goos: linux 76 | goarch: amd64 77 | pkg: github.com/symbolicsoft/kyber-k2so 78 | cpu: Intel(R) Core(TM) Ultra 9 275HX 79 | BenchmarkKemKeypair512-24 52944 22653 ns/op 80 | BenchmarkKemKeypair768-24 29696 40083 ns/op 81 | BenchmarkKemKeypair1024-24 19209 60778 ns/op 82 | BenchmarkKemEncrypt512-24 48856 23307 ns/op 83 | BenchmarkKemEncrypt768-24 32428 39273 ns/op 84 | BenchmarkKemEncrypt1024-24 19483 57528 ns/op 85 | BenchmarkKemDecrypt512-24 36138 33402 ns/op 86 | BenchmarkKemDecrypt768-24 25008 47869 ns/op 87 | BenchmarkKemDecrypt1024-24 17690 67303 ns/op 88 | PASS 89 | ok github.com/symbolicsoft/kyber-k2so 15.135s 90 | ``` 91 | 92 | ## About Kyber-K2SO 93 | 94 | Kyber-K2SO is published by [Symbolic Software](https://symbolic.software) under the MIT License. 95 | -------------------------------------------------------------------------------- /indcpa.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | import ( 7 | "crypto/rand" 8 | 9 | "golang.org/x/crypto/sha3" 10 | ) 11 | 12 | // indcpaPackPublicKey serializes the public key as a concatenation of the 13 | // serialized vector of polynomials of the public key, and the public seed 14 | // used to generate the matrix `A`. 15 | func indcpaPackPublicKey(publicKey polyvec, seed []byte, paramsK int) []byte { 16 | return append(polyvecToBytes(publicKey, paramsK), seed...) 17 | } 18 | 19 | // indcpaUnpackPublicKey de-serializes the public key from a byte array 20 | // and represents the approximate inverse of indcpaPackPublicKey. 21 | func indcpaUnpackPublicKey(packedPublicKey []byte, paramsK int) (polyvec, []byte) { 22 | switch paramsK { 23 | case 2: 24 | publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK512], paramsK) 25 | seed := packedPublicKey[paramsPolyvecBytesK512:] 26 | return publicKeyPolyvec, seed 27 | case 3: 28 | publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK768], paramsK) 29 | seed := packedPublicKey[paramsPolyvecBytesK768:] 30 | return publicKeyPolyvec, seed 31 | default: 32 | publicKeyPolyvec := polyvecFromBytes(packedPublicKey[:paramsPolyvecBytesK1024], paramsK) 33 | seed := packedPublicKey[paramsPolyvecBytesK1024:] 34 | return publicKeyPolyvec, seed 35 | } 36 | } 37 | 38 | // indcpaPackPrivateKey serializes the private key. 39 | func indcpaPackPrivateKey(privateKey polyvec, paramsK int) []byte { 40 | return polyvecToBytes(privateKey, paramsK) 41 | } 42 | 43 | // indcpaUnpackPrivateKey de-serializes the private key and represents 44 | // the inverse of indcpaPackPrivateKey. 45 | func indcpaUnpackPrivateKey(packedPrivateKey []byte, paramsK int) polyvec { 46 | return polyvecFromBytes(packedPrivateKey, paramsK) 47 | } 48 | 49 | // indcpaPackCiphertext serializes the ciphertext as a concatenation of 50 | // the compressed and serialized vector of polynomials `b` and the 51 | // compressed and serialized polynomial `v`. 52 | func indcpaPackCiphertext(b polyvec, v poly, paramsK int) []byte { 53 | return append(polyvecCompress(b, paramsK), polyCompress(v, paramsK)...) 54 | } 55 | 56 | // indcpaUnpackCiphertext de-serializes and decompresses the ciphertext 57 | // from a byte array, and represents the approximate inverse of 58 | // indcpaPackCiphertext. 59 | func indcpaUnpackCiphertext(c []byte, paramsK int) (polyvec, poly) { 60 | switch paramsK { 61 | case 2: 62 | b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK512], paramsK) 63 | v := polyDecompress(c[paramsPolyvecCompressedBytesK512:], paramsK) 64 | return b, v 65 | case 3: 66 | b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK768], paramsK) 67 | v := polyDecompress(c[paramsPolyvecCompressedBytesK768:], paramsK) 68 | return b, v 69 | default: 70 | b := polyvecDecompress(c[:paramsPolyvecCompressedBytesK1024], paramsK) 71 | v := polyDecompress(c[paramsPolyvecCompressedBytesK1024:], paramsK) 72 | return b, v 73 | } 74 | } 75 | 76 | // indcpaRejUniform runs rejection sampling on uniform random bytes 77 | // to generate uniform random integers modulo `Q`. 78 | func indcpaRejUniform(buf []byte, bufl int, l int) (poly, int) { 79 | var r poly 80 | var d1 uint16 81 | var d2 uint16 82 | i := 0 83 | j := 0 84 | for i < l && j+3 <= bufl { 85 | d1 = (uint16((buf[j])>>0) | (uint16(buf[j+1]) << 8)) & 0xFFF 86 | d2 = (uint16((buf[j+1])>>4) | (uint16(buf[j+2]) << 4)) & 0xFFF 87 | j += 3 88 | if d1 < uint16(paramsQ) { 89 | r[i] = int16(d1) 90 | i++ 91 | } 92 | if i < l && d2 < uint16(paramsQ) { 93 | r[i] = int16(d2) 94 | i++ 95 | } 96 | } 97 | return r, i 98 | } 99 | 100 | // indcpaGenMatrix deterministically generates a matrix `A` (or the transpose of `A`) 101 | // from a seed. Entries of the matrix are polynomials that look uniformly random. 102 | // Performs rejection sampling on the output of an extendable-output function (XOF). 103 | func indcpaGenMatrix(seed []byte, transposed bool, paramsK int) ([]polyvec, error) { 104 | r := make([]polyvec, paramsK) 105 | var buf [672]byte 106 | var xofInput [34]byte 107 | copy(xofInput[:32], seed) 108 | xof := sha3.NewShake128() 109 | ctr := 0 110 | for i := 0; i < paramsK; i++ { 111 | r[i] = polyvecNew(paramsK) 112 | for j := 0; j < paramsK; j++ { 113 | xof.Reset() 114 | if transposed { 115 | xofInput[32] = byte(i) 116 | xofInput[33] = byte(j) 117 | } else { 118 | xofInput[32] = byte(j) 119 | xofInput[33] = byte(i) 120 | } 121 | _, err := xof.Write(xofInput[:]) 122 | if err != nil { 123 | return nil, err 124 | } 125 | _, err = xof.Read(buf[:]) 126 | if err != nil { 127 | return nil, err 128 | } 129 | r[i][j], ctr = indcpaRejUniform(buf[:504], 504, paramsN) 130 | // Retry with remaining buffer bytes if needed 131 | // Bound iterations as a safety measure (probability of needing >1 iteration is ~10^-82) 132 | for iterations := 0; ctr < paramsN && iterations < 100; iterations++ { 133 | missing, ctrn := indcpaRejUniform(buf[504:], 168, paramsN-ctr) 134 | for k := ctr; k < paramsN; k++ { 135 | r[i][j][k] = missing[k-ctr] 136 | } 137 | ctr += ctrn 138 | } 139 | } 140 | } 141 | return r, nil 142 | } 143 | 144 | // indcpaPrf provides a pseudo-random function (PRF) which returns 145 | // a byte array of length `l`, using the provided key and nonce 146 | // to instantiate the PRF's underlying hash function. 147 | func indcpaPrf(l int, key []byte, nonce byte) []byte { 148 | hash := make([]byte, l) 149 | var prfInput [33]byte 150 | copy(prfInput[:32], key) 151 | prfInput[32] = nonce 152 | sha3.ShakeSum256(hash, prfInput[:]) 153 | return hash 154 | } 155 | 156 | // indcpaKeypair generates public and private keys for the CPA-secure 157 | // public-key encryption scheme underlying Kyber. 158 | func indcpaKeypair(paramsK int) ([]byte, []byte, error) { 159 | skpv := polyvecNew(paramsK) 160 | pkpv := polyvecNew(paramsK) 161 | e := polyvecNew(paramsK) 162 | var buf [64]byte 163 | var hashInput [33]byte 164 | h := sha3.New512() 165 | _, err := rand.Read(hashInput[:paramsSymBytes]) 166 | if err != nil { 167 | return nil, nil, err 168 | } 169 | hashInput[32] = byte(paramsK) 170 | _, err = h.Write(hashInput[:]) 171 | if err != nil { 172 | return nil, nil, err 173 | } 174 | h.Sum(buf[:0]) 175 | var publicSeed [paramsSymBytes]byte 176 | var noiseSeed [paramsSymBytes]byte 177 | copy(publicSeed[:], buf[:paramsSymBytes]) 178 | copy(noiseSeed[:], buf[paramsSymBytes:]) 179 | a, err := indcpaGenMatrix(publicSeed[:], false, paramsK) 180 | if err != nil { 181 | return nil, nil, err 182 | } 183 | var nonce byte 184 | for i := 0; i < paramsK; i++ { 185 | skpv[i] = polyGetNoise(noiseSeed[:], nonce, paramsK) 186 | nonce++ 187 | } 188 | for i := 0; i < paramsK; i++ { 189 | e[i] = polyGetNoise(noiseSeed[:], nonce, paramsK) 190 | nonce++ 191 | } 192 | polyvecNtt(skpv, paramsK) 193 | polyvecReduce(skpv, paramsK) 194 | polyvecNtt(e, paramsK) 195 | for i := 0; i < paramsK; i++ { 196 | pkpv[i] = polyToMont(polyvecPointWiseAccMontgomery(a[i], skpv, paramsK)) 197 | } 198 | polyvecAdd(pkpv, e, paramsK) 199 | polyvecReduce(pkpv, paramsK) 200 | return indcpaPackPrivateKey(skpv, paramsK), indcpaPackPublicKey(pkpv, publicSeed[:], paramsK), nil 201 | } 202 | 203 | // indcpaEncrypt is the encryption function of the CPA-secure 204 | // public-key encryption scheme underlying Kyber. 205 | func indcpaEncrypt(m []byte, publicKey []byte, coins []byte, paramsK int) ([]byte, error) { 206 | sp := polyvecNew(paramsK) 207 | ep := polyvecNew(paramsK) 208 | bp := polyvecNew(paramsK) 209 | publicKeyPolyvec, seed := indcpaUnpackPublicKey(publicKey, paramsK) 210 | k := polyFromMsg(m) 211 | at, err := indcpaGenMatrix(seed[:paramsSymBytes], true, paramsK) 212 | if err != nil { 213 | return []byte{}, err 214 | } 215 | for i := 0; i < paramsK; i++ { 216 | sp[i] = polyGetNoise(coins, byte(i), paramsK) 217 | ep[i] = polyGetNoise(coins, byte(i+paramsK), 3) 218 | } 219 | epp := polyGetNoise(coins, byte(paramsK*2), 3) 220 | polyvecNtt(sp, paramsK) 221 | polyvecReduce(sp, paramsK) 222 | for i := 0; i < paramsK; i++ { 223 | bp[i] = polyvecPointWiseAccMontgomery(at[i], sp, paramsK) 224 | } 225 | v := polyvecPointWiseAccMontgomery(publicKeyPolyvec, sp, paramsK) 226 | polyvecInvNttToMont(bp, paramsK) 227 | v = polyInvNttToMont(v) 228 | polyvecAdd(bp, ep, paramsK) 229 | v = polyAdd(polyAdd(v, epp), k) 230 | polyvecReduce(bp, paramsK) 231 | return indcpaPackCiphertext(bp, polyReduce(v), paramsK), nil 232 | } 233 | 234 | // indcpaDecrypt is the decryption function of the CPA-secure 235 | // public-key encryption scheme underlying Kyber. 236 | func indcpaDecrypt(c []byte, privateKey []byte, paramsK int) []byte { 237 | bp, v := indcpaUnpackCiphertext(c, paramsK) 238 | privateKeyPolyvec := indcpaUnpackPrivateKey(privateKey, paramsK) 239 | polyvecNtt(bp, paramsK) 240 | mp := polyvecPointWiseAccMontgomery(privateKeyPolyvec, bp, paramsK) 241 | mp = polyInvNttToMont(mp) 242 | mp = polySub(v, mp) 243 | mp = polyReduce(mp) 244 | return polyToMsg(mp) 245 | } 246 | -------------------------------------------------------------------------------- /kem.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | // Package kyberk2so provides a clean implementation of ML-KEM (FIPS 203), 5 | // a module-lattice-based key encapsulation mechanism (KEM) whose security 6 | // is based on the hardness of solving the learning-with-errors (LWE) problem 7 | // over module lattices. 8 | package kyberk2so 9 | 10 | import ( 11 | "crypto/rand" 12 | "crypto/subtle" 13 | 14 | "golang.org/x/crypto/sha3" 15 | ) 16 | 17 | // KemKeypair512 returns an ML-KEM-512 private key 18 | // and a corresponding ML-KEM-512 public key. 19 | // An accompanying error is returned if no sufficient 20 | // randomness could be obtained from the system. 21 | func KemKeypair512() ([Kyber512SKBytes]byte, [Kyber512PKBytes]byte, error) { 22 | const paramsK = 2 23 | var privateKeyFixedLength [Kyber512SKBytes]byte 24 | var publicKeyFixedLength [Kyber512PKBytes]byte 25 | indcpaPrivateKey, indcpaPublicKey, err := indcpaKeypair(paramsK) 26 | if err != nil { 27 | return privateKeyFixedLength, publicKeyFixedLength, err 28 | } 29 | pkh := sha3.Sum256(indcpaPublicKey) 30 | skStart := copy(privateKeyFixedLength[:], indcpaPrivateKey) 31 | skStart += copy(privateKeyFixedLength[skStart:], indcpaPublicKey) 32 | skStart += copy(privateKeyFixedLength[skStart:], pkh[:]) 33 | _, err = rand.Read(privateKeyFixedLength[skStart:]) 34 | if err != nil { 35 | for i := range privateKeyFixedLength { 36 | privateKeyFixedLength[i] = 0 37 | } 38 | return privateKeyFixedLength, publicKeyFixedLength, err 39 | } 40 | copy(publicKeyFixedLength[:], indcpaPublicKey) 41 | return privateKeyFixedLength, publicKeyFixedLength, nil 42 | } 43 | 44 | // KemKeypair768 returns an ML-KEM-768 private key 45 | // and a corresponding ML-KEM-768 public key. 46 | // An accompanying error is returned if no sufficient 47 | // randomness could be obtained from the system. 48 | func KemKeypair768() ([Kyber768SKBytes]byte, [Kyber768PKBytes]byte, error) { 49 | const paramsK = 3 50 | var privateKeyFixedLength [Kyber768SKBytes]byte 51 | var publicKeyFixedLength [Kyber768PKBytes]byte 52 | indcpaPrivateKey, indcpaPublicKey, err := indcpaKeypair(paramsK) 53 | if err != nil { 54 | return privateKeyFixedLength, publicKeyFixedLength, err 55 | } 56 | pkh := sha3.Sum256(indcpaPublicKey) 57 | skStart := copy(privateKeyFixedLength[:], indcpaPrivateKey) 58 | skStart += copy(privateKeyFixedLength[skStart:], indcpaPublicKey) 59 | skStart += copy(privateKeyFixedLength[skStart:], pkh[:]) 60 | _, err = rand.Read(privateKeyFixedLength[skStart:]) 61 | if err != nil { 62 | for i := range privateKeyFixedLength { 63 | privateKeyFixedLength[i] = 0 64 | } 65 | return privateKeyFixedLength, publicKeyFixedLength, err 66 | } 67 | copy(publicKeyFixedLength[:], indcpaPublicKey) 68 | return privateKeyFixedLength, publicKeyFixedLength, nil 69 | } 70 | 71 | // KemKeypair1024 returns an ML-KEM-1024 private key 72 | // and a corresponding ML-KEM-1024 public key. 73 | // An accompanying error is returned if no sufficient 74 | // randomness could be obtained from the system. 75 | func KemKeypair1024() ([Kyber1024SKBytes]byte, [Kyber1024PKBytes]byte, error) { 76 | const paramsK = 4 77 | var privateKeyFixedLength [Kyber1024SKBytes]byte 78 | var publicKeyFixedLength [Kyber1024PKBytes]byte 79 | indcpaPrivateKey, indcpaPublicKey, err := indcpaKeypair(paramsK) 80 | if err != nil { 81 | return privateKeyFixedLength, publicKeyFixedLength, err 82 | } 83 | pkh := sha3.Sum256(indcpaPublicKey) 84 | skStart := copy(privateKeyFixedLength[:], indcpaPrivateKey) 85 | skStart += copy(privateKeyFixedLength[skStart:], indcpaPublicKey) 86 | skStart += copy(privateKeyFixedLength[skStart:], pkh[:]) 87 | _, err = rand.Read(privateKeyFixedLength[skStart:]) 88 | if err != nil { 89 | for i := range privateKeyFixedLength { 90 | privateKeyFixedLength[i] = 0 91 | } 92 | return privateKeyFixedLength, publicKeyFixedLength, err 93 | } 94 | copy(publicKeyFixedLength[:], indcpaPublicKey) 95 | return privateKeyFixedLength, publicKeyFixedLength, nil 96 | } 97 | 98 | // KemEncrypt512 takes a public key (from KemKeypair512) as input 99 | // and returns a ciphertext and a 32-byte shared secret. 100 | // An accompanying error is returned if no sufficient 101 | // randomness could be obtained from the system. 102 | func KemEncrypt512(publicKey [Kyber512PKBytes]byte) ( 103 | [Kyber512CTBytes]byte, [KyberSSBytes]byte, error, 104 | ) { 105 | const paramsK = 2 106 | var ciphertextFixedLength [Kyber512CTBytes]byte 107 | var sharedSecretFixedLength [KyberSSBytes]byte 108 | var d [paramsSymBytes]byte 109 | _, err := rand.Read(d[:]) 110 | if err != nil { 111 | return ciphertextFixedLength, sharedSecretFixedLength, err 112 | } 113 | m := sha3.Sum256(d[:]) 114 | pkh := sha3.Sum256(publicKey[:]) 115 | var krInput [64]byte 116 | copy(krInput[:32], m[:]) 117 | copy(krInput[32:], pkh[:]) 118 | kr := sha3.Sum512(krInput[:]) 119 | ciphertext, err := indcpaEncrypt(m[:], publicKey[:], kr[paramsSymBytes:], paramsK) 120 | copy(ciphertextFixedLength[:], ciphertext) 121 | copy(sharedSecretFixedLength[:], kr[:paramsSymBytes]) 122 | byteopsZeroBytes(d[:]) 123 | byteopsZeroBytes(m[:]) 124 | byteopsZeroBytes(krInput[:]) 125 | byteopsZeroBytes(kr[:]) 126 | return ciphertextFixedLength, sharedSecretFixedLength, err 127 | } 128 | 129 | // KemEncrypt768 takes a public key (from KemKeypair768) as input and 130 | // returns a ciphertext and a 32-byte shared secret. 131 | // An accompanying error is returned if no sufficient 132 | // randomness could be obtained from the system. 133 | func KemEncrypt768(publicKey [Kyber768PKBytes]byte) ( 134 | [Kyber768CTBytes]byte, [KyberSSBytes]byte, error, 135 | ) { 136 | const paramsK = 3 137 | var ciphertextFixedLength [Kyber768CTBytes]byte 138 | var sharedSecretFixedLength [KyberSSBytes]byte 139 | var d [paramsSymBytes]byte 140 | _, err := rand.Read(d[:]) 141 | if err != nil { 142 | return ciphertextFixedLength, sharedSecretFixedLength, err 143 | } 144 | m := sha3.Sum256(d[:]) 145 | pkh := sha3.Sum256(publicKey[:]) 146 | var krInput [64]byte 147 | copy(krInput[:32], m[:]) 148 | copy(krInput[32:], pkh[:]) 149 | kr := sha3.Sum512(krInput[:]) 150 | ciphertext, err := indcpaEncrypt(m[:], publicKey[:], kr[paramsSymBytes:], paramsK) 151 | copy(ciphertextFixedLength[:], ciphertext) 152 | copy(sharedSecretFixedLength[:], kr[:paramsSymBytes]) 153 | byteopsZeroBytes(d[:]) 154 | byteopsZeroBytes(m[:]) 155 | byteopsZeroBytes(krInput[:]) 156 | byteopsZeroBytes(kr[:]) 157 | return ciphertextFixedLength, sharedSecretFixedLength, err 158 | } 159 | 160 | // KemEncrypt1024 takes a public key (from KemKeypair1024) as input 161 | // and returns a ciphertext and a 32-byte shared secret. 162 | // An accompanying error is returned if no sufficient 163 | // randomness could be obtained from the system. 164 | func KemEncrypt1024(publicKey [Kyber1024PKBytes]byte) ( 165 | [Kyber1024CTBytes]byte, [KyberSSBytes]byte, error, 166 | ) { 167 | const paramsK = 4 168 | var ciphertextFixedLength [Kyber1024CTBytes]byte 169 | var sharedSecretFixedLength [KyberSSBytes]byte 170 | var d [paramsSymBytes]byte 171 | _, err := rand.Read(d[:]) 172 | if err != nil { 173 | return ciphertextFixedLength, sharedSecretFixedLength, err 174 | } 175 | m := sha3.Sum256(d[:]) 176 | pkh := sha3.Sum256(publicKey[:]) 177 | var krInput [64]byte 178 | copy(krInput[:32], m[:]) 179 | copy(krInput[32:], pkh[:]) 180 | kr := sha3.Sum512(krInput[:]) 181 | ciphertext, err := indcpaEncrypt(m[:], publicKey[:], kr[paramsSymBytes:], paramsK) 182 | copy(ciphertextFixedLength[:], ciphertext) 183 | copy(sharedSecretFixedLength[:], kr[:paramsSymBytes]) 184 | byteopsZeroBytes(d[:]) 185 | byteopsZeroBytes(m[:]) 186 | byteopsZeroBytes(krInput[:]) 187 | byteopsZeroBytes(kr[:]) 188 | return ciphertextFixedLength, sharedSecretFixedLength, err 189 | } 190 | 191 | // KemDecrypt512 takes a ciphertext (from KeyEncrypt512), 192 | // a private key (from KemKeypair512) and returns a 32-byte shared secret. 193 | // An accompanying error is returned if no sufficient 194 | // randomness could be obtained from the system. 195 | func KemDecrypt512( 196 | ciphertext [Kyber512CTBytes]byte, 197 | privateKey [Kyber512SKBytes]byte, 198 | ) ([KyberSSBytes]byte, error) { 199 | const paramsK = 2 200 | var sharedSecretFixedLength [KyberSSBytes]byte 201 | indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK512] 202 | pki := paramsIndcpaSecretKeyBytesK512 + paramsIndcpaPublicKeyBytesK512 203 | publicKey := privateKey[paramsIndcpaSecretKeyBytesK512:pki] 204 | h := privateKey[pki : pki+paramsSymBytes] 205 | z := privateKey[Kyber512SKBytes-paramsSymBytes:] 206 | mPrime := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) 207 | var krInput [64]byte 208 | copy(krInput[:32], mPrime) 209 | copy(krInput[32:], h) 210 | kr := sha3.Sum512(krInput[:]) 211 | var kBar [KyberSSBytes]byte 212 | var jInput [paramsSymBytes + Kyber512CTBytes]byte 213 | copy(jInput[:paramsSymBytes], z) 214 | copy(jInput[paramsSymBytes:], ciphertext[:]) 215 | sha3.ShakeSum256(kBar[:], jInput[:]) 216 | cmp, err := indcpaEncrypt(mPrime, publicKey, kr[paramsSymBytes:], paramsK) 217 | fail := byte(subtle.ConstantTimeCompare(ciphertext[:], cmp) - 1) 218 | for i := 0; i < KyberSSBytes; i++ { 219 | sharedSecretFixedLength[i] = kr[i] ^ (fail & (kr[i] ^ kBar[i])) 220 | } 221 | byteopsZeroBytes(mPrime) 222 | byteopsZeroBytes(krInput[:]) 223 | byteopsZeroBytes(kr[:]) 224 | byteopsZeroBytes(kBar[:]) 225 | return sharedSecretFixedLength, err 226 | } 227 | 228 | // KemDecrypt768 takes a ciphertext (from KeyEncrypt768), 229 | // a private key (from KemKeypair768) and returns a 32-byte shared secret. 230 | // An accompanying error is returned if no sufficient 231 | // randomness could be obtained from the system. 232 | func KemDecrypt768( 233 | ciphertext [Kyber768CTBytes]byte, 234 | privateKey [Kyber768SKBytes]byte, 235 | ) ([KyberSSBytes]byte, error) { 236 | const paramsK = 3 237 | var sharedSecretFixedLength [KyberSSBytes]byte 238 | indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK768] 239 | pki := paramsIndcpaSecretKeyBytesK768 + paramsIndcpaPublicKeyBytesK768 240 | publicKey := privateKey[paramsIndcpaSecretKeyBytesK768:pki] 241 | h := privateKey[pki : pki+paramsSymBytes] 242 | z := privateKey[Kyber768SKBytes-paramsSymBytes:] 243 | mPrime := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) 244 | var krInput [64]byte 245 | copy(krInput[:32], mPrime) 246 | copy(krInput[32:], h) 247 | kr := sha3.Sum512(krInput[:]) 248 | var kBar [KyberSSBytes]byte 249 | var jInput [paramsSymBytes + Kyber768CTBytes]byte 250 | copy(jInput[:paramsSymBytes], z) 251 | copy(jInput[paramsSymBytes:], ciphertext[:]) 252 | sha3.ShakeSum256(kBar[:], jInput[:]) 253 | cmp, err := indcpaEncrypt(mPrime, publicKey, kr[paramsSymBytes:], paramsK) 254 | fail := byte(subtle.ConstantTimeCompare(ciphertext[:], cmp) - 1) 255 | for i := 0; i < KyberSSBytes; i++ { 256 | sharedSecretFixedLength[i] = kr[i] ^ (fail & (kr[i] ^ kBar[i])) 257 | } 258 | byteopsZeroBytes(mPrime) 259 | byteopsZeroBytes(krInput[:]) 260 | byteopsZeroBytes(kr[:]) 261 | byteopsZeroBytes(kBar[:]) 262 | return sharedSecretFixedLength, err 263 | } 264 | 265 | // KemDecrypt1024 takes a ciphertext (from KeyEncrypt1024), 266 | // a private key (from KemKeypair1024) and returns a 32-byte shared secret. 267 | // An accompanying error is returned if no sufficient 268 | // randomness could be obtained from the system. 269 | func KemDecrypt1024( 270 | ciphertext [Kyber1024CTBytes]byte, 271 | privateKey [Kyber1024SKBytes]byte, 272 | ) ([KyberSSBytes]byte, error) { 273 | const paramsK = 4 274 | var sharedSecretFixedLength [KyberSSBytes]byte 275 | indcpaPrivateKey := privateKey[:paramsIndcpaSecretKeyBytesK1024] 276 | pki := paramsIndcpaSecretKeyBytesK1024 + paramsIndcpaPublicKeyBytesK1024 277 | publicKey := privateKey[paramsIndcpaSecretKeyBytesK1024:pki] 278 | h := privateKey[pki : pki+paramsSymBytes] 279 | z := privateKey[Kyber1024SKBytes-paramsSymBytes:] 280 | mPrime := indcpaDecrypt(ciphertext[:], indcpaPrivateKey, paramsK) 281 | var krInput [64]byte 282 | copy(krInput[:32], mPrime) 283 | copy(krInput[32:], h) 284 | kr := sha3.Sum512(krInput[:]) 285 | var kBar [KyberSSBytes]byte 286 | var jInput [paramsSymBytes + Kyber1024CTBytes]byte 287 | copy(jInput[:paramsSymBytes], z) 288 | copy(jInput[paramsSymBytes:], ciphertext[:]) 289 | sha3.ShakeSum256(kBar[:], jInput[:]) 290 | cmp, err := indcpaEncrypt(mPrime, publicKey, kr[paramsSymBytes:], paramsK) 291 | fail := byte(subtle.ConstantTimeCompare(ciphertext[:], cmp) - 1) 292 | for i := 0; i < KyberSSBytes; i++ { 293 | sharedSecretFixedLength[i] = kr[i] ^ (fail & (kr[i] ^ kBar[i])) 294 | } 295 | byteopsZeroBytes(mPrime) 296 | byteopsZeroBytes(krInput[:]) 297 | byteopsZeroBytes(kr[:]) 298 | byteopsZeroBytes(kBar[:]) 299 | return sharedSecretFixedLength, err 300 | } 301 | -------------------------------------------------------------------------------- /poly.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | type poly [paramsN]int16 7 | type polyvec []poly 8 | 9 | // polyCompress lossily compresses and subsequently serializes a polynomial. 10 | func polyCompress(a poly, paramsK int) []byte { 11 | var t [8]byte 12 | a = polyCSubQ(a) 13 | rr := 0 14 | switch paramsK { 15 | case 2, 3: 16 | r := make([]byte, paramsPolyCompressedBytesK768) 17 | for i := 0; i < paramsN/8; i++ { 18 | for j := 0; j < 8; j++ { 19 | t[j] = byte((((uint32(a[8*i+j]) << 4) + paramsQDivBy2Ceil) * params2Pow28DivByQ) >> 28) 20 | } 21 | r[rr] = t[0] | (t[1] << 4) 22 | r[rr+1] = t[2] | (t[3] << 4) 23 | r[rr+2] = t[4] | (t[5] << 4) 24 | r[rr+3] = t[6] | (t[7] << 4) 25 | rr += 4 26 | } 27 | return r 28 | default: 29 | r := make([]byte, paramsPolyCompressedBytesK1024) 30 | for i := 0; i < paramsN/8; i++ { 31 | for j := 0; j < 8; j++ { 32 | t[j] = byte((((uint32(a[8*i+j]) << 5) + (paramsQDivBy2Ceil - 1)) * params2Pow27DivByQ) >> 27) 33 | } 34 | r[rr] = t[0] | (t[1] << 5) 35 | r[rr+1] = (t[1] >> 3) | (t[2] << 2) | (t[3] << 7) 36 | r[rr+2] = (t[3] >> 1) | (t[4] << 4) 37 | r[rr+3] = (t[4] >> 4) | (t[5] << 1) | (t[6] << 6) 38 | r[rr+4] = (t[6] >> 2) | (t[7] << 3) 39 | rr += 5 40 | } 41 | return r 42 | } 43 | } 44 | 45 | // polyDecompress de-serializes and subsequently decompresses a polynomial, 46 | // representing the approximate inverse of polyCompress. 47 | // Note that compression is lossy, and thus decompression will not match the 48 | // original input. 49 | func polyDecompress(a []byte, paramsK int) poly { 50 | var r poly 51 | var t [8]byte 52 | aa := 0 53 | switch paramsK { 54 | case 2, 3: 55 | for i := 0; i < paramsN/2; i++ { 56 | r[2*i] = int16(((uint16(a[aa]&15) * uint16(paramsQ)) + 8) >> 4) 57 | r[2*i+1] = int16(((uint16(a[aa]>>4) * uint16(paramsQ)) + 8) >> 4) 58 | aa++ 59 | } 60 | case 4: 61 | for i := 0; i < paramsN/8; i++ { 62 | t[0] = a[aa] 63 | t[1] = (a[aa] >> 5) | (a[aa+1] << 3) 64 | t[2] = a[aa+1] >> 2 65 | t[3] = (a[aa+1] >> 7) | (a[aa+2] << 1) 66 | t[4] = (a[aa+2] >> 4) | (a[aa+3] << 4) 67 | t[5] = a[aa+3] >> 1 68 | t[6] = (a[aa+3] >> 6) | (a[aa+4] << 2) 69 | t[7] = a[aa+4] >> 3 70 | aa += 5 71 | for j := 0; j < 8; j++ { 72 | r[8*i+j] = int16(((uint32(t[j]&31) * uint32(paramsQ)) + 16) >> 5) 73 | } 74 | } 75 | } 76 | return r 77 | } 78 | 79 | // polyToBytes serializes a polynomial into an array of bytes. 80 | func polyToBytes(a poly) []byte { 81 | var t0, t1 uint16 82 | r := make([]byte, paramsPolyBytes) 83 | a = polyCSubQ(a) 84 | for i := 0; i < paramsN/2; i++ { 85 | t0 = uint16(a[2*i]) 86 | t1 = uint16(a[2*i+1]) 87 | r[3*i+0] = byte(t0 >> 0) 88 | r[3*i+1] = byte(t0>>8) | byte(t1<<4) 89 | r[3*i+2] = byte(t1 >> 4) 90 | } 91 | return r 92 | } 93 | 94 | // polyFromBytes de-serialises an array of bytes into a polynomial, 95 | // and represents the inverse of polyToBytes. 96 | func polyFromBytes(a []byte) poly { 97 | var r poly 98 | for i := 0; i < paramsN/2; i++ { 99 | r[2*i] = int16(((uint16(a[3*i+0]) >> 0) | (uint16(a[3*i+1]) << 8)) & 0xFFF) 100 | r[2*i+1] = int16(((uint16(a[3*i+1]) >> 4) | (uint16(a[3*i+2]) << 4)) & 0xFFF) 101 | } 102 | return r 103 | } 104 | 105 | // polyFromMsg converts a 32-byte message to a polynomial. 106 | func polyFromMsg(msg []byte) poly { 107 | var r poly 108 | var mask int16 109 | for i := 0; i < paramsN/8; i++ { 110 | for j := 0; j < 8; j++ { 111 | mask = -int16((msg[i] >> j) & 1) 112 | r[8*i+j] = mask & int16((paramsQ+1)/2) 113 | } 114 | } 115 | return r 116 | } 117 | 118 | // polyToMsg converts a polynomial to a 32-byte message 119 | // and represents the inverse of polyFromMsg. 120 | func polyToMsg(a poly) []byte { 121 | msg := make([]byte, paramsSymBytes) 122 | var t uint32 123 | a = polyCSubQ(a) 124 | for i := 0; i < paramsN/8; i++ { 125 | msg[i] = 0 126 | for j := 0; j < 8; j++ { 127 | t = (uint32(a[8*i+j]) << 1) + paramsQDivBy2Ceil 128 | t = ((t * params2Pow28DivByQ) >> 28) & 1 129 | msg[i] |= byte(t << j) 130 | } 131 | } 132 | return msg 133 | } 134 | 135 | // polyGetNoise samples a polynomial deterministically from a seed 136 | // and nonce, with the output polynomial being close to a centered 137 | // binomial distribution. 138 | func polyGetNoise(seed []byte, nonce byte, paramsK int) poly { 139 | switch paramsK { 140 | case 2: 141 | l := paramsETAK512 * paramsN / 4 142 | p := indcpaPrf(l, seed, nonce) 143 | return byteopsCbd(p, paramsK) 144 | default: 145 | l := paramsETAK768K1024 * paramsN / 4 146 | p := indcpaPrf(l, seed, nonce) 147 | return byteopsCbd(p, paramsK) 148 | } 149 | } 150 | 151 | // polyNtt computes a negacyclic number-theoretic transform (NTT) of 152 | // a polynomial in-place; the input is assumed to be in normal order, 153 | // while the output is in bit-reversed order. 154 | func polyNtt(r poly) poly { 155 | return ntt(r) 156 | } 157 | 158 | // polyInvNttToMont computes the inverse of a negacyclic number-theoretic 159 | // transform (NTT) of a polynomial in-place; the input is assumed to be in 160 | // bit-reversed order, while the output is in normal order. 161 | func polyInvNttToMont(r poly) poly { 162 | return nttInv(r) 163 | } 164 | 165 | // polyBaseMulMontgomery performs the multiplication of two polynomials 166 | // in the number-theoretic transform (NTT) domain. 167 | func polyBaseMulMontgomery(a poly, b poly) poly { 168 | for i := 0; i < paramsN/4; i++ { 169 | a[4*i+0], a[4*i+1] = nttBaseMul( 170 | a[4*i+0], a[4*i+1], 171 | b[4*i+0], b[4*i+1], 172 | nttZetas[64+i], 173 | ) 174 | a[4*i+2], a[4*i+3] = nttBaseMul( 175 | a[4*i+2], a[4*i+3], 176 | b[4*i+2], b[4*i+3], 177 | -nttZetas[64+i], 178 | ) 179 | } 180 | return a 181 | } 182 | 183 | // polyToMont performs the in-place conversion of all coefficients 184 | // of a polynomial from the normal domain to the Montgomery domain. 185 | func polyToMont(r poly) poly { 186 | for i := 0; i < paramsN; i++ { 187 | r[i] = byteopsMontgomeryReduce(int32(r[i]) * int32(paramsMontFactor)) 188 | } 189 | return r 190 | } 191 | 192 | // polyReduce applies Barrett reduction to all coefficients of a polynomial. 193 | func polyReduce(r poly) poly { 194 | for i := 0; i < paramsN; i++ { 195 | r[i] = byteopsBarrettReduce(r[i]) 196 | } 197 | return r 198 | } 199 | 200 | // polyCSubQ applies the conditional subtraction of `Q` to each coefficient 201 | // of a polynomial. 202 | func polyCSubQ(r poly) poly { 203 | for i := 0; i < paramsN; i++ { 204 | r[i] = byteopsCSubQ(r[i]) 205 | } 206 | return r 207 | } 208 | 209 | // polyAdd adds two polynomials. 210 | func polyAdd(a poly, b poly) poly { 211 | for i := 0; i < paramsN; i++ { 212 | a[i] += b[i] 213 | } 214 | return a 215 | } 216 | 217 | // polySub subtracts two polynomials. 218 | func polySub(a poly, b poly) poly { 219 | for i := 0; i < paramsN; i++ { 220 | a[i] -= b[i] 221 | } 222 | return a 223 | } 224 | 225 | // polyvecNew instantiates a new vector of polynomials. 226 | func polyvecNew(paramsK int) polyvec { 227 | var pv polyvec = make([]poly, paramsK) 228 | return pv 229 | } 230 | 231 | // polyvecCompress lossily compresses and serializes a vector of polynomials. 232 | func polyvecCompress(a polyvec, paramsK int) []byte { 233 | polyvecCSubQ(a, paramsK) 234 | rr := 0 235 | switch paramsK { 236 | case 2, 3: 237 | var rSize int 238 | if paramsK == 2 { 239 | rSize = paramsPolyvecCompressedBytesK512 240 | } else { 241 | rSize = paramsPolyvecCompressedBytesK768 242 | } 243 | r := make([]byte, rSize) 244 | var t [4]uint16 245 | for i := 0; i < paramsK; i++ { 246 | for j := 0; j < paramsN/4; j++ { 247 | for k := 0; k < 4; k++ { 248 | t[k] = uint16(((((uint64(a[i][4*j+k]) << 10) + uint64(paramsQDivBy2Ceil)) * params2Pow32DivByQ) >> 32) & 0x3ff) 249 | } 250 | r[rr] = byte(t[0]) 251 | r[rr+1] = byte((t[0] >> 8) | (t[1] << 2)) 252 | r[rr+2] = byte((t[1] >> 6) | (t[2] << 4)) 253 | r[rr+3] = byte((t[2] >> 4) | (t[3] << 6)) 254 | r[rr+4] = byte(t[3] >> 2) 255 | rr += 5 256 | } 257 | } 258 | return r 259 | default: 260 | r := make([]byte, paramsPolyvecCompressedBytesK1024) 261 | var t [8]uint16 262 | for i := 0; i < paramsK; i++ { 263 | for j := 0; j < paramsN/8; j++ { 264 | for k := 0; k < 8; k++ { 265 | t[k] = uint16(((((uint64(a[i][8*j+k]) << 11) + uint64(paramsQDivBy2Ceil-1)) * params2Pow31DivByQ) >> 31) & 0x7ff) 266 | } 267 | r[rr] = byte(t[0]) 268 | r[rr+1] = byte((t[0] >> 8) | (t[1] << 3)) 269 | r[rr+2] = byte((t[1] >> 5) | (t[2] << 6)) 270 | r[rr+3] = byte(t[2] >> 2) 271 | r[rr+4] = byte((t[2] >> 10) | (t[3] << 1)) 272 | r[rr+5] = byte((t[3] >> 7) | (t[4] << 4)) 273 | r[rr+6] = byte((t[4] >> 4) | (t[5] << 7)) 274 | r[rr+7] = byte(t[5] >> 1) 275 | r[rr+8] = byte((t[5] >> 9) | (t[6] << 2)) 276 | r[rr+9] = byte((t[6] >> 6) | (t[7] << 5)) 277 | r[rr+10] = byte(t[7] >> 3) 278 | rr += 11 279 | } 280 | } 281 | return r 282 | } 283 | } 284 | 285 | // polyvecDecompress de-serializes and decompresses a vector of polynomials and 286 | // represents the approximate inverse of polyvecCompress. Since compression is lossy, 287 | // the results of decompression will may not match the original vector of polynomials. 288 | func polyvecDecompress(a []byte, paramsK int) polyvec { 289 | r := polyvecNew(paramsK) 290 | aa := 0 291 | switch paramsK { 292 | case 2, 3: 293 | var t [4]uint16 294 | for i := 0; i < paramsK; i++ { 295 | for j := 0; j < paramsN/4; j++ { 296 | t[0] = uint16(a[aa]) | (uint16(a[aa+1]) << 8) 297 | t[1] = (uint16(a[aa+1]) >> 2) | (uint16(a[aa+2]) << 6) 298 | t[2] = (uint16(a[aa+2]) >> 4) | (uint16(a[aa+3]) << 4) 299 | t[3] = (uint16(a[aa+3]) >> 6) | (uint16(a[aa+4]) << 2) 300 | aa += 5 301 | for k := 0; k < 4; k++ { 302 | r[i][4*j+k] = int16((uint32(t[k]&0x3FF)*uint32(paramsQ) + 512) >> 10) 303 | } 304 | } 305 | } 306 | case 4: 307 | var t [8]uint16 308 | for i := 0; i < paramsK; i++ { 309 | for j := 0; j < paramsN/8; j++ { 310 | t[0] = uint16(a[aa]) | (uint16(a[aa+1]) << 8) 311 | t[1] = (uint16(a[aa+1]) >> 3) | (uint16(a[aa+2]) << 5) 312 | t[2] = (uint16(a[aa+2]) >> 6) | (uint16(a[aa+3]) << 2) | (uint16(a[aa+4]) << 10) 313 | t[3] = (uint16(a[aa+4]) >> 1) | (uint16(a[aa+5]) << 7) 314 | t[4] = (uint16(a[aa+5]) >> 4) | (uint16(a[aa+6]) << 4) 315 | t[5] = (uint16(a[aa+6]) >> 7) | (uint16(a[aa+7]) << 1) | (uint16(a[aa+8]) << 9) 316 | t[6] = (uint16(a[aa+8]) >> 2) | (uint16(a[aa+9]) << 6) 317 | t[7] = (uint16(a[aa+9]) >> 5) | (uint16(a[aa+10]) << 3) 318 | aa += 11 319 | for k := 0; k < 8; k++ { 320 | r[i][8*j+k] = int16((uint32(t[k]&0x7FF)*uint32(paramsQ) + 1024) >> 11) 321 | } 322 | } 323 | } 324 | } 325 | return r 326 | } 327 | 328 | // polyvecToBytes serializes a vector of polynomials. 329 | func polyvecToBytes(a polyvec, paramsK int) []byte { 330 | r := make([]byte, paramsK*paramsPolyBytes) 331 | for i := 0; i < paramsK; i++ { 332 | copy(r[i*paramsPolyBytes:], polyToBytes(a[i])) 333 | } 334 | return r 335 | } 336 | 337 | // polyvecFromBytes deserializes a vector of polynomials. 338 | func polyvecFromBytes(a []byte, paramsK int) polyvec { 339 | r := polyvecNew(paramsK) 340 | for i := 0; i < paramsK; i++ { 341 | start := (i * paramsPolyBytes) 342 | end := (i + 1) * paramsPolyBytes 343 | r[i] = polyFromBytes(a[start:end]) 344 | } 345 | return r 346 | } 347 | 348 | // polyvecNtt applies forward number-theoretic transforms (NTT) 349 | // to all elements of a vector of polynomials. 350 | func polyvecNtt(r polyvec, paramsK int) { 351 | for i := 0; i < paramsK; i++ { 352 | r[i] = polyNtt(r[i]) 353 | } 354 | } 355 | 356 | // polyvecInvNttToMont applies the inverse number-theoretic transform (NTT) 357 | // to all elements of a vector of polynomials and multiplies by Montgomery 358 | // factor `2^16`. 359 | func polyvecInvNttToMont(r polyvec, paramsK int) { 360 | for i := 0; i < paramsK; i++ { 361 | r[i] = polyInvNttToMont(r[i]) 362 | } 363 | } 364 | 365 | // polyvecPointWiseAccMontgomery pointwise-multiplies elements of polynomial-vectors 366 | // `a` and `b`, accumulates the results into `r`, and then multiplies by `2^-16`. 367 | func polyvecPointWiseAccMontgomery(a polyvec, b polyvec, paramsK int) poly { 368 | r := polyBaseMulMontgomery(a[0], b[0]) 369 | for i := 1; i < paramsK; i++ { 370 | t := polyBaseMulMontgomery(a[i], b[i]) 371 | r = polyAdd(r, t) 372 | } 373 | return polyReduce(r) 374 | } 375 | 376 | // polyvecReduce applies Barrett reduction to each coefficient of each element 377 | // of a vector of polynomials. 378 | func polyvecReduce(r polyvec, paramsK int) { 379 | for i := 0; i < paramsK; i++ { 380 | r[i] = polyReduce(r[i]) 381 | } 382 | } 383 | 384 | // polyvecCSubQ applies the conditional subtraction of `Q` to each coefficient 385 | // of each element of a vector of polynomials. 386 | func polyvecCSubQ(r polyvec, paramsK int) { 387 | for i := 0; i < paramsK; i++ { 388 | r[i] = polyCSubQ(r[i]) 389 | } 390 | } 391 | 392 | // polyvecAdd adds two vectors of polynomials. 393 | func polyvecAdd(a polyvec, b polyvec, paramsK int) { 394 | for i := 0; i < paramsK; i++ { 395 | a[i] = polyAdd(a[i], b[i]) 396 | } 397 | } 398 | -------------------------------------------------------------------------------- /mlkem_test.go: -------------------------------------------------------------------------------- 1 | /* SPDX-FileCopyrightText: © 2020-2026 Nadim Kobeissi 2 | * SPDX-License-Identifier: MIT */ 3 | 4 | package kyberk2so 5 | 6 | import ( 7 | "crypto/subtle" 8 | "encoding/hex" 9 | "testing" 10 | ) 11 | 12 | // ML-KEM test vectors from https://github.com/C2SP/CCTV/tree/main/ML-KEM 13 | // These are the official FIPS 203 intermediate test vectors 14 | 15 | // ML-KEM-512 test vector 16 | // 17 | //nolint:gosec,lll // Test vectors are not credentials and must be long hex strings 18 | var mlkem512TestVector = struct { 19 | dk string // decapsulation key (hex) 20 | c string // ciphertext (hex) 21 | K string // expected shared secret (hex) 22 | }{ 23 | dk: "2d17c4ba262edec00f00f2247692b87bc2564ffc7910f113fb8c7d8b1c3981ac95d8a8488781bedab191e69170151c2ce942191276691eeb395cc29afde6091591ccef038ae9235a201510eef014554626010c9574e7ccfa5657d0999445664045fb20a22a5b3168268b12abfb9933e2486e45b84b8d3973785c8e158915fb499493e0195cf70443bcc4517b7cc323b0051ba31ae44bdb415e63c66fa0f14334f75e376411b91bb7296a18f323b6ef37bee5c8b84ecbb65f9c00bd504b316a9a6a7c6091d28fe65c3436b5c9c0f89a47104767f7b0f79a8fc2901d49e31611b2b18c52b510ac054e926229f532d2187657244836254c193014fde766e8d29038e0a388774cb55335439732bb032399c32301052a8f155e3a49a33914581729b11fe0602bbac54b495c1a96b5ff16152a659617c7b79d8245c7d4aba65b5d5ccc941e24c09dea2630d1acda6811b21b78ac89b662581cba46586257c7348022632948cb1cc8a12c8c6712176e6154d5b2ce9e17b20ed770fc959f03f4a2e251845ca128031c5f37631f6e6241fce131164092ba676483c7050890279672a9f8a2329b752eb9b09eb7e4747e13587ec54b99582e2d905ed2b55ef7aa52a185caacf037d4946a95c94f0b80423fd4884a8370af576f0e4b1b6c851583312500b4420676a288019bfd8c2c24116eec8c5dd5822da84c446e09c47887b9c912b8c1786f9ad83e1d36961750175398606b42b2c3a095d104bc0aac455a72554aa9ac1d9ac5a801ab8c6723d53a259ba20c094bbe3094534c88817703a114f94b2070c4901a4bb7d02858b31dbc614aa7c3a10344956b940eb619adafd9c29397bd63c0600d910e97a0554515163221194dc2bcecb5786f081f79478af2f2559056b66453789ce53877db743a3c8378d20f92b3c42f46a712aa92eb765de933bd0a53a531f552218c5da3b57775881bbd8240eabc1881ec3519ca80243a8fc4ccaa7108099a959726b72aee522780649c91f4a82aab2045834b2f64039e542c37b7b81f602be2f0688cb5250f295591c999390499822140ff4325011860fa753b81e85bf29c866c361d910341f296c64b46c2a2e30b1535a5c0602593415d156b43036b21b14ba3a0c72e848c5ee03466cab97721d83defdab1f6708971d3c4581441ce397fe7bbc7a08acfdcc417c5f56dfdd4423e306f1f69a86e5b5ba3e031bf92a16702861a51a2feb974f4844812b8302dc83026ea566a1c110cd2c4af48bedd742f1c02cd3fca0dfb8331f414b115849bcb05a2df615b49a90ec0a433e8787e185142aa406003d35409ec842af304459b40fffa0d84340156b5767ee0ca6e2850ec8ccaed317ecf711e27d59b3c985555e44c935988092981da95b42573b9bc38122ed44a800510756429f1b0640685b048597842cbb93dc6b2f9144dcf41a624eb90202a30381410f4cbaaccf61e485524ee593ad7d5974f774ae59ba20c7c6eb0009b39962dc0f828e8538ef583b4dc40c09555a3968ab257d407cc4ab801c7521bf7564b163fab927b4703225b1b497a096b4eb984dfd3512ecb2eaea46fb53c3b3840af3c4aaf78b1461e0564290c0a8f93a1e1b1c50cdb5fd56970046103600a1cc109b58cc32e52c7a168e17107a9c77cc448b01a5a7dc90603d708c6d717c3165e772c74763821f35934a613b0297246370020eb113645d75593c5130072acc0483118067bfb8093b518066318c3f936bd29e8acba8318c68a3c47a1be713c5b5a3c3df82514a5db7f70150b44b3927aa5c7b5507f8e693eed04369340141b1c75069b5f3abb016833b936a1adf7ccbedfe775f0494acb6404307a787e19b2fcba2aa8b3cf94ea6aa4303cfe4821fc9a62ee5acbcaa42556d046e4ab2e65e57e4dcc874276c89f980ac1a52fde549064b7abeab30e81d540af393a51bb917d87098ae238946033f3bb31d82b418ea863b9fc2008613e4751aed8c9aaa41840579116b571ad6df0cfc1185b17612008900d104a83b67a36acd8c783c5b1c21026f03b4ab57b23bd160a103920729b13334395a500a2a5a126bd66c75c5c2a046a97ebb8565cbb65fc6a3e1c11a990e20a9c88c96aa3924961159ef22082e45c86a9bb80d7b8936cc9eec593e5e4523ddc5438c819551328557982cf6784b1720e4ed5ac0add457f573a041465bcbd7ca4e1d7d53eaadeda511962a36eb0a9ce0ecbcfbbbd9a2636e23a0c1c22683943639cd44af051ff235b8dfbb73a4dc6f5785a6f2b42e843228be53eb768d64c6f9d4355ae95f083e51ed57c437310", 24 | c: "96c7855b455b6dbb607be569d4cbc46665f645eaf96b54c617bc9b4485ddee95271344a6c8e42f74c3ad95f1abcbccbd4e0eb7e65cc738a06aa1cad3ef9c27e2c33f1085f9cb8f33c547c7f74385a0ec64d99d5d485204b4642d5298da8bf0fc8cd6859416215f9bd36f703548dc7dd9fdce96ff50c261bc1670ce55d92a785348ca16d49cf68760e4f62f9f8a628bc2376b67239ef7741f8b8882decffc4de88d108a6579fb54781929d1b19dbfddb84d656ede851f9e5d8c832ac07421e7d1153c69e383afbd67cad7915129eef3c8f760e7f71864c9eff2575d96e5b9956509942208c6a2c15571e8aa0439a20349050f6cddaf903b163206deb765c49d440a5182ae53d0438e097e9520d428a1c9f4a70602eb548b1411dfc36120d2ab6a2d01418d9a27bb785f82bf7e9d93f85e8b9c711aabbceeb27ef01521c3c1891de892ffaaf5e03810479c6907dd09e2d71930d6bf2b99ddf05898c2610681226f5a4bf1c006e940deb9665432396d4920336c413471e077c38b85413b21e5836daee8cb4446bf328ab759bc4277fbc1043e67fe664358ea352f1babe7a1fd363d41e37a5207325c565e1794c3ce36ec97fd72fd7f04a18a266383630e92e02e9700a26830c6ead2057a7883bb46606e64efbd92feb5a87cf8eb40835b3f201288af60abb625972f774608e65189e016cc7b61ac28806966173e55d8572f7eb5f5f2de7adcad59f00e6f66623e7640645069877b779d373ad09400dfa2dcedeb65c9786bb7cac61de399eec7779ddbebda8a3262054a1318157de19d76612f71ae5829cec49a7b895f36e72cdc215058338aa4d9b99815ed13def463bb27ad3e8a9d0995f964436519bd7037e3cf73f79bec5115762ba82314d2152701e325aefb33a7cdb9721c7c91cc84633e69bac5e0bfa7be28bc496659d64ee9dcdbfd384ea48ea125ab89a3b6e1d0692b728b171111115d2bfc055a73bdc37a70f2145c7d15a69b1ebad781dfa3cdb5418b2d299933585726f884a20b6f61624a520c6ee91db94df42fbf0d0f6f3bbf16bfbf4ef014b644cc4a60818b31aef64cbb60da3f", 25 | K: "62a8c220b01793ecd183dea9762c5602211e0aab001cbc892d0a95693ab17cc1", 26 | } 27 | 28 | // ML-KEM-768 test vector 29 | // 30 | //nolint:gosec,lll // Test vectors are not credentials and must be long hex strings 31 | var mlkem768TestVector = struct { 32 | dk string 33 | c string 34 | K string 35 | }{ 36 | dk: "3261790e9484c6453502778de5faccecc56d94fbbb3d6a039d03ceb70b254f2a201a9192b5992885eb6c9f44b9bb70c584cc4891782e47caa4124cab6db66f6eacb09aec6d6cf5324ed22cfc2a3ca366ad3da0b0b9b0aa1ae7c07c9776c7f2734c28a4a57577378aa2b7fa49e489a8f7a2c80d360e622a3a41180fb1167e56533ff3e3a00365090b8455170896946c7fe95b100cd176bf3c442d6c96e8d4b996a980d671a2406c15a826cab288359cfa2b5a3580f5bb5584b6c8dcbc5024c7149fbc580f081da2eb739a1c560a39b1d1b02b15f730fef1315589455ca13e3409526a5640f3924090586cfd376c21375b77630b7e0aabdc8c811e564c7155158ac14cff898ee621af8c05cfcd2cc41be341215818e6e5a37c167a381ba1c4a65724f64da94ac06e530d260229bb2c61a1d17630aa3b3f4aa4a1db7f740bafbd9aba41490f19b6127dfb651c2032317123662830c6eab9ed4b47037c30a5d4048ea69de1769323a85726766277a82554c00cf3b500f6a9ba070b6e1e010fe667429d5259d027ccaaec559c760ae99b62c014cf76b08c89a72d3e233af5251976e288da3a8fa2820e6ca8ca953ab8f504060fd2c0104558af67c34691c1c4c2878d5904c94390b1639548e206659804a3759a6a7930921a3fc2853d8e90cd94b523fe1a30704030f04b9d4fc919fd22c27966aa53180916009e3f831a47b53f5951087d110c010cb81a415d88c3cb19031e3f30a21496903f59ce75742227b22ca7687ad3b05c0aca42e3747bc5fb1683b796a10bac6a158a745836478829707b70aca820d3a9b6adda8091bc0c44c9bca1f57bae60165940ac87815c1e36a783a88b2b68655d22ccebd1490d4440a00775dd1a68f0d3125c7cb48d8a91db2993542c5541137e817206705bb23a261eb3740d952b1af92181f8a629e156b7a6d703d9225706a5bee26845bd80552df9a620f16b4c1444ed2514ec8c2aed453bedb9805174ada9610c91aca500ccb221c109a56377bfc043fc0410340c0d4a509be7d1252d65af888905ad42bfd9cc0e84b63d82032de9cb1cc1264fb6b65c2304818c566fdb36c47e43b3e43b405f7b8944340f78830901507d145b1818c86d9b003d03e5a33310ca66c44d63b2bdb427cc0dd68fd32176292359ce639c9177454e6a36d7cb808e13a9d309310b455202ec6bcf2891d2590755b92e4f0354e766b049d77b4b5cbd8b332a6d1261cc58a384ebb842d92273202eb7b45570cc42fb039c18b4a1308062d6710601c209f5b425d3a6b8735911fdb6bb26a556595c5e69d66a5fc346552526d8a8a292b3cda869b28fab0b2cb6162e583e8c9c558c4c8c457cb83e67c2f03960011091fdf5a35b88712d3aa5e0d0961242653850acca27be2a8523b0557dc38a3fd90acddb190c813c3e86d50e2dac874f2316c607a3582aacef1c14dee409551a9e02f60e9737c9940799b39932cd645341444414c77f4fa9046ee4020fb68d607b46759615803572502a296c291965b2c92320c663d504e06a0bce3286a854c74a6256e168c0b750805359986493a25f75b5f8923bbbd4b2a30b6e9092ce0a5035939c47e900c4235a447796a426713e1bd269a13493d0f1a25721155a28143a355f99c582c2d258a270b9f78941af004116e94f2f9825af17c50b600705ea1a9c2440df6803518a5ba558acb5b732d06a3c80e555fa374d80d82f08c5b47ca12cbe6520340538c2100b5e094392acacafe1ce4ad74fa451c4379219140103e9c3ba7ba683cddca0d3b02d57104c292b2e25bc791d8b11d5079477d7b0f930176fb0a5cda99afbfb6fca86226cb8152b250a536ca60b6ccf93880007068b41bbc5c6e9b8bd92af8a717e37c69ac7db7e76a2c36eb028c1e098915274b4d59def6c42d3c25bb35310846809c96bc43b639e52a3730654cc8db548faf21e446ca874a3caba777bac25b56902481c79a744d70d12130ad6bc8136349e3234c556267e97b0a8ccf772528299fa26111172c223f2484102917bdb6de7770682698763e5b2038779f812363a727f40949b76b6913c8910d6f51dd48983ba149c070590582662fbc9053b667e1eea1ca7173e421576f2d8ad7bf415899484bd1530b4fc855354952cf28d0f1216dfba00ec100bdd76c4f4b405c913baa78958ae31c578463877138cf89255a3935cfbd1b77dbb5f66b87543131da4aabb6781427f417e841141dcb47fcd82a92aeb3c50030d71eb7f3b17b0207a6cdf654d563a420bb742ac30c0e01235910886ff045652d5035b7ab98a1b6abac88ea5996b965c2d3f3305e075bbca267e8547c6c7769fb9f91e20849b524b7822965fe036a515c60e24636a3665b23b878c5b7658f7c49f2059255c89536426510875cbdca111bdc29183459340c125df30c05a7698490193d88c52410a14a7f12a118495413487b434568d105f2d7b9c0fc32e80a13e142528ca1825e0830cd41071aac7124042145be35e30d541c2ba31a82a5f479b3bad881164aa347386c9bfa505c9968fdea866120c8b4e809bdb1482d790a03fcb1ca88c15aae8047e1678ba66833ca430d592638bf3be19592027b3ccb044a9a4d01f3804c2fe0ca60cc7c017dc14dff60562f20d912650fc24449a78998d2b0ce4d9b131955affe7b5c12278cb5b23847c8090fc0578f71760918e9ee8274673845ca29f8d338fa89637265c518f902a9ba8ad9615764ce9835b322e0b5bb1fe928888318988e435cc3c3a918ccc773371a9687884f0822558ae634a54d7c74556528f2d7cb212f1901a82ae68905330b4550518b7b44c117f335f9c818635a2a29e59b39784b021d88354d707cfe0b3599b54fa4a1218261dc72a00cf2ccef8c6c2fe131899f9a239f545c4605296d7b39beb0832732eb608464df29d193b2f2c3b7c69cbbc2082cda14863071235a75544320c1c7930262f82ac7ba788c6c09586c2420d7385a0a6c50d050910e2956844b558fa78b6cb2969e866a0bcc1b7b980a02669c226b6c9267f6e66995d6b37eedc8e34f516ef48a69a3461f3742cc6268b26a8c158e4bf34c072d4e37a4e273ee3464aea81b57c52ab4d5a1e94a668c9a14612196f6a792646581a8ff545179323aaa8369c21181c1329464099cdf940fa56ce1f204ad4b048d5e783e1948b3493b273b2a33d8c0183e386786c7e7e421b1536610cecb16b7a98641b95693879a7d8bc444931cedaa6c45303aac0a48ac29041085526ffff11b531b1800f4e1fa75c4d008c4f9a112932c669d543551204405da8b4704649a5d8034c6224ae18950bd7b979342c03c7499f7bab9cdea742db9e086cd1d49a515250dbceb9f6e3fcc1c7d5306918964b21ddb22207e03e57f0600da8", 37 | c: "afb72488670b7d47769ee04d7479303a40b8b9d395eccdae13519e11f68df02993fa4fd89727963aecfaebdc3e9885815453be1c92e3d8eb8595d7e044ce8ad0b11765354a00729648fc38ffeaea30faf201f760c830c06300d0b602b764c903e4af7f0060ff4b21231e6142e12d11dac0d68ce4c5a65db1d9af1d9ecfebc8b4b10e5be9ac8525937e0da1366a23d6cd9441c52087dc2bf9cef4ea55371b0e00172310ed6225296269011ca3b8e402b2cdc859196f48ef89d7c82f78250960d1e384c73045f914bc8f16fbbb53ac6bb33bb9658944f78b0e708204177cda7fd92bac79211cc691fb06b37f19a5b4728d5e44bf1393167e61d7a46a2d092eb5f8e155ef3d4ee2beae490bb97044971e90c055b6f829f0434366c03ef20d99db1d86aa1e94bb33550a1534075d2727c08a9e0671d7029b0607471a3b5230d2308006bd48533a2d34ee1a4e2badd2e29a7c76c83984fa0aadb9954c99a1258faecb5ac9304619c8ff6003510ea38a720006ce7b96ae58e668b90cd310c05a6d3be3a3f1ca5749596919811fbbc324bbd2025b680e4e5f595e1e83865f05494415a0c979107c5e880133526cb60b82314b6bfe0d7a2d4be6e936fa9d9fe64e16fcce1207bff6e28fb3a7f2fa0a019745cd0d53a44553a19cf96be357a4cfcde068389f47347b7b977f73755ccf05afead81b488fce6f2964def2b6d1a59557ba59ad71af9c2900b63836ff669e8de1438b929edf802d59f1c4b76f65d04f7ceb8f9e36f87d4d8391007e1990191938e14856ed4f84f63d38b9b655189d7f76ef508204af931a75e1fd011ff899f13d0bfff972cb1a679e2f073587dfdf3b8a6457fe897fcefd1ff8956bf3d7ad1355611ddd9002af05ba9f3a66346ab26b015035be428f3146227be21447c9dc002d7ea371f2e98a156a1f2f2205d43961fdadd1939763ee054d07ca1a6f1bae0f102d207a9f11039d5b6041d9e92071d72f755c57f3525d0e1e954a1e465b5406fd5af583aca7e48cdc5a9c266ddae6f3c124c4a9698aed2d69ce2c4d25485d70480fdd0da18a4f6d42aeaf97baa4858b440602582a634f96938eadb63f9cc74ac1e54fd0772a3ff66fa92dbdb6a4ec7c7923d0ee6fb5f9bc300701ac6c2f920d7e23188ed6798cf47f0df850f40a471cc8840ecbaf7834bb5b78b3ace2bd0a5e9dee5fafda8d73388671abe30574ff6e0478e3d1138d50289cd8254129a9cc3a7ac758428d73cf58ed848abb192c70b842809b141b4e82a899bcbd4d281f15443287bc3eec2330367688da43f24e30f3ad10f435508b461608b18d3baead359c35dd61d29ff574a100686c8b4521a82a10588cc11c91ff18bcf20b6c1cdfc323b09f33d00972b0a447ddd2a1d95c32efcc9d2624200aa3b049f382e8a987a76fab5bdd808988a7c5a2dc939f2b34fba7db71848b1caa6a20424f2136bc5d0f8c04639fc818ce2ad94bfc072891451675c6566cd668b6ae5eff71a1ff5ecf5769870c182b31c0f79e31e044eec7f9feea416e410b", 38 | K: "4b4eba37eff0315dc6009dcffb4dfbbb680f8f2ebde8715fa3d6daf70256a2d9", 39 | } 40 | 41 | // ML-KEM-1024 test vector 42 | // 43 | //nolint:gosec,lll // Test vectors are not credentials and must be long hex strings 44 | var mlkem1024TestVector = struct { 45 | dk string 46 | c string 47 | K string 48 | }{ 49 | dk: "82b25c61f7099f870f988428ad662203f75c638390c593035b3aa1a91092b2d276fddb56bcab637e00a26ee68058376ad13c0e5d7b31ed29b2c24710b47b60246214bf308f8dd77379590842740c84ab6d8adc71a1cb22c4e98e58112d803998a104bd606cb439e0b686384148a209f5d171e4b63e29f66863aacba2c5a489600dfb64b65ce21f848b4c7621c6a3e65792497d1354a72e22405057cf9330332b419ca396cf69d374df850a2481c943488ab45cb71c992e12034baa94272a32cc694294ab6867d4f8758f972520ec5f90159fda33ad91d893273ac9a3e35f2976bbff525a2de84cbc55acbca91fa9ea91700abb87323d2a67c95d62cad3dc534aa83ff3fb6837ea908f0b953ca88bfb01b62068a1ade3c19e7b153f8b34b19295e054334f59b73855c030e597b688c3d2a14a336a2929393e0b27171315805e5cc3053b6d90843d82b933c1cc1574c18d9dda933f3b33b8cb7bf7143f6f595b1ec7113fd46f629930f28c4d4f666ce24bccd8bca5d74488de3b2480b176c0eb6f9dd19434ec33563303fcf7cdbe09c55d4216ad524379e451e74a2121c442c4412de5e137ed18421e07779b598860cc2e4e42c10f58a01617936c4548453183739c5624c68915b79a25a90257b1437a1583a9107d7d0587bee15570f3c008b9140f779cdce06fba05ac57991020162bb2096911594109f9032a09007eea48e1b5b5e11c9643d75284256c5a73b935668abb12aeeab7b040f71e17a74235e166d1942f7caa890869ccb1184cd44a9e82a89f31e83602e21de2b5cacfc254282470e0c66b020a5b058043e9135ab06c4fe1329d7a280ba6dc3de017c1041a26cf3b10d305c73594c469ac11b091bcb20412dfdc3ef0b712aee31fc2a81bfbd9990ca296fe45bcb992ba92d4148591a2f80990f218a2991bb3f3098a36c6568db8a40ab03b9e017305d893b48285a5178ee6b80dd2c5555837c3e976afd36b895c10acb438ca7009ad7723b31b3aa6a680156ec3cfe4e813dbf010e6e5b39a890b76984d20e965c5a39a6cda801ec55c55245abe3b946bda7c8e70c99ac62e2ca2c2ce165722d710a1c443a6467f91c63ed53c3f223580fbd058b6c8b4f134ae3b490ae97466e1983b0e669b22615df1732df9e9a98d4575720571ffa60dc888a65f6644902a9ad89a93c4b22132515d808ca445bab49e3276caa8a48d272b823477a0bc151491c229ab38bd6a4485257374cbcd03d6a6c655b6a1d06751995fa1c394116476f399673b5b40857cb9c68721168a936d89a3008923b81c2d7eb94ba63c3eff45b4ccb48a9d2142e8d5238ce904376bcd1e088b864a6a5acab75b138b9be44b08f00825bc3a804835337cac36bc41ca8bab90384a51341875c7ad27d109afa36e2965cafe1922811762a077af789b8d6c490c0f102b62869ca38a83effb05f14ac82346645de63cb60bba30b665a3f1c9f842952aea89fe630f6404b63164a39692180d806d65da3c1be076ae77b42d665edd2b93c1210427909d294a2fbad460444032a727960210005b7b3ad367cc300738187b3c64bbc00b16257f09182537129b14bf94c482bfdb9b82634821862a7fb0234ae0b5685582de054cb5575e9f8464336609cbd7ad5b486117ac4ea2468be9147e8c96b3a07993d5725f85c31c79405541c2c13b3abdc55b2dfa2ab02842a711a2b877097769c22be065622ab0a92d5108bfa10c644935ee4652efca95fafca3b5cccf0187a45708cf68838ffa79427a0400f8439f15b11e064b2b777c0715aa8a91c96ea7f1a851f47f8f077514a36c901059892273ca8695b3b418c8187558d18f1b9153d6c0b966e08f9a675889aa519224cdc4fc1fee51082ec26cea593124435f4bf95b3d852ad9554953088aae602e0ad3023d17576123929b1150fa496b850ab4e5f372ab7188f3127d2dcb970df56c4f5a2548903bd5ec50fb9196cf2374d7cc728243757743083d4a93c4b6adca54314a3200fe5a303aba4c07d4cf3eb632eca5b353190decd984395192d1f63d3d9291e00aa17e0872df27a405832d0a7b43115b05e6a4cb9293812ab26eb2e148e4b5082ba99039d6847a03be1693afc09a3ab95044107586e0db02720145c84cc800b669c51c8a96e526c4a99970234409414ed623cced859032bbb74c7412a7271c8ef23a5a08c31551a2a5cb8ada78538ce6c96d11625cb45e87b133163b16a4705aba1514f006afccbccf3c5c05acbc4563771bb8508072745d0ba56f57566181c3073b6c2b0dda50a2a83e74c67bcd48c0820b809d03a6ae4193dbd3bc06d072d2c64fd1e0b408a9c29818cd2df7790f9779536b0a076b4fb57b56f8cbb8ea3290c2f8b381c993d9eb49d8b4873ec660895528ab9ac306a66ae8209e2db9b79cb89220fab9d5cbb290ac690c79199218229aa8276393a73c826459429baed8365b88733871450e9523959c3f28691a3a0622e923259d2c90a61ca1525caf75a3a4292036957a5966d55840927234e1c5445b6685396890e3ccb9869147e63ce0b23defbb1c00f46df3819dd4a5756a355f61c1793b25a9b713c3abd12605198083773cf26909ffc92cd423cd290ba1801c694e7c80d6e2c9c1b943c69a5e7387ade187b21bd2a598c49ec772cde74656f1b43fa21021bcb86db8f91e03522bb4a76823483033f68659b57fa972c42673795b9a5dafa229fd8818f940a1b38a925cf06af712332b789a6f64ce91c7010af8a531101de8541a3ba4b4b893bfe7e791e693a5987a3d2d7224aeb213c5aba14aa0686efbbf31258d37122c304151916a9291181ffe788864f52acf001d788a3d80e4ad14623e9c402090b73fae5cb4225802cc4ab9c323c8cb914231c31c14c12a20ab7b5df5b6815bc04d991a32b6ccd229cf9692bf2e549ceb66512be58afe284b1d4990e46b5bafcca72589024e0b710169445e793cbbda9ca2664882199a54c31484d9364c2545876758999027c8f6423fa1a9ea810731092ca41c2451f29452c24d1d120517e06eaac91a77a47dec3c036ff84bfda92bcb396a36a75007014c0b6882a939b7e2c109c6046c04e9c1e4b1bd83444cf7838ca909946b512cb8d06ed3ab66a62071eb439632cccbced23c06c3456cdabf321328a17263e6aa3fee0028bef312cc99477ac71c53909740502e340b7cc06111eeb90bb08c58bfb97d78acbb10e7240ee5672062783636565c1744217787ffa33287f5075464b97707aec6864b38c918e0ebaf40fc9aac6128db4863a40c80b9d23261344a370c7b2dcb76e38623e5443242bcce9fb872b4c589b886aeb9fc79347cbf216b6ea8d1c7f5c5802fa24d1e68a4dc161699f90681069898e41d34a82cced119189b6547587972c4b17851a292f728f638a90164b97416c5b5b9231f1c675cf12c8ec1ce4866673f61addf98950263680efb56124b5936862f5328a3de923c4b2435b543ceee3b9761b5515aaaaecbdbb9cf8202090c82a2e5301fd0aee4fa4b39b0302d1813ab87cc1ba15595c36ef953401a92b8a88465c29ca4062626bc45b2a762c4ed957e53551827322351701db42c73e340566a476f559ac776283816da810c76af10f41482577544302e80eb6ab2fb720c2a950464c62970a450e65983c336dde77039722c614143a2b72c40256cf2142365ebaf288b0d9d69b56671b45f72c90a26ad50229d8fd6291ae6306d614aba34b65af02fa7cc2f5d7c5c8f4b30dc715f65f82c6320088fe0cfd4f05dc63a22ffeb0f38f68c5e08af5603c605007c9efc1f1574c94d322191f248cf34246d9bb06c650e85a8ce1832860689af7791054bd01b79a11cc41ca05f3a174d9393191bc541678989b0a6fbea82c488aa828b077003a69b335098d00616e28d7853ab98b54c0b99840ff94393532285ccbc003120cd8b77c5c18d0e654750893de5c071a60a152350cc537a34a60c7032c21623c1bd0ce38046b986e786051e9891a7784223b45cc09682dbfacd92776f594377d07290667a81726c7a62ab1e883329b22205b3e72165c846261429dd3cccedd4933bb38f4b9000a066c2464844ab47899269033078c1f2473e564a2c697caeded4cda82b8516c287b23a84ee997fe9206874f73d130a73d5a14e10668365089a9695b7870806fa8431663120eac9b9133937117cb3bcf862e306747139253f181deb0352e6bb6b888561d46b3406fa31ec08520b275bacb6b1fd1bc40076958ed1bf96b70c5f567c9f83a3abdcc85e81aa0f99a50c2aac18a4177aa0bd1d7caaabb11e09a3bc211685396750a4723c8c8334433019c1844c9aa22b6e6995bc703ddb09edbe4f1a61a62a23531cf707976a861efef13e8347210d77f3d080e9ba89fa12bd4f75caa74f23b4af606902f6187dd9be62a43b1b529344f1114e69391d5f574ef7f013d4336801fed022178c3ed91d0b6d51325315fc1dcabf4770a2ea", 50 | c: "87bd17e107d5e37b7d67c45df2453f04e778cfb38c425da461d52be742d7f53eca92a84fa0752f78c062b63bf7f67cb281c243a1ecaf67aead78868b20a9d6c3567b32782143ce00616aa573b1e01153702f8bab17513ba24a3b777dba87f026d92e960b75ab084b2f2f549d532b4016a9ec7fe9494d9aa5907c854c20f993eafff4221d7688f6a188c6a70bf794d76f62df8d350e122028d6038f76ac91d0c4814fa1ba1fa4c8dd42e9f4d733aa3b9a52a4d1b1241962986d64e28b62ae149de34d96de819b64441af85e044d894b17bb3f69d72cee5e698260af9312a03967f0b82cb24b01828730b17f5ebd2bc76d510e0fc0b00bfe814fe9a7ee4c430b803109fb6ac0454ddf65931e9745542bb02ab1620ebf8968426c6425599419f9dcb814f9c302146778baf342df8922de9e229d9af6a4113143211597da785d11a874f3c90bf1733f331a5e3348355863e45a0f0661076e4e2d5cda109fe370e64fedfaeee5d3ea0131d7b8bf3a6344abf7dcc77699f4325c6f0e9aaa8b4bfae413e22c5ea945b5d59b417c733780d03069f169be61813c15391f2dd1619cd93357ceadf8688e97b37e162a19335e3e17c676e540a0646a50f0c88357dad7e868ac1570e0ec068bce9b87e1de6c3e03098f77ee87854d97e20cf9e1bc15d9ce1833814a9f15667d8f61396bfdaf0132211ba1e639f65a4a7735e6edf3b6355f586e7434a5ffba59f7790c091c806debd0921d64d3360cf8eb42aa6238924865dbe79b50552317752633b44e1f64ebeab992225c395010a91d0966ca3b5356d2023489c38fbab20582a6cf6ba5d946e97a090da7496f8ab2e197f3a2113893980d6c48bcf834536b255ff6520350638e563b049ade5243a6c7210e56d8873f96ad5a2a8d527d4598255b7f5d5d663e8e18917f2ffa7fca37a5e0917c8a2343446b3587345ca13c78c29e813455744b8037a3c6da691087f9cd3b3bb64b948eba4eddf7d0dbcff1d31b2759e0c82d36b56e3ea3c0d21fe7d19855ff5632cdeeea2c78a5bdea96263445c653e4a12447d4c1200240ad537875571d4bf88dcc40b35437942b85aedeb211969551a9ff863704d6d9f097858ff2da1d3525aeb656a908819d0438ebb16eb81b10087f3c0d9c036a59bf3ee15579f85f1ecb16ecbf807167548635401ab6c1ca6c1edb6eed3c1efe0d2eaad683ccbfc89aa7c4dea656983cc0b959445f92c596011667e83528a964ba4a067991e745372bc05792958ed2f776b57ec98f8181e2caf7b542999c04dea5075247e70fa43ceae837fc9b3a9354668309cfbffe0194937c7064e03ad6c756c5b3e4bb0c514018d31b7084db418f105b48ba93b86095591df4157aaa82f9c186e54181e6966751ce26a6f8357b61000944218bc69c309f720a35cff5dfd27bad2197000c3f9ccb97bd00049a4a66bbc18105026430fab2aac0713534473cf1f46cf92d31f3129eeb95772f2c3ad5590210a3fcbd6603cd7420fca5fb5516629edf73d86ef03899f279d3b8f1962a71dc0d3702cb164bacac3414e9f4736992ac8fe2afeffeca9d6dfb0af61901adf9b210afcb2a1e990cd9bbba5b90ed1e230f9e39b1888ccd8fd9e1b5c37bfbf802fd6d90adfa632ec26f90cd0f4d7ab3859ef964d726851c4d2b120d154c86ac60a05918a9b0381f4cdad739261d43ce4e79de1f4fbaf09cbb3dc329303bb03af964fbd45c0b1e801530293e39754ff36c5681395931b0bff4896cb3ece79c531c72597de5451a1c56448cc25d2aacc9f1404b6440f6eb10567b0965d8d18a0368ba4d1723359130fadc83f80848c23274e7cdfcaf97ee63750cb206e25caf8b0b8b2cf066c2ccd61d844873d912109e28ee334f71eeba2d3cdc31d93415e55e82362119f81cef055694d869bff6e0d70e1ed69834b9fd40768611e1d88be19bd77d946f4910c0564b1ce0ccf0e53a45b8f065d8ec2023f226f20928f351971d3e9fa566626f295ff96c21f0d113de8e81c502d6553ddc41652271e41215f6bf3a6e0a26d284ad504b3c4fae26995b644789a262e8db27a0b41c21b82194d6ed1c4cd6ac97355b83e3f7e4f8656d33761f4bef98bfd50f1d03178b6a5be67bc1ea79dff6a7cd673dc10f63900631fd6052e0f4deedbc21dc87ca42554047ac78dba3aa4ccaae14e5eae6c1ded7990cb44b72b971e43882329347888957446a75", 51 | K: "6c4f4a231255a8cdfb7424c8dabf3a624cefaffd28964efe220ab6178fa6b324", 52 | } 53 | 54 | func TestMLKEM512Vector(t *testing.T) { 55 | dkBytes, err := hex.DecodeString(mlkem512TestVector.dk) 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | cBytes, err := hex.DecodeString(mlkem512TestVector.c) 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | expectedK, err := hex.DecodeString(mlkem512TestVector.K) 64 | if err != nil { 65 | t.Fatal(err) 66 | } 67 | 68 | var dk [Kyber512SKBytes]byte 69 | var c [Kyber512CTBytes]byte 70 | copy(dk[:], dkBytes) 71 | copy(c[:], cBytes) 72 | 73 | K, err := KemDecrypt512(c, dk) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | 78 | if subtle.ConstantTimeCompare(K[:], expectedK) == 0 { 79 | t.Errorf("ML-KEM-512 test vector failed\nExpected: %x\nGot: %x", expectedK, K[:]) 80 | } 81 | } 82 | 83 | func TestMLKEM768Vector(t *testing.T) { 84 | dkBytes, err := hex.DecodeString(mlkem768TestVector.dk) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | cBytes, err := hex.DecodeString(mlkem768TestVector.c) 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | expectedK, err := hex.DecodeString(mlkem768TestVector.K) 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | 97 | var dk [Kyber768SKBytes]byte 98 | var c [Kyber768CTBytes]byte 99 | copy(dk[:], dkBytes) 100 | copy(c[:], cBytes) 101 | 102 | K, err := KemDecrypt768(c, dk) 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | 107 | if subtle.ConstantTimeCompare(K[:], expectedK) == 0 { 108 | t.Errorf("ML-KEM-768 test vector failed\nExpected: %x\nGot: %x", expectedK, K[:]) 109 | } 110 | } 111 | 112 | func TestMLKEM1024Vector(t *testing.T) { 113 | dkBytes, err := hex.DecodeString(mlkem1024TestVector.dk) 114 | if err != nil { 115 | t.Fatal(err) 116 | } 117 | cBytes, err := hex.DecodeString(mlkem1024TestVector.c) 118 | if err != nil { 119 | t.Fatal(err) 120 | } 121 | expectedK, err := hex.DecodeString(mlkem1024TestVector.K) 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | 126 | var dk [Kyber1024SKBytes]byte 127 | var c [Kyber1024CTBytes]byte 128 | copy(dk[:], dkBytes) 129 | copy(c[:], cBytes) 130 | 131 | K, err := KemDecrypt1024(c, dk) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | 136 | if subtle.ConstantTimeCompare(K[:], expectedK) == 0 { 137 | t.Errorf("ML-KEM-1024 test vector failed\nExpected: %x\nGot: %x", expectedK, K[:]) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /assets/mlkem/ML-KEM-512.txt: -------------------------------------------------------------------------------- 1 | d = e1e3206875e67d7e81353774fe9025035b9b41a4a9f6ec00b91c600442fd717d 2 | ρ = b1720e4ed5ac0add457f573a041465bcbd7ca4e1d7d53eaadeda511962a36eb0 3 | σ = 176c5e5bdef7f0b03349110742125810116450aa6ed6a02a87a8c04cb508d6fa 4 | A[0, 0] = {2322, 479, 3, 783, 2874, 1746, 2961, 2018, 1000, 667, 1686, 115, 1257, 268, 1040, 2914, 1051, 1438, 1500, 1887, 2121, 3171, 454, 2842, 2683, 1412, 2461, 1063, 1892, 2180, 3248, 309, 1300, 776, 3273, 2631, 445, 2127, 16, 1737, 413, 1477, 834, 1429, 1741, 147, 1742, 1378, 1295, 2642, 2169, 379, 676, 2115, 1815, 2490, 1119, 204, 1412, 172, 381, 2697, 1565, 29, 2338, 2357, 3221, 1595, 1597, 2667, 144, 850, 1017, 986, 2430, 200, 802, 142, 596, 1755, 1439, 3007, 828, 454, 440, 1161, 1463, 746, 797, 2419, 3124, 992, 2372, 1092, 889, 2094, 2488, 1185, 630, 1399, 2478, 2984, 482, 2890, 471, 1276, 1713, 2058, 1890, 1567, 2203, 1421, 2351, 2554, 1681, 1725, 2133, 1981, 167, 1588, 531, 2889, 90, 2503, 3001, 135, 1878, 1550, 3185, 1483, 1006, 3242, 2771, 2745, 212, 3307, 1178, 9, 2523, 373, 2435, 168, 1264, 688, 1091, 1278, 2638, 1319, 380, 2722, 643, 2199, 653, 1549, 1400, 695, 3187, 1756, 1214, 1235, 2941, 2467, 1026, 2740, 1758, 511, 2380, 3261, 1177, 2729, 569, 76, 284, 1958, 1375, 2648, 246, 2220, 2132, 2115, 2202, 908, 2697, 1437, 647, 1006, 443, 1826, 121, 2597, 835, 432, 213, 1740, 1488, 899, 3279, 158, 998, 2213, 2724, 2194, 276, 593, 254, 1065, 2437, 26, 2632, 64, 2763, 1179, 827, 2678, 2272, 575, 2761, 1224, 114, 2154, 2896, 1511, 2591, 1538, 1452, 3097, 3082, 1448, 1411, 587, 2265, 3223, 3046, 2500, 2157, 97, 658, 859, 16, 2552, 1376, 2131, 795, 2368, 2151, 1782, 2712, 1663, 3168, 1803, 1552, 3098, 2249, 1110, 1645, 1015} = 12f91d03f0303a2b6d912b7ee8b329963607e9c4101024b61be459dcf5754938c6c6a1b17b4a589d7942644788b05c13148530c97ca4bdf18410906c9d515c425359cd3609ce26560f25a579b817a4328417a79b5fc40c84c50a7d91a81dd60122599395bc633db6a6902035f9a33d7e890c22e30854b26d9ff5bb3c631cb89148b7a52e1d3397340c3e44494479e382b8194a767257ae89bae2a1b4d7c14fb1a68062f7619bd8582fa99f91d66b55d87ba740631392b45a709cb97b0856e76071bc5ceea3cad39aabd4b0ce9a9400db591783890af0042b43e44f4e7a527c21aa8372898dd26078752b73cc6dbe344d7d3b9a0244abdef61f4cd9cb9994aa39c2041c617a5f85a5f6c08a5438849ac83889da5987e23ebb21727950a243031bd5c06cd03538cfec09e6538aa42a89141125fe904285a901480a04cbba493b63a7e0f823c98a4c72a086507b5e1f2a60ac95c10a8c5a83b524d978c9e64b9c6d180692b23510809f6035851b039467686f98fa6760bc7010a6c1c968456d763f 5 | A = 12f91d03f0303a2b6d912b7ee8b329963607e9c4101024b61be459dcf5754938c6c6a1b17b4a589d7942644788b05c13148530c97ca4bdf18410906c9d515c425359cd3609ce26560f25a579b817a4328417a79b5fc40c84c50a7d91a81dd60122599395bc633db6a6902035f9a33d7e890c22e30854b26d9ff5bb3c631cb89148b7a52e1d3397340c3e44494479e382b8194a767257ae89bae2a1b4d7c14fb1a68062f7619bd8582fa99f91d66b55d87ba740631392b45a709cb97b0856e76071bc5ceea3cad39aabd4b0ce9a9400db591783890af0042b43e44f4e7a527c21aa8372898dd26078752b73cc6dbe344d7d3b9a0244abdef61f4cd9cb9994aa39c2041c617a5f85a5f6c08a5438849ac83889da5987e23ebb21727950a243031bd5c06cd03538cfec09e6538aa42a89141125fe904285a901480a04cbba493b63a7e0f823c98a4c72a086507b5e1f2a60ac95c10a8c5a83b524d978c9e64b9c6d180692b23510809f6035851b039467686f98fa6760bc7010a6c1c968456d763fef59a6173936fe4814603bab0589b771d17c12778bcccc4309f67ddeb95b4b873c81f72a0783a8ee123ff5b6235a56b486c768bd8113a4f09d19c9af80fb98ef8b6c697b42e7bc009807762de9365391547cf822c07b869f49104ffab3098746da759ae3378961385521da9470a42c78073bee570feb4a9b14ecbe666a136462aaa0dc2ce173b61c625563fb50b5c9c2f208ba42e656d221431fab0a2868b154366db5e5b19a4822b0385936e4be96b2ad83013a904970efec5329f0a563f6406a57b82df2965b9ba492b29c678262e6829a11011478460d8da1a75d14972996bb8e0762237bbb8c9b044d0337cff8312ae18094082f7d6ca361d69bf0d004dffbc9d55b905f8359f421b4748328a9e722cacca07ffc210e7c35cd461abc069fe4d7980a570e83b01103b878491c200f70aa8bc71a296bb711acc2452c98f88810e75869ee4997bcc17a09712338f83d66b96161e908a4c4279a615ecbe2682349a21e55af66127ad08114b06709c724653d59561aa249c1c050b08c1397421102097622d5b54febc61881472d7c5eec9834c1d304b8c94d89a04b5bb672302464a8bb43ac854e8f6cc0546a1d2016b8ad221bb2939e613ac5ae7b7ec9576d50265dd02ca683b69a6620cb87c08d6aab49a56c09219863134947f17c96f50cb2f01493d9d431672a68893b0f2594c204eaac6871242bd00365fc019ff1530ac10c7d1051489cbb2efb499b888497a61615f25144db1943c03c99eca96009a749c44a7709cdaef18047282ac9615b04b42318a2ac01d834c447a9ebfc758b469e7fe43355903a9cfc9bf830a085538631c6553432cddcc78900f35d3db7418a03951a0577235b9f6036b23b3269c64bceebba4bbd3c1592b541c0857c93d14eb1a5231e74532273b1bc666e2484b42e4b5e0fa32b2337362f60c73e5a80d6ab7ae78a7cdbca4a8be5ae94c063489a2469f2b09c8ba6f2e92459768c30daa218c387107ba32d6a072dc63bd9a38ececb571988c727ab0fc332835aca1925f4c1f2210b006d96cd2a7e1c4caead7ccd55b05931b3b94f9c02fa2651b750b79f227e843ba4e1571bb9c8a547e2a3fcd25e3866653728bd57a8c9c480404295819e559ae6727d81d61b785b7817dc3b67560db36c2b0509976ff39435869e49044987fc4d92b9bfd07a37fa08193057aecf585734280d0201a0ea237da26b5741f528dfea6a58d291a5d5543be52d31b5468b82143e754d0714747981aea189025a8931eba7a0ef2561bb0860b8c2442285b4ed22b40c882f19350789642bdd6797e8d98566eb411221a425f34c73aacf92e0a5d8b35272302b815041833b29b1a409d1087b6ef00197893f3b2a93ff61646d77c0c5089f93ec7261347a871c9e7559c11677a84cdab7598c3bd2709e9df33186954682b7ae1f888043a738cd906cab0a06ecb74badd6b303eaaa5ed261e51009c867b8548a84b4ca62845b9f7b9aae1f4107caf9cd2b43469185616ab96b2c09438f06ad95b5605c646b67a8b88b5587fcb0235656ae23d0530fe996bfb691429716e46a2bcd299573c80d7c37ba4fe14e38d556c3281a1b416b2996cb9aacb059345aa6ec078714c52fd3b01d176f 6 | s[0] = {1, 0, 3328, 1, 3328, 0, 0, 2, 0, 3328, 1, 0, 2, 2, 0, 3328, 3328, 2, 1, 3328, 3328, 1, 3328, 1, 0, 0, 1, 3328, 3328, 1, 3327, 0, 3328, 0, 1, 1, 0, 1, 0, 0, 3328, 3327, 1, 0, 3328, 0, 1, 1, 3328, 0, 3328, 2, 1, 0, 0, 2, 1, 2, 1, 3328, 0, 1, 3326, 3327, 1, 0, 0, 2, 3326, 3328, 0, 3328, 3328, 3327, 1, 3328, 3328, 3327, 0, 0, 1, 2, 3, 2, 1, 3327, 3328, 0, 3, 1, 0, 0, 2, 1, 0, 0, 3328, 3327, 0, 0, 1, 1, 0, 1, 1, 3328, 3327, 1, 1, 3328, 0, 0, 0, 3327, 3328, 2, 1, 0, 3328, 1, 3327, 1, 0, 0, 3327, 3328, 2, 0, 0, 0, 1, 0, 1, 3328, 0, 1, 0, 3328, 3328, 1, 1, 1, 3328, 2, 1, 1, 1, 3328, 3328, 3328, 3328, 3328, 2, 1, 1, 3328, 1, 0, 0, 1, 3328, 1, 3328, 1, 1, 2, 3, 0, 0, 0, 3328, 0, 0, 1, 2, 3328, 3327, 3327, 1, 1, 3328, 3328, 3328, 1, 1, 2, 0, 3328, 1, 3328, 3328, 3327, 0, 0, 0, 3328, 3328, 1, 0, 1, 3326, 3327, 3328, 3327, 1, 2, 3326, 3328, 1, 0, 0, 0, 1, 0, 0, 3, 3, 3327, 1, 3328, 3328, 0, 0, 2, 1, 3328, 0, 0, 0, 0, 0, 0, 3328, 0, 0, 1, 0, 0, 0, 3328, 3328, 3328, 0, 3327, 0, 1, 3328, 3328, 0, 3328, 0, 2, 0, 0, 1, 3328} = 010000001d00000d000020000000d00100000220000000d0002d000100d0001d00001d000000000100d0001d00ff0c00000d0001100000100000000000fdcf010000000d00011000000d00002d000100000020000120000100d0001000fefccf010000002000fe0cd00000d000fdcf0100d000fdcf00000001200003200001f0cf000d0003100000000002100000000000fdcf0000000110000010000100d0ff1c000100d000000000f0cf002d00010000001d00ff1c00000000ff0cd00200000000000100000100d00010000000d0001d00011000002d000110000100d0000dd0000dd00210000100d0010000001000001d00001d00012000030000000000000d000010000200d0fffccf011000000dd0001d000120000000d00100d000fdcf0000000000d0001d00001000fefccf00fdcf012000fe0cd001000000000001000000300003f0cf0100d0000d000020000100d0000000000000000000000d000010000000000000d0000dd000f0cf001000000dd00000d00020000000000100d0 7 | s = 010000001d00000d000020000000d00100000220000000d0002d000100d0001d00001d000000000100d0001d00ff0c00000d0001100000100000000000fdcf010000000d00011000000d00002d000100000020000120000100d0001000fefccf010000002000fe0cd00000d000fdcf0100d000fdcf00000001200003200001f0cf000d0003100000000002100000000000fdcf0000000110000010000100d0ff1c000100d000000000f0cf002d00010000001d00ff1c00000000ff0cd00200000000000100000100d00010000000d0001d00011000002d000110000100d0000dd0000dd00210000100d0010000001000001d00001d00012000030000000000000d000010000200d0fffccf011000000dd0001d000120000000d00100d000fdcf0000000000d0001d00001000fefccf00fdcf012000fe0cd001000000000001000000300003f0cf0100d0000d000020000100d0000000000000000000000d000010000000000000d0000dd000f0cf001000000dd00000d00020000000000100d0000dd0000d00000d00000d000000d0000dd000f0cf000dd0000000ff0c00002d000100d0010000001d000010000100d00100d00130000300000010000000d0000000001d00020000000000ff0c0000f0cf000dd00000000100d0000d00001d00000d000100d0010000000d00000dd0000dd00000000100000200d00000d00100d0000000000000ff0c000000d0000dd000000000fdcf00f0cf000d0001f0cfff1c000100d0011000ff2c000200d0001d00000d0001000000100001e0cf000d00000d00000000001d00021000002000001000020000ff0cd0000dd00000d0000dd0001000010000ff0c00012000012000000dd002f0cf001000000000000d00000dd0012000000d000010000210000100d0020000001000000000ff1c000100d0011000011000001000000d00000000000dd00110000110000200000100d0000dd0000000000d0001200001000002f0cf001000000d0001100001f0cf000d0001200001100001f0cf000000000000001d00000dd0000d000210000000d0ff1c00 8 | NTT(s[0]) = {1837, 3137, 1722, 738, 222, 252, 512, 591, 630, 2953, 635, 1388, 3151, 1951, 272, 319, 3323, 2008, 3211, 913, 3201, 2394, 2264, 1162, 391, 3048, 474, 2331, 486, 1801, 3093, 705, 745, 404, 1554, 1687, 2846, 926, 604, 2476, 1789, 158, 277, 3273, 1007, 2208, 1001, 1442, 1312, 257, 238, 335, 1621, 612, 3073, 2384, 1908, 3278, 1786, 1397, 2512, 2377, 1605, 1030, 2885, 527, 2722, 1458, 2097, 614, 651, 2737, 2555, 825, 2274, 1764, 2117, 1211, 2445, 1843, 3192, 2277, 2325, 344, 2555, 2372, 147, 414, 1884, 79, 3139, 3147, 2897, 1991, 963, 2818, 2821, 2609, 1050, 1214, 475, 1508, 1635, 1788, 416, 1087, 1844, 1519, 1079, 278, 3001, 2929, 2601, 390, 1011, 2914, 2031, 3043, 2277, 2956, 2894, 2924, 3167, 9, 189, 1205, 2609, 2470, 3178, 1543, 657, 2301, 3302, 837, 1334, 3227, 2240, 2479, 71, 1137, 1895, 2831, 2807, 2297, 194, 473, 841, 366, 529, 2843, 652, 2901, 3088, 90, 590, 1577, 1321, 815, 2258, 1889, 1111, 1154, 1334, 1218, 25, 323, 2045, 1646, 744, 2317, 56, 2622, 1928, 1223, 949, 853, 1859, 809, 955, 560, 921, 572, 1281, 672, 1423, 1505, 2362, 2612, 1081, 1409, 2327, 2834, 31, 1550, 2603, 3163, 2379, 1476, 1562, 2905, 1791, 337, 1322, 2406, 1815, 2940, 669, 1112, 1223, 2749, 2982, 1493, 3164, 2380, 1054, 3074, 2717, 622, 304, 2765, 2266, 278, 2994, 1921, 2476, 2920, 2146, 453, 1722, 1412, 1890, 3189, 52, 552, 2403, 1154, 3275, 3201, 3233, 2242, 615, 369, 366, 1350, 725, 3307, 1950, 2849, 1806, 1805, 1532, 2553, 1027, 2607, 482, 2117} = 2d17c4ba262edec00f00f2247692b87bc2564ffc7910f113fb8c7d8b1c3981ac95d8a8488781bedab191e69170151c2ce942191276691eeb395cc29afde6091591ccef038ae9235a201510eef014554626010c9574e7ccfa5657d0999445664045fb20a22a5b3168268b12abfb9933e2486e45b84b8d3973785c8e158915fb499493e0195cf70443bcc4517b7cc323b0051ba31ae44bdb415e63c66fa0f14334f75e376411b91bb7296a18f323b6ef37bee5c8b84ecbb65f9c00bd504b316a9a6a7c6091d28fe65c3436b5c9c0f89a47104767f7b0f79a8fc2901d49e31611b2b18c52b510ac054e926229f532d2187657244836254c193014fde766e8d29038e0a388774cb55335439732bb032399c32301052a8f155e3a49a33914581729b11fe0602bbac54b495c1a96b5ff16152a659617c7b79d8245c7d4aba65b5d5ccc941e24c09dea2630d1acda6811b21b78ac89b662581cba46586257c7348022632948cb1cc8a12c8c6712176e6154d5b2ce9e17b20ed770fc959f03f4a2e25184 9 | dkPKE = NTT(s) = 2d17c4ba262edec00f00f2247692b87bc2564ffc7910f113fb8c7d8b1c3981ac95d8a8488781bedab191e69170151c2ce942191276691eeb395cc29afde6091591ccef038ae9235a201510eef014554626010c9574e7ccfa5657d0999445664045fb20a22a5b3168268b12abfb9933e2486e45b84b8d3973785c8e158915fb499493e0195cf70443bcc4517b7cc323b0051ba31ae44bdb415e63c66fa0f14334f75e376411b91bb7296a18f323b6ef37bee5c8b84ecbb65f9c00bd504b316a9a6a7c6091d28fe65c3436b5c9c0f89a47104767f7b0f79a8fc2901d49e31611b2b18c52b510ac054e926229f532d2187657244836254c193014fde766e8d29038e0a388774cb55335439732bb032399c32301052a8f155e3a49a33914581729b11fe0602bbac54b495c1a96b5ff16152a659617c7b79d8245c7d4aba65b5d5ccc941e24c09dea2630d1acda6811b21b78ac89b662581cba46586257c7348022632948cb1cc8a12c8c6712176e6154d5b2ce9e17b20ed770fc959f03f4a2e251845ca128031c5f37631f6e6241fce131164092ba676483c7050890279672a9f8a2329b752eb9b09eb7e4747e13587ec54b99582e2d905ed2b55ef7aa52a185caacf037d4946a95c94f0b80423fd4884a8370af576f0e4b1b6c851583312500b4420676a288019bfd8c2c24116eec8c5dd5822da84c446e09c47887b9c912b8c1786f9ad83e1d36961750175398606b42b2c3a095d104bc0aac455a72554aa9ac1d9ac5a801ab8c6723d53a259ba20c094bbe3094534c88817703a114f94b2070c4901a4bb7d02858b31dbc614aa7c3a10344956b940eb619adafd9c29397bd63c0600d910e97a0554515163221194dc2bcecb5786f081f79478af2f2559056b66453789ce53877db743a3c8378d20f92b3c42f46a712aa92eb765de933bd0a53a531f552218c5da3b57775881bbd8240eabc1881ec3519ca80243a8fc4ccaa7108099a959726b72aee522780649c91f4a82aab2045834b2f64039e542c37b7b81f602be2f0688cb5250f295591c999390499822140ff4325011860fa753b81e85b 10 | e = 010000003d0000f0cf000dd0ff0cd001100001f0cf00f0cfff1c0000f0cf0100d00100000000000000d00120000100d0010000002000ff0c0001f0cf010000000000001d0000f0cfff2c00012000ff0cd001100002000000fdcf002d000100d0fefccf000dd0002d0002f0cf001d0001100000000001f0cf000d00002000ff0cd0ff0c00fe1c0000f0cf011000001d00003d0001f0cf001000ff1c000100000110000200000100d003000000000000f0cf00100000f0cf00fdcf001d0000100000fdcfff1c000000000100000100000000d0013000ff0c000210000300000000d0000d00000000021000000000ff0c00000d0000f0cf01000000f0cf00fdcf000000ff0c00020000ff0cd0000dd0000d00010000000d00ff0cd0ff0cd000000001300001000001000000f0cf0000000100d0000dd0000d00011000000dd00000d00100d0000dd00100d0000dd00000d0030000020000000dd0001000000000ff0c00000dd0002000000000000000000000010000ff0cd0000000000dd00000d0000000fe0c00001000000000000000ff0cd00200d0000d00ff0c00001d00000d00011000ff1c0000f0cf0100d00100d00000d000fdcf01f0cf0000d00010000100d001200000100000000000fdcf000000000d00010000000000ff0c0000e0cf001000000dd0000d00010000000d000200d0001d000020000020000000d000f0cf030000ff2c00ff0cd0020000000d00ff2c00002000001d0002100002f0cf000dd0011000000d00ff0c000200000000d0011000ff1c000000d0011000001000000000000000001000010000001000010000002d000030000000d0011000000d00001d000100d00110000000d00100d0001000000d00000dd00100d0ff2c000110000000d00100d0ff0c000000d0021000000000000dd0010000fffccfff0c000000d0012000ff0c0000200001100001000000000000000001e0cf0010000200d0fe0c000010000000d000fdcf00e0cfff1c00000d000100d0010000001d00000000010000ff1c00011000001000022000000dd0002d00000dd0000dd0001000 11 | NTT(e) = 48da710c5755845012fa6625ec2c1b94927987b8872f159642936295570672d66f9bc8828e74c464ea9b521669e95324b3750c32887c4018a846a0c377d960b41893662809b9acc2a96143b904b704c78b11f1c1ab3067cf9c92be99976f5b4af107a7c5a8485f66b8e3642e3d935db64a398783059341c421438d1d21c4c0a7a486e41889c9174edb92e2f811e9601cb96977337b1045d06f5259215fbb978963a9294068be9191a9c475701752f3694d3d55478b1c73ddb065d1537c56db7716d0bb53aa563a14b8fa37646437501253ab64da5f68e27c5b915f1e4b227f2c6057133ac6b2326dd6a411406a0deb325ab7b7b60284eb9206f3a82f255470f82a699d30bd5da5b2ea684996e67506f2809f451055564289435f4f45becd6053ca540eaa7c134dcc5856a56eb3c89dd6fb0335ca3740d55085acbe655b696f7a2875675b4c007efdd2ae2f6a5966e8838aab4463fbb1d5d651a7a5288bd59b7aa19cce68c4f87c4f8032a98ab918511c5a6e3c8b3f53a48c462b2540802f4a92993510b2b47ff7a16cd38007e64404cc5c6537519c5cecb5dc5aac38246a6f0833d4b07cd4e6cd70c96e0ca53601d9b71ff47ce5d15168d94302721d795246e2fa532a5c2571179982a323bfe595d1886d683c9daf8883b360564048888e9ac78c574b8ecb523ca10df2076f65b5a54f56b1f42023faf726349879ca6626c5cc812768b2d8eb5dd506c83bfa383792484b06a7eb9197824a1f6cb79598f9a0b0d594887c3c6528456d024f9cb447997a5794151a125bae9b7b44082270db878dc939ce22e79065d60ac7b95b5f73c55fb5741a71c5195143f83a220588214d4c4e245a41d0986cdcb8ba7c3530d86c2dcec51579261f2eec1f1b8c3d06e73c0f67bed418ccd34108313038b116628df1bb42c84e36930aae39ca450c6c7689628d9456ef6241bbb29b686b20490b0c99b19804fb312b912f48b60c054596ff29b2d5a62663875dc1a340118c5d21a05fcf204daea91b47689d005db2ebc829dd7199d456a13ee84508561cedda0357d76bdb047621f4c85c2152129c5f95d576 12 | t = f29c866c361d910341f296c64b46c2a2e30b1535a5c0602593415d156b43036b21b14ba3a0c72e848c5ee03466cab97721d83defdab1f6708971d3c4581441ce397fe7bbc7a08acfdcc417c5f56dfdd4423e306f1f69a86e5b5ba3e031bf92a16702861a51a2feb974f4844812b8302dc83026ea566a1c110cd2c4af48bedd742f1c02cd3fca0dfb8331f414b115849bcb05a2df615b49a90ec0a433e8787e185142aa406003d35409ec842af304459b40fffa0d84340156b5767ee0ca6e2850ec8ccaed317ecf711e27d59b3c985555e44c935988092981da95b42573b9bc38122ed44a800510756429f1b0640685b048597842cbb93dc6b2f9144dcf41a624eb90202a30381410f4cbaaccf61e485524ee593ad7d5974f774ae59ba20c7c6eb0009b39962dc0f828e8538ef583b4dc40c09555a3968ab257d407cc4ab801c7521bf7564b163fab927b4703225b1b497a096b4eb984dfd3512ecb2eaea46fb53c3b3840af3c4aaf78b1461e0564290c0a8f93a1e1b1c50cdb5fd56970046103600a1cc109b58cc32e52c7a168e17107a9c77cc448b01a5a7dc90603d708c6d717c3165e772c74763821f35934a613b0297246370020eb113645d75593c5130072acc0483118067bfb8093b518066318c3f936bd29e8acba8318c68a3c47a1be713c5b5a3c3df82514a5db7f70150b44b3927aa5c7b5507f8e693eed04369340141b1c75069b5f3abb016833b936a1adf7ccbedfe775f0494acb6404307a787e19b2fcba2aa8b3cf94ea6aa4303cfe4821fc9a62ee5acbcaa42556d046e4ab2e65e57e4dcc874276c89f980ac1a52fde549064b7abeab30e81d540af393a51bb917d87098ae238946033f3bb31d82b418ea863b9fc2008613e4751aed8c9aaa41840579116b571ad6df0cfc1185b17612008900d104a83b67a36acd8c783c5b1c21026f03b4ab57b23bd160a103920729b13334395a500a2a5a126bd66c75c5c2a046a97ebb8565cbb65fc6a3e1c11a990e20a9c88c96aa3924961159ef22082e45c86a9bb80d7b8936cc9eec593e5e4523ddc5438c819551328557982cf6784 13 | ek = f29c866c361d910341f296c64b46c2a2e30b1535a5c0602593415d156b43036b21b14ba3a0c72e848c5ee03466cab97721d83defdab1f6708971d3c4581441ce397fe7bbc7a08acfdcc417c5f56dfdd4423e306f1f69a86e5b5ba3e031bf92a16702861a51a2feb974f4844812b8302dc83026ea566a1c110cd2c4af48bedd742f1c02cd3fca0dfb8331f414b115849bcb05a2df615b49a90ec0a433e8787e185142aa406003d35409ec842af304459b40fffa0d84340156b5767ee0ca6e2850ec8ccaed317ecf711e27d59b3c985555e44c935988092981da95b42573b9bc38122ed44a800510756429f1b0640685b048597842cbb93dc6b2f9144dcf41a624eb90202a30381410f4cbaaccf61e485524ee593ad7d5974f774ae59ba20c7c6eb0009b39962dc0f828e8538ef583b4dc40c09555a3968ab257d407cc4ab801c7521bf7564b163fab927b4703225b1b497a096b4eb984dfd3512ecb2eaea46fb53c3b3840af3c4aaf78b1461e0564290c0a8f93a1e1b1c50cdb5fd56970046103600a1cc109b58cc32e52c7a168e17107a9c77cc448b01a5a7dc90603d708c6d717c3165e772c74763821f35934a613b0297246370020eb113645d75593c5130072acc0483118067bfb8093b518066318c3f936bd29e8acba8318c68a3c47a1be713c5b5a3c3df82514a5db7f70150b44b3927aa5c7b5507f8e693eed04369340141b1c75069b5f3abb016833b936a1adf7ccbedfe775f0494acb6404307a787e19b2fcba2aa8b3cf94ea6aa4303cfe4821fc9a62ee5acbcaa42556d046e4ab2e65e57e4dcc874276c89f980ac1a52fde549064b7abeab30e81d540af393a51bb917d87098ae238946033f3bb31d82b418ea863b9fc2008613e4751aed8c9aaa41840579116b571ad6df0cfc1185b17612008900d104a83b67a36acd8c783c5b1c21026f03b4ab57b23bd160a103920729b13334395a500a2a5a126bd66c75c5c2a046a97ebb8565cbb65fc6a3e1c11a990e20a9c88c96aa3924961159ef22082e45c86a9bb80d7b8936cc9eec593e5e4523ddc5438c819551328557982cf6784b1720e4ed5ac0add457f573a041465bcbd7ca4e1d7d53eaadeda511962a36eb0 14 | dkPKE = f29c866c361d910341f296c64b46c2a2e30b1535a5c0602593415d156b43036b21b14ba3a0c72e848c5ee03466cab97721d83defdab1f6708971d3c4581441ce397fe7bbc7a08acfdcc417c5f56dfdd4423e306f1f69a86e5b5ba3e031bf92a16702861a51a2feb974f4844812b8302dc83026ea566a1c110cd2c4af48bedd742f1c02cd3fca0dfb8331f414b115849bcb05a2df615b49a90ec0a433e8787e185142aa406003d35409ec842af304459b40fffa0d84340156b5767ee0ca6e2850ec8ccaed317ecf711e27d59b3c985555e44c935988092981da95b42573b9bc38122ed44a800510756429f1b0640685b048597842cbb93dc6b2f9144dcf41a624eb90202a30381410f4cbaaccf61e485524ee593ad7d5974f774ae59ba20c7c6eb0009b39962dc0f828e8538ef583b4dc40c09555a3968ab257d407cc4ab801c7521bf7564b163fab927b4703225b1b497a096b4eb984dfd3512ecb2eaea46fb53c3b3840af3c4aaf78b1461e0564290c0a8f93a1e1b1c50cdb5fd56970046103600a1cc109b58cc32e52c7a168e17107a9c77cc448b01a5a7dc90603d708c6d717c3165e772c74763821f35934a613b0297246370020eb113645d75593c5130072acc0483118067bfb8093b518066318c3f936bd29e8acba8318c68a3c47a1be713c5b5a3c3df82514a5db7f70150b44b3927aa5c7b5507f8e693eed04369340141b1c75069b5f3abb016833b936a1adf7ccbedfe775f0494acb6404307a787e19b2fcba2aa8b3cf94ea6aa4303cfe4821fc9a62ee5acbcaa42556d046e4ab2e65e57e4dcc874276c89f980ac1a52fde549064b7abeab30e81d540af393a51bb917d87098ae238946033f3bb31d82b418ea863b9fc2008613e4751aed8c9aaa41840579116b571ad6df0cfc1185b17612008900d104a83b67a36acd8c783c5b1c21026f03b4ab57b23bd160a103920729b13334395a500a2a5a126bd66c75c5c2a046a97ebb8565cbb65fc6a3e1c11a990e20a9c88c96aa3924961159ef22082e45c86a9bb80d7b8936cc9eec593e5e4523ddc5438c819551328557982cf6784b1720e4ed5ac0add457f573a041465bcbd7ca4e1d7d53eaadeda511962a36eb0 15 | z = c6f5785a6f2b42e843228be53eb768d64c6f9d4355ae95f083e51ed57c437310 16 | H(ek) = a9ce0ecbcfbbbd9a2636e23a0c1c22683943639cd44af051ff235b8dfbb73a4d 17 | dk = 2d17c4ba262edec00f00f2247692b87bc2564ffc7910f113fb8c7d8b1c3981ac95d8a8488781bedab191e69170151c2ce942191276691eeb395cc29afde6091591ccef038ae9235a201510eef014554626010c9574e7ccfa5657d0999445664045fb20a22a5b3168268b12abfb9933e2486e45b84b8d3973785c8e158915fb499493e0195cf70443bcc4517b7cc323b0051ba31ae44bdb415e63c66fa0f14334f75e376411b91bb7296a18f323b6ef37bee5c8b84ecbb65f9c00bd504b316a9a6a7c6091d28fe65c3436b5c9c0f89a47104767f7b0f79a8fc2901d49e31611b2b18c52b510ac054e926229f532d2187657244836254c193014fde766e8d29038e0a388774cb55335439732bb032399c32301052a8f155e3a49a33914581729b11fe0602bbac54b495c1a96b5ff16152a659617c7b79d8245c7d4aba65b5d5ccc941e24c09dea2630d1acda6811b21b78ac89b662581cba46586257c7348022632948cb1cc8a12c8c6712176e6154d5b2ce9e17b20ed770fc959f03f4a2e251845ca128031c5f37631f6e6241fce131164092ba676483c7050890279672a9f8a2329b752eb9b09eb7e4747e13587ec54b99582e2d905ed2b55ef7aa52a185caacf037d4946a95c94f0b80423fd4884a8370af576f0e4b1b6c851583312500b4420676a288019bfd8c2c24116eec8c5dd5822da84c446e09c47887b9c912b8c1786f9ad83e1d36961750175398606b42b2c3a095d104bc0aac455a72554aa9ac1d9ac5a801ab8c6723d53a259ba20c094bbe3094534c88817703a114f94b2070c4901a4bb7d02858b31dbc614aa7c3a10344956b940eb619adafd9c29397bd63c0600d910e97a0554515163221194dc2bcecb5786f081f79478af2f2559056b66453789ce53877db743a3c8378d20f92b3c42f46a712aa92eb765de933bd0a53a531f552218c5da3b57775881bbd8240eabc1881ec3519ca80243a8fc4ccaa7108099a959726b72aee522780649c91f4a82aab2045834b2f64039e542c37b7b81f602be2f0688cb5250f295591c999390499822140ff4325011860fa753b81e85bf29c866c361d910341f296c64b46c2a2e30b1535a5c0602593415d156b43036b21b14ba3a0c72e848c5ee03466cab97721d83defdab1f6708971d3c4581441ce397fe7bbc7a08acfdcc417c5f56dfdd4423e306f1f69a86e5b5ba3e031bf92a16702861a51a2feb974f4844812b8302dc83026ea566a1c110cd2c4af48bedd742f1c02cd3fca0dfb8331f414b115849bcb05a2df615b49a90ec0a433e8787e185142aa406003d35409ec842af304459b40fffa0d84340156b5767ee0ca6e2850ec8ccaed317ecf711e27d59b3c985555e44c935988092981da95b42573b9bc38122ed44a800510756429f1b0640685b048597842cbb93dc6b2f9144dcf41a624eb90202a30381410f4cbaaccf61e485524ee593ad7d5974f774ae59ba20c7c6eb0009b39962dc0f828e8538ef583b4dc40c09555a3968ab257d407cc4ab801c7521bf7564b163fab927b4703225b1b497a096b4eb984dfd3512ecb2eaea46fb53c3b3840af3c4aaf78b1461e0564290c0a8f93a1e1b1c50cdb5fd56970046103600a1cc109b58cc32e52c7a168e17107a9c77cc448b01a5a7dc90603d708c6d717c3165e772c74763821f35934a613b0297246370020eb113645d75593c5130072acc0483118067bfb8093b518066318c3f936bd29e8acba8318c68a3c47a1be713c5b5a3c3df82514a5db7f70150b44b3927aa5c7b5507f8e693eed04369340141b1c75069b5f3abb016833b936a1adf7ccbedfe775f0494acb6404307a787e19b2fcba2aa8b3cf94ea6aa4303cfe4821fc9a62ee5acbcaa42556d046e4ab2e65e57e4dcc874276c89f980ac1a52fde549064b7abeab30e81d540af393a51bb917d87098ae238946033f3bb31d82b418ea863b9fc2008613e4751aed8c9aaa41840579116b571ad6df0cfc1185b17612008900d104a83b67a36acd8c783c5b1c21026f03b4ab57b23bd160a103920729b13334395a500a2a5a126bd66c75c5c2a046a97ebb8565cbb65fc6a3e1c11a990e20a9c88c96aa3924961159ef22082e45c86a9bb80d7b8936cc9eec593e5e4523ddc5438c819551328557982cf6784b1720e4ed5ac0add457f573a041465bcbd7ca4e1d7d53eaadeda511962a36eb0a9ce0ecbcfbbbd9a2636e23a0c1c22683943639cd44af051ff235b8dfbb73a4dc6f5785a6f2b42e843228be53eb768d64c6f9d4355ae95f083e51ed57c437310 18 | m = a741ec2002be6f4fa76037b7f0644f833fa823e630401a39d3240c6e82a430bb 19 | K = 62a8c220b01793ecd183dea9762c5602211e0aab001cbc892d0a95693ab17cc1 20 | r = 16ce593e016201dee38926330bb78b1f0cab703862c66dc1f5fdf80f2da01ca7 21 | μ = 811668810600001068001068810600000000000000810600000000811668001068811668000000000000001068000000001068000000000000000000001068811668811668001068811668811668001068810600811668811668000000810600811668810600001068001068000000000000001068810600811668810600811668000000811668810600811668001068000000000000811668811668000000810600001068810600811668811668000000810600811668000000000000001068811668811668811668000000000000001068001068001068811668000000001068000000001068810600001068811668000000000000811668000000000000000000000000810600001068001068810600000000810600001068811668000000811668000000810600811668000000810600001068000000000000811668000000000000001068811668001068810600001068000000000000001068000000810600001068001068000000000000811668000000811668001068811668001068 22 | A^T = 12f91d03f0303a2b6d912b7ee8b329963607e9c4101024b61be459dcf5754938c6c6a1b17b4a589d7942644788b05c13148530c97ca4bdf18410906c9d515c425359cd3609ce26560f25a579b817a4328417a79b5fc40c84c50a7d91a81dd60122599395bc633db6a6902035f9a33d7e890c22e30854b26d9ff5bb3c631cb89148b7a52e1d3397340c3e44494479e382b8194a767257ae89bae2a1b4d7c14fb1a68062f7619bd8582fa99f91d66b55d87ba740631392b45a709cb97b0856e76071bc5ceea3cad39aabd4b0ce9a9400db591783890af0042b43e44f4e7a527c21aa8372898dd26078752b73cc6dbe344d7d3b9a0244abdef61f4cd9cb9994aa39c2041c617a5f85a5f6c08a5438849ac83889da5987e23ebb21727950a243031bd5c06cd03538cfec09e6538aa42a89141125fe904285a901480a04cbba493b63a7e0f823c98a4c72a086507b5e1f2a60ac95c10a8c5a83b524d978c9e64b9c6d180692b23510809f6035851b039467686f98fa6760bc7010a6c1c968456d763f02097622d5b54febc61881472d7c5eec9834c1d304b8c94d89a04b5bb672302464a8bb43ac854e8f6cc0546a1d2016b8ad221bb2939e613ac5ae7b7ec9576d50265dd02ca683b69a6620cb87c08d6aab49a56c09219863134947f17c96f50cb2f01493d9d431672a68893b0f2594c204eaac6871242bd00365fc019ff1530ac10c7d1051489cbb2efb499b888497a61615f25144db1943c03c99eca96009a749c44a7709cdaef18047282ac9615b04b42318a2ac01d834c447a9ebfc758b469e7fe43355903a9cfc9bf830a085538631c6553432cddcc78900f35d3db7418a03951a0577235b9f6036b23b3269c64bceebba4bbd3c1592b541c0857c93d14eb1a5231e74532273b1bc666e2484b42e4b5e0fa32b2337362f60c73e5a80d6ab7ae78a7cdbca4a8be5ae94c063489a2469f2b09c8ba6f2e92459768c30daa218c387107ba32d6a072dc63bd9a38ececb571988c727ab0fc332835aca1925f4c1f2210b006d96cd2a7e1c4caead7ccd55b05931b3b94f9c02fa2651b750b79f227eef59a6173936fe4814603bab0589b771d17c12778bcccc4309f67ddeb95b4b873c81f72a0783a8ee123ff5b6235a56b486c768bd8113a4f09d19c9af80fb98ef8b6c697b42e7bc009807762de9365391547cf822c07b869f49104ffab3098746da759ae3378961385521da9470a42c78073bee570feb4a9b14ecbe666a136462aaa0dc2ce173b61c625563fb50b5c9c2f208ba42e656d221431fab0a2868b154366db5e5b19a4822b0385936e4be96b2ad83013a904970efec5329f0a563f6406a57b82df2965b9ba492b29c678262e6829a11011478460d8da1a75d14972996bb8e0762237bbb8c9b044d0337cff8312ae18094082f7d6ca361d69bf0d004dffbc9d55b905f8359f421b4748328a9e722cacca07ffc210e7c35cd461abc069fe4d7980a570e83b01103b878491c200f70aa8bc71a296bb711acc2452c98f88810e75869ee4997bcc17a09712338f83d66b96161e908a4c4279a615ecbe2682349a21e55af66127ad08114b06709c724653d59561aa249c1c050b08c13974211843ba4e1571bb9c8a547e2a3fcd25e3866653728bd57a8c9c480404295819e559ae6727d81d61b785b7817dc3b67560db36c2b0509976ff39435869e49044987fc4d92b9bfd07a37fa08193057aecf585734280d0201a0ea237da26b5741f528dfea6a58d291a5d5543be52d31b5468b82143e754d0714747981aea189025a8931eba7a0ef2561bb0860b8c2442285b4ed22b40c882f19350789642bdd6797e8d98566eb411221a425f34c73aacf92e0a5d8b35272302b815041833b29b1a409d1087b6ef00197893f3b2a93ff61646d77c0c5089f93ec7261347a871c9e7559c11677a84cdab7598c3bd2709e9df33186954682b7ae1f888043a738cd906cab0a06ecb74badd6b303eaaa5ed261e51009c867b8548a84b4ca62845b9f7b9aae1f4107caf9cd2b43469185616ab96b2c09438f06ad95b5605c646b67a8b88b5587fcb0235656ae23d0530fe996bfb691429716e46a2bcd299573c80d7c37ba4fe14e38d556c3281a1b416b2996cb9aacb059345aa6ec078714c52fd3b01d176f 23 | r = 0120000200d0001000fe0cd0002d00000d00002000000d00000d0003f0cf000000000d000100d0020000002d000100d002f0cfff0c000100d0000dd00000d00100d0000000002000001d0001200001200003f0cf00e0cf00f0cf000dd0ff0c00021000001d0001000001f0cf0100d0ff0cd00100d0000d0001000001100001f0cf0100d00000d0000000ff0c000100d001100003f0cf0000d000000001000001f0cf002d000200000220000020000210000000000200d000000002100000f0cf031000010000002000000d000000d0000dd00200d0000d00ff0cd0000d00020000ff0c00010000000000000d000200d0021000000000ff0c00000000000000001000000d00021000020000032000fe1c000100d00000d0000000011000000d0000fdcf00fdcf0200d001f0cf00fdcf0100d0010000002d00000000000000010000010000020000ff0c000100d0021000001000011000ff0c000000000100d00200000100d0000d000000000100d0001000ff0c0000100002f0cf0000d0011000000000010000001d00ff0cd0000d000000d001000002000000f0cf000dd0001000000dd0ff0c00ff2c000000d000000000200001300000100000fdcf00f0cf0000d0003d000000d00110000300d0001d0001100002e0cfff0c00001d000000d0000dd0010000000d000100d0fefccf000dd0000d00020000001d000100d000f0cfff0cd0ff0cd0000000000dd0022000000000000d00020000ff0cd0ff2c00000dd002f0cf02f0cf00f0cf002000000dd0001000000dd00100d0002d00ff2c0001f0cf00f0cf000d000100000100000210000100d001000001100002f0cf00f0cf001000000dd00000d0ff0c00000000010000002000ff1c000000d0021000010000ff2c0000f0cf000000001d00000dd00230000100d001f0cf011000011000032000000dd001100000000000fdcf001d000000d0000dd0000000000dd0030000ff1c00ff0c00ff2c0000e0cf01f0cf0100d000f0cf01f0cf00f0cf010000000000002d00000dd00000d00210000000d0010000001000011000001d00002000 24 | NTT(r) = 8ed81b00a3474f536d9254be888958edc6ce34a4864e37a69439bf5f98982e3433a187aaa98503ab0c7bbb2a8e47a41c24988eaff40802c9ad4a81ae6189cdb851cf7c350b447b05f1e0ac2044a459e3aed2441fc587739b11c88d61538c573b6bb33f8b3320d54c7241043a1c1a26e771441893657b1a3d4dab4f5f754335062fb014a66efbb13b4872a0761b6497cae58c5434f7c75d2a43e1c22b25885ce6281783891aec5c2aef2aa869dc28bfea0c71db10b3158603a68017465e8e8b7c82e53fda6622a8412903d34013f3960f0324c9f3218056ae7acaaf1d7968384c0cdd185ebeba5e6a38323e8c614d27ab7c494d2d584147e75ccee78596815f55bb4a8ae8b1ba7979fa031d5a7b2e72e42c9340cda6d926cf765506709e0a8298a3d6c92ab78fe6e35f58b4486ed1770b016e08c5219133799112518478b0cae45c8c91c5435acc38a115bb8128d6441f18867303f32f03ea45a83217747c72b80127cdfa4af9b95b4330a13ee6b33284940417a5b5692c4760b88c6a9cda3130cac056087c22b623bf270a116825a7baacc63e2997c26b820df40fac57203ea13652db3f4ec0957d90b5fd487fd4392d220c450988c84220ac3e54b4fd9186736bc7b3264fc8a4ce74d0b305376c6ff1088028660e030307802de0b7340fe3bbbac337608747911514e33545add502e8bac64142121cf5a6eca9253822b6abab8d914ba3a6f952f9c756cddacfeba2b499d42de47030c4fc8ac6bac603cbb3e64c11b6a3a494c5523b75af587928730a26bcf89bb8fc6dad8b769c322d54e597bc97685cf7a7cf6c8e08604e8a795bc2b53a58d4493b2c74cb792c1b61c319b976fe1ab5fd690ca49a97722a54a008064eaac08f4a102f6c7b136430439cb6f6885687aa89fde44568eb33fa446c6102154d12465b063cd9e3c489896912144638111f6b50435937b6762a173503b180839839f1a2c29769494cca2d4482dfc480057672c138a3f20535f9db9e5703250785750841cbb70b74830c8c0ec2b9ef27beca7c93eb079c16bb7e5b3600a1573da415628536c29cbb80b2d0c1c9ec39 25 | e1 = 0000d0ff1c00000d000000d000f0cf002000001000002d000100d00110000000d0000dd00000000100d0001000000d00020000012000010000000d000100000000d0000d000000d0000000010000001d00000dd00000000110000100000010000000d0000000ff0c000000000000d0ff0c0001000000f0cf0000d00000d0000dd001f0cf0010000100d000f0cf0010000110000100d001f0cf001000010000000000000000000d00fffccf01f0cf0010000100d000000000100001f0cf000000001000000000000000ff0cd0001000000dd0000000010000010000000dd0000000000000000d00000d00010000000dd00100d0010000021000000000000000ff1c0002000000fdcf0100d0000d0000fdcf011000022000000d00011000021000ff1c00000d00001000011000001d00001d00ff0c000100d0000000ff0c000200d0001d000000d00000000000d00100d0020000001d00000dd0010000010000000dd00000000120000000d0010000000d00011000ff1c00001d00ff0c00000000fffccf021000000000002d000000d0001000000dd0010000000000001d00000d000120000120000000d00000d00000d000f0cf0100d00000d000f0cf000d000100d0020000000d00011000000000000d00000d000000d0000d0001000001f0cf001d00000dd0020000000000000000011000000dd00100000000d00200000000d0020000000d00000d000100d0020000010000000d00ff1c00010000002d00000000001d00ff0c00000d00ff0cd00000d0ff1c00001000ff0c00ff2c00021000012000010000001d000000000100d0000dd00000000000000100000200000010000000d00000d0010000000d00010000000d00000dd0021000000000010000000d000000000110000100d00010000000d0ff2c000100d00000d0011000000dd0ff0cd0000000001000010000022000002d00000dd00100000000d0ff0cd00100d00000d0000d00000dd00000d00100d0001d00ff0c0000f0cf001000ff1c000010000100d00210000200d00000d0000d000010000100d0000d00000000000d00 26 | e2 = 001000ff1c00000000002d00000dd0000dd001f0cf000d000100000000d00000d001000000f0cf000000ff0cd0001000001d00ff1c00001d00000000000d00000d00011000002d00000000010000000d000200d000000000f0cf010000000dd0000000000d00001d0001f0cf000000011000000000000d00000dd0011000010000010000ff0c0002100001f0cf010000ff1c000110000000d00010000200d00120000000d00000d00000d0ff0cd0000000000d00000d000000d00000d0fffccf0000d00000d00110000200d0002d00ff0cd0000d00010000ff2c00001000001000001d00ff0cd0010000ff1c00001000000000011000001d000100000010000000d0000d0001f0cf000d00000d00001d00000d000110000100d0002000ff0c000010000100d00200d0001d00001d00ff1c00ff2c00000dd00100d00000000010000010000000d0021000ff1c000000d000f0cf000000001d00000d00001d00011000001d00010000001d00020000000dd00110000000d0001d00001d0000fdcf 27 | u = aaeb4a983538688494a536642866396702a08d24533651bee94c3948411323dc3b11f1b3458662e243371b223eb9395ebad549129a8b9462b3fba20bd2b8a77b112e8848122a12ac080363f0069fcdc041aebba5936b4392e1a1e33c449946c08404b402a63a09413474e7240bd1896872c33433552ea17887b3c48ef5ab69a146aa01b3d504b40a737a3c231edc169947d0bb6a84b087102daf51a448c0a6174b6e37a159bd598142089481502d1d3b2c2036c9397b90bb216a917ca6d4b2bccbf1829f976230636c2c4521e705b2eb1cb495e5446db9b4727bb7031672291802b90a1b2ea64b336c55a39cc9873c5450dc17938321c62c7453bb4eb16c745397a38a3967f6357a29564aa2870763a869383184b4551137c88a4cd31cbe503bd2c99cf8e58e96db502cea04914c4ae320805f570d77c08d0d9131e4a207d974071624214c253e1c4805c7d93f4cb210f92932dd361a6dac8a38c9001324890a089846cb1ee8dc66c04b6f4a46713db587c128998609bfd7c9112cda16fb11187369cabb98c7d8f2026ff17e174ba9fda1b7fa550183d49bcbb3b0ea39486fd8164bc1682f9a874d02c4f258752fb0b4d908393a5a2ea4b40362da57d2b00f9656613aec474e3030ac933240a98db32c3e45619b240762390599980a9565e0038b1abbe8c736a0d74c95e915ed8b18849c2c07342af6cb4298969e87a44c4ca02efe822cc18cb05aeacd9e210d28421f038b2decb27659595d1ca083536150af68ab92366eb26098e4f0ba6367c2e768bdf4fa8894b19b2da06ae91a03afa36e38f293199b1eac3b5d4ea1062b16799b9612580c13040921e16453bf64321311669e79668d852649b6630cc67dd2090ce9171fad0c60cf000566f86d520baf4a1b2fa572074d66840b269bb297a3475b602d72a11e6a807659a2c41497e7b8b140681c16213c3e5042681cb780d812a1d95b379279ded29fb53b7b65362cf0629ee00641df7a846bc7b05ab248282910d26233101c20b42885ffd50726b540b1405293a309427bb861dc60ad25c048244fd4888873101b70a41fc4594c5f59cc 28 | u[0] = {2986, 1198, 1432, 899, 1128, 2376, 1701, 1603, 1576, 918, 615, 2560, 1165, 1330, 310, 3045, 3305, 916, 328, 308, 3107, 957, 273, 2879, 1605, 1576, 994, 884, 539, 994, 2489, 1507, 1466, 1181, 2578, 2233, 660, 2870, 763, 186, 2258, 2683, 379, 737, 2184, 292, 554, 2753, 776, 1584, 1776, 2544, 205, 1052, 2990, 2651, 2963, 1078, 402, 2590, 3299, 1091, 1689, 3076, 1156, 2880, 1538, 938, 265, 836, 1908, 590, 267, 2205, 616, 3127, 820, 1363, 302, 1930, 903, 3147, 1422, 2751, 361, 1130, 426, 2864, 1237, 2880, 778, 1959, 828, 482, 1756, 2449, 71, 3005, 1130, 2824, 135, 721, 431, 2629, 72, 2668, 2839, 1764, 311, 1434, 2493, 2069, 2114, 2368, 129, 725, 2845, 707, 1568, 3219, 2873, 2311, 443, 1698, 3217, 2663, 724, 3019, 459, 2095, 1951, 1577, 816, 1734, 1324, 532, 1511, 2848, 3307, 2881, 1429, 1102, 2413, 2891, 2930, 2935, 1539, 1825, 2089, 33, 2745, 432, 1582, 1210, 3123, 1366, 3235, 3225, 3207, 1347, 3152, 381, 915, 536, 3270, 1858, 2899, 1259, 3249, 1862, 1875, 2617, 2442, 1651, 1526, 1955, 1577, 1189, 1954, 120, 2147, 1690, 312, 2115, 1460, 277, 2103, 2220, 844, 461, 190, 949, 2514, 2508, 1528, 2286, 2966, 1293, 2604, 78, 3217, 1188, 227, 2050, 1887, 213, 119, 2268, 269, 793, 740, 122, 1241, 119, 1046, 530, 1356, 994, 2076, 84, 2503, 1021, 588, 267, 2553, 802, 1757, 419, 3181, 2218, 2360, 12, 1043, 2194, 2058, 2432, 2886, 492, 3304, 1645, 3008, 1780, 1610, 1812, 1341, 2171, 2241, 2450, 2438, 3056, 2519, 284, 2604, 365, 507, 385} 29 | compress(u[0]) = {918, 369, 440, 277, 347, 731, 523, 493, 485, 282, 189, 787, 358, 409, 95, 937, 1017, 282, 101, 95, 956, 294, 84, 886, 494, 485, 306, 272, 166, 306, 766, 464, 451, 363, 793, 687, 203, 883, 235, 57, 695, 825, 117, 227, 672, 90, 170, 847, 239, 487, 546, 783, 63, 324, 920, 815, 911, 332, 124, 797, 1015, 336, 520, 946, 356, 886, 473, 289, 82, 257, 587, 181, 82, 678, 189, 962, 252, 419, 93, 594, 278, 968, 437, 846, 111, 348, 131, 881, 381, 886, 239, 603, 255, 148, 540, 753, 22, 924, 348, 869, 42, 222, 133, 809, 22, 821, 873, 543, 96, 441, 767, 636, 650, 728, 40, 223, 875, 217, 482, 990, 884, 711, 136, 522, 990, 819, 223, 929, 141, 644, 600, 485, 251, 533, 407, 164, 465, 876, 1017, 886, 440, 339, 742, 889, 901, 903, 473, 561, 643, 10, 844, 133, 487, 372, 961, 420, 995, 992, 986, 414, 970, 117, 281, 165, 1006, 572, 892, 387, 999, 573, 577, 805, 751, 508, 469, 601, 485, 366, 601, 37, 660, 520, 96, 651, 449, 85, 647, 683, 260, 142, 58, 292, 773, 771, 470, 703, 912, 398, 801, 24, 990, 365, 70, 631, 580, 66, 37, 698, 83, 244, 228, 38, 382, 37, 322, 163, 417, 306, 639, 26, 770, 314, 181, 82, 785, 247, 540, 129, 978, 682, 726, 4, 321, 675, 633, 748, 888, 151, 1016, 506, 925, 548, 495, 557, 412, 668, 689, 754, 750, 940, 775, 87, 801, 112, 156, 118} 30 | c1 = 96c7855b455b6dbb607be569d4cbc46665f645eaf96b54c617bc9b4485ddee95271344a6c8e42f74c3ad95f1abcbccbd4e0eb7e65cc738a06aa1cad3ef9c27e2c33f1085f9cb8f33c547c7f74385a0ec64d99d5d485204b4642d5298da8bf0fc8cd6859416215f9bd36f703548dc7dd9fdce96ff50c261bc1670ce55d92a785348ca16d49cf68760e4f62f9f8a628bc2376b67239ef7741f8b8882decffc4de88d108a6579fb54781929d1b19dbfddb84d656ede851f9e5d8c832ac07421e7d1153c69e383afbd67cad7915129eef3c8f760e7f71864c9eff2575d96e5b9956509942208c6a2c15571e8aa0439a20349050f6cddaf903b163206deb765c49d440a5182ae53d0438e097e9520d428a1c9f4a70602eb548b1411dfc36120d2ab6a2d01418d9a27bb785f82bf7e9d93f85e8b9c711aabbceeb27ef01521c3c1891de892ffaaf5e03810479c6907dd09e2d71930d6bf2b99ddf05898c2610681226f5a4bf1c006e940deb9665432396d4920336c413471e077c38b85413b21e5836daee8cb4446bf328ab759bc4277fbc1043e67fe664358ea352f1babe7a1fd363d41e37a5207325c565e1794c3ce36ec97fd72fd7f04a18a266383630e92e02e9700a26830c6ead2057a7883bb46606e64efbd92feb5a87cf8eb40835b3f201288af60abb625972f774608e65189e016cc7b61ac28806966173e55d8572f7eb5f5f2de7adcad59f00e6f66623e7640645069877b779d373ad09400dfa2dcedeb65c9786bb7cac61de399eec7779ddbebda8a3262054a1318157de19d76612f71ae5829cec49a7b895f36e72cdc215058338aa4d9b99815ed13def463bb27ad3e8a9d0995f964436519bd7037e3cf73f79bec5115762ba82314d2152701e325aefb 31 | v = {600, 669, 1463, 1988, 2661, 2401, 1809, 2360, 352, 1372, 2581, 247, 2484, 1425, 232, 1772, 2463, 2480, 911, 1705, 581, 1200, 2947, 693, 1883, 1279, 2146, 2354, 1078, 2587, 100, 2836, 3060, 2332, 1478, 2153, 2875, 2252, 1746, 463, 2473, 2354, 1959, 763, 1157, 1161, 1868, 1020, 1223, 2808, 3001, 862, 1884, 2868, 2488, 2666, 2277, 2659, 2631, 3192, 1715, 594, 2859, 874, 923, 2079, 2903, 1768, 167, 2181, 1141, 464, 2333, 2141, 1791, 1739, 687, 2152, 1230, 2282, 269, 2901, 4, 2676, 1858, 1266, 2267, 515, 414, 1528, 2223, 1743, 1478, 188, 219, 292, 267, 278, 134, 280, 2698, 1010, 2367, 461, 2587, 3083, 1015, 3302, 2049, 1128, 526, 1395, 2672, 2354, 628, 2537, 1983, 1533, 3301, 1503, 479, 3135, 857, 235, 2570, 1048, 2798, 1473, 993, 130, 1171, 2058, 2316, 1958, 3001, 233, 2048, 2224, 1450, 2712, 304, 1611, 3148, 2690, 571, 2102, 2611, 2430, 1063, 2245, 241, 844, 2360, 1595, 2625, 470, 1939, 461, 1937, 1842, 672, 551, 1624, 1023, 1475, 955, 1345, 512, 1735, 3192, 729, 1619, 391, 2047, 2317, 3266, 3153, 1233, 131, 1249, 497, 1253, 2006, 928, 430, 965, 2438, 3228, 2880, 1269, 1902, 3007, 2647, 207, 1924, 2340, 2735, 743, 729, 3147, 3103, 437, 3095, 2247, 2736, 3230, 3199, 91, 3030, 1211, 2374, 524, 3187, 2252, 1222, 152, 3223, 2390, 3151, 2208, 2861, 873, 3229, 3201, 784, 110, 1227, 2389, 807, 895, 2484, 2395, 2063, 846, 3280, 1325, 203, 1742, 2342, 1601, 277, 584, 2911, 2163, 1208, 3155, 2506, 755, 2306, 2319, 3264, 1283, 2109, 2649, 3046, 523} = 58d229b7457c651a9611879360c155157a0fb41959e8c06e9f099b8f936a45024b835b2b5bf74f62289336b4a16440b1f4cb91c695863bcb8cd2f61ca92993a7b72f8594484cc73fc784afb9eb355c47b3b8a9a6e538a6478ac7b326252bab369bf381578b6ea7508875041d1dd985ffb66caf8286cea48e0d51b50440a742274fdb38209e815faff86cc6c50bdb40120b61118680118a2a3f3fd91c1bbac0f763ce0188460e3257702a9374929ebfd75fe5fc5ddff1c359b30e0a8a41ee1a5ce1230893a4800c697ab99b0e00088baa85a930b1644c2ca83b628333ea9727548cf1c03438b963416a1d93d71c912773a0722258f63fc3b53b410520c786c7d9326587f17f0d29cc511c4d83104ef1514ed6073aae513c86c9c9405b4f6ef7bb57fa0c844792af7a2ed9b2c41f5c1b177c8cb0eac97fbc05d6bb4b46c92073cc8cc68409976c954f0c8a2d9b369d1cc810e306cb549527f337b4b9950fe834d0dc52cbe06c2619641581245f3b87b834c5ca392f02f990c03c503d98a5e6bb20 32 | compress(v) = {3, 3, 7, 10, 13, 12, 9, 11, 2, 7, 12, 1, 12, 7, 1, 9, 12, 12, 4, 8, 3, 6, 14, 3, 9, 6, 10, 11, 5, 12, 0, 14, 15, 11, 7, 10, 14, 11, 8, 2, 12, 11, 9, 4, 6, 6, 9, 5, 6, 13, 14, 4, 9, 14, 12, 13, 11, 13, 13, 15, 8, 3, 14, 4, 4, 10, 14, 8, 1, 10, 5, 2, 11, 10, 9, 8, 3, 10, 6, 11, 1, 14, 0, 13, 9, 6, 11, 2, 2, 7, 11, 8, 7, 1, 1, 1, 1, 1, 1, 1, 13, 5, 11, 2, 12, 15, 5, 0, 10, 5, 3, 7, 13, 11, 3, 12, 10, 7, 0, 7, 2, 15, 4, 1, 12, 5, 13, 7, 5, 1, 6, 10, 11, 9, 14, 1, 10, 11, 7, 13, 1, 8, 15, 13, 3, 10, 13, 12, 5, 11, 1, 4, 11, 8, 13, 2, 9, 2, 9, 9, 3, 3, 8, 5, 7, 5, 6, 2, 8, 15, 4, 8, 2, 10, 11, 0, 15, 6, 1, 6, 2, 6, 10, 4, 2, 5, 12, 0, 14, 6, 9, 14, 13, 1, 9, 11, 13, 4, 4, 15, 15, 2, 15, 11, 13, 0, 15, 0, 15, 6, 11, 3, 15, 11, 6, 1, 15, 11, 15, 11, 14, 4, 0, 15, 4, 1, 6, 11, 4, 4, 12, 12, 10, 4, 0, 6, 1, 8, 11, 8, 1, 3, 14, 10, 6, 15, 12, 4, 11, 11, 0, 6, 10, 13, 15, 3} 33 | c2 = 33a7cdb9721c7c91cc84633e69bac5e0bfa7be28bc496659d64ee9dcdbfd384ea48ea125ab89a3b6e1d0692b728b171111115d2bfc055a73bdc37a70f2145c7d15a69b1ebad781dfa3cdb5418b2d299933585726f884a20b6f61624a520c6ee91db94df42fbf0d0f6f3bbf16bfbf4ef014b644cc4a60818b31aef64cbb60da3f 34 | c = 96c7855b455b6dbb607be569d4cbc46665f645eaf96b54c617bc9b4485ddee95271344a6c8e42f74c3ad95f1abcbccbd4e0eb7e65cc738a06aa1cad3ef9c27e2c33f1085f9cb8f33c547c7f74385a0ec64d99d5d485204b4642d5298da8bf0fc8cd6859416215f9bd36f703548dc7dd9fdce96ff50c261bc1670ce55d92a785348ca16d49cf68760e4f62f9f8a628bc2376b67239ef7741f8b8882decffc4de88d108a6579fb54781929d1b19dbfddb84d656ede851f9e5d8c832ac07421e7d1153c69e383afbd67cad7915129eef3c8f760e7f71864c9eff2575d96e5b9956509942208c6a2c15571e8aa0439a20349050f6cddaf903b163206deb765c49d440a5182ae53d0438e097e9520d428a1c9f4a70602eb548b1411dfc36120d2ab6a2d01418d9a27bb785f82bf7e9d93f85e8b9c711aabbceeb27ef01521c3c1891de892ffaaf5e03810479c6907dd09e2d71930d6bf2b99ddf05898c2610681226f5a4bf1c006e940deb9665432396d4920336c413471e077c38b85413b21e5836daee8cb4446bf328ab759bc4277fbc1043e67fe664358ea352f1babe7a1fd363d41e37a5207325c565e1794c3ce36ec97fd72fd7f04a18a266383630e92e02e9700a26830c6ead2057a7883bb46606e64efbd92feb5a87cf8eb40835b3f201288af60abb625972f774608e65189e016cc7b61ac28806966173e55d8572f7eb5f5f2de7adcad59f00e6f66623e7640645069877b779d373ad09400dfa2dcedeb65c9786bb7cac61de399eec7779ddbebda8a3262054a1318157de19d76612f71ae5829cec49a7b895f36e72cdc215058338aa4d9b99815ed13def463bb27ad3e8a9d0995f964436519bd7037e3cf73f79bec5115762ba82314d2152701e325aefb33a7cdb9721c7c91cc84633e69bac5e0bfa7be28bc496659d64ee9dcdbfd384ea48ea125ab89a3b6e1d0692b728b171111115d2bfc055a73bdc37a70f2145c7d15a69b1ebad781dfa3cdb5418b2d299933585726f884a20b6f61624a520c6ee91db94df42fbf0d0f6f3bbf16bfbf4ef014b644cc4a60818b31aef64cbb60da3f 35 | uᵈ = a80b4b965538688494a4366429563966f29f8c24533561beea5c3948511324cc3b1101b4469662e343371c323eba495ebac549129a8b9472b3fc920bd3a8a77c212e8958122922ac09f362ef269fcdd041afaba5927b4393f1a1e44c449b36c08504b402c63a0b413474c7240bc1896672c33323552eb17888b3c48de5ab69b146aa01b3d704b409837a3d131edc069948c0bb6b94b089202db061a448d0a6165b6e38a159bd498141f89382502d1d1b2c1f26c93a7b90ba116a927ca6d5c2bccae1829f976230536c2b5521e805b2ea0cb496e5446ca9b4718bb70206722a1802b80a1b2f964b345c55a39cc9852c5451cc17928321c64c7454ab4eb07c745497a3893967f5257a29664aa2870762b869384184b4451137c88a4de31cbd503bd1b99cf8d58e95eb502cea04923c4ae430805e770d78d08d0e9131e5c207da84071724214c353e1d5805c7d93f4cb210f83932dc361a6b9c8a38d9001424890a089847bb1ee7dc66bf6b6f4936713bc587c038998609bfd8b9112cca16fb01187369cab988c7d8e2026f017f164ba9feb1b7fb450182d49bccc3b0eb29486fc8164ce1682f8a874c12c4f148752ea0b4da2839395a2ea3b40363ca57d3a00f9756613bfc474e2030ac93323fa98db33c3e45719b23f76138059997fa9465e0038a2abbe9b7369fd74c96f915ed9b18858c2c07442af6eb4297869e89a44c4b802eff822cc09cb05adacd9d310d29521f038b2decb27659495d1db083527150b058ab91266eb36098e4f0ba6477c2e768bdf6fa8893a19b2eb06ae91a03af936e39f293198b1eac4b5d4fb1062c26799b8612582c1304f920e05453c064321401669d69668d652649c6630bc67dd1090ce9171facec5fd0100565f86d51fbae4a2b2fa482074c46840b069bb297a3475b602c92a11f7a807669a2c3f496e7d8b141781c18213c3e4042681cb77fe812a0d95b368279dff29fb52b7b66162cef529edf0641df8a846bc7b059c248281910d26233102c20b31885fec50725a540b05052929309448bb862bc60ad35c048144fd3988872001b6fb41fc4694c5f69cc 36 | NTT(uᵈ) = 2c622f3ec163ec18ac66a8938f2a40fd982a453c50bc7c627ad03b775cbc47549a7ba6a543e40a5336a56c0185c2b65e8c19b562b1b836d3a61a865b92640a95fc3f55e90f5cd350151a18a0fa0f180c4c91215a8d4612b62c36be089174c45a3ac1957e8525d562484aaa85697a5975521939052104cab9fbc39d61694378fb58338b435c14536016842974bb3580b659e847a7a3b327a58ab232888549927c0a3e3892af0007cdb03a0bfe8bae8eb09f14454a2241a4e1f29793774b3b578d65d43c064a8bbfb3a82019b4e5072565b37cba2740afe6911dd3425648ba98067edb29cec703c0a1e9796d750de4f34eeb9500a642170fa118a0fb7ab298ab352a19e2ebab17626616e96f094422d18968864b7458018120227c0082408f086cddc2038016977dd6c3f52c4f662b6aaa6ac32e71254d51c9f26111beec9c037517ef0782c8946c613a86bf1b166eab1e5e325731b62c1f9a25acaa136a7899cf3529b954239ad54274b2c1b0b312bc166b0164956e244177f2c13ab48a6d8132b2ea6e36d54578b3ccae73c102a1746f510c8fb604ef23989bb0ad912ab467d13892a8469d7120b0e716dbf7bef19ab2c0966da8118fcde2a9514128e54c9ac4413b2235ade5a8194d3b3ebd6757e3f9cb00900a80e15c2bf3441bd11e2e233687b935c5347984b5b303ac103c03bdff8814251240c0fc7d2f58b6e7829153584aea5573e8aa2939192e9bf164ebd79f532640de2a3305a51ddf579edf9731e0626a4d3c2e60e7531d8b412db7b3a51705c8366b46b5aa3d356b490040cb0887fee83ee5a09739c17c7f3c63f917553eb0795888ccac2230d0eb923c75772dda23f93bc9670c8f5b9545d63c4f366578e97570225a00596527c5b6182db72b5ee12edf0543b2a61b8bcaae21f8522c7159800214224c9b57d7caea882c2f7345a2633df0725e2a93832e7a2a873b122d8abed6317c0a118085c57f5876b8be7c6feae8762b336de8a2c59e321c892c0bf55b186df34557f94161eb47226379b552478fb0600bba44badaa71c4ca529047337b8c511e5bfe5d35b86707ad5ca8f 37 | vᵈ = 700227b01582911a9c51178fa0015bc1090dc1095bd01075c1199c40136870024e610b2751074e21188f10149c0010b6311c8fb01582611b8f81061ac1198f510734e0044e510741e014a9610b345117b6c119a9f118a9911ac3810627610b34401382611b68d0108210041af11882511768701282e0148fd010b60010a951074ef1081aa0015bf11868b0050dd0000dd0000dd0000d910a41f1081ac119c310040021084170025b911a8f70129c21085b00005ba011c340030dc10941910a5b10040de01482f11875610b0d21188fb015a9d01068311ca9701282911a9c10148fd00034f11868910a1a51071a511775700227810641b00541e0041a8116c3401368a01182f10800310c4ed0004ea0014e210834a00141c10900610b4e5117b6910a0d51178f910a344013c3310c1a311c8f910a00310c00310c4ef10827311c8fe0040d311c8f311c8f610b340010c340030de0148f400334c1199c21083400004ed01068f11868d00027611b82e014c3c10934f1188f00004e2118a9310c27 38 | w = 71d6634f0609da2c6af2dc68e916ce94dc0124a00460760250800216666be5ec6655f6601680ced2ec03cf3c618a4c0980405fafaccd0da00173dcc909606256666c01177032c06cc806621d2664f22c668816029ab671e75667e3ccced836cf5e26672746cd2dd05d4fcc63b2dccf866002b46c5fee16020ad668cc560086d6664220c97cf660db0601b5e66b2c3064b94c0071f0cad5966b8cc662955c05c2e6cf36f068c296cac4b66120a6637d60c646c60074966af13c01bf3c00a38c68b7d66bdc766454665d985cceb7f005ee4c6c5ecc69cefc6aa6966489000223106bb8dccc7d8c69d7b6ccb10c680c366dba5c04ac7c00b4a673b2ccc9bfbcc79e3000ecfc0582a602ac3c6936c0687276c7dcecc82dd6045a107193f662b0dccbcf5664c54ccf2a66021b8766892c005e26cf14306b939ccc812008f4356f4dec047c10cd04d0614f566d4a7066ffd6c2383071f40cce2b10cca63c6d06f0cdc0b6cc0fe068198068f2ac02d6ccc89b5665b7dc0c6b166844c06705d66b3a906c 39 | KBar = d08b06e61415a845c0fe5447ba9b22072130d897fd7117457a9060121955a1e5 40 | -------------------------------------------------------------------------------- /assets/mlkem/ML-KEM-768.txt: -------------------------------------------------------------------------------- 1 | d = f688563f7c66a5da2d8bdb5a5f3e07bd8dce6f7efcec7f41298d79863459f7cd 2 | ρ = 26ffff11b531b1800f4e1fa75c4d008c4f9a112932c669d543551204405da8b4 3 | σ = 6de0c01beda8a2ba3f697c1bb8d5a46dc2ac63880c4f9b57db9f10534cef9a42 4 | A[0, 0] = {1413, 10, 604, 2877, 3203, 1546, 1604, 1174, 616, 1275, 2938, 394, 1529, 420, 397, 2532, 619, 1129, 2868, 294, 2356, 2288, 1330, 2649, 2530, 1123, 2119, 3163, 1542, 2725, 1282, 2023, 3308, 1299, 1987, 2334, 726, 3141, 3114, 1371, 667, 1415, 99, 2891, 2749, 1816, 584, 2813, 1463, 459, 1848, 185, 1602, 2052, 3209, 3220, 495, 851, 905, 3314, 2542, 658, 1122, 1500, 663, 1044, 493, 1593, 1541, 3202, 836, 2236, 281, 829, 2078, 517, 168, 2997, 845, 383, 432, 2232, 3267, 324, 1024, 1450, 2805, 1484, 1362, 39, 956, 974, 2289, 1780, 3227, 2180, 619, 1941, 3313, 267, 2992, 1172, 3292, 2044, 1272, 821, 2785, 697, 2584, 2497, 1377, 3075, 3040, 1149, 2763, 1577, 142, 1624, 1186, 2703, 166, 1986, 2618, 3051, 1146, 402, 2003, 1391, 585, 1617, 646, 2176, 3262, 1603, 1543, 2426, 2188, 1624, 1727, 2735, 335, 2465, 1106, 803, 2364, 500, 31, 219, 1922, 126, 917, 1821, 3033, 699, 1866, 2205, 1098, 3229, 2689, 1060, 3256, 1558, 1545, 3206, 678, 672, 1430, 1052, 2905, 3299, 612, 1960, 2661, 2527, 2546, 2593, 3153, 3168, 922, 2152, 528, 3076, 746, 3246, 1601, 779, 2135, 2877, 1892, 946, 2771, 354, 3081, 2793, 3314, 1840, 822, 3124, 2778, 2066, 3188, 2553, 125, 413, 2694, 2297, 456, 1415, 40, 1073, 51, 476, 1759, 752, 1244, 3141, 1886, 1189, 1274, 2352, 514, 1853, 2527, 3161, 1094, 3105, 1794, 1454, 1970, 2704, 2253, 983, 68, 669, 766, 2535, 259, 348, 2019, 3290, 3261, 1379, 3102, 1277, 401, 110, 2539, 1702, 588, 2866, 1895, 1203, 1371, 239, 2622, 2237} = 85a5005cd2b383ac6044664968b24f7aab18f9451a8d419e6b9246346b1234098f3295a5e2394647b8c50656aa02757eec3c51c3e791d652c42abc559b725863b0b4bd8a7148d2afb7b51c38970b424680894cc9ef31358923cfee292962c45d974241ed91630526c844c38b19d1331e5820a850bb4df317b0818bc34c1400a45af5ca5c527502bce33cf1486f9b4c886b5279f1bc10b04b49dccc7ff85433e19a2b181a9c6135c0e0db47cb9a628e8065a2f4a8a6207c3ababe7a2419d3f756491265860288be3c6407a6978c8865bff6aa4f119a5234323c491f1fb00d82e70795d371d9bb2b4ad7894ad4c9814a42b86c610966c8a6022a96c541593bce64827a65fa9df219a2510cc69a83861042c0eae2ca41b63057d8b364273bd32a16099caef20c733643c3da2a81749c9f7dd019869a8fc8715828104333c01ddf062fdc54c45e574afa049302d273df99c54614c202e75ab207a9cd783d44d029fe729e03c115e3a7cdbd3c561edc4f91e106eb696a4c22b367374b5bf50e3eda8b 5 | A = 85a5005cd2b383ac6044664968b24f7aab18f9451a8d419e6b9246346b1234098f3295a5e2394647b8c50656aa02757eec3c51c3e791d652c42abc559b725863b0b4bd8a7148d2afb7b51c38970b424680894cc9ef31358923cfee292962c45d974241ed91630526c844c38b19d1331e5820a850bb4df317b0818bc34c1400a45af5ca5c527502bce33cf1486f9b4c886b5279f1bc10b04b49dccc7ff85433e19a2b181a9c6135c0e0db47cb9a628e8065a2f4a8a6207c3ababe7a2419d3f756491265860288be3c6407a6978c8865bff6aa4f119a5234323c491f1fb00d82e70795d371d9bb2b4ad7894ad4c9814a42b86c610966c8a6022a96c541593bce64827a65fa9df219a2510cc69a83861042c0eae2ca41b63057d8b364273bd32a16099caef20c733643c3da2a81749c9f7dd019869a8fc8715828104333c01ddf062fdc54c45e574afa049302d273df99c54614c202e75ab207a9cd783d44d029fe729e03c115e3a7cdbd3c561edc4f91e106eb696a4c22b367374b5bf50e3eda8be51127e38293dcd01397e236eadb3775c0114d2165f7d32d5043a283c58afbd358b335bb02a3abef72cf0e201e1c7cae95f7071a85b94b0b243f469d8b392318e85f018a96f75429657944b5c08b316153bacbcf52892a24e06ae01818d2c658d16b923f8a73831017b5d0ca6c91904e8b918cdb54957c2924fc9b3876cda87c3e0823290733717b8a98a9d9a85807a96a7a177e8c621f134b8440613ea936479b2a761ca47f522965fc136752447b06877007587b24a33c16af24a011e4b76a2112707399340a07c2f6b34f2a813929fcc06d75cf180a39c4898bf1b586f006986b8583e1790aeb0786d62381635c1e84d033b7b9aef43a4ff16a45bdb78073e832060a4720fa35048027fed98f1ba597a4d033d706312f92c7552a218b5a0af67847f3a83b9e1325ba522a57843686ac1e24849c41737d368bb9cd3622ec27a7e1432f81b37d44601e84f8b0736a4b22c243d5d3a78bb243445711076023c2b607932197b230892e900ced651f1b85b8bc836c14ea3b88a1c5154a8875b9037bd902438666c28c3717cbc9001d2b0a8263eac97db0858ecabc8a6732ae64378178a8baa954a9a1daca6ff930f51701aec28b8a9a502b3c68a0a52f30024ffbe929698a86e6758dd646afd98156825a793607b2ebdc29aef8290fa89503971556d63a665c7a3ea50af10791a2032d45eba531b2b41f675ab5a7c012610f3e94cf25d20a5f9950ae088b79192583e6bea1f073c2c7ca3b0aa5c7d8a8a7c905fc913129578a55e0217a6233e414c441d460a834bc5f85437f4b73d983927693665796090231813eb26365eb9237dcac2909a3d92c4888976eb8e684cb807d15e13f0a20c3d48c042544b06ee522d9c02baa6a295cf061c5e4819ae98ea999719d54844618921db3c9d69760401154818112e909a503c17f548323bd14bad132b60e02ad286113c9c3a75fe6b8eeba67092546752b99df66150685a7d6a24f9deb1c87a355c5860d86b6900e7b56e4db9bdbbc56655332b8a8b6eb789314117dcf91acb434720c14a5bf1c856e8a9ef70a095167b5d442479a8c7ee94278bc914763b3040db68ae9da02201733fcf6a724fc9ba037404b2b9888a865fa81757412b79f14473c106dc9909d2083c79b50c48dc3c8b0e331ddd7996e447e93c96aa175b74c484ac3b3cbd3301823c890f78727540830eff042f17534153a0e7ec6569ea9065c2b89934ac0d5402eb3baa0c667351b8058dca7625511c4753c0a30f33d79643b13663661a2588ac16652924835cac6ece1bbfa111895010959333bd9cc8b44b6cba956c2b980918f5906d2137dd0b04a5f770226c5a37eb9b004eb03a18b1f6556732dd300e0b20612c3b9e29a62b6b2bfcd153e3b928bd158396768a998671e7b2715498b8c538946fcfb84500b57c69ca2ea76afb0652cb4d9082a288984ac7e5493ac9e78c289d78816d3c882ab679cec9ad37cb42d24b182668d355b39ea428f81bc2ecee397d6f04ff74b89e2e4360c420d8868685122178f420d11400f1b0163f286a4605c0d3499b8e21909eff8ba6cf6c4e3780353d2252b8b2d40b1b26b2c7cb1e33e94db35a8db85773916b4459428d6bfa3707c2e079aae30012553ab5f10cbce648ae2966a4b125a5cdbacba165021a235b7ba8d655325adb92182b80df1b7259d821e9bd53f33baa30796b91b929401f1125044bf28b0ce510801b8b8a0f1a983e037cd8c712b4c971a6d05161d8b03cb362fc7640bdd14aa13dc218eb97db5f0cf3d56b99192c9ffacc5e271a8e7950202e7886d0a9e1c0c7aabfc2c0342a1d712b78f624e487a5173329a48298ce6292f0216bdf3317d2e2aca6fc6c8cda658fe67605ae36adf1a8c65da46e10c4b8f518cd08c9be4b269fd8c973f54bc3af1517c787dda30960e399d14fa247b6acaba19b609936c947b9278b7575c115ee7f91de7b30f18b94f3893b1cbf69c320b2f5f777438718738eb71b123a747500fe2483ee2517b7bc54184d6cbf286b30ff2865416542366ceb165891b7c21dcc102a487530440a2449b11d6817250020a658a717a69a388229984b65927c7caaba3b9f59917df489d95652e55a0c7f96a8c576acf1b8acb29d51969924bf1a4052efcba804b833a898e280868094c1b43b661b75924b512352471b350d67ee11cc9ae03419f477e090632ae50b7285b331f820816c01c0ba06249fb894f421909c12cf9555c96860471e32ed5b9306c99a5d1043dc83b730a58878e31146806a2ebb7cf666a519a3140af80107659a71b6108a1231e38e667dddb281b399dc3db72ebc3aa99073c778188b8f6243c5186ed7ca98f56b6730b63ba90a660e22ba9a7399d940b956a3955b456a84b3d269260c7702c12d1cfb008ba3250a06e04599da007a4d862da67251158b8a11484159b0b08138635e7691ab97d14e124af645a8a020c19a916fa4b3f5ec9ca4dc15772158ccbe416b3a1a5346b22c42c2901781f6cb9536241590c21b82ce31406257c4a7cab9fa988a3fc8fce8553a3889782c817a7125ab069c77ba586034ccf3bf702ba0103d7da1558b1b812f34aa9c88f12c63c81c72f45567a8279c74078a5337b67226586d79c5de2252690841e6d24c7cb86c20e5945f2550cae40a2ef112f0ed47b09f9cf5c139fb8ca9c41e54e0240cd6285c2e6826857aa523ba7cfd5304556b936bfa5a6fbf251df37cc8ef0b8ce04913739a3f681341809a0aee971b90201d0d9cb387011aaeb4960826964e2bc3562ad894c597caa5d26a6a804059fb7c9824d4b0f5c91181309797dd65c9ceb3845a98eb5ba224be121d2e0b12a86a83880355de16f9d585c0d655d899a8f3261afa2c478ff62749707829c9849f0f38cc40629efe7570ad1566b8a0d4e062e727c4398b241e638ba04d05384aba8a290b2a1115c9c5205e0d78b9bc38f72f11b06871a7e79746752998990aa59639992dcc07064134551584a823d7cab532578ca6b0aa167441dcb5653dd495d6d9b9587a64560f2c2bcb40427c10199c0043d734e4e7a837cf504d58793053b4290647502120ad74215a295946b7acce421be4d451fa8ca1d2ab2a3bd1542493686ced9c6a5e577d23b8d39c4bf40f27020c06158ebc01eb9934740c5956a1343f440b03ba287395964b674cd707af0085019c8c1c7843f630ab0d2b067c4548a6103946a55c1f80473f932a5f0dca3d9cb80bca99d51361fb6b38a1ea0abbed2864472622390ba1c10292dc4800e236cbc5ba64baa24d46cc7acf61bb5bc054f845d84921546b02c12e0c7a355cad8c74bfaca69a83caf69a4a8fc6b12d9ccc9cde8c11a7a73ae0a3b4540a1573723b3576b2728c5a1b3a26ab4b7dc20a3c6895fdbb97172441f3ea67313d759c1aa3a16a445babc116833197a7002e8f68c8d38b955690d4d87242e45a4b5a30970c5aa14a6cf8eb997d41c4513a472e42a27d52652d0d26c43c85fd35a80ed7a51e8c1b5a7d9a781f4079628aa39bacbe9885168c20b262a77379a66f3d96a5fd902c8ac84b211bd3c226a5197330ea0a55b9c7123f63fcd7516d3b559eaeab3e3ba54219bcdd205c051118bf3180049639b8b062bfa6345a51ccef4f8547b40cfe10375e69392ddf5bfaa59277bd25a75fb3750a42ec47a5f62168c78f2996fa9407f578674bb95b88504eb401541ac5fd3c424313b99d38b113664b9caa67abc06329a6c6a8a89ba04ccc347344983ec9f98d5a930a96ec0d15008810afdf576e068096634b025d46cb478b75c00058370a70eb4687051a225884a6dda99e995b9e987167706aaee9896ca0612d89243ad208c91a237ff9414b6803a7a3b08d70717974c3262e45a4f3a501d442571e095db9ab536382d212520abc7110501560592b4e6480730d0027dc1143aa3c517710b301b871fd3ba9f159eae036d95b62895d9774c9176844399da0a870b0b5c476c9921816d7573bf75a632f6f82aed27657ae18126ca646fa2a112602ea0e34bdb6092adea950ae3b610977639896e264396d87813af70a045b46040c3294d93415ab576d1316bfa6c2fe125ca886cb6177c9244823053b25bc13ac567b409c4273b96a9c375231f301486d70509a9d60e31982c052b34a9f3c381c0c615717f1460bbfc96856b60c143cc63e0c9c892e3c511ea09faa770fc028199c5528bc0761a3a34b1f82a9744a8f907652ce628683441ab5cb6c31947f2fc7378504ab630a4c85907c0030e33152bdb15c05944a99b170ae1f3615611c3793568340a740f537604bcae3a01cd3218674bd8a62dbcc35e6137ed4850d6355667c47a3bc255fef61a 6 | s[0] = {3328, 0, 0, 3328, 3328, 3328, 1, 0, 0, 0, 1, 2, 1, 1, 0, 3328, 0, 3328, 0, 2, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 3328, 0, 1, 0, 1, 0, 3328, 1, 1, 0, 0, 3328, 3328, 0, 3328, 3328, 1, 3327, 1, 0, 3328, 0, 3328, 1, 3328, 1, 2, 2, 0, 3328, 2, 0, 0, 0, 3328, 3328, 0, 0, 0, 0, 1, 1, 3327, 1, 1, 0, 3328, 1, 0, 1, 3328, 3328, 0, 1, 3328, 1, 3328, 1, 0, 0, 1, 3328, 1, 0, 0, 0, 0, 3328, 3328, 1, 0, 0, 2, 0, 0, 1, 0, 3328, 3328, 0, 3328, 1, 1, 3328, 3328, 3327, 3328, 0, 3327, 0, 0, 0, 1, 0, 3328, 2, 1, 3328, 0, 3328, 1, 0, 1, 0, 3328, 3328, 1, 1, 3328, 1, 1, 1, 0, 0, 0, 0, 3328, 3328, 0, 3328, 3328, 1, 0, 0, 0, 0, 0, 0, 3327, 1, 3328, 0, 1, 1, 0, 3328, 3328, 1, 3328, 3328, 1, 0, 1, 2, 3328, 0, 1, 3328, 3328, 0, 1, 0, 3328, 0, 0, 1, 1, 0, 0, 1, 1, 0, 3328, 0, 1, 0, 1, 1, 3328, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 3327, 3327, 1, 0, 1, 0, 0, 3328, 3328, 3328, 3327, 1, 3328, 0, 0, 0, 2, 1, 2, 0, 0, 3328, 1, 1, 0, 0, 3328, 3328, 0, 1, 1, 3328, 3328, 0, 3328, 0, 1, 1, 0, 0, 2, 1, 3328, 0, 0, 1} = 000d000000d0000dd00100000000000120000110000000d00000d0002000000000010000020000001000010000000d00010000010000001d000100000000d0000d00000dd001f0cf010000000d00001d00001d000220000000d0020000000000000dd0000000000000011000ff1c00010000001d00001000000dd0001000001d00001d000000000100d00100000000000000d0001d000000000200000010000000d0000d00001d000100d000fdcf000d00ff0c00000000010000002d000100d00000d0010000010000000dd0011000001d00011000000000000000000dd00000d0001d00000000000000000000ff1c00000d000110000000d0001d00000dd0010000012000000d000100d0000d00010000000d00001000010000001000010000000d00010000011000000d00010000010000001000011000001000fffccf0100000100000000d0000dd0ff1c00000d000000000210000200000000d0011000000000000dd00010000100d0000d00000d00011000000000021000000d00001000 7 | s = 000d000000d0000dd00100000000000120000110000000d00000d0002000000000010000020000001000010000000d00010000010000001d000100000000d0000d00000dd001f0cf010000000d00001d00001d000220000000d0020000000000000dd0000000000000011000ff1c00010000001d00001000000dd0001000001d00001d000000000100d00100000000000000d0001d000000000200000010000000d0000d00001d000100d000fdcf000d00ff0c00000000010000002d000100d00000d0010000010000000dd0011000001d00011000000000000000000dd00000d0001d00000000000000000000ff1c00000d000110000000d0001d00000dd0010000012000000d000100d0000d00010000000d00001000010000001000010000000d00010000011000000d00010000010000001000011000001000fffccf0100000100000000d0000dd0ff1c00000d000000000210000200000000d0011000000000000dd00010000100d0000d00000d00011000000000021000000d000010000020000100000000000200000100d001f0cf010000ff0c0001f0cfff1c0000f0cf0100d0000d000100000100d0ff0c00000d00000dd00000d00000d00000d0001d000000d0000dd00200d0001d000000000000d0000dd00120000000d000000001f0cf010000000dd0000d00000000000dd00100d00110000000d0001d00000d000010000010000120000100d00000000000000100000100d0000d00000dd0000d0000f0cf0200d00100000100d0020000001d000010000100d0000d00001000010000022000000d00011000ff2c00002d00000dd001000001000000f0cf0200d0002d00010000000d00000000000dd0ff0c000000000000d00010000010000110000000000100d0001000000000002d00000dd00100d0020000010000002000001000000d00000d00010000000000011000000000001000000d000210000100d0001000000dd00100000110000110000000d0001000000d00000dd000f0cf000dd0001d00000000011000011000000dd0001d00010000000d00001000000d000000000000d00210000000d00000d0000d00000000000d000000d00020000000d0000d000000000010000000d0000dd00100d0010000000000021000010000011000000d0000fdcf0100d000100000000001f0cf000dd0001d000200000010000100d0000d00ff1c000200000010000100d0000dd0000000010000001d000010000100d00000d001000002f0cf0100000220000000d00020000200000000d00000000100d0000d00000000021000000000000dd0001d00000000010000001000010000000dd0000dd00020000010000000d000fdcf0100d0000000000d00001000001000000000001d000210000000000000d0001000010000ff2c00000000000dd0000d000000000100000110000100d00100d00100000000d0000dd00200000010000000d000f0cf0110000000d000fdcfff0c000200000200d0001d000010000100d0001000010000021000000d000110000000d0010000010000001000000000001d000000000010000100000000d000fdcf001000000d00001000000000 8 | NTT(s[0]) = {306, 1942, 1038, 2121, 1478, 852, 1794, 2263, 2789, 3279, 1516, 1756, 2964, 3007, 2621, 54, 925, 3296, 2999, 592, 2639, 514, 282, 2345, 2485, 649, 2949, 1742, 1183, 2964, 187, 3159, 3204, 1164, 2193, 743, 2631, 2636, 3090, 2740, 1645, 1787, 3182, 2826, 3226, 1758, 1388, 815, 590, 717, 2812, 962, 1699, 2774, 61, 2826, 185, 2731, 1818, 3086, 1916, 1897, 711, 1855, 2124, 2626, 1445, 1911, 2615, 2600, 2743, 1183, 2532, 2696, 759, 3210, 1549, 227, 2658, 930, 2113, 241, 1713, 2017, 854, 1013, 1011, 2574, 1283, 150, 1035, 1368, 2071, 2400, 3220, 2038, 3049, 261, 268, 1901, 3263, 1091, 3117, 2406, 1256, 2973, 2454, 2058, 470, 2599, 3136, 342, 1704, 3234, 2226, 856, 2716, 703, 1370, 2051, 3061, 1371, 1668, 3211, 3292, 1291, 1828, 332, 3231, 1419, 2063, 464, 2978, 1854, 3226, 1377, 2314, 2835, 209, 699, 1813, 783, 510, 799, 2389, 1112, 348, 1002, 2356, 1312, 1642, 1029, 755, 1033, 2192, 1733, 2045, 1731, 1825, 1459, 887, 182, 2686, 2736, 3292, 2072, 1566, 1221, 1393, 341, 394, 1228, 2559, 2280, 486, 2802, 1420, 3312, 3277, 3138, 795, 1054, 2081, 389, 1510, 2622, 1660, 1953, 2872, 2577, 1732, 1402, 1572, 1247, 2729, 3076, 878, 213, 550, 656, 3259, 1554, 417, 1901, 2608, 954, 2623, 2628, 2977, 2045, 2932, 2800, 2749, 2985, 2369, 244, 1561, 299, 2941, 1631, 28, 802, 305, 567, 2150, 770, 2758, 2974, 3053, 1140, 3075, 775, 1189, 77, 1678, 2522, 1761, 2359, 2083, 1402, 1574, 1575, 2167, 602, 84, 204, 1523, 11, 2550, 2986, 2823, 1760, 286, 240, 2022, 1062} = 3261790e9484c6453502778de5faccecc56d94fbbb3d6a039d03ceb70b254f2a201a9192b5992885eb6c9f44b9bb70c584cc4891782e47caa4124cab6db66f6eacb09aec6d6cf5324ed22cfc2a3ca366ad3da0b0b9b0aa1ae7c07c9776c7f2734c28a4a57577378aa2b7fa49e489a8f7a2c80d360e622a3a41180fb1167e56533ff3e3a00365090b8455170896946c7fe95b100cd176bf3c442d6c96e8d4b996a980d671a2406c15a826cab288359cfa2b5a3580f5bb5584b6c8dcbc5024c7149fbc580f081da2eb739a1c560a39b1d1b02b15f730fef1315589455ca13e3409526a5640f3924090586cfd376c21375b77630b7e0aabdc8c811e564c7155158ac14cff898ee621af8c05cfcd2cc41be341215818e6e5a37c167a381ba1c4a65724f64da94ac06e530d260229bb2c61a1d17630aa3b3f4aa4a1db7f740bafbd9aba41490f19b6127dfb651c2032317123662830c6eab9ed4b47037c30a5d4048ea69de1769323a85726766277a82554c00cf3b500f6a9ba070b6e1e010fe66742 9 | dkPKE = NTT(s) = 3261790e9484c6453502778de5faccecc56d94fbbb3d6a039d03ceb70b254f2a201a9192b5992885eb6c9f44b9bb70c584cc4891782e47caa4124cab6db66f6eacb09aec6d6cf5324ed22cfc2a3ca366ad3da0b0b9b0aa1ae7c07c9776c7f2734c28a4a57577378aa2b7fa49e489a8f7a2c80d360e622a3a41180fb1167e56533ff3e3a00365090b8455170896946c7fe95b100cd176bf3c442d6c96e8d4b996a980d671a2406c15a826cab288359cfa2b5a3580f5bb5584b6c8dcbc5024c7149fbc580f081da2eb739a1c560a39b1d1b02b15f730fef1315589455ca13e3409526a5640f3924090586cfd376c21375b77630b7e0aabdc8c811e564c7155158ac14cff898ee621af8c05cfcd2cc41be341215818e6e5a37c167a381ba1c4a65724f64da94ac06e530d260229bb2c61a1d17630aa3b3f4aa4a1db7f740bafbd9aba41490f19b6127dfb651c2032317123662830c6eab9ed4b47037c30a5d4048ea69de1769323a85726766277a82554c00cf3b500f6a9ba070b6e1e010fe667429d5259d027ccaaec559c760ae99b62c014cf76b08c89a72d3e233af5251976e288da3a8fa2820e6ca8ca953ab8f504060fd2c0104558af67c34691c1c4c2878d5904c94390b1639548e206659804a3759a6a7930921a3fc2853d8e90cd94b523fe1a30704030f04b9d4fc919fd22c27966aa53180916009e3f831a47b53f5951087d110c010cb81a415d88c3cb19031e3f30a21496903f59ce75742227b22ca7687ad3b05c0aca42e3747bc5fb1683b796a10bac6a158a745836478829707b70aca820d3a9b6adda8091bc0c44c9bca1f57bae60165940ac87815c1e36a783a88b2b68655d22ccebd1490d4440a00775dd1a68f0d3125c7cb48d8a91db2993542c5541137e817206705bb23a261eb3740d952b1af92181f8a629e156b7a6d703d9225706a5bee26845bd80552df9a620f16b4c1444ed2514ec8c2aed453bedb9805174ada9610c91aca500ccb221c109a56377bfc043fc0410340c0d4a509be7d1252d65af888905ad42bfd9cc0e84b63d82032de9cb1cc1264fb6b65c2304818c566fdb36c47e43b3e43b405f7b8944340f78830901507d145b1818c86d9b003d03e5a33310ca66c44d63b2bdb427cc0dd68fd32176292359ce639c9177454e6a36d7cb808e13a9d309310b455202ec6bcf2891d2590755b92e4f0354e766b049d77b4b5cbd8b332a6d1261cc58a384ebb842d92273202eb7b45570cc42fb039c18b4a1308062d6710601c209f5b425d3a6b8735911fdb6bb26a556595c5e69d66a5fc346552526d8a8a292b3cda869b28fab0b2cb6162e583e8c9c558c4c8c457cb83e67c2f03960011091fdf5a35b88712d3aa5e0d0961242653850acca27be2a8523b0557dc38a3fd90acddb190c813c3e86d50e2dac874f2316c607a3582aacef1c14dee409551a9e02f60e9737c9940799b39932cd645341444414c77f4fa9046ee4020fb68d607b46759615803572502a296c291965b2c92320c663d504e06a0bce3286a854c74a6256e168c0b750805359986493a25f75b5f8923bbbd4b2a30b6e9092ce0a5035939c47e900c4235a447796a426713e1bd269a13493 10 | e = 000d00011000000dd0000dd00000000110000110000100d0011000010000000d000100d0001d00ff0c00ff0c00002000000000020000012000000000000000000d00011000000000000000000dd000100000000001f0cf000dd0002d0001f0cf0000d0000d00010000ff0cd0001d00010000000dd00200d00100d00020000100d0010000011000000000000000000d00000000000d000000000000d0000dd0001000002000001d000000d00100d0000d00010000002d00000dd0000d00000d00010000000d00ff2c00000d00010000ff0c000100d00000d00000d0000000000dd0001d000200000000d0000dd00000d00000d0010000ff2c000000d000000000f0cf0000000000d00100d000f0cf0000d0001000000000001d000000000100d0000d00ff0cd00000d000f0cf0100000100d0001d000010000010000100d0021000021000010000000000000d000010000110000000d00200d0ff0cd0ff0c00000d0000000000100000100000fdcf0010000110000100000000d0021000020000010000000dd0000dd00000d0000dd0011000000000000000010000002d000000d00100000010000000d00200000000000000d00000d000fdcf0200000000d00000d0000000002000ff0cd0001000002000001d000000000000d0001000000d00000000010000002000001d0000200000100001000000f0cf000dd001100002f0cf01f0cf001000000000002d00000000021000011000000dd000000000000001f0cf0000d0010000001d000010000200d0010000000dd0020000011000011000000d00000d000100000100d00200000000d0000d0000fdcf002d00000dd00000d00200000000d0011000001d00001000ff0c00000000ff1c00ff0c00000000002d00000dd0000d00001d00021000001d000000d0000d000100d0000d000200d0000dd0002d00000d00010000000d00010000000d0000000000000000100000f0cf0100d0000000ff1c00000000001000000d000100000000d00100d0ff1c000100000000d0000d0000f0cf0100d0020000000d000000000010000100d000100001f0cf0000d0000dd00100000100d0ff1c00000d000000000000d0000dd00100000100d0000d00000d0001000000100000f0cf0000000110000000d0002d00001000000d000000000000000000d0002d000000d0000d000100000100000000000000000000d00000d0001d00001000000000001000002d00001d00ff0c00010000001000011000ff0c00002d000000000000000110000100000100d0001000000000011000001000000d00000dd0000dd0001d000000d00100d0001000000d0000000001f0cf001d000000000000000000d00100d00100d0000000020000000000000000001d000010000010000000d0001d00001000011000001000000dd001f0cf0100000000d00110000110000120000000d0001d000000000200d0000000000d0000000000fdcf0100000000000120000110000000d00000d00110000110000000000020000000d0001000002d00020000010000010000011000010000012000ff1c00000dd0000d000100000100d0001d00000000000d00011000010000 11 | NTT(e) = c64791062736bb66327cb057d81a1f1c839cff7699d58a5d9b90858bb01228065ae232c4f2f0a46bb50d77dac7ab6402da4c1c9166acf79b61eb816a497a1b6a70992b8aa07a63477bab90a0319ea7fc5a77b7b800c4785b3522ede5835eb360fdf7cfebfc2e0c1c0cca60c72e9749cb8cbaee86a4ad5509d29178bf68cb65e59760a3a543460784346ee64aceb32c348613874ae99256163fc208be9417a8dbe389de1388e7ba4c945a8bcedbb97804a8bc9863f613cd39d6cf5dc280bb410e393b9da6ba30e065c17d979a4e563d8eb12c7f668c39aa448af32a87266927694a1f70b28e1a7b88568001bc85dd5c4777b48bc1b1655377a6aed743317527ef273ee7352b8e7b58ca237ca3f9aa1c538a986658ffda8493b1993d201ba1c68a3e856c8ed93f0407c116992aa07b86e89b7b829638e3f86853c1cac3ba25021856be63613aba612ec523b3315c74633c58d7483461cea937bed9da919b728debd144288461b795af50128dfacc5e0e6c4f8430bcbe26c3e4dcb3201a4b000a4c47496fac612ff0219b28b24978175d7c092dd752789ed20feed1cd29e41ba1e99a601ace5f9054c8933ed3c94960906adec66645842bad0b01a7618ae3b48d51d36e4eda101020a635c13ebc4825fe55000a5a6a52c026ab5b7138b6ab00c19d6738b2b8023007e91feb813d38515dfda85a8624858a3529b9bc46d80b3d591b8b0cd31b5bf63ca29b89a180594f8c8227da4844e07d82c20011b718ffb156b5a4bf9971c627032cf7d2a860950f8a1074886762174c6197b7c7876c90d7123d153470177b96f70430c567b3d818337d6a863dc5af805a7921793d28848bf1e5789cf76bbc286a1c2679aa8bb810b90d588487a7372819c11c1c73888f0829cff1bcf2129cff9ba55b4aa5a7ac5e29797c3b3c1a2c73bef50540ec4b8e835c33bedc57665a124435a03ac36ec7711c7648bde7c75cd0f6b8a5eb645d646a0d868ce9f8048e138d30238b71d05e2fb7898632b9d48842882192589272f2176c6f46557baa676d8aa4be981e8d843c751334a38c56023a3fc406c013a08efb719c8a2ab0279abad6154989067c57b5941b11af97b436fe901b1f416c24cc1395f13ea7a02981e091e103234dbc81ec044d526711d94012b23a22f3e90f7e098bf2f4277e306429eab80edc7fe7d10229807d2e05a4c19c51bfe392bb76898f68c3ff629b913639c37253450c8b02ebbd19f0592d101947040e3bd2354a1b0f6d27b4bd099f383b3ea7a554347a1bd082a9dbda9ef5acc3b40a380e769d9f2814cfc74ee9747a0186a6bdc57435d168f3ba0f3b05aa78158719e42be3c581fac8936d3810fe235e63810b0609b85b7590e4d3708b86c2720ab83cc8b811e4383872a9695b5f578a36ad211c46cc1852912a16937f14d54450f322d3425b0ed8a566825bea635bd17bbe72221fa39a292897b99af5126cf5bd8077bb2703b2c7e120077a6b25683223b90727b808da2048732c5f94a4a536a518b7d374a893916b1058bd141162695d38d69ead38a490d48324b406699b8ca263beffa201f48a071bc91613238094c06d99c18c45d77b94b9b343ebc2de50c3039c1aa9534588e083 12 | t = d0f1a25721155a28143a355f99c582c2d258a270b9f78941af004116e94f2f9825af17c50b600705ea1a9c2440df6803518a5ba558acb5b732d06a3c80e555fa374d80d82f08c5b47ca12cbe6520340538c2100b5e094392acacafe1ce4ad74fa451c4379219140103e9c3ba7ba683cddca0d3b02d57104c292b2e25bc791d8b11d5079477d7b0f930176fb0a5cda99afbfb6fca86226cb8152b250a536ca60b6ccf93880007068b41bbc5c6e9b8bd92af8a717e37c69ac7db7e76a2c36eb028c1e098915274b4d59def6c42d3c25bb35310846809c96bc43b639e52a3730654cc8db548faf21e446ca874a3caba777bac25b56902481c79a744d70d12130ad6bc8136349e3234c556267e97b0a8ccf772528299fa26111172c223f2484102917bdb6de7770682698763e5b2038779f812363a727f40949b76b6913c8910d6f51dd48983ba149c070590582662fbc9053b667e1eea1ca7173e421576f2d8ad7bf415899484bd1530b4fc855354952cf28d0f1216dfba00ec100bdd76c4f4b405c913baa78958ae31c578463877138cf89255a3935cfbd1b77dbb5f66b87543131da4aabb6781427f417e841141dcb47fcd82a92aeb3c50030d71eb7f3b17b0207a6cdf654d563a420bb742ac30c0e01235910886ff045652d5035b7ab98a1b6abac88ea5996b965c2d3f3305e075bbca267e8547c6c7769fb9f91e20849b524b7822965fe036a515c60e24636a3665b23b878c5b7658f7c49f2059255c89536426510875cbdca111bdc29183459340c125df30c05a7698490193d88c52410a14a7f12a118495413487b434568d105f2d7b9c0fc32e80a13e142528ca1825e0830cd41071aac7124042145be35e30d541c2ba31a82a5f479b3bad881164aa347386c9bfa505c9968fdea866120c8b4e809bdb1482d790a03fcb1ca88c15aae8047e1678ba66833ca430d592638bf3be19592027b3ccb044a9a4d01f3804c2fe0ca60cc7c017dc14dff60562f20d912650fc24449a78998d2b0ce4d9b131955affe7b5c12278cb5b23847c8090fc0578f71760918e9ee8274673845ca29f8d338fa89637265c518f902a9ba8ad9615764ce9835b322e0b5bb1fe928888318988e435cc3c3a918ccc773371a9687884f0822558ae634a54d7c74556528f2d7cb212f1901a82ae68905330b4550518b7b44c117f335f9c818635a2a29e59b39784b021d88354d707cfe0b3599b54fa4a1218261dc72a00cf2ccef8c6c2fe131899f9a239f545c4605296d7b39beb0832732eb608464df29d193b2f2c3b7c69cbbc2082cda14863071235a75544320c1c7930262f82ac7ba788c6c09586c2420d7385a0a6c50d050910e2956844b558fa78b6cb2969e866a0bcc1b7b980a02669c226b6c9267f6e66995d6b37eedc8e34f516ef48a69a3461f3742cc6268b26a8c158e4bf34c072d4e37a4e273ee3464aea81b57c52ab4d5a1e94a668c9a14612196f6a792646581a8ff545179323aaa8369c21181c1329464099cdf940fa56ce1f204ad4b048d5e783e1948b3493b273b2a33d8c0183e386786c7e7e421b1536610cecb16b7a98641b95693879a7d8bc444931cedaa6c45303aac0a48ac290410855 13 | ek = d0f1a25721155a28143a355f99c582c2d258a270b9f78941af004116e94f2f9825af17c50b600705ea1a9c2440df6803518a5ba558acb5b732d06a3c80e555fa374d80d82f08c5b47ca12cbe6520340538c2100b5e094392acacafe1ce4ad74fa451c4379219140103e9c3ba7ba683cddca0d3b02d57104c292b2e25bc791d8b11d5079477d7b0f930176fb0a5cda99afbfb6fca86226cb8152b250a536ca60b6ccf93880007068b41bbc5c6e9b8bd92af8a717e37c69ac7db7e76a2c36eb028c1e098915274b4d59def6c42d3c25bb35310846809c96bc43b639e52a3730654cc8db548faf21e446ca874a3caba777bac25b56902481c79a744d70d12130ad6bc8136349e3234c556267e97b0a8ccf772528299fa26111172c223f2484102917bdb6de7770682698763e5b2038779f812363a727f40949b76b6913c8910d6f51dd48983ba149c070590582662fbc9053b667e1eea1ca7173e421576f2d8ad7bf415899484bd1530b4fc855354952cf28d0f1216dfba00ec100bdd76c4f4b405c913baa78958ae31c578463877138cf89255a3935cfbd1b77dbb5f66b87543131da4aabb6781427f417e841141dcb47fcd82a92aeb3c50030d71eb7f3b17b0207a6cdf654d563a420bb742ac30c0e01235910886ff045652d5035b7ab98a1b6abac88ea5996b965c2d3f3305e075bbca267e8547c6c7769fb9f91e20849b524b7822965fe036a515c60e24636a3665b23b878c5b7658f7c49f2059255c89536426510875cbdca111bdc29183459340c125df30c05a7698490193d88c52410a14a7f12a118495413487b434568d105f2d7b9c0fc32e80a13e142528ca1825e0830cd41071aac7124042145be35e30d541c2ba31a82a5f479b3bad881164aa347386c9bfa505c9968fdea866120c8b4e809bdb1482d790a03fcb1ca88c15aae8047e1678ba66833ca430d592638bf3be19592027b3ccb044a9a4d01f3804c2fe0ca60cc7c017dc14dff60562f20d912650fc24449a78998d2b0ce4d9b131955affe7b5c12278cb5b23847c8090fc0578f71760918e9ee8274673845ca29f8d338fa89637265c518f902a9ba8ad9615764ce9835b322e0b5bb1fe928888318988e435cc3c3a918ccc773371a9687884f0822558ae634a54d7c74556528f2d7cb212f1901a82ae68905330b4550518b7b44c117f335f9c818635a2a29e59b39784b021d88354d707cfe0b3599b54fa4a1218261dc72a00cf2ccef8c6c2fe131899f9a239f545c4605296d7b39beb0832732eb608464df29d193b2f2c3b7c69cbbc2082cda14863071235a75544320c1c7930262f82ac7ba788c6c09586c2420d7385a0a6c50d050910e2956844b558fa78b6cb2969e866a0bcc1b7b980a02669c226b6c9267f6e66995d6b37eedc8e34f516ef48a69a3461f3742cc6268b26a8c158e4bf34c072d4e37a4e273ee3464aea81b57c52ab4d5a1e94a668c9a14612196f6a792646581a8ff545179323aaa8369c21181c1329464099cdf940fa56ce1f204ad4b048d5e783e1948b3493b273b2a33d8c0183e386786c7e7e421b1536610cecb16b7a98641b95693879a7d8bc444931cedaa6c45303aac0a48ac29041085526ffff11b531b1800f4e1fa75c4d008c4f9a112932c669d543551204405da8b4 14 | dkPKE = d0f1a25721155a28143a355f99c582c2d258a270b9f78941af004116e94f2f9825af17c50b600705ea1a9c2440df6803518a5ba558acb5b732d06a3c80e555fa374d80d82f08c5b47ca12cbe6520340538c2100b5e094392acacafe1ce4ad74fa451c4379219140103e9c3ba7ba683cddca0d3b02d57104c292b2e25bc791d8b11d5079477d7b0f930176fb0a5cda99afbfb6fca86226cb8152b250a536ca60b6ccf93880007068b41bbc5c6e9b8bd92af8a717e37c69ac7db7e76a2c36eb028c1e098915274b4d59def6c42d3c25bb35310846809c96bc43b639e52a3730654cc8db548faf21e446ca874a3caba777bac25b56902481c79a744d70d12130ad6bc8136349e3234c556267e97b0a8ccf772528299fa26111172c223f2484102917bdb6de7770682698763e5b2038779f812363a727f40949b76b6913c8910d6f51dd48983ba149c070590582662fbc9053b667e1eea1ca7173e421576f2d8ad7bf415899484bd1530b4fc855354952cf28d0f1216dfba00ec100bdd76c4f4b405c913baa78958ae31c578463877138cf89255a3935cfbd1b77dbb5f66b87543131da4aabb6781427f417e841141dcb47fcd82a92aeb3c50030d71eb7f3b17b0207a6cdf654d563a420bb742ac30c0e01235910886ff045652d5035b7ab98a1b6abac88ea5996b965c2d3f3305e075bbca267e8547c6c7769fb9f91e20849b524b7822965fe036a515c60e24636a3665b23b878c5b7658f7c49f2059255c89536426510875cbdca111bdc29183459340c125df30c05a7698490193d88c52410a14a7f12a118495413487b434568d105f2d7b9c0fc32e80a13e142528ca1825e0830cd41071aac7124042145be35e30d541c2ba31a82a5f479b3bad881164aa347386c9bfa505c9968fdea866120c8b4e809bdb1482d790a03fcb1ca88c15aae8047e1678ba66833ca430d592638bf3be19592027b3ccb044a9a4d01f3804c2fe0ca60cc7c017dc14dff60562f20d912650fc24449a78998d2b0ce4d9b131955affe7b5c12278cb5b23847c8090fc0578f71760918e9ee8274673845ca29f8d338fa89637265c518f902a9ba8ad9615764ce9835b322e0b5bb1fe928888318988e435cc3c3a918ccc773371a9687884f0822558ae634a54d7c74556528f2d7cb212f1901a82ae68905330b4550518b7b44c117f335f9c818635a2a29e59b39784b021d88354d707cfe0b3599b54fa4a1218261dc72a00cf2ccef8c6c2fe131899f9a239f545c4605296d7b39beb0832732eb608464df29d193b2f2c3b7c69cbbc2082cda14863071235a75544320c1c7930262f82ac7ba788c6c09586c2420d7385a0a6c50d050910e2956844b558fa78b6cb2969e866a0bcc1b7b980a02669c226b6c9267f6e66995d6b37eedc8e34f516ef48a69a3461f3742cc6268b26a8c158e4bf34c072d4e37a4e273ee3464aea81b57c52ab4d5a1e94a668c9a14612196f6a792646581a8ff545179323aaa8369c21181c1329464099cdf940fa56ce1f204ad4b048d5e783e1948b3493b273b2a33d8c0183e386786c7e7e421b1536610cecb16b7a98641b95693879a7d8bc444931cedaa6c45303aac0a48ac29041085526ffff11b531b1800f4e1fa75c4d008c4f9a112932c669d543551204405da8b4 15 | z = d1d49a515250dbceb9f6e3fcc1c7d5306918964b21ddb22207e03e57f0600da8 16 | H(ek) = 704649a5d8034c6224ae18950bd7b979342c03c7499f7bab9cdea742db9e086c 17 | dk = 3261790e9484c6453502778de5faccecc56d94fbbb3d6a039d03ceb70b254f2a201a9192b5992885eb6c9f44b9bb70c584cc4891782e47caa4124cab6db66f6eacb09aec6d6cf5324ed22cfc2a3ca366ad3da0b0b9b0aa1ae7c07c9776c7f2734c28a4a57577378aa2b7fa49e489a8f7a2c80d360e622a3a41180fb1167e56533ff3e3a00365090b8455170896946c7fe95b100cd176bf3c442d6c96e8d4b996a980d671a2406c15a826cab288359cfa2b5a3580f5bb5584b6c8dcbc5024c7149fbc580f081da2eb739a1c560a39b1d1b02b15f730fef1315589455ca13e3409526a5640f3924090586cfd376c21375b77630b7e0aabdc8c811e564c7155158ac14cff898ee621af8c05cfcd2cc41be341215818e6e5a37c167a381ba1c4a65724f64da94ac06e530d260229bb2c61a1d17630aa3b3f4aa4a1db7f740bafbd9aba41490f19b6127dfb651c2032317123662830c6eab9ed4b47037c30a5d4048ea69de1769323a85726766277a82554c00cf3b500f6a9ba070b6e1e010fe667429d5259d027ccaaec559c760ae99b62c014cf76b08c89a72d3e233af5251976e288da3a8fa2820e6ca8ca953ab8f504060fd2c0104558af67c34691c1c4c2878d5904c94390b1639548e206659804a3759a6a7930921a3fc2853d8e90cd94b523fe1a30704030f04b9d4fc919fd22c27966aa53180916009e3f831a47b53f5951087d110c010cb81a415d88c3cb19031e3f30a21496903f59ce75742227b22ca7687ad3b05c0aca42e3747bc5fb1683b796a10bac6a158a745836478829707b70aca820d3a9b6adda8091bc0c44c9bca1f57bae60165940ac87815c1e36a783a88b2b68655d22ccebd1490d4440a00775dd1a68f0d3125c7cb48d8a91db2993542c5541137e817206705bb23a261eb3740d952b1af92181f8a629e156b7a6d703d9225706a5bee26845bd80552df9a620f16b4c1444ed2514ec8c2aed453bedb9805174ada9610c91aca500ccb221c109a56377bfc043fc0410340c0d4a509be7d1252d65af888905ad42bfd9cc0e84b63d82032de9cb1cc1264fb6b65c2304818c566fdb36c47e43b3e43b405f7b8944340f78830901507d145b1818c86d9b003d03e5a33310ca66c44d63b2bdb427cc0dd68fd32176292359ce639c9177454e6a36d7cb808e13a9d309310b455202ec6bcf2891d2590755b92e4f0354e766b049d77b4b5cbd8b332a6d1261cc58a384ebb842d92273202eb7b45570cc42fb039c18b4a1308062d6710601c209f5b425d3a6b8735911fdb6bb26a556595c5e69d66a5fc346552526d8a8a292b3cda869b28fab0b2cb6162e583e8c9c558c4c8c457cb83e67c2f03960011091fdf5a35b88712d3aa5e0d0961242653850acca27be2a8523b0557dc38a3fd90acddb190c813c3e86d50e2dac874f2316c607a3582aacef1c14dee409551a9e02f60e9737c9940799b39932cd645341444414c77f4fa9046ee4020fb68d607b46759615803572502a296c291965b2c92320c663d504e06a0bce3286a854c74a6256e168c0b750805359986493a25f75b5f8923bbbd4b2a30b6e9092ce0a5035939c47e900c4235a447796a426713e1bd269a13493d0f1a25721155a28143a355f99c582c2d258a270b9f78941af004116e94f2f9825af17c50b600705ea1a9c2440df6803518a5ba558acb5b732d06a3c80e555fa374d80d82f08c5b47ca12cbe6520340538c2100b5e094392acacafe1ce4ad74fa451c4379219140103e9c3ba7ba683cddca0d3b02d57104c292b2e25bc791d8b11d5079477d7b0f930176fb0a5cda99afbfb6fca86226cb8152b250a536ca60b6ccf93880007068b41bbc5c6e9b8bd92af8a717e37c69ac7db7e76a2c36eb028c1e098915274b4d59def6c42d3c25bb35310846809c96bc43b639e52a3730654cc8db548faf21e446ca874a3caba777bac25b56902481c79a744d70d12130ad6bc8136349e3234c556267e97b0a8ccf772528299fa26111172c223f2484102917bdb6de7770682698763e5b2038779f812363a727f40949b76b6913c8910d6f51dd48983ba149c070590582662fbc9053b667e1eea1ca7173e421576f2d8ad7bf415899484bd1530b4fc855354952cf28d0f1216dfba00ec100bdd76c4f4b405c913baa78958ae31c578463877138cf89255a3935cfbd1b77dbb5f66b87543131da4aabb6781427f417e841141dcb47fcd82a92aeb3c50030d71eb7f3b17b0207a6cdf654d563a420bb742ac30c0e01235910886ff045652d5035b7ab98a1b6abac88ea5996b965c2d3f3305e075bbca267e8547c6c7769fb9f91e20849b524b7822965fe036a515c60e24636a3665b23b878c5b7658f7c49f2059255c89536426510875cbdca111bdc29183459340c125df30c05a7698490193d88c52410a14a7f12a118495413487b434568d105f2d7b9c0fc32e80a13e142528ca1825e0830cd41071aac7124042145be35e30d541c2ba31a82a5f479b3bad881164aa347386c9bfa505c9968fdea866120c8b4e809bdb1482d790a03fcb1ca88c15aae8047e1678ba66833ca430d592638bf3be19592027b3ccb044a9a4d01f3804c2fe0ca60cc7c017dc14dff60562f20d912650fc24449a78998d2b0ce4d9b131955affe7b5c12278cb5b23847c8090fc0578f71760918e9ee8274673845ca29f8d338fa89637265c518f902a9ba8ad9615764ce9835b322e0b5bb1fe928888318988e435cc3c3a918ccc773371a9687884f0822558ae634a54d7c74556528f2d7cb212f1901a82ae68905330b4550518b7b44c117f335f9c818635a2a29e59b39784b021d88354d707cfe0b3599b54fa4a1218261dc72a00cf2ccef8c6c2fe131899f9a239f545c4605296d7b39beb0832732eb608464df29d193b2f2c3b7c69cbbc2082cda14863071235a75544320c1c7930262f82ac7ba788c6c09586c2420d7385a0a6c50d050910e2956844b558fa78b6cb2969e866a0bcc1b7b980a02669c226b6c9267f6e66995d6b37eedc8e34f516ef48a69a3461f3742cc6268b26a8c158e4bf34c072d4e37a4e273ee3464aea81b57c52ab4d5a1e94a668c9a14612196f6a792646581a8ff545179323aaa8369c21181c1329464099cdf940fa56ce1f204ad4b048d5e783e1948b3493b273b2a33d8c0183e386786c7e7e421b1536610cecb16b7a98641b95693879a7d8bc444931cedaa6c45303aac0a48ac29041085526ffff11b531b1800f4e1fa75c4d008c4f9a112932c669d543551204405da8b4704649a5d8034c6224ae18950bd7b979342c03c7499f7bab9cdea742db9e086cd1d49a515250dbceb9f6e3fcc1c7d5306918964b21ddb22207e03e57f0600da8 18 | m = 3dc27ca0a6594b0e56320457c45a0f76bb8a213ea4a76d442186a0aefadbcdb9 19 | K = 4b4eba37eff0315dc6009dcffb4dfbbb680f8f2ebde8715fa3d6daf70256a2d9 20 | r = c52fc55f9edb30bda71bb983e058bc111b227dc3c853b66caa5fcb7f32e69b57 21 | μ = 810600811668811668000000001068000000000000811668000000811668811668810600000000000000001068001068001068810600001068001068810600001068810600810600811668001068000000810600001068811668000000000000001068810600810600810600001068000000811668000000000000810600000000000000811668810600810600810600000000810600000000811668001068001068810600810600811668811668000000000000001068810600811668810600811668001068811668001068001068001068000000001068810600000000001068000000001068811668811668000000000000810600001068001068811668810600001068001068810600811668001068810600000000810600000000810600810600000000001068000000001068810600000000001068000000000000001068001068001068811668001068001068001068001068811668811668811668001068810600811668810600811668000000811668810600001068811668001068 22 | A^T = 85a5005cd2b383ac6044664968b24f7aab18f9451a8d419e6b9246346b1234098f3295a5e2394647b8c50656aa02757eec3c51c3e791d652c42abc559b725863b0b4bd8a7148d2afb7b51c38970b424680894cc9ef31358923cfee292962c45d974241ed91630526c844c38b19d1331e5820a850bb4df317b0818bc34c1400a45af5ca5c527502bce33cf1486f9b4c886b5279f1bc10b04b49dccc7ff85433e19a2b181a9c6135c0e0db47cb9a628e8065a2f4a8a6207c3ababe7a2419d3f756491265860288be3c6407a6978c8865bff6aa4f119a5234323c491f1fb00d82e70795d371d9bb2b4ad7894ad4c9814a42b86c610966c8a6022a96c541593bce64827a65fa9df219a2510cc69a83861042c0eae2ca41b63057d8b364273bd32a16099caef20c733643c3da2a81749c9f7dd019869a8fc8715828104333c01ddf062fdc54c45e574afa049302d273df99c54614c202e75ab207a9cd783d44d029fe729e03c115e3a7cdbd3c561edc4f91e106eb696a4c22b367374b5bf50e3eda8b63b3040db68ae9da02201733fcf6a724fc9ba037404b2b9888a865fa81757412b79f14473c106dc9909d2083c79b50c48dc3c8b0e331ddd7996e447e93c96aa175b74c484ac3b3cbd3301823c890f78727540830eff042f17534153a0e7ec6569ea9065c2b89934ac0d5402eb3baa0c667351b8058dca7625511c4753c0a30f33d79643b13663661a2588ac16652924835cac6ece1bbfa111895010959333bd9cc8b44b6cba956c2b980918f5906d2137dd0b04a5f770226c5a37eb9b004eb03a18b1f6556732dd300e0b20612c3b9e29a62b6b2bfcd153e3b928bd158396768a998671e7b2715498b8c538946fcfb84500b57c69ca2ea76afb0652cb4d9082a288984ac7e5493ac9e78c289d78816d3c882ab679cec9ad37cb42d24b182668d355b39ea428f81bc2ecee397d6f04ff74b89e2e4360c420d8868685122178f420d11400f1b0163f286a4605c0d3499b8e21909eff8ba6cf6c4e3780353d2252b8b2d40b1b26b2c7cb1e33e94db35a8db85773916b4459428d6bfa3707c2e079abfa5a6fbf251df37cc8ef0b8ce04913739a3f681341809a0aee971b90201d0d9cb387011aaeb4960826964e2bc3562ad894c597caa5d26a6a804059fb7c9824d4b0f5c91181309797dd65c9ceb3845a98eb5ba224be121d2e0b12a86a83880355de16f9d585c0d655d899a8f3261afa2c478ff62749707829c9849f0f38cc40629efe7570ad1566b8a0d4e062e727c4398b241e638ba04d05384aba8a290b2a1115c9c5205e0d78b9bc38f72f11b06871a7e79746752998990aa59639992dcc07064134551584a823d7cab532578ca6b0aa167441dcb5653dd495d6d9b9587a64560f2c2bcb40427c10199c0043d734e4e7a837cf504d58793053b4290647502120ad74215a295946b7acce421be4d451fa8ca1d2ab2a3bd1542493686ced9c6a5e577d23b8d39c4bf40f27020c06158ebc01eb9934740c5956a1343f440b03ba287395964b674cd707af0085019c8c1c7843f630ab0d2b067c4548a6103946a55c1f80473f932a5f0dca3d9cb80bca99d51361fb6b38a1ea0abbed286447262e51127e38293dcd01397e236eadb3775c0114d2165f7d32d5043a283c58afbd358b335bb02a3abef72cf0e201e1c7cae95f7071a85b94b0b243f469d8b392318e85f018a96f75429657944b5c08b316153bacbcf52892a24e06ae01818d2c658d16b923f8a73831017b5d0ca6c91904e8b918cdb54957c2924fc9b3876cda87c3e0823290733717b8a98a9d9a85807a96a7a177e8c621f134b8440613ea936479b2a761ca47f522965fc136752447b06877007587b24a33c16af24a011e4b76a2112707399340a07c2f6b34f2a813929fcc06d75cf180a39c4898bf1b586f006986b8583e1790aeb0786d62381635c1e84d033b7b9aef43a4ff16a45bdb78073e832060a4720fa35048027fed98f1ba597a4d033d706312f92c7552a218b5a0af67847f3a83b9e1325ba522a57843686ac1e24849c41737d368bb9cd3622ec27a7e1432f81b37d44601e84f8b0736a4b22c243d5d3a78bb243445711076023c2b607932197b230892e900ced651f1b85b8bc836c14ea3b88a1c5154a8875b903ae30012553ab5f10cbce648ae2966a4b125a5cdbacba165021a235b7ba8d655325adb92182b80df1b7259d821e9bd53f33baa30796b91b929401f1125044bf28b0ce510801b8b8a0f1a983e037cd8c712b4c971a6d05161d8b03cb362fc7640bdd14aa13dc218eb97db5f0cf3d56b99192c9ffacc5e271a8e7950202e7886d0a9e1c0c7aabfc2c0342a1d712b78f624e487a5173329a48298ce6292f0216bdf3317d2e2aca6fc6c8cda658fe67605ae36adf1a8c65da46e10c4b8f518cd08c9be4b269fd8c973f54bc3af1517c787dda30960e399d14fa247b6acaba19b609936c947b9278b7575c115ee7f91de7b30f18b94f3893b1cbf69c320b2f5f777438718738eb71b123a747500fe2483ee2517b7bc54184d6cbf286b30ff2865416542366ceb165891b7c21dcc102a487530440a2449b11d6817250020a658a717a69a388229984b65927c7caaba3b9f59917df489d95652e55a0c7f96a8c576acf1b8acb29d51969924bf1a4052efcba804b833a898e280868094c1b43b661b759242390ba1c10292dc4800e236cbc5ba64baa24d46cc7acf61bb5bc054f845d84921546b02c12e0c7a355cad8c74bfaca69a83caf69a4a8fc6b12d9ccc9cde8c11a7a73ae0a3b4540a1573723b3576b2728c5a1b3a26ab4b7dc20a3c6895fdbb97172441f3ea67313d759c1aa3a16a445babc116833197a7002e8f68c8d38b955690d4d87242e45a4b5a30970c5aa14a6cf8eb997d41c4513a472e42a27d52652d0d26c43c85fd35a80ed7a51e8c1b5a7d9a781f4079628aa39bacbe9885168c20b262a77379a66f3d96a5fd902c8ac84b211bd3c226a5197330ea0a55b9c7123f63fcd7516d3b559eaeab3e3ba54219bcdd205c051118bf3180049639b8b062bfa6345a51ccef4f8547b40cfe10375e69392ddf5bfaa59277bd25a75fb3750a42ec47a5f62168c78f2996fa9407f578674bb95b88504eb401541ac5fd3c424313b99d38b113664b9caa67abc06329a6c6a8a89ba04ccc347344983ec9f98d5a930a96ec0d15008810afdf576e068096634b025d46cb478b75c00058370a70eb4687bd902438666c28c3717cbc9001d2b0a8263eac97db0858ecabc8a6732ae64378178a8baa954a9a1daca6ff930f51701aec28b8a9a502b3c68a0a52f30024ffbe929698a86e6758dd646afd98156825a793607b2ebdc29aef8290fa89503971556d63a665c7a3ea50af10791a2032d45eba531b2b41f675ab5a7c012610f3e94cf25d20a5f9950ae088b79192583e6bea1f073c2c7ca3b0aa5c7d8a8a7c905fc913129578a55e0217a6233e414c441d460a834bc5f85437f4b73d983927693665796090231813eb26365eb9237dcac2909a3d92c4888976eb8e684cb807d15e13f0a20c3d48c042544b06ee522d9c02baa6a295cf061c5e4819ae98ea999719d54844618921db3c9d69760401154818112e909a503c17f548323bd14bad132b60e02ad286113c9c3a75fe6b8eeba67092546752b99df66150685a7d6a24f9deb1c87a355c5860d86b6900e7b56e4db9bdbbc56655332b8a8b6eb789314117dcf91acb434720c14a5bf1c856e8a9ef70a095167b5d442479a8c7ee94278bc9147b512352471b350d67ee11cc9ae03419f477e090632ae50b7285b331f820816c01c0ba06249fb894f421909c12cf9555c96860471e32ed5b9306c99a5d1043dc83b730a58878e31146806a2ebb7cf666a519a3140af80107659a71b6108a1231e38e667dddb281b399dc3db72ebc3aa99073c778188b8f6243c5186ed7ca98f56b6730b63ba90a660e22ba9a7399d940b956a3955b456a84b3d269260c7702c12d1cfb008ba3250a06e04599da007a4d862da67251158b8a11484159b0b08138635e7691ab97d14e124af645a8a020c19a916fa4b3f5ec9ca4dc15772158ccbe416b3a1a5346b22c42c2901781f6cb9536241590c21b82ce31406257c4a7cab9fa988a3fc8fce8553a3889782c817a7125ab069c77ba586034ccf3bf702ba0103d7da1558b1b812f34aa9c88f12c63c81c72f45567a8279c74078a5337b67226586d79c5de2252690841e6d24c7cb86c20e5945f2550cae40a2ef112f0ed47b09f9cf5c139fb8ca9c41e54e0240cd6285c2e6826857aa523ba7cfd5304556b9367051a225884a6dda99e995b9e987167706aaee9896ca0612d89243ad208c91a237ff9414b6803a7a3b08d70717974c3262e45a4f3a501d442571e095db9ab536382d212520abc7110501560592b4e6480730d0027dc1143aa3c517710b301b871fd3ba9f159eae036d95b62895d9774c9176844399da0a870b0b5c476c9921816d7573bf75a632f6f82aed27657ae18126ca646fa2a112602ea0e34bdb6092adea950ae3b610977639896e264396d87813af70a045b46040c3294d93415ab576d1316bfa6c2fe125ca886cb6177c9244823053b25bc13ac567b409c4273b96a9c375231f301486d70509a9d60e31982c052b34a9f3c381c0c615717f1460bbfc96856b60c143cc63e0c9c892e3c511ea09faa770fc028199c5528bc0761a3a34b1f82a9744a8f907652ce628683441ab5cb6c31947f2fc7378504ab630a4c85907c0030e33152bdb15c05944a99b170ae1f3615611c3793568340a740f537604bcae3a01cd3218674bd8a62dbcc35e6137ed4850d6355667c47a3bc255fef61a 23 | r = 000d00010000001000010000000dd0000d00000000000000001d00000d00000000011000020000000dd00200000000d001f0cf0000d0000d00000d00002d00001000000dd00000d00000d001000000f0cf0000d0011000000000000000ff1c00001d000010000100d00000000100000000000100d0011000000d000000000200d000000002f0cf000dd0011000001d0000000000200001100000000000fdcf000dd0010000000d00001000fffccf00f0cf001000000dd0000000010000010000010000000d00000000000d00000dd00000000000d000fdcf001d000000000000000100000100d001000000fdcf01f0cf001d00000000001000000dd000f0cf001d00001000010000011000000d00001000001d00020000000d0000fdcf0010000100000100d0ff0cd0001000000d000000d0000d0000000000fdcf001d00001000001000001d000000000100d0001000000d0000fdcf001000002000002000020000000000011000000d00000dd00110000000d0000d00001d00010000010000000dd0001d0001100001000000f0cf0210000100d00100d00000d0001000000dd0002000001d00001d00022000000dd0001d00000d000100d00000d0000dd0000000011000000dd0020000001000ff0c000000d00000d000f0cf0000000000d00000d00100000200d0000d00ff1c00001000000000000000000d00000000000dd00000d00000d0000dd0000000000dd0012000001d000100d0000d000000d0000000000dd00000d00110000100000000d000000000fdcf000d000200000110000110000210000100d0001000011000001d00000dd0001000000000000d000000000100d00110000100d00000d0000dd0011000020000000000000dd00000d00100d0011000ff0c00002000010000010000010000000dd0021000002000000000000d00011000022000000d000100d0000000ff1c00000000010000001d00000dd0012000000dd0010000001d00000dd00000d00000d0000d00021000ff1c0000f0cf00fdcf0000d00010000000000110000010000120000110000200d0000d000000d00000d0010000011000010000001d00011000012000011000001d0001000000000000f0cf000000ff0c00000d000100000110000000000100d0000000000d00010000000d000100000100d0011000011000012000000000000d00ff1c000220000000d000fdcf000dd0ff0cd00010000120000000d0000dd00100d00000d00000d00210000000000000d0000000001d00000d0001100000100000f0cf011000000d00000d00000d000000d0001d00000d0000100001f0cf0010000010000000d0fffccf020000000000000d000100d0001d00002000000000000d000000000000000100000200d0000000001d0000f0cf0100d00100d0010000012000011000ff0c0000000000fdcf00fdcf0000000000d0020000ff1c000000d0000000001d00000d000100d000100000f0cf000d00000d00000d000100d001200001000000fdcf012000000000001d0000000000100000fdcf02000002100000200000f0cf001000000000ff2c00000d0001200000f0cf0000000000d000100000fdcf 24 | NTT(r) = 04f9ca89c561434b8b211a27d12154a6a154d5e2b684a6b3f63c7e2e9553051261653ba2c6e1ad0dbb1e41a12ca3c0966d2c1881e6bfd9677c009b3e42533573696f12c463da7867fed5a6b391bb59493747350193737981d22b3f27c386564a44ba1dba4174978004bcc3489f198757121dce3779f24550bd32211dc2a3b778af87075df0a1889ab60b334b02bfa10b656513791ac4d67a8cf7181f17195586e33f662832bb58795741170f244dd3907713239beee1b271a3a7c1f60df44757b2f4cd212369e8d8b4bf5a268d0c49e01b2ea2f9462139b9adb030d4718961847348295a87c9c6a5a1b55af7767094b37cca7dc377148ad037ebfb3b91ba34be800680c7b69b9a6d4bc73d6256b9c170692cf58f834c213096758012219354bb048c0799819d8eeb44e1a20165a128c10699550088ce416e88a2ba7b440a6295c89aba9f54d65cdad373c9440e520622f38781ab0658a1abcf698b447e9a3cbcdbcef583183e94230abb1141da2a15818c91bb6b97bc073f8228010b24008051257a762bc1747a49960fb82922c097256bbc1851002740b8bc2a08f0ac99a08ccd8a3c168f54148a32a3ecab1e178741042199baf4265b797aa6f68b16f1313c94679c91a6ff703199981c620ca47dd8cbd407924a071c0ad2bdc0dc5aa2c2c937243a7aaaa127589151593223a9316d959b2d5cbd67eb420d7855cc6987e55c40260a6ff444a2ccc415fd624c28aa52215449b1267559dc57d1114cd8da9fe589adbc76027d6bafaa8caa2e2b463f299adcbb5192aca10d9879680146038b67226a9608b8a74d870a809755f7114780d668dbc4a7e4006138068051199370e788842ac006325ac61761d65349da2130fbe59cd0214f21541d986a6e9d984416294fbc461c4a873e1d02c81a259fd600667bc8593056a9d04c6b10c3177d302d6380318ff408e01404286a9684d8aaa8595079a2c749ec4906bb739a7a6a03616eab0b390efca37c79516f59cddef05f11ca1f12478942e0455e53211b5bc5d7c62e63f709f749aefef8008458425225bb8a1320ebccb69229c1e3a5b470166d1b11cd92747e698b3e2df96272c2cc37d66e34731cfc957a054b7361020328c929acb81a4fd029d7b380a39ba65cc73e95a63e755246b1862e41f3c12bfa09df36be0ad6bc442857ae234823bc28da432f3fc63e4cb1ab0633026040b0718242f7e2c109e4adf07270a5f91dcaf77030132e87d22167943fea568b74179798c358d6c1bf68b12be9949058d9c786462eef1293e1e53ae0a1c14fc71e7aa54ff68330c733c4d9215c41293d8925b639f16e93985d0d3264578b41de624cdfe234750005f6458da266490599385083af91b954f4e28a15e2023615803414b6c2fa6e845067ee706e00208c20d4b8b38cc13cc07cfdf1be12bab1846243fb1a6db7a60bc9235b29984b66e499c71a9098c353c6f49e2dba8e5c3810e50b12cf8a4111d1b4e3da0c9ad163010ccaebfaba28bc86f476a2960511b7fa24e4977a83b59930d51bcaa964a6888f2d2655c53ca05a40a5fcfb2f4735934e15ad21c4549d539d9b8c5a57b410822b7603a498b6510d61507394e0264f4a56624ca0202c37 25 | e1 = 02000000000001f0cf0100000000d000f0cf000d00001000011000000000002d000110000000d001000000100001000000100001f0cf02000000000000f0cf002d000000d0ff0c000100d002100000f0cf010000001d000200d00000000120000100d0010000000d00020000000000010000001000000000001000020000021000000d00001d00ff1c00021000ff0cd0000000ff0cd00010000010000110000100000010000010000100000010000100d0020000ff1c0000fdcf01100001100000f0cf0000d0010000ff0cd0020000fffccf01000000200000fdcf0000000000000010000000000000d0011000002000010000001d000000d0020000001d000000d0020000001d000100000100d000f0cf001d000100000000000000d00000d0000dd0001d00011000000dd000fdcf0000d00100d0010000ff0cd00100d0002000000000000000011000ff1c0001000000000002100000fdcf010000000000001d000010000000d0ff0c000000d00100d001100002f0cf0010000000000100d0000dd0021000000000000d0000000000200000f0cf0200000000000100000100d00000d0ff0c00000d000000d001f0cf0100d0010000000d0000200001100000fdcf00100000f0cf0000d0000dd00010000100d0000dd0010000000d00012000000d000110000000d0000d000100d0000d0001100001f0cf010000000d000100000200d000f0cf022000ff1c00001000011000ff1c00000000002d000000d00100d00000d0000d00011000000dd0ff1c00010000000dd001100001f0cf0110000110000200d00000000010000000d0001000001d00000000011000000000000d0001200001f0cf000d00001000000dd0000d00001d0000f0cf0210000000d00100000100d0010000000dd0010000000d000000000000d00210000000d0000dd0000d000000000020000100000000d0020000ff1c00002d00010000002000fffccffffccf0000d0000dd0001d00001000002d000110000100000200d00000d0002d00000000000dd0001000002d0000f0cf0000d0010000ff0cd00000d0000000001d00000000000dd001100000fdcf020000000000000dd0000d00002000000000010000000d000000d0010000020000000d000100d0000dd00100d0000d00001000002000ff0c000100000100d00100d00000d0ff2c0000fdcf0020000000d0021000011000000d00000d000000000200d00200d0022000000dd0000000000d00000dd0012000011000000dd00000d0000000020000001000000d000010000200000110000100d001000000100000f0cf010000010000000dd00000d0000000ff1c000200d00110000000000010000000d0010000020000011000001d000100d000fdcf001d00ff0c00ff1c00012000000dd00000d0002d00000d00000d000100d00110000000d000000000f0cf0000d0002d00000000001d000100d0001d000100d0000000000000011000000000020000000d000000000100000100000100000100d0001000001d00ff0cd0ff1c00ff1c000000d0000dd00110000100d0010000ff0cd0000dd00000000100d0fffccf002d00011000000dd00000d0000000 26 | e2 = 00100000f0cf0110000020000200d0000d000000000000d00110000000d0000d00002d0002200000100000fdcf0100000100000100000200d0000d00000dd0ff1c00000000011000000d000100000000d0000d00000d00ff1c00020000000dd0000000010000002d00010000000d00ff1c00000d000010000200000020000010000000d0fffccf000d00000d0000f0cf001000000d00ff0cd00000000110000100d0000d000000d0021000ff0cd00000d00010000110000020000100d0000d00000d00001d000000d0ff0c000110000000d000000000000000f0cf000dd0ff0cd000000001000001000001000001100001000000100001000001100002200000000001100001000001f0cf0000d0000dd0000d000010000000d0000000000000000d000200d001000002f0cf000000000d000000d0ff0c00010000000dd000000000f0cf0010000100d0000dd0000dd00110000100000100d0000d00000000000d000000d0020000011000000000011000ff0cd00000d0000dd0020000001000 27 | u = f91b3da8215463536106bb8018f637ab5b27be306bb9dbabe521b580f90f88f714fd2a734b1629e5eb407f9c627f842f7fb99bdd05332fa5132b94436a721b3a37b03dc647cab4b65f778c5a93908c3346124200f2a76f3e3c2ebfe98def49cb54f65ab0d6a29cb0301510a9cf285b7487a30ba0ceee3c00f8ca9bc2761ca1f31ec22625b8060ba0647227165804169082dcb11cb6a8fc223e18ba0b68d4a725711e6078afb5462c1a9b4fcfe27813040a7341b3cdc3c2b8cabed5ba91db02000cba01294950ba231c6808011d1a707357028355723c851468c1c60312af4b5c6a9b5707798431fe10a2db837841d4985118902fec43f05b2c216c9688e43a3bab712cb01fe9a0121418ce2b9523711513b605a197ab5a5c9a6791433c4cca723191c6ff7377c94613c0d4859a347758199366764c9e29327d67c17cd93b636309e7e07a6040076a3494650602cd90364cd1bf344a0bb11bb43ca18a63a096822a4562033577914b40da012ed28055b36091007ed5595b56e52f09711cec196855a371e9532f91a0b877553fccb32212b77d13086ebd6b6bac89a9b77b7938d414a65774f54b8ce5a127244469f5bc4e4a33ba1d977071214ed8926467b870c927555b92276c139c648494263785902965e5a14894440e68f6977ad290cb43022941bbd9724da3c7baa0606d753475dc401147357f57d36430f1135caa42e02798674028b44abbd6226353ba5181782c6d16cbd5f73f48f0c2cfc305ec88263bf3919f388e39c2002c75accb724355852ac4367feb5c2cda7485e2750a4fa82dc66b37062c64ac5b2ddcfa4ae145c3a818b057b0c6f2c25a8430bb52aa94e97549c8649763c415a4497f84104e85eb2bbd19bbc242b7990a2136a6b5e2447705fa9f147c4653e34055cbce33562c6846afaa407680567b42c31451e00fce1146c2fc37132b32f7535767a413c05b54a5a941a741c3fc8315bd64b464201967c614950c097eb62652b215d007cb73212b783bce283c70056bba059267dff5cf64cb791c5b6750c90f53f4ade7a575c8b949c99b813c373992e8909d0400d46a9a982a1442e11b0f783f7091a3cc02a99fac84f03781378211189bc4ee1604f2c50f793c8d685c623a97c151f35e08c2152a5bb72c130d93a08364360e4af39690f63401d60bfaf9aed803a2bdb4c54ad4ba18267972874f8a854453d38ed40a6bf06b3c9e22b325e1552c69b1ab4c1977679f6565873f3925d557a6aa321e29b404b7710c0e960d2ab240e367b9ae197b5c624957a2379406689a785080972ef6128d122c9f88d13cb5b5ce4e5361482a51bda989d383577557c0d4242d4613c2a8247e616c2757039c1169261bb5b63487134ed87f88d2c6dea4687173c6e1799fa2f16bed29cd816b2a229c6c743a8c20b68b18c67fc5dcc76a457376f150103a8b213634e50c5a0c202da20310cc116cb705b0b963785b28a63e376107f5358abbc807c5c02de89537d5236b71230c507e013ab774f088f3a1b2c9c220a6133537da6de4389f49072731076e63253794f903674a8d35504d492371630769f609733fb7b1e5ba7eed2318bdc3811cf3121f905420eb47c3a68888e06880329d 28 | u[0] = {3065, 977, 424, 1346, 867, 1557, 2822, 2059, 1560, 895, 2987, 629, 190, 1715, 3001, 2749, 485, 2898, 2432, 255, 1928, 335, 2813, 1842, 1611, 657, 3045, 1038, 3199, 1577, 1151, 760, 2431, 2491, 1501, 816, 1327, 314, 1067, 1081, 618, 439, 1850, 2819, 1597, 1148, 1226, 2923, 1887, 2247, 858, 2313, 908, 1123, 530, 4, 2034, 1786, 3134, 739, 2495, 2270, 2543, 3252, 1620, 1455, 1712, 2605, 156, 779, 21, 2705, 2255, 1458, 1908, 2616, 11, 3306, 3310, 3, 2808, 2492, 1730, 455, 929, 495, 1730, 594, 1720, 176, 1184, 1830, 1575, 1409, 1540, 2305, 3202, 2845, 1564, 2699, 764, 994, 2584, 187, 1128, 2685, 293, 487, 2144, 2807, 1717, 708, 2842, 1273, 719, 1934, 1043, 160, 371, 2868, 973, 3116, 2744, 3052, 2773, 2331, 731, 0, 2572, 27, 2345, 1284, 954, 450, 2152, 16, 2589, 1793, 1907, 37, 1411, 1829, 1340, 328, 360, 3180, 515, 2801, 3147, 1701, 1947, 117, 1145, 792, 254, 2593, 987, 1928, 1089, 2445, 2129, 2305, 3119, 1086, 3056, 709, 3105, 2406, 1160, 942, 2875, 1818, 44, 507, 233, 298, 2068, 3297, 1323, 569, 1393, 305, 1462, 2576, 2967, 1450, 2652, 1657, 913, 964, 2636, 1836, 305, 3177, 1023, 1911, 1737, 308, 1216, 2141, 1178, 1907, 2392, 2353, 1638, 1223, 2462, 802, 1917, 3094, 2428, 957, 867, 150, 231, 1966, 96, 116, 1130, 2371, 1637, 32, 205, 873, 332, 3069, 2612, 180, 2993, 2881, 316, 2218, 99, 2410, 2690, 1106, 866, 848, 375, 1209, 2624, 29, 558, 2061, 853, 1547, 145, 2016, 2517, 1461, 1366, 766, 265, 455, 2540, 1665} 29 | compress(u[0]) = {943, 301, 130, 414, 267, 479, 868, 633, 480, 275, 919, 193, 58, 528, 923, 846, 149, 891, 748, 78, 593, 103, 865, 567, 496, 202, 937, 319, 984, 485, 354, 234, 748, 766, 462, 251, 408, 97, 328, 333, 190, 135, 569, 867, 491, 353, 377, 899, 580, 691, 264, 711, 279, 345, 163, 1, 626, 549, 964, 227, 767, 698, 782, 1000, 498, 448, 527, 801, 48, 240, 6, 832, 694, 448, 587, 805, 3, 1017, 1018, 1, 864, 767, 532, 140, 286, 152, 532, 183, 529, 54, 364, 563, 484, 433, 474, 709, 985, 875, 481, 830, 235, 306, 795, 58, 347, 826, 90, 150, 659, 863, 528, 218, 874, 392, 221, 595, 321, 49, 114, 882, 299, 958, 844, 939, 853, 717, 225, 0, 791, 8, 721, 395, 293, 138, 662, 5, 796, 552, 587, 11, 434, 563, 412, 101, 111, 978, 158, 862, 968, 523, 599, 36, 352, 244, 78, 798, 304, 593, 335, 752, 655, 709, 959, 334, 940, 218, 955, 740, 357, 290, 884, 559, 14, 156, 72, 92, 636, 1014, 407, 175, 428, 94, 450, 792, 913, 446, 816, 510, 281, 297, 811, 565, 94, 977, 315, 588, 534, 95, 374, 659, 362, 587, 736, 724, 504, 376, 757, 247, 590, 952, 747, 294, 267, 46, 71, 605, 30, 36, 348, 729, 504, 10, 63, 269, 102, 944, 803, 55, 921, 886, 97, 682, 30, 741, 827, 340, 266, 261, 115, 372, 807, 9, 172, 634, 262, 476, 45, 620, 774, 449, 420, 236, 82, 140, 781, 512} 30 | c1 = afb72488670b7d47769ee04d7479303a40b8b9d395eccdae13519e11f68df02993fa4fd89727963aecfaebdc3e9885815453be1c92e3d8eb8595d7e044ce8ad0b11765354a00729648fc38ffeaea30faf201f760c830c06300d0b602b764c903e4af7f0060ff4b21231e6142e12d11dac0d68ce4c5a65db1d9af1d9ecfebc8b4b10e5be9ac8525937e0da1366a23d6cd9441c52087dc2bf9cef4ea55371b0e00172310ed6225296269011ca3b8e402b2cdc859196f48ef89d7c82f78250960d1e384c73045f914bc8f16fbbb53ac6bb33bb9658944f78b0e708204177cda7fd92bac79211cc691fb06b37f19a5b4728d5e44bf1393167e61d7a46a2d092eb5f8e155ef3d4ee2beae490bb97044971e90c055b6f829f0434366c03ef20d99db1d86aa1e94bb33550a1534075d2727c08a9e0671d7029b0607471a3b5230d2308006bd48533a2d34ee1a4e2badd2e29a7c76c83984fa0aadb9954c99a1258faecb5ac9304619c8ff6003510ea38a720006ce7b96ae58e668b90cd310c05a6d3be3a3f1ca5749596919811fbbc324bbd2025b680e4e5f595e1e83865f05494415a0c979107c5e880133526cb60b82314b6bfe0d7a2d4be6e936fa9d9fe64e16fcce1207bff6e28fb3a7f2fa0a019745cd0d53a44553a19cf96be357a4cfcde068389f47347b7b977f73755ccf05afead81b488fce6f2964def2b6d1a59557ba59ad71af9c2900b63836ff669e8de1438b929edf802d59f1c4b76f65d04f7ceb8f9e36f87d4d8391007e1990191938e14856ed4f84f63d38b9b655189d7f76ef508204af931a75e1fd011ff899f13d0bfff972cb1a679e2f073587dfdf3b8a6457fe897fcefd1ff8956bf3d7ad1355611ddd9002af05ba9f3a66346ab26b015035be428f3146227be21447c9dc002d7ea371f2e98a156a1f2f2205d43961fdadd1939763ee054d07ca1a6f1bae0f102d207a9f11039d5b6041d9e92071d72f755c57f3525d0e1e954a1e465b5406fd5af583aca7e48cdc5a9c266ddae6f3c124c4a9698aed2d69ce2c4d25485d70480fdd0da18a4f6d42aeaf97baa4858b440602582a634f96938eadb63f9cc74ac1e54fd0772a3ff66fa92dbdb6a4ec7c7923d0ee6fb5f9bc300701ac6c2f920d7e23188ed6798cf47f0df850f40a471cc8840ecbaf7834bb5b78b3ace2bd0a5e9dee5fafda8d73388671abe30574ff6e0478e3d1138d50289cd8254129a9cc3a7ac758428d73cf58ed848abb192c70b842809b141b4e82a899bcbd4d281f15443287bc3eec2330367688da43f24e30f3ad10f435508b461608b18d3baead359c35dd61d29ff574a100686c8b4521a82a10588cc1 31 | v = {2446, 190, 239, 1807, 3181, 3106, 1567, 142, 2474, 2283, 393, 3127, 2371, 37, 2459, 1292, 2590, 312, 3172, 2780, 560, 2552, 590, 507, 46, 2245, 3118, 1859, 683, 610, 3, 2730, 1832, 3240, 339, 1375, 3326, 2321, 872, 2076, 1438, 840, 2768, 2610, 389, 2755, 190, 2044, 1800, 2608, 2546, 948, 392, 544, 3174, 3001, 2511, 2522, 2707, 1879, 1165, 336, 915, 480, 3315, 331, 2154, 73, 566, 2157, 27, 2278, 1858, 792, 707, 3113, 514, 1687, 1635, 2825, 1926, 2173, 1470, 1650, 1558, 2137, 3137, 1348, 2218, 2105, 2320, 948, 2603, 2610, 3302, 1672, 1800, 1768, 1607, 1580, 1526, 1993, 1076, 2419, 489, 1982, 2535, 2646, 606, 1809, 3116, 1789, 2374, 489, 919, 716, 2272, 3160, 1377, 2158, 2380, 2664, 126, 1486, 810, 1739, 2221, 1605, 2507, 221, 2077, 2096, 2039, 1174, 18, 403, 458, 764, 3195, 729, 172, 403, 1187, 654, 2405, 2389, 2750, 1095, 3216, 3275, 2525, 1571, 846, 3252, 581, 1238, 3047, 1797, 1574, 2452, 1741, 242, 2813, 2587, 2053, 424, 1825, 2615, 2208, 929, 2556, 3130, 1377, 59, 1592, 315, 235, 1882, 1033, 760, 1204, 306, 984, 1410, 1318, 2556, 1292, 1104, 2583, 1319, 1230, 2615, 1561, 1254, 1162, 2284, 2865, 2089, 2953, 969, 3102, 3195, 252, 1494, 156, 2063, 3127, 3223, 2897, 1081, 3045, 2592, 1420, 1107, 1790, 1224, 1374, 1589, 2422, 74, 1570, 146, 2320, 477, 110, 638, 3241, 2576, 1404, 3214, 2962, 1793, 311, 638, 3318, 2844, 907, 877, 2825, 2879, 1415, 2519, 1914, 3144, 2887, 3062, 2027, 2922, 297, 828, 2890, 1302, 133, 855, 2363, 3228} = 8ee90beff0706d2cc21fe608aab98e8971c34359029bc9501e8a1364ccad30829f4eb21f2e508c2e3c74ab222603a0aa2887ca53f155fe1c9168c3819e8534d02aa38531acbec07f0807a3f2493b880122669cbbcfa99d937a758d041593031ef3bc146a980436d2861b608e428731c392c20272696396b086d787be2567169685414c54aa988310493b2b2aa3e68c6808876e47c662f6957c343497e9e17be769a55e12712cdc6f46991e97c32ce088c561e5864c89a67ee05c2ab36cad5864cbd90d1d0883f76749123019cac12f7b9c2dac3019a3e428655995be7a4490bcccdd39624e43cb45624de75b70264699cd260ffdbaa105881a2177a3a0183afca9c361b50338b613eba07509842fb42413d8235826c59f0c0545177a52ce74a319664e8ac48e319b82899b3c1ebcc7fc605d9cf080377cc9519b43e50ba28c3545fe864c5e556376a90422260910d91d6ee027a90ca17ce5c8921b7037e127f6ccb18bd33609fbb387759d7a87c4476bbfeba7b629c1334a6b518570353bc9c9 32 | compress(v) = {12, 1, 1, 9, 15, 15, 8, 1, 12, 11, 2, 15, 11, 0, 12, 6, 12, 1, 15, 13, 3, 12, 3, 2, 0, 11, 15, 9, 3, 3, 0, 13, 9, 0, 2, 7, 0, 11, 4, 10, 7, 4, 13, 13, 2, 13, 1, 10, 9, 13, 12, 5, 2, 3, 15, 14, 12, 12, 13, 9, 6, 2, 4, 2, 0, 2, 10, 0, 3, 10, 0, 11, 9, 4, 3, 15, 2, 8, 8, 14, 9, 10, 7, 8, 7, 10, 15, 6, 11, 10, 11, 5, 13, 13, 0, 8, 9, 8, 8, 8, 7, 10, 5, 12, 2, 10, 12, 13, 3, 9, 15, 9, 11, 2, 4, 3, 11, 15, 7, 10, 11, 13, 1, 7, 4, 8, 11, 8, 12, 1, 10, 10, 10, 6, 0, 2, 2, 4, 15, 4, 1, 2, 6, 3, 12, 11, 13, 5, 15, 0, 12, 8, 4, 0, 3, 6, 15, 9, 8, 12, 8, 1, 14, 12, 10, 2, 9, 13, 11, 4, 12, 15, 7, 0, 8, 2, 1, 9, 5, 4, 6, 1, 5, 7, 6, 12, 6, 5, 12, 6, 6, 13, 8, 6, 6, 11, 14, 10, 14, 5, 15, 15, 1, 7, 1, 10, 15, 15, 14, 5, 15, 12, 7, 5, 9, 6, 7, 8, 12, 0, 8, 1, 11, 2, 1, 3, 0, 12, 7, 15, 14, 9, 1, 3, 0, 14, 4, 4, 14, 14, 7, 12, 9, 15, 14, 15, 10, 14, 1, 4, 14, 6, 1, 4, 11, 0} 33 | c2 = 1c91ff18bcf20b6c1cdfc323b09f33d00972b0a447ddd2a1d95c32efcc9d2624200aa3b049f382e8a987a76fab5bdd808988a7c5a2dc939f2b34fba7db71848b1caa6a20424f2136bc5d0f8c04639fc818ce2ad94bfc072891451675c6566cd668b6ae5eff71a1ff5ecf5769870c182b31c0f79e31e044eec7f9feea416e410b 34 | c = afb72488670b7d47769ee04d7479303a40b8b9d395eccdae13519e11f68df02993fa4fd89727963aecfaebdc3e9885815453be1c92e3d8eb8595d7e044ce8ad0b11765354a00729648fc38ffeaea30faf201f760c830c06300d0b602b764c903e4af7f0060ff4b21231e6142e12d11dac0d68ce4c5a65db1d9af1d9ecfebc8b4b10e5be9ac8525937e0da1366a23d6cd9441c52087dc2bf9cef4ea55371b0e00172310ed6225296269011ca3b8e402b2cdc859196f48ef89d7c82f78250960d1e384c73045f914bc8f16fbbb53ac6bb33bb9658944f78b0e708204177cda7fd92bac79211cc691fb06b37f19a5b4728d5e44bf1393167e61d7a46a2d092eb5f8e155ef3d4ee2beae490bb97044971e90c055b6f829f0434366c03ef20d99db1d86aa1e94bb33550a1534075d2727c08a9e0671d7029b0607471a3b5230d2308006bd48533a2d34ee1a4e2badd2e29a7c76c83984fa0aadb9954c99a1258faecb5ac9304619c8ff6003510ea38a720006ce7b96ae58e668b90cd310c05a6d3be3a3f1ca5749596919811fbbc324bbd2025b680e4e5f595e1e83865f05494415a0c979107c5e880133526cb60b82314b6bfe0d7a2d4be6e936fa9d9fe64e16fcce1207bff6e28fb3a7f2fa0a019745cd0d53a44553a19cf96be357a4cfcde068389f47347b7b977f73755ccf05afead81b488fce6f2964def2b6d1a59557ba59ad71af9c2900b63836ff669e8de1438b929edf802d59f1c4b76f65d04f7ceb8f9e36f87d4d8391007e1990191938e14856ed4f84f63d38b9b655189d7f76ef508204af931a75e1fd011ff899f13d0bfff972cb1a679e2f073587dfdf3b8a6457fe897fcefd1ff8956bf3d7ad1355611ddd9002af05ba9f3a66346ab26b015035be428f3146227be21447c9dc002d7ea371f2e98a156a1f2f2205d43961fdadd1939763ee054d07ca1a6f1bae0f102d207a9f11039d5b6041d9e92071d72f755c57f3525d0e1e954a1e465b5406fd5af583aca7e48cdc5a9c266ddae6f3c124c4a9698aed2d69ce2c4d25485d70480fdd0da18a4f6d42aeaf97baa4858b440602582a634f96938eadb63f9cc74ac1e54fd0772a3ff66fa92dbdb6a4ec7c7923d0ee6fb5f9bc300701ac6c2f920d7e23188ed6798cf47f0df850f40a471cc8840ecbaf7834bb5b78b3ace2bd0a5e9dee5fafda8d73388671abe30574ff6e0478e3d1138d50289cd8254129a9cc3a7ac758428d73cf58ed848abb192c70b842809b141b4e82a899bcbd4d281f15443287bc3eec2330367688da43f24e30f3ad10f435508b461608b18d3baead359c35dd61d29ff574a100686c8b4521a82a10588cc11c91ff18bcf20b6c1cdfc323b09f33d00972b0a447ddd2a1d95c32efcc9d2624200aa3b049f382e8a987a76fab5bdd808988a7c5a2dc939f2b34fba7db71848b1caa6a20424f2136bc5d0f8c04639fc818ce2ad94bfc072891451675c6566cd668b6ae5eff71a1ff5ecf5769870c182b31c0f79e31e044eec7f9feea416e410b 35 | uᵈ = fa3b3da7215464536106ab8018e637ac3b27bd506bb9ebabe411b580e90f88f714fc3a734c1629e6db407f9c627f942f80a99bde05332eb5132ab4436a721b3a37b03cc647cab4b65e678c5a73908b2346123200f3976f3e2c2ebdd98dee39cb53065bb1c6a29cc0301410a9d0085b7497a30aa0ceed3c00f9da9bc2761ca2e31ec23625b8060b9f647225065805169082dcb11ca6a8fc323e19da0b68d4a725811e5e68afb5562c19ab4fcee27814f4097331b3cca3c2b8dabed5ba91db02000caa01284950b9131c6808011c3a707447028365723b851469b1c60222af4b4c6a9b5707789431fe20a2dc837841d4985118902eec43f05b2c216c9689f43a3a9b712eb01feab0121408ce2b95236f2513b7f5a098ab5a5daa6792633c4dda723281c6008477c85613c0e48599447759299366664c9d39327e77c17cc93b646309e7f07a6250076b2494661602cdb0364cd1bf333a0bb20bb43b918a629096811a4561133576914b40da012fd28054b36092007ed4495b55f52f0b711ceb1968549371e9532f92b0b876653fccc32213c77d14f86dbc5b6bad79a9b58b7937c414a54774f63b8ce4a127244469f4ac4e4a23ba1d977073014ed8926468c870c9275559a2276b139c658494261785903965e4914896440e6606987ae290cc43022851bbd8724da2c7ba9f506d754475dd401148357f57c36432f1135daa42e03798684028b55abbd5226353ba5182882c6d06cbd6074048e0c2cfb305ed68263ae3919f488e39d2002b85accb724355752ac5367fea5c2cda8485e1650a4eb82dc68b37073c64ac5b2ddc0a4be145c3a938b058b0c6f2d25a8520bb538a94e86549ca649762c415a3397f85004e85eb2bbd29bbc142b79bfa2036a6b5e0447705fa9f148c4654d34054dbce32562c6666afa9407681567b44c31451e00fce2146c3ec37133b32f6635768b413bf5b54a3a941a741c3fd9315bd74b465301966c61496fc087da62653c215cf07cb73112b784bce273c70065bba05a267dee5cf64bb791d3b674fe90f55f4ade8a575c7c949c9ab813d273992e8909c0400d57a9a972a1442d11b10683f6f91a3cb12a99fbc84f04781368211198bc4ef1604f1e50f783c8d685c623aa7c151e35e08c2152a4bb72d030d92b08363460e4af39691163502d60bfbf9aed9f3a1bdb4c54bc4ba18267971774f89854454d38ed51a6bf06b3c9e32b325f1552b69b1ac3c1978579f6655873f3925d677a6ab421e2ab404b7610c0fa60d29a240e357b9ad297b5d624956b23794166899785081872ef5028d102c9f89c13cb4a5ce4d5361491a51bd9989d393577437c0d3242d4713c2a9347e627c2757139c1169261bb5b63387134ed87f87b2c6dda4687183c6e1899fa3e16bee39cd817b2a218c6c743a8c1f968b18d67fc6ccc769357376e1500f3a8b224634e40c5a0d202da21310ca216cb7f5afb953785b38a63d276107e5358bcbc807d5c02ef89538c5236c61230d607e024ab775f088f1a1b2c8b220a5133536ca6de4289f4a072730f76d62153793e903673a8d34704d4a3371641769f5097340d7b1e5da7eed3318bcd3811ce31221805420fb47c2968889e06880429d 36 | NTT(uᵈ) = 658a6b5efbc194a138f66535a84c094fa4843ab572afd4c8a2f4ad134a8794691e7efb1418606e20541b27340275ea4361911cdd3c19a49b6c1fda588324b40b172612d19ec44c4aa7e1c1668726748317bbb7cd84764bda1b9de00230305889694a55dd47095f97122f322ce4f10f4229a9b8fb4b3360b065d3bb1ab60bda5895b58621a71687daa125d8a9cb9ea1a59e629b802008f28780de2c29bd726991629d56614980b66005800533b907b28b2860cb0609016966058848b08a9bcc5eaa4234cfa9377025b67035197105c32f5aa2e46741a7a3973f976fca314a6535c7e6d6b3522773602c4be442bebb793b2ebcabc5bbb713ba5ecf3b7195a8765aa9baece8748b8920df179ed4227cc88ac7d6170615113ea09b3da2cc446e889d0b9c6b72f9685fac48f39a9997c3ccbfa281b255b38a5c15285611df2728d6e31efac48fcbe27118fa2b9f28bd272b69e72c9688862cf8030a9cab5d83654463491ec1e8ac3c1c59a763207c841ceeb2380720195d950ab504b1942837602177b4844e63c3bf700181e51067f675945f1c522724ca71fa418b617e0665025d18261345c6a56602b984cb37500a563c7cc56612d4d0cefd77021c2494776235178ac3a5035e806a1fd4d926571a8d33f60397804265f55ed580ba750ac8878c3bf7498aeb4373947c68ebd6160391ba444026bc12bc5019396bac7272ba514f2162f38b783a990fea3bcba7334485930a705644f8d8761a2376e375976c206a4864bb6d212d483aa075f4a7fd7209c40934ae4965bd8822aaa2b07be104a82596fd8302bd459727819f6e060368b0058bc071956a4363693307d780f1b44107aa280e9b7079194dccaca1c5173eb5300f51467d2d5aa3aa1217f91a6680a2408125239461277289a2a5b4a963454ff286c67d5baea9a99990c03d0d8c9efd96b905a04ddfe24d4eac80f17b3fa5d44493b7477c696d94d2a97d80a86032593aa07d2cd39e39a6af03e01c169bbde74a3f812091a7d1a49538ab4dd6a2a0d7590dc1a70cd61e5c2a5d963b2a94e45fd25b557a864097d93e4bea711fcb0a83901adf4a6614606ae61abc211a2665f33324da039590ad42a09d0b19707ea98334c8bae38304de943745474350b6bfd859959e928ab3477b707a0a73aa8683d876da4a78db3003eab448395439d8019c24561d78ac95a0420323583c55609685fb346759571d37a0d40b7cae14947b7ca819190dacb7389a3b04f0d159ea891307267baf81714f986da4fcb8550326ac81c739ec8dd2091c2bda40440841b00784f3a8a41604cf25d57c95560a16cb50b45c4359781218d1386831b32f53a7ff9899c3fb8980e16bd5b26a0b6b39d9734c125403cffcabb6a5a44b00657f14a8c6934c5d979c70c603552b3321698966ba3f386a23f2bb1381a040c120980b546fa4158e24499246c991fb144d9a64928001834f85c48b144e21d6780197c899c31c2835a75b255fd66c136eb0b303b695a43a08717855782b0052132259279c1c519c18aa8c56454706e655c53c0cfbc6c29b07452ed9c49ae12ee4fb1c43927c73b76784788c1ae4a760056a444a17066911989367eb62c5b1a4056c666121a902 37 | vᵈ = c1090dd01075311cc381060dc1198fa011c3f10800c1094ec1090d311ca970129c70021a00108f311c757002270010a9510700a0015b00108f401382b00534911aa9a011a9d010825117a9c10941a00127311cb6c1199c911a75e0041a40031a00001a21080070128200108f5107347012c3a011688116b6511782b01568b01582310c4ef11882f10841911aa9001068511768811668b0158210149ca01182c119a9701275311c75f1081a400327f118c3b01582f118a9d0005b401368f11868c1090d21188221084e00001aa00134310c34d0001ae00427c1198f910a41310c00c1196840030070024e311c7581169c81060d611b9c21081a5117a9f10834c119c3b0050081061ad01075100434e0040d10045be0149ce00441c1094ee014a981064ee0148f611b82610b41311cc3d0005bd01082311cc3610b41311c9cb0054151074eb01568c1090081060df1081ad0002700109cb015c3611b75d000270010b6400334611bb6b0159c5117c3611bc32118b6d00034610b4ed00034f10800 38 | w = df460546766a5b666761e00314d06606a0cc4e9ccc74f662ccecc979566189e6689286caf39c01f7fc06bc7c6a08b0662cd070d4260100b06115f067be4607a43c70fd26cb48860396a66abecc6b4df0062746c7891c658a266d490000877cca1ad06f2ab6c854e6c874560137406e9b8c0137f667dd6c0784eccc46f602c1dcc917e0c9b6f66236a601e19605c17604432003d30608c41c05ab066aca6c7127906a79060989560706266a2476620620ce4910c9ca0c6cf916cb38d661ddd6046ab667befc66c9066914c065e4cc6eb2cc6719600542a069b1d6c4bebccec18c6916c005050072d4b66a12b7725340050350011c77ca2fe06cee5c6dff266135a6cc29806351306c4736003c466ac41c699a3601d9bcc946e6cc744cc9d0a6013867009000040cd065d5dc070920636126c72c3003d9ec607050ca3640cc4a906548e069837069cce6697ab06ce5ec6172506a2d406013175b4c766f089667d56c6e2186cdd4766df5a602390661444002fcd66829d602efac610b776b9a5c6c 39 | KBar = 2f3eac7d224d818aceb416748cf117f7f61a8554e8b2b6522fdcc6dfb37e9995 40 | --------------------------------------------------------------------------------