diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67e2993..4aa66cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: goVer: ['1.23', '1.24'] steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.goVer }} @@ -44,7 +44,7 @@ jobs: - name: Upload coverage to Codecov if: ${{ matrix.goVer == '1.24' }} - uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0 + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 with: files: ./coverage1.txt,./coverage2.txt env: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index a959a3c..1181d30 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit @@ -37,12 +37,12 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 + uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5 with: languages: go - name: Autobuild - uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 + uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 + uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5 diff --git a/.github/workflows/licenses.yml b/.github/workflows/licenses.yml index 7a4726a..4a9d82a 100644 --- a/.github/workflows/licenses.yml +++ b/.github/workflows/licenses.yml @@ -14,12 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: '1.23' - name: Install go-licenses diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index cc6549b..cd282b6 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -15,14 +15,14 @@ jobs: runs-on: macos-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: 1.23 diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index d5ae8b3..83a79e1 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index f28eacb..98b643a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit @@ -78,6 +78,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5 + uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5 with: sarif_file: results.sarif diff --git a/.github/workflows/test_ppc64.yaml b/.github/workflows/test_ppc64.yaml index a8dba35..261a7c5 100644 --- a/.github/workflows/test_ppc64.yaml +++ b/.github/workflows/test_ppc64.yaml @@ -23,12 +23,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/test_qemu.yml b/.github/workflows/test_qemu.yml index e358eb8..4ebbde4 100644 --- a/.github/workflows/test_qemu.yml +++ b/.github/workflows/test_qemu.yml @@ -22,12 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/test_riscv64.yaml b/.github/workflows/test_riscv64.yaml index 75d56ef..a772790 100644 --- a/.github/workflows/test_riscv64.yaml +++ b/.github/workflows/test_riscv64.yaml @@ -22,12 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/test_s390x.yaml b/.github/workflows/test_s390x.yaml index 2a105ab..38f148d 100644 --- a/.github/workflows/test_s390x.yaml +++ b/.github/workflows/test_s390x.yaml @@ -22,12 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.go-version }} diff --git a/.github/workflows/test_sm_ni.yml b/.github/workflows/test_sm_ni.yml index 37a71bf..c20f0fc 100644 --- a/.github/workflows/test_sm_ni.yml +++ b/.github/workflows/test_sm_ni.yml @@ -22,12 +22,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - name: Set up Go - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: ${{ matrix.go-version }} diff --git a/README-EN.md b/README-EN.md index 65fde92..4c74aa1 100644 --- a/README-EN.md +++ b/README-EN.md @@ -40,6 +40,8 @@ ShangMi (SM) cipher suites for Golang, referred to as **GMSM**, is a secure, hig - **DRBG** - Random Number Generation Using Deterministic Random Bit Generators, for detail, please reference **NIST Special Publication 800-90A** and **GM/T 0105-2021**: CTR-DRBG using derivation function and HASH-DRBG. NIST related implementations are tested with part of NIST provided test vectors. It's **NOT** concurrent safe! You can also use [randomness](https://github.com/Trisia/randomness) tool to check the generated random bits. +- **MLKEM** - NIST FIPS 203 Module-Lattice-Based Key-Encapsulation Mechanism Standard. + - **MLDSA** - NIST FIPS 204 Module-Lattice-Based Digital Signature Standard. - **SLHDSA** - NIST FIPS 205 Stateless Hash-Based Digital Signature Standard diff --git a/README.md b/README.md index a2fb3b1..3f6a111 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ Go语言商用密码软件,简称**GMSM**,一个安全、高性能、易于 - **DRBG** - 《GM/T 0105-2021软件随机数发生器设计指南》实现。本实现同时支持**NIST Special Publication 800-90A**(部分) 和 **GM/T 0105-2021**,NIST相关实现使用了NIST提供的测试数据进行测试。本实现**不支持并发使用**。 +- **MLKEM** - NIST FIPS 203 Module-Lattice-Based Key-Encapsulation Mechanism Standard实现,基于Golang标准库,支持所有三组参数集(ML-KEM-512/ML-KEM-768/ML-KEM-1024)。 + - **MLDSA** - NIST FIPS 204 Module-Lattice-Based Digital Signature Standard实现。 - **SLHDSA** - NIST FIPS 205 Stateless Hash-Based Digital Signature Standard实现。 diff --git a/mlkem/field.go b/mlkem/field.go new file mode 100644 index 0000000..684e94f --- /dev/null +++ b/mlkem/field.go @@ -0,0 +1,568 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package mlkem + +import ( + "crypto/sha3" + "errors" + + "github.com/emmansun/gmsm/internal/byteorder" +) + +// fieldElement is an integer modulo q, an element of ℤ_q. It is always reduced. +type fieldElement uint16 + +// fieldCheckReduced checks that a value a is < q. +func fieldCheckReduced(a uint16) (fieldElement, error) { + if a >= q { + return 0, errors.New("unreduced field element") + } + return fieldElement(a), nil +} + +// fieldReduceOnce reduces a value a < 2q. +func fieldReduceOnce(a uint16) fieldElement { + x := a - q + // If x underflowed, then x >= 2¹⁶ - q > 2¹⁵, so the top bit is set. + x += (x >> 15) * q + return fieldElement(x) +} + +func fieldAdd(a, b fieldElement) fieldElement { + x := uint16(a + b) + return fieldReduceOnce(x) +} + +func fieldSub(a, b fieldElement) fieldElement { + x := uint16(a - b + q) + return fieldReduceOnce(x) +} + +const ( + barrettMultiplier = 5039 // 2¹² * 2¹² / q + barrettShift = 24 // log₂(2¹² * 2¹²) +) + +// fieldReduce reduces a value a < 2q² using Barrett reduction, to avoid +// potentially variable-time division. +func fieldReduce(a uint32) fieldElement { + quotient := uint32((uint64(a) * barrettMultiplier) >> barrettShift) + return fieldReduceOnce(uint16(a - quotient*q)) +} + +func fieldMul(a, b fieldElement) fieldElement { + x := uint32(a) * uint32(b) + return fieldReduce(x) +} + +// fieldMulSub returns a * (b - c). This operation is fused to save a +// fieldReduceOnce after the subtraction. +func fieldMulSub(a, b, c fieldElement) fieldElement { + x := uint32(a) * uint32(b-c+q) + return fieldReduce(x) +} + +// fieldAddMul returns a * b + c * d. This operation is fused to save a +// fieldReduceOnce and a fieldReduce. +func fieldAddMul(a, b, c, d fieldElement) fieldElement { + x := uint32(a) * uint32(b) + x += uint32(c) * uint32(d) + return fieldReduce(x) +} + +// compress maps a field element uniformly to the range 0 to 2ᵈ-1, according to +// FIPS 203, Definition 4.7. +func compress(x fieldElement, d uint8) uint16 { + // We want to compute (x * 2ᵈ) / q, rounded to nearest integer, with 1/2 + // rounding up (see FIPS 203, Section 2.3). + + // Barrett reduction produces a quotient and a remainder in the range [0, 2q), + // such that dividend = quotient * q + remainder. + dividend := uint32(x) << d // x * 2ᵈ + quotient := uint32(uint64(dividend) * barrettMultiplier >> barrettShift) + remainder := dividend - quotient*q + + // Since the remainder is in the range [0, 2q), not [0, q), we need to + // portion it into three spans for rounding. + // + // [ 0, q/2 ) -> round to 0 + // [ q/2, q + q/2 ) -> round to 1 + // [ q + q/2, 2q ) -> round to 2 + // + // We can convert that to the following logic: add 1 if remainder > q/2, + // then add 1 again if remainder > q + q/2. + // + // Note that if remainder > x, then ⌊x⌋ - remainder underflows, and the top + // bit of the difference will be set. + quotient += (q/2 - remainder) >> 31 & 1 + quotient += (q + q/2 - remainder) >> 31 & 1 + + // quotient might have overflowed at this point, so reduce it by masking. + var mask uint32 = (1 << d) - 1 + return uint16(quotient & mask) +} + +// decompress maps a number x between 0 and 2ᵈ-1 uniformly to the full range of +// field elements, according to FIPS 203, Definition 4.8. +func decompress(y uint16, d uint8) fieldElement { + // We want to compute (y * q) / 2ᵈ, rounded to nearest integer, with 1/2 + // rounding up (see FIPS 203, Section 2.3). + + dividend := uint32(y) * q + quotient := dividend >> d // (y * q) / 2ᵈ + + // The d'th least-significant bit of the dividend (the most significant bit + // of the remainder) is 1 for the top half of the values that divide to the + // same quotient, which are the ones that round up. + quotient += dividend >> (d - 1) & 1 + + // quotient is at most (2¹¹-1) * q / 2¹¹ + 1 = 3328, so it didn't overflow. + return fieldElement(quotient) +} + +// ringElement is a polynomial, an element of R_q, represented as an array +// according to FIPS 203, Section 2.4.4. +type ringElement [n]fieldElement + +// polyAdd adds two ringElements or nttElements. +func polyAdd[T ~[n]fieldElement](a, b T) (s T) { + for i := range s { + s[i] = fieldAdd(a[i], b[i]) + } + return s +} + +// polySub subtracts two ringElements or nttElements. +func polySub[T ~[n]fieldElement](a, b T) (s T) { + for i := range s { + s[i] = fieldSub(a[i], b[i]) + } + return s +} + +// polyByteEncode appends the 384-byte encoding of f to b. +// +// It implements ByteEncode₁₂, according to FIPS 203, Algorithm 5. +func polyByteEncode[T ~[n]fieldElement](b []byte, f T) []byte { + out, B := sliceForAppend(b, encodingSize12) + for i := 0; i < n; i += 2 { + x := uint32(f[i]) | uint32(f[i+1])<<12 + B[0] = uint8(x) + B[1] = uint8(x >> 8) + B[2] = uint8(x >> 16) + B = B[3:] + } + return out +} + +// polyByteDecode decodes the 384-byte encoding of a polynomial, checking that +// all the coefficients are properly reduced. This fulfills the "Modulus check" +// step of ML-KEM Encapsulation. +// +// It implements ByteDecode₁₂, according to FIPS 203, Algorithm 6. +func polyByteDecode[T ~[n]fieldElement](b []byte) (T, error) { + if len(b) != encodingSize12 { + return T{}, errors.New("mlkem: invalid encoding length") + } + var f T + for i := 0; i < n; i += 2 { + d := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 + const mask12 = 0b1111_1111_1111 + var err error + if f[i], err = fieldCheckReduced(uint16(d & mask12)); err != nil { + return T{}, errors.New("mlkem: invalid polynomial encoding") + } + if f[i+1], err = fieldCheckReduced(uint16(d >> 12)); err != nil { + return T{}, errors.New("mlkem: invalid polynomial encoding") + } + b = b[3:] + } + return f, nil +} + +// sliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +// ringCompressAndEncode1 appends a 32-byte encoding of a ring element to s, +// compressing one coefficients per bit. +// +// It implements Compress₁, according to FIPS 203, Definition 4.7, +// followed by ByteEncode₁, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode1(s []byte, f ringElement) []byte { + s, b := sliceForAppend(s, encodingSize1) + for i := range b { + b[i] = 0 + } + for i := range f { + b[i/8] |= uint8(compress(f[i], 1) << (i % 8)) + } + return s +} + +// ringDecodeAndDecompress1 decodes a 32-byte slice to a ring element where each +// bit is mapped to 0 or ⌈q/2⌋. +// +// It implements ByteDecode₁, according to FIPS 203, Algorithm 6, +// followed by Decompress₁, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress1(b *[encodingSize1]byte) ringElement { + var f ringElement + for i := range f { + b_i := b[i/8] >> (i % 8) & 1 + const halfQ = (q + 1) / 2 // ⌈q/2⌋, rounded up per FIPS 203, Section 2.3 + f[i] = fieldElement(b_i) * halfQ // 0 decompresses to 0, and 1 to ⌈q/2⌋ + } + return f +} + +// ringCompressAndEncode4 appends a 128-byte encoding of a ring element to s, +// compressing two coefficients per byte. +// +// It implements Compress₄, according to FIPS 203, Definition 4.7, +// followed by ByteEncode₄, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode4(s []byte, f ringElement) []byte { + s, b := sliceForAppend(s, encodingSize4) + for i := 0; i < n; i += 2 { + b[i/2] = uint8(compress(f[i], 4) | compress(f[i+1], 4)<<4) + } + return s +} + +// ringDecodeAndDecompress4 decodes a 128-byte encoding of a ring element where +// each four bits are mapped to an equidistant distribution. +// +// It implements ByteDecode₄, according to FIPS 203, Algorithm 6, +// followed by Decompress₄, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress4(b *[encodingSize4]byte) ringElement { + var f ringElement + for i := 0; i < n; i += 2 { + f[i] = fieldElement(decompress(uint16(b[i/2]&0b1111), 4)) + f[i+1] = fieldElement(decompress(uint16(b[i/2]>>4), 4)) + } + return f +} + +// ringCompressAndEncode10 appends a 320-byte encoding of a ring element to s, +// compressing four coefficients per five bytes. +// +// It implements Compress₁₀, according to FIPS 203, Definition 4.7, +// followed by ByteEncode₁₀, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode10(s []byte, f ringElement) []byte { + s, b := sliceForAppend(s, encodingSize10) + for i := 0; i < n; i += 4 { + var x uint64 + x |= uint64(compress(f[i], 10)) + x |= uint64(compress(f[i+1], 10)) << 10 + x |= uint64(compress(f[i+2], 10)) << 20 + x |= uint64(compress(f[i+3], 10)) << 30 + b[0] = uint8(x) + b[1] = uint8(x >> 8) + b[2] = uint8(x >> 16) + b[3] = uint8(x >> 24) + b[4] = uint8(x >> 32) + b = b[5:] + } + return s +} + +// ringDecodeAndDecompress10 decodes a 320-byte encoding of a ring element where +// each ten bits are mapped to an equidistant distribution. +// +// It implements ByteDecode₁₀, according to FIPS 203, Algorithm 6, +// followed by Decompress₁₀, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress10(bb *[encodingSize10]byte) ringElement { + b := bb[:] + var f ringElement + for i := 0; i < n; i += 4 { + x := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 + b = b[5:] + f[i] = fieldElement(decompress(uint16(x>>0&0b11_1111_1111), 10)) + f[i+1] = fieldElement(decompress(uint16(x>>10&0b11_1111_1111), 10)) + f[i+2] = fieldElement(decompress(uint16(x>>20&0b11_1111_1111), 10)) + f[i+3] = fieldElement(decompress(uint16(x>>30&0b11_1111_1111), 10)) + } + return f +} + +// ringCompressAndEncode appends an encoding of a ring element to s, +// compressing each coefficient to d bits. +// +// It implements Compress, according to FIPS 203, Definition 4.7, +// followed by ByteEncode, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode(s []byte, f ringElement, d uint8) []byte { + var b byte + var bIdx uint8 + for i := range n { + c := compress(f[i], d) + var cIdx uint8 + for cIdx < d { + b |= byte(c>>cIdx) << bIdx + bits := min(8-bIdx, d-cIdx) + bIdx += bits + cIdx += bits + if bIdx == 8 { + s = append(s, b) + b = 0 + bIdx = 0 + } + } + } + if bIdx != 0 { + panic("mlkem: internal error: bitsFilled != 0") + } + return s +} + +// ringDecodeAndDecompress decodes an encoding of a ring element where +// each d bits are mapped to an equidistant distribution. +// +// It implements ByteDecode, according to FIPS 203, Algorithm 6, +// followed by Decompress, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress(b []byte, d uint8) ringElement { + var f ringElement + var bIdx uint8 + for i := 0; i < n; i++ { + var c uint16 + var cIdx uint8 + for cIdx < d { + c |= uint16(b[0]>>bIdx) << cIdx + c &= (1 << d) - 1 + bits := min(8-bIdx, d-cIdx) + bIdx += bits + cIdx += bits + if bIdx == 8 { + b = b[1:] + bIdx = 0 + } + } + f[i] = fieldElement(decompress(c, d)) + } + if len(b) != 0 { + panic("mlkem: internal error: leftover bytes") + } + return f +} + +// ringCompressAndEncode5 appends a 160-byte encoding of a ring element to s, +// compressing eight coefficients per five bytes. +// +// It implements Compress₅, according to FIPS 203, Definition 4.7, +// followed by ByteEncode₅, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode5(s []byte, f ringElement) []byte { + return ringCompressAndEncode(s, f, 5) +} + +// ringDecodeAndDecompress5 decodes a 160-byte encoding of a ring element where +// each five bits are mapped to an equidistant distribution. +// +// It implements ByteDecode₅, according to FIPS 203, Algorithm 6, +// followed by Decompress₅, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress5(bb *[encodingSize5]byte) ringElement { + return ringDecodeAndDecompress(bb[:], 5) +} + +// ringCompressAndEncode11 appends a 352-byte encoding of a ring element to s, +// compressing eight coefficients per eleven bytes. +// +// It implements Compress₁₁, according to FIPS 203, Definition 4.7, +// followed by ByteEncode₁₁, according to FIPS 203, Algorithm 5. +func ringCompressAndEncode11(s []byte, f ringElement) []byte { + return ringCompressAndEncode(s, f, 11) +} + +// ringDecodeAndDecompress11 decodes a 352-byte encoding of a ring element where +// each eleven bits are mapped to an equidistant distribution. +// +// It implements ByteDecode₁₁, according to FIPS 203, Algorithm 6, +// followed by Decompress₁₁, according to FIPS 203, Definition 4.8. +func ringDecodeAndDecompress11(bb *[encodingSize11]byte) ringElement { + return ringDecodeAndDecompress(bb[:], 11) +} + +// samplePolyCBD draws a ringElement from the special Dη distribution given a +// stream of random bytes generated by the PRF function, according to FIPS 203, +// Algorithm 8 and Definition 4.3. +func samplePolyCBD(s []byte, b, η byte) ringElement { + prf := sha3.NewSHAKE256() + prf.Write(s) + prf.Write([]byte{b}) + var B [maxBytesOf64Mulη]byte + prf.Read(B[:64*η]) + + // SamplePolyCBD simply draws four (2η) bits for each coefficient, and adds + // the first two and subtracts the last two. + + var f ringElement + switch η { + case 2: + for i := 0; i < n; i += 2 { + b := B[i/2] + b_7, b_6, b_5, b_4 := b>>7, b>>6&1, b>>5&1, b>>4&1 + b_3, b_2, b_1, b_0 := b>>3&1, b>>2&1, b>>1&1, b&1 + f[i] = fieldSub(fieldElement(b_0+b_1), fieldElement(b_2+b_3)) + f[i+1] = fieldSub(fieldElement(b_4+b_5), fieldElement(b_6+b_7)) + } + case 3: + for i := 0; i < n; i += 4 { + j := (i >> 2) * 3 + + bits := uint32(B[j]) | uint32(B[j+1])<<8 | uint32(B[j+2])<<16 + for k := range 4 { + off := 6 * k + sum := ((bits >> off) & 1) + ((bits >> (off + 1)) & 1) + ((bits >> (off + 2)) & 1) + sub := ((bits >> (off + 3)) & 1) + ((bits >> (off + 4)) & 1) + ((bits >> (off + 5)) & 1) + f[i+k] = fieldSub(fieldElement(sum), fieldElement(sub)) + } + } + } + return f +} + +// nttElement is an NTT representation, an element of T_q, represented as an +// array according to FIPS 203, Section 2.4.4. +type nttElement [n]fieldElement + +// gammas are the values ζ^2BitRev7(i)+1 mod q for each index i, according to +// FIPS 203, Appendix A (with negative values reduced to positive). +var gammas = [128]fieldElement{17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, 2110, 1219, 2935, 394, 885, 2444, 2154, 1175} + +// nttMul multiplies two nttElements. +// +// It implements MultiplyNTTs, according to FIPS 203, Algorithm 11. +func nttMul(f, g nttElement) nttElement { + var h nttElement + // We use i += 2 for bounds check elimination. See https://go.dev/issue/66826. + for i := 0; i < 256; i += 2 { + a0, a1 := f[i], f[i+1] + b0, b1 := g[i], g[i+1] + h[i] = fieldAddMul(a0, b0, fieldMul(a1, b1), gammas[i/2]) + h[i+1] = fieldAddMul(a0, b1, a1, b0) + } + return h +} + +// zetas are the values ζ^BitRev7(k) mod q for each index k, according to FIPS +// 203, Appendix A. +var zetas = [128]fieldElement{1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154} + +// ntt maps a ringElement to its nttElement representation. +// +// It implements NTT, according to FIPS 203, Algorithm 9. +func ntt(f ringElement) nttElement { + k := 1 + for len := 128; len >= 2; len /= 2 { + for start := 0; start < 256; start += 2 * len { + zeta := zetas[k] + k++ + // Bounds check elimination hint. + f, flen := f[start:start+len], f[start+len:start+len+len] + for j := 0; j < len; j++ { + t := fieldMul(zeta, flen[j]) + flen[j] = fieldSub(f[j], t) + f[j] = fieldAdd(f[j], t) + } + } + } + return nttElement(f) +} + +// inverseNTT maps a nttElement back to the ringElement it represents. +// +// It implements NTT⁻¹, according to FIPS 203, Algorithm 10. +func inverseNTT(f nttElement) ringElement { + k := 127 + for len := 2; len <= 128; len *= 2 { + for start := 0; start < 256; start += 2 * len { + zeta := zetas[k] + k-- + // Bounds check elimination hint. + f, flen := f[start:start+len], f[start+len:start+len+len] + for j := 0; j < len; j++ { + t := f[j] + f[j] = fieldAdd(t, flen[j]) + flen[j] = fieldMulSub(zeta, flen[j], t) + } + } + } + for i := range f { + f[i] = fieldMul(f[i], 3303) // 3303 = 128⁻¹ mod q + } + return ringElement(f) +} + +// sampleNTT draws a uniformly random nttElement from a stream of uniformly +// random bytes generated by the XOF function, according to FIPS 203, +// Algorithm 7. +func sampleNTT(rho []byte, ii, jj byte) nttElement { + B := sha3.NewSHAKE128() + B.Write(rho) + B.Write([]byte{ii, jj}) + + // SampleNTT essentially draws 12 bits at a time from r, interprets them in + // little-endian, and rejects values higher than q, until it drew 256 + // values. (The rejection rate is approximately 19%.) + // + // To do this from a bytes stream, it draws three bytes at a time, and + // splits them into two uint16 appropriately masked. + // + // r₀ r₁ r₂ + // |- - - - - - - -|- - - - - - - -|- - - - - - - -| + // + // Uint16(r₀ || r₁) + // |- - - - - - - - - - - - - - - -| + // |- - - - - - - - - - - -| + // d₁ + // + // Uint16(r₁ || r₂) + // |- - - - - - - - - - - - - - - -| + // |- - - - - - - - - - - -| + // d₂ + // + // Note that in little-endian, the rightmost bits are the most significant + // bits (dropped with a mask) and the leftmost bits are the least + // significant bits (dropped with a right shift). + + var a nttElement + var j int // index into a + var buf [24]byte // buffered reads from B + off := len(buf) // index into buf, starts in a "buffer fully consumed" state + for { + if off >= len(buf) { + B.Read(buf[:]) + off = 0 + } + d1 := byteorder.LEUint16(buf[off:]) & 0b1111_1111_1111 + d2 := byteorder.LEUint16(buf[off+1:]) >> 4 + off += 3 + if d1 < q { + a[j] = fieldElement(d1) + j++ + } + if j >= len(a) { + break + } + if d2 < q { + a[j] = fieldElement(d2) + j++ + } + if j >= len(a) { + break + } + } + return a +} diff --git a/mlkem/field_test.go b/mlkem/field_test.go new file mode 100644 index 0000000..27cbdc3 --- /dev/null +++ b/mlkem/field_test.go @@ -0,0 +1,271 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/rand" + "math/big" + mathrand "math/rand/v2" + "strconv" + "testing" +) + +func TestFieldReduce(t *testing.T) { + for a := range uint32(2*q*q) { + got := fieldReduce(a) + exp := fieldElement(a % q) + if got != exp { + t.Fatalf("reduce(%d) = %d, expected %d", a, got, exp) + } + } +} + +func TestFieldAdd(t *testing.T) { + for a := range fieldElement(q) { + for b := range fieldElement(q) { + got := fieldAdd(a, b) + exp := (a + b) % q + if got != exp { + t.Fatalf("%d + %d = %d, expected %d", a, b, got, exp) + } + } + } +} + +func TestFieldSub(t *testing.T) { + for a := range fieldElement(q) { + for b := range fieldElement(q) { + got := fieldSub(a, b) + exp := (a - b + q) % q + if got != exp { + t.Fatalf("%d - %d = %d, expected %d", a, b, got, exp) + } + } + } +} + +func TestFieldMul(t *testing.T) { + for a := range fieldElement(q) { + for b := range fieldElement(q) { + got := fieldMul(a, b) + exp := fieldElement((uint32(a) * uint32(b)) % q) + if got != exp { + t.Fatalf("%d * %d = %d, expected %d", a, b, got, exp) + } + } + } +} + +func TestDecompressCompress(t *testing.T) { + for _, bits := range []uint8{1, 4, 10} { + for a := uint16(0); a < 1<= q { + t.Fatalf("decompress(%d, %d) = %d >= q", a, bits, f) + } + got := compress(f, bits) + if got != a { + t.Fatalf("compress(decompress(%d, %d), %d) = %d", a, bits, bits, got) + } + } + + for a := fieldElement(0); a < q; a++ { + c := compress(a, bits) + if c >= 1<= 2^bits", a, bits, c) + } + got := decompress(c, bits) + diff := min(a-got, got-a, a-got+q, got-a+q) + ceil := q / (1 << bits) + if diff > fieldElement(ceil) { + t.Fatalf("decompress(compress(%d, %d), %d) = %d (diff %d, max diff %d)", + a, bits, bits, got, diff, ceil) + } + } + } +} + +func CompressRat(x fieldElement, d uint8) uint16 { + if x >= q { + panic("x out of range") + } + if d <= 0 || d >= 12 { + panic("d out of range") + } + + precise := big.NewRat((1<= 1<= 12 { + panic("d out of range") + } + + precise := big.NewRat(q*int64(y), 1<>7 != 0 { + panic("not 7 bits") + } + var r uint8 + r |= n >> 6 & 0b0000_0001 + r |= n >> 4 & 0b0000_0010 + r |= n >> 2 & 0b0000_0100 + r |= n /**/ & 0b0000_1000 + r |= n << 2 & 0b0001_0000 + r |= n << 4 & 0b0010_0000 + r |= n << 6 & 0b0100_0000 + return r +} + +func TestZetas(t *testing.T) { + ζ := big.NewInt(17) + q := big.NewInt(q) + for k, zeta := range zetas { + // ζ^BitRev7(k) mod q + exp := new(big.Int).Exp(ζ, big.NewInt(int64(BitRev7(uint8(k)))), q) + if big.NewInt(int64(zeta)).Cmp(exp) != 0 { + t.Errorf("zetas[%d] = %v, expected %v", k, zeta, exp) + } + } +} + +func TestGammas(t *testing.T) { + ζ := big.NewInt(17) + q := big.NewInt(q) + for k, gamma := range gammas { + // ζ^2BitRev7(i)+1 + exp := new(big.Int).Exp(ζ, big.NewInt(int64(BitRev7(uint8(k)))*2+1), q) + if big.NewInt(int64(gamma)).Cmp(exp) != 0 { + t.Errorf("gammas[%d] = %v, expected %v", k, gamma, exp) + } + } +} diff --git a/mlkem/generate.go b/mlkem/generate.go new file mode 100644 index 0000000..cbab78d --- /dev/null +++ b/mlkem/generate.go @@ -0,0 +1,177 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore + +package main + +import ( + "flag" + "go/ast" + "go/format" + "go/parser" + "go/token" + "log" + "os" + "strings" +) + +var replacements512 = map[string]string{ + "k": "k512", + "η1": "η1_512", + "η2": "η2_512", + + "CiphertextSize768": "CiphertextSize512", + "EncapsulationKeySize768": "EncapsulationKeySize512", + "DecapsulationKeySize768": "DecapsulationKeySize512", + + "encryptionKey": "encryptionKey512", + "decryptionKey": "decryptionKey512", + + "EncapsulationKey768": "EncapsulationKey512", + "NewEncapsulationKey768": "NewEncapsulationKey512", + "parseEK": "parseEK512", + + "kemEncaps": "kemEncaps512", + "pkeEncrypt": "pkeEncrypt512", + + "DecapsulationKey768": "DecapsulationKey512", + "NewDecapsulationKey768": "NewDecapsulationKey512", + "NewDecapsulationKeyFromSeed768": "NewDecapsulationKeyFromSeed512", + "newKeyFromSeed": "newKeyFromSeed512", + + "kemDecaps": "kemDecaps512", + "pkeDecrypt": "pkeDecrypt512", + + "GenerateKey768": "GenerateKey512", + "GenerateKeyInternal768": "GenerateKeyInternal512", + "generateKey": "generateKey512", + + "kemKeyGen": "kemKeyGen512", +} + +var replacements1024 = map[string]string{ + "k": "k1024", + "η1": "η1_1024", + "η2": "η2_1024", + + "CiphertextSize768": "CiphertextSize1024", + "EncapsulationKeySize768": "EncapsulationKeySize1024", + "DecapsulationKeySize768": "DecapsulationKeySize1024", + + "encryptionKey": "encryptionKey1024", + "decryptionKey": "decryptionKey1024", + + "EncapsulationKey768": "EncapsulationKey1024", + "NewEncapsulationKey768": "NewEncapsulationKey1024", + "parseEK": "parseEK1024", + + "kemEncaps": "kemEncaps1024", + "pkeEncrypt": "pkeEncrypt1024", + + "DecapsulationKey768": "DecapsulationKey1024", + "NewDecapsulationKey768": "NewDecapsulationKey1024", + "NewDecapsulationKeyFromSeed768": "NewDecapsulationKeyFromSeed1024", + "newKeyFromSeed": "newKeyFromSeed1024", + + "kemDecaps": "kemDecaps1024", + "pkeDecrypt": "pkeDecrypt1024", + + "GenerateKey768": "GenerateKey1024", + "GenerateKeyInternal768": "GenerateKeyInternal1024", + "generateKey": "generateKey1024", + + "kemKeyGen": "kemKeyGen1024", + "kemPCT": "kemPCT1024", + + "encodingSize4": "encodingSize5", + "encodingSize10": "encodingSize11", + "ringCompressAndEncode4": "ringCompressAndEncode5", + "ringCompressAndEncode10": "ringCompressAndEncode11", + "ringDecodeAndDecompress4": "ringDecodeAndDecompress5", + "ringDecodeAndDecompress10": "ringDecodeAndDecompress11", +} + +func replaceIdentifiers(inputFile, outputFile *string, replacements map[string]string) { + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, *inputFile, nil, parser.SkipObjectResolution|parser.ParseComments) + if err != nil { + log.Fatal(err) + } + cmap := ast.NewCommentMap(fset, f, f.Comments) + + // Drop header comments. + cmap[ast.Node(f)] = nil + + // Remove top-level consts used across the main and generated files. + var newDecls []ast.Decl + for _, decl := range f.Decls { + switch d := decl.(type) { + case *ast.GenDecl: + if d.Tok == token.CONST { + continue // Skip const declarations + } + if d.Tok == token.IMPORT { + cmap[decl] = nil // Drop pre-import comments. + } + } + newDecls = append(newDecls, decl) + } + f.Decls = newDecls + + // Replace identifiers. + ast.Inspect(f, func(n ast.Node) bool { + switch x := n.(type) { + case *ast.Ident: + if replacement, ok := replacements[x.Name]; ok { + x.Name = replacement + } + } + return true + }) + + // Replace identifiers in comments. + for _, c := range f.Comments { + for _, l := range c.List { + for k, v := range replacements { + if k == "k" || k == "l" { + continue + } + l.Text = strings.ReplaceAll(l.Text, k, v) + } + } + } + + out, err := os.Create(*outputFile) + if err != nil { + log.Fatal(err) + } + defer out.Close() + + out.WriteString(`// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. +// Code generated by generate.go. DO NOT EDIT. + +//go:build go1.24 + +`) + + f.Comments = cmap.Filter(f).Comments() + err = format.Node(out, fset, f) + if err != nil { + log.Fatal(err) + } + +} + +func main() { + inputFile := flag.String("mlkem768", "", "") + outputFile512 := flag.String("mlkem512", "", "") + outputFile1024 := flag.String("mlkem1024", "", "") + flag.Parse() + + replaceIdentifiers(inputFile, outputFile512, replacements512) + replaceIdentifiers(inputFile, outputFile1024, replacements1024) +} diff --git a/mlkem/mlkem1024.go b/mlkem/mlkem1024.go new file mode 100644 index 0000000..a26ab81 --- /dev/null +++ b/mlkem/mlkem1024.go @@ -0,0 +1,412 @@ +// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. +// Code generated by generate.go. DO NOT EDIT. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/sha3" + "crypto/subtle" + "errors" + "io" +) + +// A DecapsulationKey1024 is the secret key used to decapsulate a shared key from a +// ciphertext. It includes various precomputed values. +type DecapsulationKey1024 struct { + d [32]byte // decapsulation key seed + z [32]byte // implicit rejection sampling seed + + ρ [32]byte // rho, sampleNTT seed for A, stored for the encapsulation key + h [32]byte // H(ek), stored for ML-KEM.Decaps_internal + + encryptionKey1024 + decryptionKey1024 +} + +// Seed returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey1024) Seed() []byte { + var b [SeedSize]byte + copy(b[:], dk.d[:]) + copy(b[32:], dk.z[:]) + return b[:] +} + +// Bytes returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +func (dk *DecapsulationKey1024) Bytes() []byte { + b := make([]byte, 0, DecapsulationKeySize1024) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { + return &EncapsulationKey1024{ + ρ: dk.ρ, + h: dk.h, + encryptionKey1024: dk.encryptionKey1024, + } +} + +// An EncapsulationKey1024 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding [DecapsulationKey1024]. +type EncapsulationKey1024 struct { + ρ [32]byte // sampleNTT seed for A + h [32]byte // H(ek) + encryptionKey1024 +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey1024) Bytes() []byte { + // The actual logic is in a separate function to outline this allocation. + b := make([]byte, 0, EncapsulationKeySize1024) + return ek.bytes(b) +} + +func (ek *EncapsulationKey1024) bytes(b []byte) []byte { + for i := range ek.t { + b = polyByteEncode(b, ek.t[i]) + } + b = append(b, ek.ρ[:]...) + return b +} + +// encryptionKey1024 is the parsed and expanded form of a PKE encryption key. +type encryptionKey1024 struct { + t [k1024]nttElement // ByteDecode₁₂(ek[:384k]) + a [k1024 * k1024]nttElement // A[i*k+j] = sampleNTT(ρ, j, i) +} + +// decryptionKey1024 is the parsed and expanded form of a PKE decryption key. +type decryptionKey1024 struct { + s [k1024]nttElement // ByteDecode₁₂(dk[:decryptionKey1024Size]) +} + +// GenerateKey1024 generates a new decapsulation key. The decapsulation key must be kept secret. +// See FIPS 203, Algorithm 19. +func GenerateKey1024(rand io.Reader) (*DecapsulationKey1024, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey1024{} + return generateKey1024(dk, rand) +} + +func generateKey1024(dk *DecapsulationKey1024, rand io.Reader) (*DecapsulationKey1024, error) { + var d [32]byte + if _, err := io.ReadFull(rand, d[:]); err != nil { + return nil, err + } + var z [32]byte + if _, err := io.ReadFull(rand, z[:]); err != nil { + return nil, err + } + + kemKeyGen1024(dk, &d, &z) + return dk, nil +} + +// NewDecapsulationKeyFromSeed1024 parses a decapsulation key from a 64-byte +// seed in the "d || z" form. The seed must be uniformly random. +func NewDecapsulationKeyFromSeed1024(seed []byte) (*DecapsulationKey1024, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey1024{} + return newKeyFromSeed1024(dk, seed) +} + +func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKey1024, error) { + if len(seed) != SeedSize { + return nil, errors.New("mlkem: invalid seed length") + } + d := (*[32]byte)(seed[:32]) + z := (*[32]byte)(seed[32:]) + kemKeyGen1024(dk, d, z) + + return dk, nil +} + +// NewDecapsulationKey1024 parses a decapsulation key from its expanded NIST format. +func NewDecapsulationKey1024(b []byte) (*DecapsulationKey1024, error) { + if len(b) != DecapsulationKeySize1024 { + return nil, errors.New("mlkem: invalid decapsulation key length") + } + + dk := &DecapsulationKey1024{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey1024(b[:EncapsulationKeySize1024]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey1024 = ek.encryptionKey1024 + b = b[EncapsulationKeySize1024:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + + copy(dk.z[:], b[32:]) + + return dk, nil +} + +// kemKeyGen1024 generates a decapsulation key. +// +// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and +// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save +// copies and allocations. +func kemKeyGen1024(dk *DecapsulationKey1024, d, z *[32]byte) { + dk.d = *d + dk.z = *z + + g := sha3.New512() + g.Write(d[:]) + g.Write([]byte{k1024}) // Module dimension as a domain separator. + G := g.Sum(make([]byte, 0, 64)) + ρ, σ := G[:32], G[32:] // rho, sigma + dk.ρ = [32]byte(ρ) + + A := &dk.a + for i := range byte(k1024) { + for j := range byte(k1024) { + A[i*k1024+j] = sampleNTT(ρ, j, i) + } + } + + var N byte + s := &dk.s + for i := range s { + s[i] = ntt(samplePolyCBD(σ, N, η1_1024)) + N++ + } + e := make([]nttElement, k1024) + for i := range e { + e[i] = ntt(samplePolyCBD(σ, N, η1_1024)) + N++ + } + + t := &dk.t + for i := range t { // t = A ◦ s + e + t[i] = e[i] + for j := range s { + t[i] = polyAdd(t[i], nttMul(A[i*k1024+j], s[j])) + } + } + + H := sha3.New256() + ek := dk.EncapsulationKey().Bytes() + H.Write(ek) + H.Sum(dk.h[:0]) +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key. +// +// The shared key must be kept secret. See FIPS 203, Algorithm 20. +func (ek *EncapsulationKey1024) Encapsulate(rand io.Reader) (sharedKey, ciphertext []byte, err error) { + // The actual logic is in a separate function to outline this allocation. + var cc [CiphertextSize1024]byte + return ek.encapsulate(&cc, rand) +} + +func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte, rand io.Reader) (sharedKey, ciphertext []byte, err error) { + var m [messageSize]byte + if _, err := io.ReadFull(rand, m[:]); err != nil { + return nil, nil, err + } + sharedKey, ciphertext = kemEncaps1024(cc, ek, &m) + return sharedKey, ciphertext, nil +} + +// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for +// use in tests. +func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { + cc := &[CiphertextSize1024]byte{} + return kemEncaps1024(cc, ek, m) +} + +// kemEncaps1024 generates a shared key and an associated ciphertext. +// +// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17. +func kemEncaps1024(cc *[CiphertextSize1024]byte, ek *EncapsulationKey1024, m *[messageSize]byte) (K, c []byte) { + g := sha3.New512() + g.Write(m[:]) + g.Write(ek.h[:]) + G := g.Sum(nil) + K, r := G[:SharedKeySize], G[SharedKeySize:] + c = pkeEncrypt1024(cc, &ek.encryptionKey1024, m, r) + return K, c +} + +// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. +// If the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. +func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { + // The actual logic is in a separate function to outline this allocation. + ek := &EncapsulationKey1024{} + return parseEK1024(ek, encapsulationKey) +} + +// parseEK1024 parses an encryption key from its encoded form. +// +// It implements the initial stages of K-PKE.Encrypt according to FIPS 203, +// Algorithm 14. +func parseEK1024(ek *EncapsulationKey1024, ekPKE []byte) (*EncapsulationKey1024, error) { + if len(ekPKE) != EncapsulationKeySize1024 { + return nil, errors.New("mlkem: invalid encapsulation key length") + } + + h := sha3.New256() + h.Write(ekPKE) + h.Sum(ek.h[:0]) + + for i := range ek.t { + var err error + ek.t[i], err = polyByteDecode[nttElement](ekPKE[:encodingSize12]) + if err != nil { + return nil, err + } + ekPKE = ekPKE[encodingSize12:] + } + copy(ek.ρ[:], ekPKE) + + for i := range byte(k1024) { + for j := range byte(k1024) { + ek.a[i*k1024+j] = sampleNTT(ek.ρ[:], j, i) + } + } + + return ek, nil +} + +// pkeEncrypt1024 encrypt a plaintext message. +// +// It implements K-PKE.Encrypt according to FIPS 203, Algorithm 14, although the +// computation of t and AT is done in parseEK1024. +func pkeEncrypt1024(cc *[CiphertextSize1024]byte, ex *encryptionKey1024, m *[messageSize]byte, rnd []byte) []byte { + var N byte + r, e1 := make([]nttElement, k1024), make([]ringElement, k1024) + for i := range r { + r[i] = ntt(samplePolyCBD(rnd, N, η1_1024)) + N++ + } + for i := range e1 { + e1[i] = samplePolyCBD(rnd, N, η2_1024) + N++ + } + e2 := samplePolyCBD(rnd, N, η2_1024) + + u := make([]ringElement, k1024) // NTT⁻¹(AT ◦ r) + e1 + for i := range u { + u[i] = e1[i] + for j := range r { + // Note that i and j are inverted, as we need the transposed of A. + u[i] = polyAdd(u[i], inverseNTT(nttMul(ex.a[j*k1024+i], r[j]))) + } + } + + μ := ringDecodeAndDecompress1(m) + + var vNTT nttElement // t⊺ ◦ r + for i := range ex.t { + vNTT = polyAdd(vNTT, nttMul(ex.t[i], r[i])) + } + v := polyAdd(polyAdd(inverseNTT(vNTT), e2), μ) + + c := cc[:0] + for _, f := range u { + c = ringCompressAndEncode11(c, f) + } + c = ringCompressAndEncode5(c, v) + + return c +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation key. +// If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + if len(ciphertext) != CiphertextSize1024 { + return nil, errors.New("mlkem: invalid ciphertext length") + } + c := (*[CiphertextSize1024]byte)(ciphertext) + // Note that the hash check (step 3 of the decapsulation input check from + // FIPS 203, Section 7.3) is foregone as a DecapsulationKey is always + // validly generated by ML-KEM.KeyGen_internal. + return kemDecaps1024(dk, c), nil +} + +// kemDecaps1024 produces a shared key from a ciphertext. +// +// It implements ML-KEM.Decaps_internal according to FIPS 203, Algorithm 18. +func kemDecaps1024(dk *DecapsulationKey1024, c *[CiphertextSize1024]byte) (K []byte) { + m := pkeDecrypt1024(&dk.decryptionKey1024, c) + g := sha3.New512() + g.Write(m[:]) + g.Write(dk.h[:]) + G := g.Sum(make([]byte, 0, 64)) + Kprime, r := G[:SharedKeySize], G[SharedKeySize:] + J := sha3.NewSHAKE256() + J.Write(dk.z[:]) + J.Write(c[:]) + Kout := make([]byte, SharedKeySize) + J.Read(Kout) + var cc [CiphertextSize1024]byte + c1 := pkeEncrypt1024(&cc, &dk.encryptionKey1024, (*[32]byte)(m), r) + + subtle.ConstantTimeCopy(subtle.ConstantTimeCompare(c[:], c1), Kout, Kprime) + return Kout +} + +// pkeDecrypt1024 decrypts a ciphertext. +// +// It implements K-PKE.Decrypt according to FIPS 203, Algorithm 15, +// although s is retained from kemKeyGen1024. +func pkeDecrypt1024(dx *decryptionKey1024, c *[CiphertextSize1024]byte) []byte { + u := make([]ringElement, k1024) + for i := range u { + b := (*[encodingSize11]byte)(c[encodingSize11*i : encodingSize11*(i+1)]) + u[i] = ringDecodeAndDecompress11(b) + } + + b := (*[encodingSize5]byte)(c[encodingSize11*k1024:]) + v := ringDecodeAndDecompress5(b) + + var mask nttElement // s⊺ ◦ NTT(u) + for i := range dx.s { + mask = polyAdd(mask, nttMul(dx.s[i], ntt(u[i]))) + } + w := polySub(v, inverseNTT(mask)) + + return ringCompressAndEncode1(nil, w) +} diff --git a/mlkem/mlkem1024_test.go b/mlkem/mlkem1024_test.go new file mode 100644 index 0000000..6a25c7c --- /dev/null +++ b/mlkem/mlkem1024_test.go @@ -0,0 +1,336 @@ +// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "io" + "testing" +) + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-keyGen-FIPS203/internalProjection.json +var kenGen1024InternalProjectionCases = []struct { + tcId int + seed string + ek string + dk string +}{ + { + 51, + "2B5330C4F23BFDFD5C31F050BA3B38235324BF032372FC12D04DD08920F0BD590A064D6C06CEAB73E59CFCA9FF6402255A326AEF1E9CB678BF36929DAFE29A58", + "B24174A5B00DBF520CB9E3783D482E03CA3166D40D082A7BAB664845B617ADD98C07C6A3C3204F812035683A1D4F10AC1DE764706CCE96D75832A462A8B1B82CF471CBF808A26508D9FAA543F2CD980377D6F794367938064B6DD388C529756963B12CA594B306AB3F0EBBB6ED47601AEC1074A2B0D1B41717F8C695F174EF734052077E53DC9F47316B6608B7EDB54EA3631A36BB8D842CAF27367C15C18DDBA7AFE62AA572133384CB872555948A165F204C2CB4503C9D8ACE5F5217194A67EBD2A72F99562F98B2B773B6965C071BF11DEED395BC153ED6967492D88D5EF32D05E008B7958A2F36B9DB06CCDC52A626417966B19C5079A9892BA426A0362FB18E7EC07FFDC0C5DBE264DEEC41BC83A573599A25A8909B2476682336CEC9A396201A000A9C1BE792D9988FBC03C24855C1B242AE27A127CD36415CFB0D98CCBB9BD83AF5988D743773ACEA4201AC935E7CCC46E92D8E84A9D70CC626965429450980E0782EE11CD91169CE94BF89C14EC10B900EB4B77129A859039AD1253E5657B261E9855BC4B0F504C478D58F4A388215A0715168109526C2DAC555007C66E10B79EB92471ACC9A640805BD947EEA45B66A2C02D5B7C9029945B581666D470CD2654EB51C3304AC52CB11382B8628553B63742C41B293467B754DF8F52A7B9A5EF2359BF34025A9318DF92B1471EC529E895814999C551356ECC9CA945641067B16AC04B4169CC2F8A3B9A005047AC1AA4DF27FF284C7AB19A800629B5A85B06834623AC018D2E4CA1CA197D7B458C185BE0BA9B4703811F8628D13B690602544BD69AFAE215F5BDB51315CB570D48D8B55120D50560E59594C8B5603FBCC7E484EAF591070865FADD495C4D67BFC67CF1CB94ACD50C8970A226C0BC0FFA186BDC51DD235AC8D085F348374C88C6206E147233104306916E30635F0AB1B956644E02A4EE2BCC3246088D3D570A617A63150B32BD50DAA555DD6BB8B6D34C0CBA83A41C23B48B39708D7578D33C5A60AA28430CB10BA264D158ABFF301A00732BD95539190436AD02F89315CE921499C04305D81CFB7997ABA4B2D65639A34901B67972C9A2355B5A10E91F955E0A95FF33A21FA4A8B8E018EA6E43F34462DAF95A29DCC719C2B7EEAE50350497006F6AC53D28FCBF90845B02A8846C58D6494C0C603283476AF121767A9600C83A9E92B8D7B19C2680B1948044B807C9EA7EC94BE30CF3AEA676D16159FA3BC97C055ED0A439AF5C2F085BC0680A5DED6820843C3D3F65138E5ACE48C7A0C11037161674826084FD9BA3113223F18564A9B8DD98472D1837AB85B66E2C2638A4A8D9DD6C361BA6B86629FED9B6E76DC75B258908B750117E8A4763A1C0C0BBE5E2616C5D55B60720371F06D63893061FA6B3C76A600179B52F0C319F47A10189A0F566A7292C40CD9980C0112F265660C3B4F60736A9693454A7B861F834D35B838F8A647771630BCBC1F23F6AA887CB6CBD582CC2240CDBA864167506A43C434C27EAD754484D232A83BC40C2218306816030A20765A5470217E5B2C90846344CD8BC2C498B770E83CD1959DC152BCCB4ACEAB36ABF7F3618C1247F5DB201310C0A1E574FA547C398CC5A44AC23685B64D07B8F6614016C4271B71C650A8B520A5AFA08BA04EE51646B8A7FB652D3AF57A4010558F856FACC9911E09B332C5870EDB61E8E850BB1949AE5755C1FBA2CC472073029FD6542BCDA71B7952BCF811608F2A88291A04F2664BB1112E27B1235B16C19203214E79145A318C5110152DA36BB21ACA73801185D6B8B1F6A344B33B3E550FB5DC29BCD19846E84074BBC8F7580CD347B52F12A7D1616C55A2CD1F5C4AA86702DD530FA05A641302A16127468E85522E567570B65FC3F834D85A20973A0EB2B210D5812CFDFB881CC3AA06AB4CB77BBA7CDC6BCEE53C020B3343B9BD9A8298AD2055EE1A6070849140A86288721815C79734773F4097CF4741CC9635A30C8100B20733D3980B350393FAAC4CD0E66E4737C681DBBCC8D7953FA77454C422AD870E278B1121C33C8273370E5B686A6AAB17FCA3B67A3040D6491241BF5E3072FBDC1E422413FA5B4E9EE91EB3C936E21A5C7856942F44A55670A688A69E91A9A80BF4094C301DF705334DACB0CD929473829F34F37947D22B55C062F4CCF703EE3C1CC0BA7316DD658E6B6E0815F8FC9A70B1BBA325EC32E2930916", + "F7517C1258A61E1936F15B11DBBB33A7F7733CCCB2D9493CBF29945C751E2519347AA3894541CC2F1010D590447872547238C2FDEA5764BA0D098030962A0EC6BB816B077FF4EC2EF1E6B4AB45B21DC99EA433B4B8F4CB36A41D1497A06BDC43CB79AA0F739E6286AFA063B908138FE837330E48A47A097EFADC3AACBAA059B320B2B81DAD5A9C27B24A66D74B3AAB48E6E355AAF18074DA3E9BE2418CC143388153DF649572036ABCA2096C85839E034D34FCA6B9530B07E80534479A3BB6B7B09922FB4058A20B2585A74C45C14FFE076BF8D40CF5C922E14A3D34325B5909729C870B36BCC17964590E51CCDE66B1425ACFAFBC5CECD10FB168005C04C65654B7574150F18C2362F18136A09CF12029BA371E008029B4B631021A252E756E3BC40E41030A3A2A52205A917EF4B0A66A8279A8B67309A2D35460085BBE18F221BB72409F004A894BA1AE2A31141389FB963DB3F0C0DA0A179FA30926DA2D817AA6FFF02BF656716AA87F7273B747741CBAB3ACFC112D3942C193DC32A31A2A0425854206B51807C960D93D6E361A5F29A6F15A417FD01D25E264C322C8696369FF5397EDC52D6D388BBAD6206729BA14D7855DF4424AE9C48AD11A19AAA2F8FBBE50BB6AC1208385E80C740C916AB540504758F7879B0D0B5B36C0053E564E15017E563966EF0162966B0A6A330A4079C9D01A89B43893E38C4064A247B1F98627EAAB80A1603A43C01FFC5D63DCA4DAEA501379CAAFF77BBF0C238B091318548168F4033B83A2D2C6026AD2973BA43C01914B27DC248805C762D980E7B427252B222DE59698BAAD02936E1135704CF80F531C13FB9828D1199762794B69762BFA3134BCA20AD4F799CE0B5F27586654D981598172E191B4B556457B27C1CDE17B2A9A4752466439693622C68DF2608CA84B86BC553CCCAB9D9C842387B8764C946917E26409DA1C92122273AB6124784D8B1475287A3492E2A90D770BDD116BF0FC0460C56A07D86DB686983A052D7E01392C5B34F8DB41D339808D2A9AEF44C845A6B67688AB74FB1218E7AF9D172DE406BE0A32CB40710FC33098B82A21AA719124EA3D12A28E87043AFE9B56CDC58534BB38E497512CB939CC3CB4D7D3ADFB2C9F4DB11100D76A77BB2CFAD308F3171AD0478411009B11E964D1721A1225A90A285073445B9E389F72B46AACF08EC5DB33F7E038D4CB4C8FF023A5D8BBFD543BED672ED38927807B285B0C3635A1BF554729EA595595C1A1718122821C446E72BA7C5C95F3CAB365B27CE4A283C745386F260320F56815A9704F4C2718D89F60754BD65434A65063665B723033AD8E2213B1562EC9296A370B0C8D5497691718BFFA1F65E26B3709B7A632945B592FBEA90588B0BEF3877630212DFE76792B40A0B4E173161AA641119FAF78905A7C3E59E3686A0B1654E48AB9C22D18BCA456D553DAB3436EE677AB7AB9ECE7416B7A90921A8742A9269742616BF22572A2C8FABBAFCCD1441E87C9AB64593AE50A5C9564834A08851462AFA41A3DB586D49835CBB8A5CF5950B85ABE5D1A64F8C82129AC488000474E5386B3429ADE528345582A8185010DC39D0DA6873D29BEBF3954A56902AC623ED9577103164A839B89BE158CA639C1EF483902A3A1AB1513CCEB62DEF158D0078593321EFFA057B0A17C854956A73C48D55C3B1511C4B43316C95C18FB784452DC4D2C904D30206F17B4644A755344462A034C3D55F04DE42843CAD248E256C2616A98D61024B1D9547C86B37625484C3373E0B786B1220C7EC1A3E75715277C014AC81FFC7A80135B90A9D65DFBDCB03050756ADC26949B1644BBA4EFF133BECB67FBA996C4958DC49C5824B8824433AECBE468DBFB3D46289F65B6528204BE31A8A22B828EA30766CE086E380A6A94EC119C3AC9005D2B8BE47AF0FA2E7C008B06C7CA9827B7E6A247A75717954726E4D96EC82B7EBD469F9C646612C2A785EC3EF1CB5CD5A76F7D006418B19FB3C8254A381320984D03F62244C72A55AA19F60A63EE0B900BA56287235D1D480E752AC905A57D3C99B7C3210E103C50FEC53E430926CC108F0240CE4E3A5116645B5BE79F03AC0D378B00FDA6206C5C5CBC551851243E8EAC7C7B28AA13592FF38065D6F8B73A66812AA415CE44162476008288AFAF1A02B24174A5B00DBF520CB9E3783D482E03CA3166D40D082A7BAB664845B617ADD98C07C6A3C3204F812035683A1D4F10AC1DE764706CCE96D75832A462A8B1B82CF471CBF808A26508D9FAA543F2CD980377D6F794367938064B6DD388C529756963B12CA594B306AB3F0EBBB6ED47601AEC1074A2B0D1B41717F8C695F174EF734052077E53DC9F47316B6608B7EDB54EA3631A36BB8D842CAF27367C15C18DDBA7AFE62AA572133384CB872555948A165F204C2CB4503C9D8ACE5F5217194A67EBD2A72F99562F98B2B773B6965C071BF11DEED395BC153ED6967492D88D5EF32D05E008B7958A2F36B9DB06CCDC52A626417966B19C5079A9892BA426A0362FB18E7EC07FFDC0C5DBE264DEEC41BC83A573599A25A8909B2476682336CEC9A396201A000A9C1BE792D9988FBC03C24855C1B242AE27A127CD36415CFB0D98CCBB9BD83AF5988D743773ACEA4201AC935E7CCC46E92D8E84A9D70CC626965429450980E0782EE11CD91169CE94BF89C14EC10B900EB4B77129A859039AD1253E5657B261E9855BC4B0F504C478D58F4A388215A0715168109526C2DAC555007C66E10B79EB92471ACC9A640805BD947EEA45B66A2C02D5B7C9029945B581666D470CD2654EB51C3304AC52CB11382B8628553B63742C41B293467B754DF8F52A7B9A5EF2359BF34025A9318DF92B1471EC529E895814999C551356ECC9CA945641067B16AC04B4169CC2F8A3B9A005047AC1AA4DF27FF284C7AB19A800629B5A85B06834623AC018D2E4CA1CA197D7B458C185BE0BA9B4703811F8628D13B690602544BD69AFAE215F5BDB51315CB570D48D8B55120D50560E59594C8B5603FBCC7E484EAF591070865FADD495C4D67BFC67CF1CB94ACD50C8970A226C0BC0FFA186BDC51DD235AC8D085F348374C88C6206E147233104306916E30635F0AB1B956644E02A4EE2BCC3246088D3D570A617A63150B32BD50DAA555DD6BB8B6D34C0CBA83A41C23B48B39708D7578D33C5A60AA28430CB10BA264D158ABFF301A00732BD95539190436AD02F89315CE921499C04305D81CFB7997ABA4B2D65639A34901B67972C9A2355B5A10E91F955E0A95FF33A21FA4A8B8E018EA6E43F34462DAF95A29DCC719C2B7EEAE50350497006F6AC53D28FCBF90845B02A8846C58D6494C0C603283476AF121767A9600C83A9E92B8D7B19C2680B1948044B807C9EA7EC94BE30CF3AEA676D16159FA3BC97C055ED0A439AF5C2F085BC0680A5DED6820843C3D3F65138E5ACE48C7A0C11037161674826084FD9BA3113223F18564A9B8DD98472D1837AB85B66E2C2638A4A8D9DD6C361BA6B86629FED9B6E76DC75B258908B750117E8A4763A1C0C0BBE5E2616C5D55B60720371F06D63893061FA6B3C76A600179B52F0C319F47A10189A0F566A7292C40CD9980C0112F265660C3B4F60736A9693454A7B861F834D35B838F8A647771630BCBC1F23F6AA887CB6CBD582CC2240CDBA864167506A43C434C27EAD754484D232A83BC40C2218306816030A20765A5470217E5B2C90846344CD8BC2C498B770E83CD1959DC152BCCB4ACEAB36ABF7F3618C1247F5DB201310C0A1E574FA547C398CC5A44AC23685B64D07B8F6614016C4271B71C650A8B520A5AFA08BA04EE51646B8A7FB652D3AF57A4010558F856FACC9911E09B332C5870EDB61E8E850BB1949AE5755C1FBA2CC472073029FD6542BCDA71B7952BCF811608F2A88291A04F2664BB1112E27B1235B16C19203214E79145A318C5110152DA36BB21ACA73801185D6B8B1F6A344B33B3E550FB5DC29BCD19846E84074BBC8F7580CD347B52F12A7D1616C55A2CD1F5C4AA86702DD530FA05A641302A16127468E85522E567570B65FC3F834D85A20973A0EB2B210D5812CFDFB881CC3AA06AB4CB77BBA7CDC6BCEE53C020B3343B9BD9A8298AD2055EE1A6070849140A86288721815C79734773F4097CF4741CC9635A30C8100B20733D3980B350393FAAC4CD0E66E4737C681DBBCC8D7953FA77454C422AD870E278B1121C33C8273370E5B686A6AAB17FCA3B67A3040D6491241BF5E3072FBDC1E422413FA5B4E9EE91EB3C936E21A5C7856942F44A55670A688A69E91A9A80BF4094C301DF705334DACB0CD929473829F34F37947D22B55C062F4CCF703EE3C1CC0BA7316DD658E6B6E0815F8FC9A70B1BBA325EC32E293091688A8FD05CD6DA066D2BAB105299B3EE66605BD5A803760AF56A6033CB9D3B9240A064D6C06CEAB73E59CFCA9FF6402255A326AEF1E9CB678BF36929DAFE29A58", + }, + { + 52, + "D19A3DBAEB91EA7B11C5C7AB16F7A5EA6B4CE44CB18462B9695571B724EE13AC7A98EA318AD1E2705F20B0740794C3E711121A00600D00BCCCC78CCE024A9BC1", + "FEC8058988374DCA0726E72BB0237C5E59B7AE13C1DC34187062AF43877016F6272E370C1AAB06E12C1C18ABB01B2012F0E79849B802DFB8A4D713991D1575D301B605E048DFE96ED7759216C4BF379B8B6605603409210A809F01EA4A8C83ACE95C7EA6B6917D67810E22156C94705D1004D16492552713917830D9FB1571AC07264354A6A1B295221B7674138E011D27B4448A8822B7600DDF2B1CB2B6622BF506D19061C7A68661329AB095A525CC415006BAE29CBDF549423411B8A2113153155B3F12169952C9300A8C45A942DBDC9F27AA45711A26CDF702BCB3130E48A23EFC0BA9CA12F5F907B0274C3AA23F129A231E1549106204BEB88D74E35120568562D66140538B61520A8ABB410669B290A3B15249BDBE14A962144FE579572F109F23F04A54E87A79E74199717FBA90213CC38A6E854ECC14640376ACE0AB8D24799115B54CCBE4A3ECB51A7EE101921BA1F9C374D90188BE79016111108999AF0904CFD221496E6991780B9C50733021D5844E4207CA8227EA6C6F8B14B92538CC6192159031B53F9896A239A5F3954FCA6C3B15A84949703EC720462371985B13BC12945824DB5CAAD17C92CB97C9048C83854113B0112FC641CE1157872703B519276AC50D2BB146A85A41A529137386AA0103BD7E5856CA06950A398B07B974CBD44030559B8C9CA1BA12387726278E09C9CE5A201DF6A9B0811249A6B7914C45D6B3A8ED7436AED3CEFDAC91EE8568988C0BB61AA7A4C6B245B6478718A90EA007E4D9C1E4654AD8BC6DE7F0BB5A7695C2076EB9F56E4C338ED02229719A9D8ED53E983B6A797372D9478D1534AC10B92373CA240D5060136356D72A5296A88F78303199F28EB635BF33B9C92DF074E2B9670EDC86E46380EB98BB6EBA5FCF282425062A97A579FAE6BC86C92931F531B9B98EA806671AFAC44EDB65EF7B4DB4894AA2B64052E30346E80E9E34C567E36361129281A25524BB6FC865A2BB0076F5CB7038C2B9AE6BC7012729C2476887467DA5E60958C384452703C8550413C141CEF9882F37C893664DFA32232532C431DAB69A3767A3810E3F778185E396EFC8B02DD3158A0354F5499783A01AAD4455D9915AC1D20F8AB56037356AB7C51DF4B52E3F6B3EB713A3FEE23888FC9D9096AA9E4884F4E9BEA8F33BAB6BC6D8764C88D84B9CDC88AED4B335660823E14FC90467B995094014A2F5FB6A6DF76B8011BACCE86450CC46FC6779E763BFACE069E5327F994618EA23AFE05338F02885FB4524E2D47EDFBBC890514165F39FBB72286E8CC2D3624655F194812B790D714A2E833B82C4037EAC8698ACADC3B2A2815C15329707F205BC3B7C71D864839BF406F24B9371C9A63664C39A03202D630A5D1762C16642E874CCAEB90E6F312822085DB8B7831291563C1C4A4131543C014EA8F3092AC2877CFB72B67AC1493C4439379306F601A2ABBBAECAB12CA83931C8363EEA04043790DD429D16874C4EE0351B482C85C893C9C9363FB81A4AB8C49426B8396A1D2AB331E7A4B5C20A5224E1791AD9059F78778517537C4115D9F7609C956765DA280A03864582C9355584042ABBA986B3703248F2C183F27560E265318F3A7C6395C8CD35B515661CBD697039992627E4404862469592B71D9069A1AA8F72176A311A241AA9493CDA2F0B5373BA78345E11B396863892EB5F13DB117E1B12162A082D53789005164D082CF2227956C3B7D9F61494859A7D255E9A415F878CBACF73606F03C6E3D92C29D261E96C4A71AC1C78E93E47E2B803883E1E2B1CBD89159D95A979F72DB5BB39E41666F4E255453B3977361809A10F700039C1F3C1B503AF2E3639DE78B7E309B7310A8E5C9817CD963F1F38C1F5B5A696384F54307E9E60C45B2C14BFC9BE2172907B15068D918EA919626484A12F3C865DC2C7C7F30BF6FA8C3C80992ACA71F1C8A10FE5C586F68F99D2BA5F18C1125A11EBE4C4A8B2B93366805547CA22D1C8E5E6446A77C5BF9170A572AFE74677711CB7A7E85C0D177E2C5017E6E9C028B34B93ACACE87172A4BCC61F30C9265994AB511D8ACC97CEC1B3CD66C477EB91CDA5896A09130FA69C9662B48C16672B3725BE5762D2B1C5904A08F161C27934AC37D4761247A618EB58737AB56B55A5A335704526BC4991433DC88020C41E21A206EAC92788BBAAB1F8D016DF4AB5C27AB85ACC48A190F33FEE0F2F", + "CC4B670E6ABFBBA78B4B20B58DC5027147398C54737D15CB0538BD326C80D8491EB8AC09C9470C67D1B31F098364D2AC0E3028410BBF01783A36202C9F305829D7A9BC3122F7D9990D28C239920FFFC66969F2AC43F38E8341869551811D6B0F5EF7B62498371C4666DD63A45F181E772113D81203348351B00956A9614ADAA04BAF62423745576F833B22FB95F2F67F0F92C67AB48C2BD9753B157EE4B8C44B2A5EFB8B70832511BB50196D921D5753962A2587484A66B1B791C095615EB84179B410BF7388506A895FC7B44F29B1CB50AE206B08BE3AB7C2B6B16FC466A8F8ADABF0C1330C67438018DAF169C5F6CC3DBA31C6C0B786B1AC083484A8E785337A009F082C316C58D2B105ED79211E360B015C770E1586812C500CDC0A2B791CA8995922C90A3017661BB15718B0A79BF69A41C94A2BB1BF9551CADFD85F308738D0361B37EB515B315546E1AA9949BC93F5ACD1365A26B1467B857F92E462C4D6402200C43A5BC6706C09F1550248696425373AE5A5BBC5E3C5D9B51E6738902C19945760C0AB64B6A09984E689C43F1C5BBE795BE1A0A72AB39EC5614814A409D5A82D148BAE4EF8AD3DA19441FC149EA6A658574472DAA76AC97E537A152DA84BE2A8B3F3E66FFAEC3EE9323BC8FBC8F80214A4D692C972421E1452630B8E687931D6DAB0996B60FEF0A3B801ACBFF2CF3E68A85664B5E2E85546041AA76758EC877EF1D2B376BA77329A1C1A95C64C8C2F23558B696B833BE912AE3C81C3D6896B662C2C1A79E914B8C4D3557E40557749C834B20B49BB50E1736D9943B9F8C4CD0D7C6C7426BFCBD05E9DA326ECF094D4F4754A003F3848AB7D83BF045A7B767912F90492A9847A9DD7098CDB4DF98A2C742668BC47B0597409AA11A311794D9CD7145189351FA9385E694B4C336FF482CD00FA35F8A0C670ECBF8406CA9082429ABA053E717700DC5F09E65748072CBB612C0B4261A9568E409958AD3BA77DF60FD55649FEB6C2EF7937611C7D3C6A8FA293A55F0B8CB5F1A5262B35A437B467132142431B908C77ADB09592F9CF58E428B21652DFB18A51E373640866FDAB7677E84BF1D84B733C258A812A0D82B841C232DD2460BF8003CE0990DF60A32E90783BBA556EC7802B3162FE98A070398E6B6A1D2EBC81B0D3639A817D353A349A4C4ED9CA8E8E357A2E081D5F9A8B17A711A1522DCE09822999349C6C7C92CB3058B516423CBC6D878A770BA695A54CF3E63911D62923AA07DB098E8DB8789E1720F8A5856468943309BFAD2BCB9E23863DB21D9901751DC0B612E123E9BB00C63152E601D007212E78763D949199C9A67A1526CE244276600533CE22B539F96E759ABAA9F11AFC75C1EA0CBCAAD0AABB3ACF34A258FEF020F5E3309E513A05A859694C605938ADB501B96179349BC037038B8B6FAC4700C38BFD5A3F439370EB0B28D4F0418FD08F604697BD7710BAC01CAA7BC75F76C01432045B2375E2A5B499E1A79FE511527279C6A17DFE0AAD9CA354970A3CB67AC584C53635DB672835936EB63DCF827EA762781616822E7C18FFF03643F8C1A4453509A492B91C80CF15A4DAD95AB149714372B6DE2741C88B296859B7B4D187B64B36508940CB415B0C187AF5A924F968579194BE19B637A1A359F4382A04DB2365FC6091F288D7C41031B64C8CC5825CC92C7A38964251B638E315BF85389483A3D750992AF99457AB6320E0B50116B59B039361685825BCA01CB1983BC3A48C43C73BB7C50E43B15F82521505708A2CAA3129486A36A793138C7E4B41DF019CC8BB1C55F3B23BDA2161119941D08636EA2AABBB2EDCA880E36362B1A597C8372602D1103F930362FC76B7561FA72096ACB75533E360758515260827AFF58E214C6B5B2304BC12A3F6B69093536E82C7A510411E54C64BEC75C4B25114F0784D34BB9115B77AB6348B8D4A8921D4A1AF4137A993586AB600C809C3DD71770422B1210AB915D5316E345692F17AE11A4139ABBDA35609902BC94DF50973441BFB789659BC3CE8206F0367230E3864B8F5621A3C25D31B31F8B531B0517D408BB1B97A045AF9686A1B80B329C97BEB14780BACA9CC3BC8C51C3F604062983C7961934C29011999BE803C47CEE4C52CF7996936B903F31C3AF62120251833E0B7687A8027378CFEC8058988374DCA0726E72BB0237C5E59B7AE13C1DC34187062AF43877016F6272E370C1AAB06E12C1C18ABB01B2012F0E79849B802DFB8A4D713991D1575D301B605E048DFE96ED7759216C4BF379B8B6605603409210A809F01EA4A8C83ACE95C7EA6B6917D67810E22156C94705D1004D16492552713917830D9FB1571AC07264354A6A1B295221B7674138E011D27B4448A8822B7600DDF2B1CB2B6622BF506D19061C7A68661329AB095A525CC415006BAE29CBDF549423411B8A2113153155B3F12169952C9300A8C45A942DBDC9F27AA45711A26CDF702BCB3130E48A23EFC0BA9CA12F5F907B0274C3AA23F129A231E1549106204BEB88D74E35120568562D66140538B61520A8ABB410669B290A3B15249BDBE14A962144FE579572F109F23F04A54E87A79E74199717FBA90213CC38A6E854ECC14640376ACE0AB8D24799115B54CCBE4A3ECB51A7EE101921BA1F9C374D90188BE79016111108999AF0904CFD221496E6991780B9C50733021D5844E4207CA8227EA6C6F8B14B92538CC6192159031B53F9896A239A5F3954FCA6C3B15A84949703EC720462371985B13BC12945824DB5CAAD17C92CB97C9048C83854113B0112FC641CE1157872703B519276AC50D2BB146A85A41A529137386AA0103BD7E5856CA06950A398B07B974CBD44030559B8C9CA1BA12387726278E09C9CE5A201DF6A9B0811249A6B7914C45D6B3A8ED7436AED3CEFDAC91EE8568988C0BB61AA7A4C6B245B6478718A90EA007E4D9C1E4654AD8BC6DE7F0BB5A7695C2076EB9F56E4C338ED02229719A9D8ED53E983B6A797372D9478D1534AC10B92373CA240D5060136356D72A5296A88F78303199F28EB635BF33B9C92DF074E2B9670EDC86E46380EB98BB6EBA5FCF282425062A97A579FAE6BC86C92931F531B9B98EA806671AFAC44EDB65EF7B4DB4894AA2B64052E30346E80E9E34C567E36361129281A25524BB6FC865A2BB0076F5CB7038C2B9AE6BC7012729C2476887467DA5E60958C384452703C8550413C141CEF9882F37C893664DFA32232532C431DAB69A3767A3810E3F778185E396EFC8B02DD3158A0354F5499783A01AAD4455D9915AC1D20F8AB56037356AB7C51DF4B52E3F6B3EB713A3FEE23888FC9D9096AA9E4884F4E9BEA8F33BAB6BC6D8764C88D84B9CDC88AED4B335660823E14FC90467B995094014A2F5FB6A6DF76B8011BACCE86450CC46FC6779E763BFACE069E5327F994618EA23AFE05338F02885FB4524E2D47EDFBBC890514165F39FBB72286E8CC2D3624655F194812B790D714A2E833B82C4037EAC8698ACADC3B2A2815C15329707F205BC3B7C71D864839BF406F24B9371C9A63664C39A03202D630A5D1762C16642E874CCAEB90E6F312822085DB8B7831291563C1C4A4131543C014EA8F3092AC2877CFB72B67AC1493C4439379306F601A2ABBBAECAB12CA83931C8363EEA04043790DD429D16874C4EE0351B482C85C893C9C9363FB81A4AB8C49426B8396A1D2AB331E7A4B5C20A5224E1791AD9059F78778517537C4115D9F7609C956765DA280A03864582C9355584042ABBA986B3703248F2C183F27560E265318F3A7C6395C8CD35B515661CBD697039992627E4404862469592B71D9069A1AA8F72176A311A241AA9493CDA2F0B5373BA78345E11B396863892EB5F13DB117E1B12162A082D53789005164D082CF2227956C3B7D9F61494859A7D255E9A415F878CBACF73606F03C6E3D92C29D261E96C4A71AC1C78E93E47E2B803883E1E2B1CBD89159D95A979F72DB5BB39E41666F4E255453B3977361809A10F700039C1F3C1B503AF2E3639DE78B7E309B7310A8E5C9817CD963F1F38C1F5B5A696384F54307E9E60C45B2C14BFC9BE2172907B15068D918EA919626484A12F3C865DC2C7C7F30BF6FA8C3C80992ACA71F1C8A10FE5C586F68F99D2BA5F18C1125A11EBE4C4A8B2B93366805547CA22D1C8E5E6446A77C5BF9170A572AFE74677711CB7A7E85C0D177E2C5017E6E9C028B34B93ACACE87172A4BCC61F30C9265994AB511D8ACC97CEC1B3CD66C477EB91CDA5896A09130FA69C9662B48C16672B3725BE5762D2B1C5904A08F161C27934AC37D4761247A618EB58737AB56B55A5A335704526BC4991433DC88020C41E21A206EAC92788BBAAB1F8D016DF4AB5C27AB85ACC48A190F33FEE0F2F09D07691027DA18E9497800533B1C0E26B57DB34AB40EEB9DBB282827564EABE7A98EA318AD1E2705F20B0740794C3E711121A00600D00BCCCC78CCE024A9BC1", + }, + { + 53, + "202326C5E2D0D9212B062E622BD3863503B357A7CBD5DDF9B586596409871A002D7AAF4EA98867A68BC98DE1676863B12FCCC1E5D07D16CF72CF4D4A7764EBB8", + "A14B70C6FC3A4B77556A222EC5050D387C2852AA3BD06058A38877A581CD4BB425A7190C11D28F1FE63630165AA1743380F77B194905F2023EC86444C51971337A113022122472394028C7E6B51D0DB4762445513ACC7579E2AD69EA634C21162F5B2F3EE29460E70753B397176853CF9A12922B55DACC0CD8067667B76D60042E915B2E9B0C0AA76A87F7CB477A56144A77647BE293F27191BA78BF201B3EB5502318ECC78B29C07BB3012B530395DA719DA70DBF28B18070B0E5FBB0B1DB6E0592C0176397E67C23DBCB9CB78731F3871A4346B5F8737ABE820E18FAB17CD781EB96899F10602C63BF654C0CEE287697D6B4D8AC1116D2831EA194966500B9A4730874A0A9C26956D0848970666FE315820BB5B07741C6B4542CB1B0ED412653C11DB57474F7B70A79A1749BC72E29EBC6FCF09811A260C931AD9782B2BCFA967FF58946F961D5B29151BA148744C128752605FCAEAD65C878F004388B13EDC7542AC094B2FAC8DA68233F5653D7405F403545878738AD6A90B41028A4E6C23D37A046EC53DFB49D94FC9751C23245F9C5D076B6697C3131125ED8C68BDCFA65DEFCB67037C342C7C41C9CB318BC0C23A28E4DC819163B4746D416A719017979ADA696631AA09E4795514F70B52EA65B569B2D0CF02E3CF7452166864B440726D23411607E3991332AA91287D62AA8743FDC28AC13AA34BC6ABB4331CF4600971FF093428817B030364E504F61023BA29B4CC545547F517EAC19C8EFA57087887913A5923C1A597741BFBC119383E90324728147A1251645847AD647609427F9862D2C870DBF655C138C5047C2AF1AA37986608A483717EBDAAE7DBBAE928A15B920C8EF5453CE1821369985F3AB77539A922214367E3A6764C72BD4B555F9155BB29C3F2F6AB77DA92211F45D77EBB58C421D7AE6AD634BA82B6534E5B1C561AC2C540C0259CC8A28492CB8453DC761C3CB2864DB546FEA749E2FEC06DEC0694FFB6590CC5C95A9C5A326C4C70330A07B5E07726D1F5ACCA90A2948194E047997856402BBD357D5D0ABA97CB910E393AA90068ED2A81E02A99D5849F5275C7D6C061BEB364E0B8FDCB2235472AAB8F88CCE557D1EDAAB38830857B60F18F328A4A2A75B281D7D377B0807B6D7A4997696419F166F13CB6D30D5403ADC0C40E0C635588E1608CF747573FFF2020FA612A625B890A840A993067A2A7B658C2AE04223A31244B9574CA983A8350578D3F0A2B1F86F78E714F25B9ED2690039840BEEDB03EB70474E627A589199E7F7B1B4794DCFF308688993087AC149DC2FE7C246C0A0733EE2B39A68A624F79E8BC8CD588917F6266E77E71DBADA77D4698EEBD4AE225BC53B7494C71581131040BEC4218BF6CAED437E7E815D71C95420D82A92305040CC9F24160F14C1859B55855E6178881202AD96CFA9D0893B4CA762C3475792AD49D792FA265DBAB62E95B532C8C35104B170B7A29BC6F6443F060C06B231C0A10FBEDAA5DD63B79DA8AA1563CF450C83AD0BA370CB5D787A0B84D64A456A9F14A06D7FB0893E2892C3E6BD011A6321E62F43184BC040B124E808C6684DBA6434B1745BAD85B5F80C26908800EE43C87787C16BD868C5532064138B0BDB367DB15D1B783859D8932BC4649D7019AC405F4C15ADBBF7227241618B6089A7410CD35A19BAD6C24E3B2B29F134A7F527CB88992A78A08A034B90146B4221BC50E634809B57FD67CB920704CEF590C336BC6194361413CC2D970B07F84247612A8796A5DD5C0BDB445596E7889CF28F954B1190599101B32DF8CA773869AC79D6C9B3082C9D51722BB5B328F674E61B0782FCAF08FC5223BC974A4A545217A1D24B3D10226084F0B978E39F53357988656EED1300AE7A31F8D26150AAC34D46A51D08B80AC4ACF4EA8002A5932B7B9F90E99EAF53CBE1EA8D8B1C6342C3A6CF915296A27869A425B6B20C2C534221799F267A628FF98C389514845C87C19C8A343A25835619EDA20EF8C46C6BF08FF052BA6179AC54824E65190A1662827C1C4CD7B253CFC884408796C607B44AA61D15E989EB479169E980DC920D89FA4F63329D62A4150B1BCC89A88688EACF91568F71A0CAC33138340B2BB31B4BBBEA750313B00F0634B1D97F1686826B12581D415E8A534C41962C15C272045995CBFF625F9D08C8E6848123A612A9C865BC2BC9EB0623E331C99B359E8C084310", + "EF7412A2D3B13CD3828AAB65A57A308FE675FD3420F9F3AACE358487ABB55AE8AA6D7BB7D326055F387A2C6546FC172285EBB672AB67CCA32EBD005971660EFB4330ACD5672610B6B010C57E242D8FB8ADA752A8D644A34630A655C11F439A9E66426DDB617602F84A46190F031AA4A1A168D943645D0A77C66921A5455B5AE91F2DA47FA99750771331A727AF74CB916E8984CE9440DC74671B3A609F828F8D983BC5F06512883221B5AA7F8812EB5A774B272689493DAFE89C5C78AF0407CF351453C8D1C4B3E076AE462882A4828047C5E55582DA512BC20B0E0D261ABB8080658B9A6BA294251C7CC266527B2B4BF1652566EC5A320812EC77870BFC6F7954C3BCC979FEF761E8ECC59E306D1EBC955EE93759609A43B23BAD38CC79C0968FDB1BC95575D3E2113CF940FE2C211B75373570CC826C8FB77BBDED7545F7F16ACD0703E15A513A9986286ACFCCC02897DA4B399852D3E915C7634CEE146B1CD0645F9ACC0AAB55941B69E00878CF93C2CDD96FD4C6C816A73ED236A298217761800B896829BEC212366258B6C12520E11CA9578655EC36F0281F6A456116801806AA06F0EC124811345C6967F7D21E8A950CE8D7047A28A5B892BF07416C4222824C821464063A4BEC5040C7ADE1A5520C493EE14145E73C8AEEF53BE1DA6DA8D1B73B1A1FE7689DE7521965944A61352C17C2A4FB506B2012559DBC5D87F74189C962C433782BFB1FCB02B3E7F85DF9898DBCF5AF804CCDE76CAA19E28949B910F91B3AD608A9FC707D481B3CA0C05DB38C9DCD6C8DC279CAC38A76D20461985C2F2C73B833122F52FC8B793A4CA2D8C3FF021B8ACC1379818300F08977154866DC5C85B5C03B17056432373461288F647BBD88C98CEB5C0AFB745D7A45A7397BABA1321710133F2CAD797B01A265166651537EEC5A1F349690E77CC1312D0F8456E2ACB1204281BD058DD2BA83FF7848535B60AC8B64E1D3AF46B62B4181368DF198B68A2CAA7A953C014AB7A40A9E789B17A62DB6AB39EB2BBF26433694A576041453DB63B92739CC318A9312644484E834DC0C6A59637A4D0CBFAF4A16FB73C5BBEBC787A02856D487807925C4054FE74787F02147D349AB5A0C1BD2B0950B7B92B8D1AACF371C19D98DC215CD429374DF85151764B24820BD974B9219592AD9262550AA65B27C60BFE63359FC195036387344940B9112903661C77C1C069972E8A503B53770DA419475746E6C8A3413B854F6474F02BB0A024A2B0106AA36041AAC94290FDB398567636437839C39012603355DACAC286B00B0248B4B9ABFB6C11ADDEC734613A87B65846BE1AD9B5649EE4B3770D42BF84241C8742FCBFA7A8F686913C4907459A57F81ADBE121097D5911070415D6B078A4A49218AA2FCB617880A061DE9B6B94B1F5C80A401A91C409439EF6891153A0AE6877F375A436327AD5095BD57BCACC9294389A31F959250EFDCBDA72C5DD5A8488AF73F08838B54A14E9DC22B9CD6725E803810B3B771872F00A4CFD9B1BF62D235B3E2B210B8A08E51C4EAB56C33D22951E1C08BA30C133B0774CC3CC5348521D492CA779EE98B1275A17CD7692FC843C17EFB29357185AD0C54C193140D58353AD73B74A8256D566173472854A2066B886C59D350F1605589B1914EF97FB34C2539C7995C5C759C5CBF589B263F839041A88C37933C251B15469B4DF5797E7B7B603078C063C996DABBCBA4CB24B29596FEC030E46BCF17B44E78F68FFF16B77709838B5C56D079CABE851DAA94B28D304DC251847F474ECFE72A2A66CBAE063BFB198D847C20B296008A822317B56665B2A15476CD98FC25834A4E1D424B717562C0D12B776148BD6B7D9BB36C676C2F88A6AE0CF1AE027ABC672729A6D45372D579F98C23EE698ACA08535189C21A9B855EE7B8423371E87B1099566480F15D5AB95BB8E9740C43AEBBBA99F4DCAC05A7B1FA901A9250147BA446D0B9718FA72ADB73640522586719C16E503E1B85CC315972F378CB9DBCAF8E8942FCAA1402A8AF888ABD7C253B74E829DA036B66DC33DE55B82AB606B8E6225C0C7D396C919FFC3357C781A746B590E0C66F7952A80879B34B88AC6663E6979DC3573BCF651F42840ECCE036DF843734704B7FD59A2ABC0C2EE63138206660980429824630729BA14B70C6FC3A4B77556A222EC5050D387C2852AA3BD06058A38877A581CD4BB425A7190C11D28F1FE63630165AA1743380F77B194905F2023EC86444C51971337A113022122472394028C7E6B51D0DB4762445513ACC7579E2AD69EA634C21162F5B2F3EE29460E70753B397176853CF9A12922B55DACC0CD8067667B76D60042E915B2E9B0C0AA76A87F7CB477A56144A77647BE293F27191BA78BF201B3EB5502318ECC78B29C07BB3012B530395DA719DA70DBF28B18070B0E5FBB0B1DB6E0592C0176397E67C23DBCB9CB78731F3871A4346B5F8737ABE820E18FAB17CD781EB96899F10602C63BF654C0CEE287697D6B4D8AC1116D2831EA194966500B9A4730874A0A9C26956D0848970666FE315820BB5B07741C6B4542CB1B0ED412653C11DB57474F7B70A79A1749BC72E29EBC6FCF09811A260C931AD9782B2BCFA967FF58946F961D5B29151BA148744C128752605FCAEAD65C878F004388B13EDC7542AC094B2FAC8DA68233F5653D7405F403545878738AD6A90B41028A4E6C23D37A046EC53DFB49D94FC9751C23245F9C5D076B6697C3131125ED8C68BDCFA65DEFCB67037C342C7C41C9CB318BC0C23A28E4DC819163B4746D416A719017979ADA696631AA09E4795514F70B52EA65B569B2D0CF02E3CF7452166864B440726D23411607E3991332AA91287D62AA8743FDC28AC13AA34BC6ABB4331CF4600971FF093428817B030364E504F61023BA29B4CC545547F517EAC19C8EFA57087887913A5923C1A597741BFBC119383E90324728147A1251645847AD647609427F9862D2C870DBF655C138C5047C2AF1AA37986608A483717EBDAAE7DBBAE928A15B920C8EF5453CE1821369985F3AB77539A922214367E3A6764C72BD4B555F9155BB29C3F2F6AB77DA92211F45D77EBB58C421D7AE6AD634BA82B6534E5B1C561AC2C540C0259CC8A28492CB8453DC761C3CB2864DB546FEA749E2FEC06DEC0694FFB6590CC5C95A9C5A326C4C70330A07B5E07726D1F5ACCA90A2948194E047997856402BBD357D5D0ABA97CB910E393AA90068ED2A81E02A99D5849F5275C7D6C061BEB364E0B8FDCB2235472AAB8F88CCE557D1EDAAB38830857B60F18F328A4A2A75B281D7D377B0807B6D7A4997696419F166F13CB6D30D5403ADC0C40E0C635588E1608CF747573FFF2020FA612A625B890A840A993067A2A7B658C2AE04223A31244B9574CA983A8350578D3F0A2B1F86F78E714F25B9ED2690039840BEEDB03EB70474E627A589199E7F7B1B4794DCFF308688993087AC149DC2FE7C246C0A0733EE2B39A68A624F79E8BC8CD588917F6266E77E71DBADA77D4698EEBD4AE225BC53B7494C71581131040BEC4218BF6CAED437E7E815D71C95420D82A92305040CC9F24160F14C1859B55855E6178881202AD96CFA9D0893B4CA762C3475792AD49D792FA265DBAB62E95B532C8C35104B170B7A29BC6F6443F060C06B231C0A10FBEDAA5DD63B79DA8AA1563CF450C83AD0BA370CB5D787A0B84D64A456A9F14A06D7FB0893E2892C3E6BD011A6321E62F43184BC040B124E808C6684DBA6434B1745BAD85B5F80C26908800EE43C87787C16BD868C5532064138B0BDB367DB15D1B783859D8932BC4649D7019AC405F4C15ADBBF7227241618B6089A7410CD35A19BAD6C24E3B2B29F134A7F527CB88992A78A08A034B90146B4221BC50E634809B57FD67CB920704CEF590C336BC6194361413CC2D970B07F84247612A8796A5DD5C0BDB445596E7889CF28F954B1190599101B32DF8CA773869AC79D6C9B3082C9D51722BB5B328F674E61B0782FCAF08FC5223BC974A4A545217A1D24B3D10226084F0B978E39F53357988656EED1300AE7A31F8D26150AAC34D46A51D08B80AC4ACF4EA8002A5932B7B9F90E99EAF53CBE1EA8D8B1C6342C3A6CF915296A27869A425B6B20C2C534221799F267A628FF98C389514845C87C19C8A343A25835619EDA20EF8C46C6BF08FF052BA6179AC54824E65190A1662827C1C4CD7B253CFC884408796C607B44AA61D15E989EB479169E980DC920D89FA4F63329D62A4150B1BCC89A88688EACF91568F71A0CAC33138340B2BB31B4BBBEA750313B00F0634B1D97F1686826B12581D415E8A534C41962C15C272045995CBFF625F9D08C8E6848123A612A9C865BC2BC9EB0623E331C99B359E8C084310AA4160315EA4DFA5FD51510A38B58E651C146580B75CBD45DBF7DA19DA6E48FA2D7AAF4EA98867A68BC98DE1676863B12FCCC1E5D07D16CF72CF4D4A7764EBB8", + }, +} + +func TestKeyGen1024ACVP(t *testing.T) { + for i, tc := range kenGen1024InternalProjectionCases { + seed, _ := hex.DecodeString(tc.seed) + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + + dk1, err := NewDecapsulationKeyFromSeed1024(seed) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKeyFromSeed1024: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + ek1, err := NewEncapsulationKey1024(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey1024: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + + dk2, err := NewDecapsulationKey1024(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey1024: %v", i, err) + } + if !bytes.Equal(dk2.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + if !bytes.Equal(dk2.EncapsulationKey().Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + } +} + +func TestGenerateKey1024_Success(t *testing.T) { + // Provide enough random bytes for d and z (64 bytes) + randBytes := make([]byte, 64) + for i := range randBytes { + randBytes[i] = byte(i) + } + r := &mockRand{data: randBytes} + key, err := GenerateKey1024(r) + if err != nil { + t.Fatalf("expected success, got error: %v", err) + } + if key == nil { + t.Fatal("expected non-nil key") + } + // Check that the key's seed matches the input + seed := key.Seed() + if !bytes.Equal(seed[:32], randBytes[:32]) || !bytes.Equal(seed[32:], randBytes[32:]) { + t.Errorf("key.Seed() does not match input random bytes") + } +} + +func TestGenerateKey1024_ErrorOnD(t *testing.T) { + // Simulate error when reading d + r := &mockRand{err: errors.New("fail on d")} + _, err := GenerateKey1024(r) + if err == nil || err.Error() != "fail on d" { + t.Fatalf("expected error 'fail on d', got: %v", err) + } +} + +func TestGenerateKey1024_ErrorOnZ(t *testing.T) { + // Simulate error when reading z (first read ok, second fails) + r := &mockRand{data: make([]byte, 32), err: errors.New("fail on z")} + // The first 32 bytes will be read for d, then error for z + _, err := GenerateKey1024(io.MultiReader( + bytes.NewReader(r.data), + &mockRand{err: r.err}, + )) + if err == nil || err.Error() != "fail on z" { + t.Fatalf("expected error 'fail on z', got: %v", err) + } +} + +func TestNewDecapsulationKey1024_InvalidLength(t *testing.T) { + // Too short + short := make([]byte, DecapsulationKeySize1024-1) + _, err := NewDecapsulationKey1024(short) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } + // Too long + long := make([]byte, DecapsulationKeySize1024+1) + _, err = NewDecapsulationKey1024(long) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var encap1024InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + m string +}{ + { + 51, + "39690870B765DDF5A0B7092773EC1911637E8A80307A865A24E3012FBACC0FD431D12A44B38CC685335C858714026187E8F641933291CBE0B678D78944B17D7DA605DEC011B8681E113701C0074553156EE11530A4B63B8FE886637B524DE2733D039591230CD90C1EBAB61E3A5762E46B4E6067C6C4B280F371A3D8BB6839242DD3E381B41A3624396C2C557D1809934A901D8091731A43A32DA32EC9FA860FFC4AD09022B944C654980D2F928773B5BBBE048A21DB3F67167EA7F2B156246014ACCDC24455F5D1B0F7171D29CAA43DEB1CE32513078B495AC09C9B6793497BB593570CF8AA2D1F7549E5D54FA5A339B4281C90D75A9D0186B7322129B90B1DF6A39892C60A613D55E4A46EC07B69B6AF96255617C77951958A15B71DC48075562A236AA325008909CF75993CD58AA81658750985FA593EF6C0400BA07BF119C7380B1D61751A4390A398B6205A6895A857A463EC931FC262D89B069D6CB3AC9BB4E5272F35CA612D8346B65427708186C9422A37332B9955BFBA7080454CB5972397839B5465219EC6024D08A46EC4B29A15249653F413FDA1C99E82C144E7BCD9E17C19C2C3D43C4792F91EA4D8487567392AF43A513480975959E50CCA55DC89AB0A19A313B1B7E6ADBD7A1477281BD128B2C0F867CCD13877248E5844882F48B878E536D5208716F11B75051033E9A30DE9AFBBA66305E1BA265686B29C3186FB3CA1952936FABD2EA931E0D74E4D849CB685BFFC38C9E29559DE6AAF2EB4017C543EB9D59732B421C3A5CB5781BB60F1A953F07D7937B3651305E1D46E15569321721E310494C2EC16C843B3B4D9831809065BDA8F4E5A17E80123312C065C03ACE623AF5589CE361C913BDA355A031E42C54EABA24AF3F6848E6C4932427AE2A09D25808BDF0288D4C245EA2973C261CB29A6AFB13C6058A3A19F11673A198F86A251113837CDA27F2304A1E3D75C8BB88E6C67A006CC63DAC90017B865DC0268EFC53E2CB75CC6F1008B34839CC80B26A019440C0D34132BC4690519131DE15434B730B3A2492F6EF6471CF1C5568463A3387102135EB1A8635F0A63E33176914B40CC2494002C0E19C38A0D97C0CA5632261519193030F33ACEC68125D33C5AF26A9F97B1B181A00E4E9144A2D4811D7654F9B958CCE126E030A7FEC045A0BCB82ED40509643F3F5736B652116A4008EDA65FA1895EC82390C2713B4303BB6E7B4B416429D8ABBC497B98907224D065B3DC5A017907BBF7079E08B770AFD059A31A30933580EF49B5C48B0ACE675697D6B1041992DB62246092376954980AA03DCFCB5ADEA36B0B2138AE349D5E270F0D83640BE9885CC9371C6490C1F39959C9A192E584DFF9CB2D8C2297B117B9D7627AC5B2D29335558718A5A7C34564B0C0A1105F0A00E0084ACA185C5A54741E4B4053A431713678F5951D3FE47E0719A082017AB73B892EF3C77ECB170BF386AB36C89876BC8AE67B0CB82EE42793D9D7435172043E8CA3A8E1A83197891121932A0AB2EF46789EAB0BEDF5A5707A4B10035BD331B01E298370E112443A48D96CA9503832E3495DD2E571374C1F57B6B9A0A074AA29736C488FE1D823662ABD3A379CA43007DE317392D332A6826ACE381AF4C82C31F7C781788C705C4E6F06B1488850AAE55B6872731570444DB49AC9A53E9635816C1C537D2430F922124317592B36A68685859EF334B2782A5072A661C55C39294975572319474DB4E33DFFDC5E88ABA83B6B8D468753C88BA898DB116CF0415DB41C17D346462BBAC4A44207D0C93A4B71C8D00D47B969C690ABB371C0BBE1A367A3046643C466C867E341805EB435F8316358F69E6A4976BB54ABD90AA963B52BDFC93BA27C833BF5CCB4A2CA5BABA12D70016ACB875582416BA1A82246CAE8489647B36990F9128E350407CB614C926E492BB6D78021E73CB54641317FF282B9436462183B9375A5D32879355A96215B31ACE09C93779240260EB8B974FE049407229B808B7818142C3A071CC5561F56593E7605346C63817FEB0B836ABB786077BC7A7E3511275C160BA9017C57774E9503048C29B959F5C9E354A8E137CD72C873AD1978F69324DC282504E0A221FB1B740004A63AA7A2D33E00BD0DA29962B8F30233C78317F22CE7D068F16C1722994225E800491E05723201D81BE63E10D05502E1FF54580492D7DC5682EBF09E85773B4E61", + "042C24A9415FEDEB4932F34EC4D30562112740E60221697FFECB24DA0888D71456CD7924E9669DB01511EBC3ABF6EC2C291146FDC90ECCC3A270BCA938332D1A09019CECC35F8C7F8FC419622185D5B734865C2DD4A634D081B098EA311A029169C973B643C062CC7E31D73876731DACF830955A7F73B9BCC9D6B6AC4168F747885D3A3C01CA4AEFA20EF5B55FB176AE90939C12002BA9324752AA2A1DF78F3A6834FEE50B5165A4AE0BCAAC06BCC6A99E94A69E81E65207F52AC11088ECBA901612CE8BA67788CBB69D137479F3A7FF2513FB8320CA946DDCA952D63A67A5A0AB295249C76169A5E09915BB7DFBF2C876C897463C2B9A973E5906C410D7A04FAA0D2813C48CC5BBF733C4A706139A1990FA9B1E78A98658364250837B34F8120746624AD67C2FD8C5D92C3A0FF300C094988CB077FA65547E55940357AFF18266CDF511E74069E4A85797564800D377ACE053FEA919BBC64869355F0618813E603C1E5B552D624D37924D64B83471C6186EF763E9F319CA41009133360D45CCFB3C8667D3ABF1EA7CE4393683EC9EBF88B3CD3C3D30A6BF66D831FE235CB7E95AE14321FDB76EA59345DDC21D4F6C5D7F5105A67C9CDD6818F07223EE97518FA666E526A0F489677B004705770E5F2B68AED90A1E69C54BD0A6901C766B3477C1DA346C95217F5B8A4475B2D6C0A7B2708A4F519EECD1A67B6A3765003C1EFA308479ACB7584B4D389AA0F3200578C2836AB4AB532686793BA9C078A1EC587755C6DFD5B7EDF41684A3465CAB3D0DE80E7B83C16E7953C7BC7A313B2DBB68B204EC7669730AB3ECAF610C9158230323A86A4B8AB0172A8F7E92825F8C7F1362CB4CA4A4ED1973C1D620120A638EF88EAE78C11F90B0560809591293A3DB5D5409CE3C38817D1C917EAB8050E8C69AA6C8F33178B01A9E6CD31DB60B15FB7445A4149264D984FB86AA5EB6A77F700E7F5890E1ABC2C715A45B23192BD2794ADA65BE8835F2DB9563B32D64B65957224DCE30726A6368AF7A3E016B7712A4671DF346173411EF93119B071AA19767C64474DD317174C1047CE60155967982E661BE441C9363A1CFC33C7CA7CE6A620CB13879D75C92E322C50C9C0C6A041492EB5ADA1B267FA0138B63A3EFD8A2C3F9330CE805F5516065230E2B9C89A2AC3BB0C231F0F8A55265CD143C22DB873FA90068027A3E21275431922FEC6AA8E5F544A3389F8CDB051EB849C5250D27D63D8F398DC2A172E8016FADB223D47B9DD55BC01230400995B3CFA51B16C903EB8B4E4A9704E748006CD1710925B75000CCD5724F3CD81BC68C9364E36BFC972842274DFAAA2D85816298C51B656092522C895B9B1B19399A1FB28B4534952D51C6EB16CE12A16838C64EBE942C90A06890C3246C1800C0F5633834932744B8B5478364D7272F80C97D5A0E19A37E6B182812A0604B5C353274A139DA2948397ED14286F902B5429B7FF5F4C84ABB3FA7E1A805974C89AA07C906109A73C7B1B0582967A2FFF137B289CA0AFA3B07C221750A7CC960B6A679B66AA37373585F8459A7E3725BC718382FA03BD433AFAC481BE5A09B6D2075DBBC9C4CBB92BC915090D503A5ACCAFCC2A360142765FA2599A55DC05A6A2D5300F863BFE7DB0574A102D76C1773A095F3828BEAB7B0B945B19D0C288294B6ECFA551FBA26FB58BA8FF39499ABAC74B990995592CCE5A2EC8625BCE26C4360875B65C1D1831A1854303CE780198551015C2C62F9AAFB70B1A15834D467A642B180BF99393B22774E0B3706421A7AE2CF41281BAE15891E429F74314BA63B02E0A661C2891EA947B7571CC47ED749D70B0595203DD7574AFDD6AEDF8CBE55FA5D495362DD534C37B926EBDA468A48CB701C0565A81994C4AFC599223AC963677C9663211D7B8698D533673E8356C4BB6F77B6CAD027395270625F0A80D5B5A881E61C9553C165F9CA2B309BA7B21CB516392955617F9884A75BC797F1098D226F821985077669753018841A9F290170252771D14BA8FB5BCDFB33AAA17B6032D3540A08B85846212AB0C40430A209A7244420B5A81357FF669A4944291AEBB50ECC3EA8962236D2C4F8CC0354074827D0A37EBCA11292A9D41A7F06D97C37C2541593AF33AB431935CB4522370651A784CC5529CCCA9296518BF87239690870B765DDF5A0B7092773EC1911637E8A80307A865A24E3012FBACC0FD431D12A44B38CC685335C858714026187E8F641933291CBE0B678D78944B17D7DA605DEC011B8681E113701C0074553156EE11530A4B63B8FE886637B524DE2733D039591230CD90C1EBAB61E3A5762E46B4E6067C6C4B280F371A3D8BB6839242DD3E381B41A3624396C2C557D1809934A901D8091731A43A32DA32EC9FA860FFC4AD09022B944C654980D2F928773B5BBBE048A21DB3F67167EA7F2B156246014ACCDC24455F5D1B0F7171D29CAA43DEB1CE32513078B495AC09C9B6793497BB593570CF8AA2D1F7549E5D54FA5A339B4281C90D75A9D0186B7322129B90B1DF6A39892C60A613D55E4A46EC07B69B6AF96255617C77951958A15B71DC48075562A236AA325008909CF75993CD58AA81658750985FA593EF6C0400BA07BF119C7380B1D61751A4390A398B6205A6895A857A463EC931FC262D89B069D6CB3AC9BB4E5272F35CA612D8346B65427708186C9422A37332B9955BFBA7080454CB5972397839B5465219EC6024D08A46EC4B29A15249653F413FDA1C99E82C144E7BCD9E17C19C2C3D43C4792F91EA4D8487567392AF43A513480975959E50CCA55DC89AB0A19A313B1B7E6ADBD7A1477281BD128B2C0F867CCD13877248E5844882F48B878E536D5208716F11B75051033E9A30DE9AFBBA66305E1BA265686B29C3186FB3CA1952936FABD2EA931E0D74E4D849CB685BFFC38C9E29559DE6AAF2EB4017C543EB9D59732B421C3A5CB5781BB60F1A953F07D7937B3651305E1D46E15569321721E310494C2EC16C843B3B4D9831809065BDA8F4E5A17E80123312C065C03ACE623AF5589CE361C913BDA355A031E42C54EABA24AF3F6848E6C4932427AE2A09D25808BDF0288D4C245EA2973C261CB29A6AFB13C6058A3A19F11673A198F86A251113837CDA27F2304A1E3D75C8BB88E6C67A006CC63DAC90017B865DC0268EFC53E2CB75CC6F1008B34839CC80B26A019440C0D34132BC4690519131DE15434B730B3A2492F6EF6471CF1C5568463A3387102135EB1A8635F0A63E33176914B40CC2494002C0E19C38A0D97C0CA5632261519193030F33ACEC68125D33C5AF26A9F97B1B181A00E4E9144A2D4811D7654F9B958CCE126E030A7FEC045A0BCB82ED40509643F3F5736B652116A4008EDA65FA1895EC82390C2713B4303BB6E7B4B416429D8ABBC497B98907224D065B3DC5A017907BBF7079E08B770AFD059A31A30933580EF49B5C48B0ACE675697D6B1041992DB62246092376954980AA03DCFCB5ADEA36B0B2138AE349D5E270F0D83640BE9885CC9371C6490C1F39959C9A192E584DFF9CB2D8C2297B117B9D7627AC5B2D29335558718A5A7C34564B0C0A1105F0A00E0084ACA185C5A54741E4B4053A431713678F5951D3FE47E0719A082017AB73B892EF3C77ECB170BF386AB36C89876BC8AE67B0CB82EE42793D9D7435172043E8CA3A8E1A83197891121932A0AB2EF46789EAB0BEDF5A5707A4B10035BD331B01E298370E112443A48D96CA9503832E3495DD2E571374C1F57B6B9A0A074AA29736C488FE1D823662ABD3A379CA43007DE317392D332A6826ACE381AF4C82C31F7C781788C705C4E6F06B1488850AAE55B6872731570444DB49AC9A53E9635816C1C537D2430F922124317592B36A68685859EF334B2782A5072A661C55C39294975572319474DB4E33DFFDC5E88ABA83B6B8D468753C88BA898DB116CF0415DB41C17D346462BBAC4A44207D0C93A4B71C8D00D47B969C690ABB371C0BBE1A367A3046643C466C867E341805EB435F8316358F69E6A4976BB54ABD90AA963B52BDFC93BA27C833BF5CCB4A2CA5BABA12D70016ACB875582416BA1A82246CAE8489647B36990F9128E350407CB614C926E492BB6D78021E73CB54641317FF282B9436462183B9375A5D32879355A96215B31ACE09C93779240260EB8B974FE049407229B808B7818142C3A071CC5561F56593E7605346C63817FEB0B836ABB786077BC7A7E3511275C160BA9017C57774E9503048C29B959F5C9E354A8E137CD72C873AD1978F69324DC282504E0A221FB1B740004A63AA7A2D33E00BD0DA29962B8F30233C78317F22CE7D068F16C1722994225E800491E05723201D81BE63E10D05502E1FF54580492D7DC5682EBF09E85773B4E61D4ED29FB6EEFC6279C24822EBB0FE9B69246931ED0467352B7E69E79F47CF6EAF36E9099A67947E97C78ADE6C5B36316336C025F16E7335BD81A3158D5AFC87E", + "FB25DBAFA2CD7DCE979BE90E31179FCCC24AED8A86D60C3B84DD6F701B249AFBEAEE926F05859F818CB356FAD2593B6340B6E59480D7E78E54C54D3178BDE122F0D3010BA5BF2B65468220088DF430E1974416E12BF5B3B3EACC2A3ABFC038460D83257A8CC7F4283C6B3D0070DA8C8BCEEB976C0D708D056F04C86E35B81E99162AC72618F3A39832FB90B378DC914E0732F3140049B149292C8D00ADDD9C0522F3BB1775074AAB4F1E279B5946E88D7C46CCFF453B55621817B8411E24BD0B90BDC275822A8164BA11F9477AE9B03DE7DEA010100B164E02A91F0A27B84B585CC48D4AB84B0A1727454BBE84B1E8B723C2BC7A4EFBFA657F08E7AD2670CDC497D534F307246265C89AB4066E2E1101282A328E00CD1B58E4D23F24CC364CD92F891F4815FFD974ED36C4D29E3C0C7F302F0E912E1080222134ED6C973C8635062F63A39762EC96C883A012F369566B62FA5AFC142E1E284675E7C1EB4744533EEC5DE95AB8ADACE27259DDF76553B482CDB83F1414C66307389F82A51B949AFACACF5E38D677456A8CA1806CC82E3BEA884EDC7B4C5ED66007B143F1A19AFE1A5540FA69F999FE86386FE1E83FC0E57423DF35E82749ACC856D3F05EFB959169CF03C9CB3D11D88FBA1F85979F213FC59F084B01F4FEBD9CB560C0F969F8F021C30644FC8721CD12E2E2BB0014B39D9FB4916FD38C6F8D9B02D67DCDA44332DC519528A86DD5161E8B4C100CDFCC58E3E05763E9FE5E69CC5D5C28E6D2AB24A4BF853264A3CBDAE0A72AA380D37CD0D306770D711BBA1D55A13EE7CA01702556B57BE5F002FDB6CE00DE00CEF2869092803CE44F4944A60CCA2B30978F39FA644A96A66156705EF1E473BF38EAB139AEC783230472CA49D071A817375191B891A9BF63723BF02830D1F443660B33C5EBCEBD8FE04BE4812409E82AD48A21C50C241CEED5126AB40148ADE08E12B381623E5471D173E2722DAAB9085915026088537EEAD054C7207FFA1B68F9CC75DC34D540B7933F78FCFE5728841AF808C0FB9D83F6A80EBCE6B51534370DB55E274B74D216C249759F80D9494EA1FE0AD058F057FE3C72759F5F787B35B13C3FBEC4B730244C0056FE694D801C021DFC1F179FF6BB8435878EAB8936FFDCAD68FFBCBEADD1BACD3A6922759DA01E72AD933F34D10F29D74470F70632A3D6DABA1B069DE9459D15E58AE12130E2414DF92C36DCBDFE2EC3EB17E37393035ED810FBB857D971ECD41347B09C5C8DEEAB060E2759549A05C37A2C93F30CF906A9C59991DA921848363CEB06CF9914E76E1635AA0337A540470C9B3E58D152FE692AB3A1AB5F3E5C72598428DFB9D46E77C32001CC1E942902A4A1CD94D526042D3C16AE79A7F440F06ACC331E445E47FBCEA3AD5B2D4237E320C3DF1FD16CBA99AD93B136D971F4D7233898F1ED5E4017E4DE5B2243E6ADD776C4591E28B6F4A5FFA4AD7D273708D2C7C82BA28A0D621E9A10FAE863750C8FCBABCCF3F1D92E9E2FBD5D3D44B6C4C1A1A73C34523356A8B8DB1B8E7E6AF8B9A02FA03C705DEC578D6ECE7F1AB9C9498FFB1E283142B258342B53959B6E60BD23FDA24A53447049A6F5BD525B6E12DD60BEB5CC7BA0E245EDAEA2489AC0ACABCDBA4C6113F35ABFE30D69332BB6B05DD1430FE9A6AAC0AAE856F0391AC30814075085F8B784439EDD1BCF55FDFA0142424F7A9E0A92142AF3979C3D608D2CE99B9F63F9B3D487471F75D2C43FFBD137487DD9D9A8931D42992261E67FB1C531BC40500711FB83B2AA84C308AD061BE639698F3F2DFDB72864D950F0888A961261BCA7C0DBB78B9BDFB8024A51E3488D87AAEF8FD703A859E538886F0DE88A16AC90D2C596622E34E2E8D9685F918B6BE86AB5E3BD73194A21C9A4DB53FE0C88E027D8FB0621592BA8A8748267CE6EBEDFA75144E2DE1C1B39FB158095F9FEA12772216A718E4729E583B1516D8BDDF13736946073AA71E72F9AA068DD6C3E6D2C7D6BC66711B94BD75AD558DCDD77A1950096BB3922A482FD03B8ED9BA53DEC0083A8B2192848CEF5092D50FF687623A2ADDCBA7D5E9B15759DBB535B6C1ABA01CFB857BCADC6506F0F3E73DD649BC83A05FAA60CBED656F545732E30A838F95C7879D63077C18584E191E2F844D44FFD63C9223D61E38136856DFEF610BD812DA0B8E18FF2465A0FE9F4658AF94ED948B13B4E710AF03D3620", + "5D537CD0EF7B58F0FE95370473B96878F138ECC259ADFBF77EBD7328B822D9D9", + "8199CF923CE12126920108569C11CBF97CF03F44AF5CFA7D550E9B2AC7431982", + }, + { + 52, + "6FB1B3514251DD374101E8CC62DC4C20899AD4D5722B1232A0B32907726DB6139AB65688FF9C28DDC1387F6537E2E8459C096176A31FF8FC091617006B1C68520AB464113FA33068484941B79922291CC14F123B49D7BC75902B4A1634A0990FCB6612C8D07F2AB8CACE9214BB9145CACBBCC6F548CD97AC4DF928EA459E81C24CE1F8C068653AA4A29B9D23A7A48B9BCAF99A2CCC0A5727456D84ADBFA79EE58AA6CFE4987A2B1D2E201615328FD5AA92FE3B22F23823E99B1F4AFB7C69748ABFB12468D624BC677319C87616071136A56014796863D51F3D913135877DFCE7651F75AB1BA1C00066578B5CC07DE612E19A54FE062182C4B1E19B1F0A4211A9DA401E12C0407B897F3776690A45EDC578830609E31B4AD9D5A51722A221E33E74D1B5BC64C5EEBA6643EB9B32E54A1D4B770DE8C201C291D2E7981E1B9BDA14BF2675527C70664A941E4F4805AF025A069C0368736899F0A2D5FA24D8672A701528E33817A189B1CE655333D84481903AA3441AE4257BE7683735CB98D039585F4C6D842321836875C1A78F247B3D27781826FCCE47F753C88A57A8F39E8766711AD5753C060B7AE5B7EAE85021041306A70960FCB5CD64A655D366D59B703B52A5B4102C5C78B298595F1D7A8A28540C40603F2B42B79052A456E114602A948DF4252C17CC66700728D6072BA44161B05238641EDA15CFF92C4C89C754598615619B4EB2838F64C657A278BF4CA12224717EBBA1A86350BFA2A05594A885B3E61D9CA4BCE5D6B632E214F3B12B658915D2E423FD46686F031D29FAAB04688388456CF58A0BEE7A73CD016CE8B6BE1B762F160894F6D61FAB48915E54B1F919031A293211F4CE07B54C74653991D4A76E2CB68476CD7B121FE7CB80DC44A092864AAD8A12C8D3860473B60A370ADD8B25E315A99B708F80B44A7D01BC3ED4BE7719BFCC5094B9CA470BC0CE9D1A45CC9925BF43B9FC475ABC973DDC153FBB5689D624198816143C054D767CC7FAA0848FA39D0F825233EB2422292587236169C79F2C71375AE43682FC52AA75C815184028FABBC6639BDE6BA41609C27290A195489C81C64A4129252BB056654CC67072BCCE967539554E34383AC56B3FF78AA8A9325BB9314B16795AF5F25486CA214A41B7C383655303CE73F49A831907949141C2944EC8415EE4666F4FBC85E67CA3F15C68B4D23BB0887C2DDA81353C6F21740C08DB187AAAA88ED5B5E618011913534A332DC74C810C47601A4953FDF26DC7738E2BA5A56F45366840BEFE36CA04D20125856F98F28830473203728C4B9038E5CA7291074508CC55997896DB9353CECA7C76003D91E2BD8AABA8EC706F6AF3C9C3BA8882278E6097BECF179946FC3DC31AC8EC36228F49B935A57B1D7B2C16068FD977656D7122737933E84B1FA54AC1792C7219889681985A16012B524B4A92E499B5284EBE093D4DAA8D90B04714CC62E10C688F838E9C028C4D728B1D1ABF9F4240B463BB588BC4BF1ACE20F2C244ECA941B2A491000927670350B81549517866E471184674EF13695770440EB1BD49F5513C10AF5702ABD291925BC6B514963980C33D06FC130BA90BEA70CDFCB9BE6C076359D82A7FE1CA8EDAAEB1DB3130150A6FC7AD894426108B4FD7624E43FC3DC41A98D2E0475F166EBC8A9BD43293FEC14AD1E19273430FDF877C59613EA5D82782212F6BB9886CDC3C51E87DFDEB236DC90BE554C9ADA48D1E21A7BAC91D91C84CDA669571582B4FC17CDACBAEA9B09B6A4AC66405254277C95DC957AB24AC91C65DDED3C05F9B08F979470DA05E652A0155C733452747B8946E8118AB4CF354492586062C56BD2C9AEED133E5BB62D7D7C960D00444BAC4C75452AE0A89807CC450889DA8D96FB50C31C23B05CB8C839B016D3D9800282C0A791705BDF6BD98D8B5BFA94C3EBCB40F764D82C090D9F148B9D47E9BC7905AA3A8B8F122BBC4660D832E4A658430F96423906ECFE6A9866AC134B1CAFADC8589B1327D1CBED20A2194DA90E696250692B5522ABD7A938B9857328501C2DBF05C72456560E5C925FAC4FAC2BEA33A6AB588381684244B58C5D3E7C9D1170C20FBCAE4D6415EF45D874CC301C3A15E11BBACE37082F632EBEA76D8323E4BF54A63880636E1997A47476E8172F8C014DF7B477A631F10EB610CA9D603B73F363AF20BB65D6DAEC6328BA1495D7AFF28D8E2B2", + "1A344F3BF32049F2774DA2667A562F8A0B477BCAC810B5C44C100C26A41D27C5199D8A99A635959CF9274E1AA3A7B3A871D262F6421C06784DCA9C5AF223214B23C16EC066D800CCEC9A1D8D3962A9109884101454CB91FA3798CA4C23FF213C207112FEB3AA5F5B71F8921110D041DE19C2691A8F6E8503EDF36829679CB57A7FE940ACE127C32602863E000A89A081AB22C8D6FABD514B7DD53928AC22855131547B8442AE2B1866E408386CB9231AA4FE85663243BBFAF00700C410E937CA9532AD5285CD5CE464C09207C2BC458CF5872246292F1BA25B851115D9A721F156730358FFD71CB25689352684C844B3830965D0182C22B7540584240AA36FBA0008548108AEBA3AB118B09C71356B688D415065FFC6875D914728CB5295450604B576FA870FAA5AA2B30C820CB25AF6C06836448C17FB8A2A4A15C5FA2937A5845AF3A9070B1E59A2CC36D21C1134B04ACC8C8F6B72B72981D96A912A422D99C659109514A1D280345361AD567600F3845361782E916D683A9320BA2C41922DB8FA26F1EC78A08ACB96333670D740E046BF02F50C8C56C4AB82B4EE2B7B52E751FD133AC16BC82FE432230CB3929B4B212749CD553107FC4BC5E81F3646645E19C34BAC99FCBC73FE273EA5DB0DBF317D2EF6AFC20A7A62D4BE7B8A6ABF2A1B6E5C6B809CB9957B6276D76246689A788154C68CA00FB43C701B35BD6B7C7AB275B1468FF07BA4C875699896838BD81627BA9C361C55FA2841B76515DAA2CF5E502740E4842ED0CEA45B5F69D8A29E37591D53BEDD1C69EFA41BFBA7AF98F231A61B2DC7A41F10A10D5F746F6A9A67E4E386A58090A90A0A057B43558852EBE4AC58228B298C0D7B832E6761CA34F0A6C5625C7E50967B32B62A933E32143B09D1AC2C574D96CAA291416AE0708CD3A56F6981C28A430112DB52678C44DCD35BE221C872C912E6EA0224F49A600B0BDD0441B144C7789C94799C0F0B522013EB9E55679DB8007EAF1A698FB2C50A929E531407F227A23A626DE96C0307583B29C8C3CC633744752099A1849714205DE32D0FFB84ECEC1B2A23C789F22704300C827C13B58C35032962ABAB106FC77250B812B3525BAA231F2617A2857CBB24FB418E41399244684B9817A77B3AEE0371ED900F39F547974971A9A4A5DF877CD0C731C412B5C22B0077A280ADF701188798ABB79B1D9B3FFBA14484933DC3E7335A45040EF7715F513BD6386D60742603CB369AE067637239C5228F5C538398F62DF7790BB73552A43A3125C97E0DA276D4212E3BD30811A19C59738F58D4A44DD99B478A2A2F704106B58A8FD1660C39B9000D4B22F31A8DCB9A288563CADAB37670333AFC50A4C1A422B8493497CE6A985FC23339A376AB8E04A1D2548D1EE4A840F1770DB2A98968C380619894802F64F713D9D3687E0458C05C2249F63CC2194400B9125FC28B34E685B4820FDA990F187156BC24610509561F0917E56277AA003BD270CD7D41311E66C4FD1A85E3693111897DD9E331C9C595E45B2812C226140A43B10829A9B78FFD166192A79FA566581337A3A38B2DAEB7012106CF7E228C41D32A0C5897FDFC793CAAB3DFF510751C100D65CA5F3C119C0B3B72B10EEFD615B1A79942D7B348991364B3373BB6C5BB86A13F4B7537E05223F397ABCB05F2002758B9BA23233269B8BDCC999DCE208ACA33CF0206954DF77361C89881307DDE5B1FC5796B0C45895A52CB79E87009D94859A44DECF82D4B9810E3BA9F0771A62FEC937432A662D1440F2AB0F549AC3886A9B02144CBBB5C16479328C8B86909AD9941A672E6C2E6F21259937E066AA498195AA2C4A7E7003F476301E3075ED3C47860503E589A487B323C0F414D6E2B53897B17D0AA521DC4A6307359BEB33F7717696BE0040B358555100E56C961338533E0D1AAA71B2FB09B0813D15C1CA39BAD6A320B5652A60125148798E76C300223B3A60A7E6E4649019641A98133B2C455CAC5249BB9437867CDC9A0AB0F76BDCC5A6220F2AEC76267DB2C37A78C26F2F99DDA86CC62976B0F76C60488B950765AB1B64B63497B35E61C4023A63B975C2993AF99F3790D063FB5C0BF2EE4119777663AA04E54E6BBDBF492B7596A05F5B108121D9CFB033E47C21EE54A4126867BDC076C2521DE79C6F26B8F6FB1B3514251DD374101E8CC62DC4C20899AD4D5722B1232A0B32907726DB6139AB65688FF9C28DDC1387F6537E2E8459C096176A31FF8FC091617006B1C68520AB464113FA33068484941B79922291CC14F123B49D7BC75902B4A1634A0990FCB6612C8D07F2AB8CACE9214BB9145CACBBCC6F548CD97AC4DF928EA459E81C24CE1F8C068653AA4A29B9D23A7A48B9BCAF99A2CCC0A5727456D84ADBFA79EE58AA6CFE4987A2B1D2E201615328FD5AA92FE3B22F23823E99B1F4AFB7C69748ABFB12468D624BC677319C87616071136A56014796863D51F3D913135877DFCE7651F75AB1BA1C00066578B5CC07DE612E19A54FE062182C4B1E19B1F0A4211A9DA401E12C0407B897F3776690A45EDC578830609E31B4AD9D5A51722A221E33E74D1B5BC64C5EEBA6643EB9B32E54A1D4B770DE8C201C291D2E7981E1B9BDA14BF2675527C70664A941E4F4805AF025A069C0368736899F0A2D5FA24D8672A701528E33817A189B1CE655333D84481903AA3441AE4257BE7683735CB98D039585F4C6D842321836875C1A78F247B3D27781826FCCE47F753C88A57A8F39E8766711AD5753C060B7AE5B7EAE85021041306A70960FCB5CD64A655D366D59B703B52A5B4102C5C78B298595F1D7A8A28540C40603F2B42B79052A456E114602A948DF4252C17CC66700728D6072BA44161B05238641EDA15CFF92C4C89C754598615619B4EB2838F64C657A278BF4CA12224717EBBA1A86350BFA2A05594A885B3E61D9CA4BCE5D6B632E214F3B12B658915D2E423FD46686F031D29FAAB04688388456CF58A0BEE7A73CD016CE8B6BE1B762F160894F6D61FAB48915E54B1F919031A293211F4CE07B54C74653991D4A76E2CB68476CD7B121FE7CB80DC44A092864AAD8A12C8D3860473B60A370ADD8B25E315A99B708F80B44A7D01BC3ED4BE7719BFCC5094B9CA470BC0CE9D1A45CC9925BF43B9FC475ABC973DDC153FBB5689D624198816143C054D767CC7FAA0848FA39D0F825233EB2422292587236169C79F2C71375AE43682FC52AA75C815184028FABBC6639BDE6BA41609C27290A195489C81C64A4129252BB056654CC67072BCCE967539554E34383AC56B3FF78AA8A9325BB9314B16795AF5F25486CA214A41B7C383655303CE73F49A831907949141C2944EC8415EE4666F4FBC85E67CA3F15C68B4D23BB0887C2DDA81353C6F21740C08DB187AAAA88ED5B5E618011913534A332DC74C810C47601A4953FDF26DC7738E2BA5A56F45366840BEFE36CA04D20125856F98F28830473203728C4B9038E5CA7291074508CC55997896DB9353CECA7C76003D91E2BD8AABA8EC706F6AF3C9C3BA8882278E6097BECF179946FC3DC31AC8EC36228F49B935A57B1D7B2C16068FD977656D7122737933E84B1FA54AC1792C7219889681985A16012B524B4A92E499B5284EBE093D4DAA8D90B04714CC62E10C688F838E9C028C4D728B1D1ABF9F4240B463BB588BC4BF1ACE20F2C244ECA941B2A491000927670350B81549517866E471184674EF13695770440EB1BD49F5513C10AF5702ABD291925BC6B514963980C33D06FC130BA90BEA70CDFCB9BE6C076359D82A7FE1CA8EDAAEB1DB3130150A6FC7AD894426108B4FD7624E43FC3DC41A98D2E0475F166EBC8A9BD43293FEC14AD1E19273430FDF877C59613EA5D82782212F6BB9886CDC3C51E87DFDEB236DC90BE554C9ADA48D1E21A7BAC91D91C84CDA669571582B4FC17CDACBAEA9B09B6A4AC66405254277C95DC957AB24AC91C65DDED3C05F9B08F979470DA05E652A0155C733452747B8946E8118AB4CF354492586062C56BD2C9AEED133E5BB62D7D7C960D00444BAC4C75452AE0A89807CC450889DA8D96FB50C31C23B05CB8C839B016D3D9800282C0A791705BDF6BD98D8B5BFA94C3EBCB40F764D82C090D9F148B9D47E9BC7905AA3A8B8F122BBC4660D832E4A658430F96423906ECFE6A9866AC134B1CAFADC8589B1327D1CBED20A2194DA90E696250692B5522ABD7A938B9857328501C2DBF05C72456560E5C925FAC4FAC2BEA33A6AB588381684244B58C5D3E7C9D1170C20FBCAE4D6415EF45D874CC301C3A15E11BBACE37082F632EBEA76D8323E4BF54A63880636E1997A47476E8172F8C014DF7B477A631F10EB610CA9D603B73F363AF20BB65D6DAEC6328BA1495D7AFF28D8E2B208E72174CE5EB6449EAEF0C1EF53DB13666BF71EF79A6A0459B8B5005917AD042D0E0A77A112DDF508D652C42E7A0114A0AEB032823A25B116922D21BB2B4FA3", + "BB2BDDD3D002659A8C525821A9A61D32DBAC014DADAC8D13C129827D35F511782A3447D724DAEDB1417A3FBBC73ADAE3BF1408EE5425F554F32A4FE3617E775B890F9E4CBCC02F06A17CB0C3D4E97584C9F7FF6853EFB312B417FC85B0106204C666AF1C7952BCDC3EC880CA9EC121816C9C482E182C8378D6AF2F957995C655FEDCEC134967C74DC4E6386DC0506A6932C3DCF731E8E1754A3AD4573F2C0077A0AD6538F4277E438177F7B605A3911CF5E07255E21B4E3774454B0CBEDC6F5926C24B8CF1E2875DAE64E435CA6FC3B22B254BD33DDC2E6FC5F7AE1BDA493FE9190B476BC706853A19E65116E8FBAB9F3B1BEA59358FB1D94B117DDE84E2419F9A375041E1F71EB2CF839776DE04E1AE4615792F72CFCE668794CC0F76997C4AC1BD4DD9185CA24E7CB30CC77D35C4478A478479D839F0D0EFDC2C5698411EF243C1CF1D8A0065BF20F9C047EFAA6A7ED051EB0062E7209278501CAA40BC7154088BB9A4E154B7E91829CC4B8C4778798E06FDF0AA59788500ADEAF31B2065923B9FD9F2834822EC58F312C174BE6F4DE098118D34F694C001F4DC2C0D3E5AE232A50D688EAD87278543B76C4B092F644D30099CB6AC23F69B473B6402A987C051BC4835550C5B0537FBA9DE001C3B191C01644E54C22FA588913F657867C656FE1E9F4ED0CCA47D4B1452A2F820BB4BCFC9B017E5858BB224ECDCE1DD2F6AB43CE4A3D6FDDE89F0484F68EB5AA5584673DBB14C14366669FC473E51372103EC23E103A6591335DBCA27E8852B2C279D2589BE3AF8C6CDEDA91B3DB00AAC5222D6767A545A5257D750D535D800642CCF26FB1B6A291021F3A6615BB141C7069A3A0C2F16FC6C8AD4D593475D8088219FEEC87568FD794C56AC7DA908644AC17012D5ED7E0003DDC2C58007AC0354F068496C696B9F0A4740DDE001C92BB8A8940BEC2844B2FB37B9C4FAA3A34B43A6FE9EFA1996476ADBDE28954663FBD806A7CE1FF2C6D953F0FCE6D287FD438BECB856976EC4DB48223AD2A21A72405F261B4A25DBA7A03B18D005FEE3869DA18C521DFC2231B67FA2ACC0C1BA45EFD2F31B565000E59BCE78C1D9D06571B20659DD1A3799F778E4339E616C96E473A0424F1268AE5698AD674861677D966F1194372173E7A58062E014CAD86A48AA5FD45CC984F84366DC96A59B085206CD54471433AAD497CEE60789244B1AF32E677DBBEE60BC3C0B154B0A9AD33AC2E5814A4DF3C6DF0AC143F3024CB2C8C4CBB8EB95580D58715C9F731BBD9A064D98E69209B986B2AEE1D1A53BF64216299591FFEBD0B177E5AD01825E8637C8566069B62407DE341E18503CCBBD0687F8CCF5D39D08C87C9C66AE34E339F63F6D824BEADE8B48774D468B353E08C331BA3603539B79473D8BC11314272CAE5D7668AFEE89B6F3A81DB4237357D8A4E1A36F9C23436DA4F29FA0388D2F39E9AFC3DAA0157F9457AA3C08D1DB64A2685530F1F1012E4F5FE59FDCDB9F4D85F94940441019738BC6C35DA6D7A00C7C489504B24D8938EA475563EEF0D02799CEF48347AA738D7E604DE830433718340E1A7E895B5DD382EC69C42839284FB2BE923C695121CA9527EB02A75724BAE3913A44677D340D8132C0996FFA93E04152219471BDF0B3A0BD8C90556C95512445C6B07682E0C709BF8259DC0A53A730BC3119CA91B081C615744514E109FE3F32D48CF5CBCC30FB4EC405F88F2D5B19E4C7347D4C7A587E9FE695BC6C54C2A1727B2F82D43DFBE3D54654314A70AC628E01641A3DD5090414C15B3520E9C4C290604BF40F2A7BCF40264FFB5E8B96D012AD798BF69269E18B57EB11566A9747E25EE2FDBA9138B29E84C440F22FFE4C3FDBF92090D976FC9DFAD4AF0B9A363DE25BC04E59E5AAA40492904CB952D0E31BE0D2FE5EB5FC82C092C0E543783389137AB72114966FDDEACB31834605469F2F58381E814FBFBD5A3DB52ED341F34384C39A99D50342E0A9FA2E8FE487892895534DFDCDD4286505D1256D9CFBD332411821AF5A38B2B89BBF897A5345895865100B3473D38D30B628D8BCAF41D2F119E6DA56AF9D9286F747FE8C8866E3C153542F79113F0D18EED5D74ABFC4C151ADF4FE40C0EB1D57AB074645249E3E2B222E99A84C93BCC7C6CC695C67AFCB1530949C98F9DE537E2054F609F4CB77FA8003A870A61DFB39A62379889D7B8677CD32168C90756D", + "44B82092B22C1666AC208063FA54F1D140528FBE7F27B14F559E047B07000393", + "FDE093F23AA3E27045E30E0824A77EF01518BFB7B9F6E30EE2E275F2FCCCDC81", + }, + { + 53, + "F06B1C4CF6A5AC4B62A7F9B20CD5977EC72EEB573AFF6A8BA3E918E2A6196A8BB106A08CE09C4963D39F3E30562D719AD923046F89882D66CEACC0574EB7B545604AECB6364D92084BF81486E934CBDB36E7A3558D618DA6C7380BA475269A1E5F1A8720774A6F451D973289AB746F57C006633C5C5AC69160F401A2B1CE17A02F57C81D88258DC3B07FE993829E242DBFA7349B9783AE34B63F1CABE577CAD7A16B845A662F3B1172A23379B1A135069A292A5D1483776BF2386A819462CB0213138B1E7649C255639DD1C25075A8B1057D688518BC085E483A4D1637335EF28D9A5254C83A08AED98732A15E46838ED2FB2169176FAF92C309D9473BB1BA1D1A2A120B714FC6A1AB0C60B85C003F378F97599D929AB93DCB735F0CB108084D2A239A1B1720F29201A7AA247E3B23E8244F1A94145FA63E18C4416B82344DCC7BB61420A7DBC593E26430D4926176180BCC2B59E7ABEC8246054CB707C8B16E314D9CE246A7DC0475D19B2035641ABB5D131BCFF1025BB516178672120C452D48441B5CC17E75F7A3DE209A21611E0723ACF7815F0C41AB9E85994AD81509841041CA5473606C200A55AD887637870124888A1C17A3FCD6BF0170AC32624145C12AA18283932465E779C7588906073C2D7A7CBC1CB8036E390362352B16643876104BD544892A0514C6CB1C19140B60E25B6F01ACC76B6D6DF7C121F5A945073FCAC611BA5C41FE1C064CFC028724978446440DB75BEFC38BF6C803F0698F7EF24C27C622CC550F5EFC86290C46D25A5F49F3C812C516567947942B93E73414682C31DA22297A169723C5651B8C0A2697AA560B4A65D8A9E6F0398C0C2FA55C3A964CCF32F16021FC5D87E279B923A6D05419B741616343BBE0C2BC1983087A731F90EB4C33A82CBC37C09E587442676D29712A72B06B79DC1D5554C98B24643A05780FFBAF5AB89D28D6557610BCF1448080A640533A56FBF5C422EA200B65AA29C388FEE3CC3CD015A1CB2BE4C6315EF949FBD3888607877B086980218F1EA650BE30C6FBFBB17A879A45033EF7631EECB243B72C43069C4689F5BF9DC78A1D60A3785416AA6C3BA9EC6340A5B0C40450A89922C36C02F3B96FFBF4636897C622E38B268276E428C522911C8B7C015DD57B357174B40968F20B98C87216B4208BEF2B4F2647336D1ABFCCB939F3A6A4B5B51092F760D3C27F98E3CDC1AA4EA11515DA642513F4C2B30935841A7DE6932EB4EA7820C79358A7766021B60ED2071F547A1370B789A0AD0929ACDE7506849B49F7D12053554DA227157C7C25295555FA2AB93F167043C83009F455824A1EB81A27DA8B39B015B7247C4FEF343E3585B519221AA2052C93C1C4863B0F940C8E76C0CE562ACBC7F029FC1C54726261E6F66C63360956E06EC631604724100A861C2290BE296A91F5E88BF6942004E9B6013B4AEBC44B05CB9E08114BCF9A92806C7934B373FC444F88D966D1E43CFAD339F1D217BDAA8CE4CC93A5554152091C2C0C5961F5CDF71A5F8AF056D1059187784C153056680174567436D86228DB3B399769BFCAEC1A3F8BBFD6908410608BD11AB2DD9A40A4C47734E579CA70CD18B304D8D72161AB4DDCC7B71836AA01A870FB87AAF9526D09023295498F15051C8159168AF674602010ED2182303C8D8C39191B2C7374961E4B838B381C15F3E5A339D79883F35E991844723C4F47D99AAC517317B69B0C09872FF300B602C4A63833EDD7CE275A804EB1C186EA7E8CA7B63C2C90B7503378F4853BAC5D9B9334E74A68CFE868A3D4A1FC4A89BD967F127CC0EE0A174F42BBF43430B7A849AF37C1EC1AC7DF9C3F9D514756434EF8D61B3A0C5439A394BA974830C3566BB410EC9797C83B9BE7A1141858A3ABE9014EBA1BB35629E67BCBAF9592D9D6B8C2879B06D44EA5B3AD3121B83D550645C079D8E154FAE2B287A7BDDA9437E4CC57CFB4C9CFF1C3FD98B2BF829BD7829B88E47A6057AD7F365EBDB548C23C3AB4DBC0CFD7C3B3C8AECEB9834D9B6C4F325E44628CFE3BAD02BBB687E57B2168211DF44B9BEBA5204678BBCC9157274089D30A59379FE4C0C343CBB8E1D01BB2C388F42053F9A304FBCB1E594C4A0E7BC6B86097AD1A9505462626810ED6442FC9132D2DE95A264AA5CC966748EB76E5373062781F2B7566CF65DAEEF2348D39777B4126B6027C680B0266EA29B55E586156", + "3AFCAA49F37030D65083F07BF42454A5C74ABDCACEA3779863D1408BA85CA680504575CE4F421CC94315E8127FE178A705355B30B54C485B08207493714B8E9A1199D5313018F3A54B50A0288510755583855965F53A2F3722C820EB7DF72BAB0C66B45A8527584704F2A78E8BABCA0163963D2619BE58095180AA221B6E3AE79843368DAEB4A0BDE523969749B2F25DD65763F718857D68A56AC5B94B78A664729188C1BCB5144753316EEBE2391AE2A9721BA51D5063F150813333A4A95534064070B203592E2C3C2ED59B2EA4C714B29975B98E0AB86AFB825C76AB6E83D05EF51C554869633C2A5C692A403F32A6C4482691708ED5233AE77CA226FA93A15608D3E01A6EC0B296F8398FC74474A552D61756213B690D2C19C1D2B76866A9641422D1D0C81780008AD936CA2937D0E73755067B389B0DC4247850D889E3754897C55DC14B95EFA1872CC3228F82601042BE05901FED3110BEFC56A8786122609F36D7BA9DE4AD4B677C436A6662ECCDACD28A8649560D3219304034E3E383DB6325A989668624BD24596F21F20D5DFC67E6D3CA9F940D870725F4BBC57EABC219F39FBC4C7388D901FC6CC5BF68C6BEC5A35C5A5A1974A843B967D96B0D56E3324B8A2C02621B3B1754944CACF69C71DFC76C5AF0645EF64F30AB40CC7AB765B8AF3A442BFCB32331FBA769A1966DA31F4709B34F586C15D0AFEC075F8C454C8CE7CCA0B8356056C86342797E462AEFD5AFBB7B894DB81CDD9B1978C0AD4DD62F2AFBA23F0669E1E26E8D2C718088805BA80EB23092DC6764286A7E251577AEEC292F03886DE2CC0FFB8CA6BB30BC2A808CF323F5D42892885EE515351626C208415D0F59A2225036368C7E6AAB9445359CB0762B5D5385197270404C7D0063A10597C2DA794DE1447AEB0366047813F36569B21551BF5868ED076E3C3643185523868C11DD6A8FF91C85C6A3BDC6849F6AAB93DFB0CCC75B53628028D5D8C3145180C91BA9D6F2B4BF741E073CA58C554E9BE169B3261F92168840B2B9CFEBA990B0714995AC825B73A085BAB6A888F327475BB674A7DC540FB83169698617A2CD549983481915A7024EC02CBEFE98749C86578326C93E930A9FDC7B61090AF934B1ABCCBB5B789228B01862FA6337901CCFF7B679387A0AA77D01FC1DFDF628865467926AC657233BA52632AFB12964E30BB8066E97DC25A1109A85865D58F0B117A08F649749D43122E36203634AC79FA95221235C11C99EA8281A72B9835519920B02A2C315C25B60795B66A7555A02ACA6B927554101F6C432E9BB68794F9BFB8EED7840595254731463D9E48777C52C90F763E2DC0150371C44214D7BA5B0A121CF140A7AF83462F2D193300B4658421EA237A77FB40E8508CF201A93D45B86E6FC8AB0D95FDDEC8733F6757C93929031A2B08A97C87B75FA62443BD6477BF46CD77994A702170E363B40A1030A34653EFAC3D10683D5D83B00486C6B630D938A5974C55F56D16334F95AEA1154D387B910C8396C2392181BB1727113C18641B550B8A0898689E7893EB0C0474250502741BE388852613948B2888EBA65078108EFCBAC1CF958FAEAA777B8AA51B346D7D0A992A72FAFB2462BF28C9CD82617016B32D6CDB4D6279F991AD16AB2227824E3BA8D255023EDA822E5D3C0BA534C86E8CEE96868A8874A2F574E6A3199309A9707E079CD962BA9233D2DECA9BA8490E2B69132F3B49E810ACE170A41CC2B5D44CB935C9FE122086A218D2894C0FB232F3914B85095B487845FAECC06023BB3BFA25279DAB2BF18A1BE43341E947FF0D304D1272D2D8897950A4F96C5CB7EF73C00A48162778338A43712B3C7C0EC1FB752A64E94BFC4C450C8A3891F75937823C5FCD3463732A7EAF8A1E907C85FA31B9AE24FCF737CDBE32DC4585E0F9C221BE94FBDA80900815688CB53F521B7E776AEA7993594F0AAFF3319024662E848768D101DF513229C256627F87B6631B0524B0A0DC5BEAC636DAAF13A386CBB2D4747140528EE728859672671720415943A95A5CBBCC56CB2D82B8CD18C2457180B3544F975584E4B17F339CEA14052A518235FF723814B96803A90E9C64E2828B417EA494D8A7A0A373A2E584A2408088CCAB264E169AB5C89B99B5BCC891FAE70CC2B4A219E2C2257C42AF06B1C4CF6A5AC4B62A7F9B20CD5977EC72EEB573AFF6A8BA3E918E2A6196A8BB106A08CE09C4963D39F3E30562D719AD923046F89882D66CEACC0574EB7B545604AECB6364D92084BF81486E934CBDB36E7A3558D618DA6C7380BA475269A1E5F1A8720774A6F451D973289AB746F57C006633C5C5AC69160F401A2B1CE17A02F57C81D88258DC3B07FE993829E242DBFA7349B9783AE34B63F1CABE577CAD7A16B845A662F3B1172A23379B1A135069A292A5D1483776BF2386A819462CB0213138B1E7649C255639DD1C25075A8B1057D688518BC085E483A4D1637335EF28D9A5254C83A08AED98732A15E46838ED2FB2169176FAF92C309D9473BB1BA1D1A2A120B714FC6A1AB0C60B85C003F378F97599D929AB93DCB735F0CB108084D2A239A1B1720F29201A7AA247E3B23E8244F1A94145FA63E18C4416B82344DCC7BB61420A7DBC593E26430D4926176180BCC2B59E7ABEC8246054CB707C8B16E314D9CE246A7DC0475D19B2035641ABB5D131BCFF1025BB516178672120C452D48441B5CC17E75F7A3DE209A21611E0723ACF7815F0C41AB9E85994AD81509841041CA5473606C200A55AD887637870124888A1C17A3FCD6BF0170AC32624145C12AA18283932465E779C7588906073C2D7A7CBC1CB8036E390362352B16643876104BD544892A0514C6CB1C19140B60E25B6F01ACC76B6D6DF7C121F5A945073FCAC611BA5C41FE1C064CFC028724978446440DB75BEFC38BF6C803F0698F7EF24C27C622CC550F5EFC86290C46D25A5F49F3C812C516567947942B93E73414682C31DA22297A169723C5651B8C0A2697AA560B4A65D8A9E6F0398C0C2FA55C3A964CCF32F16021FC5D87E279B923A6D05419B741616343BBE0C2BC1983087A731F90EB4C33A82CBC37C09E587442676D29712A72B06B79DC1D5554C98B24643A05780FFBAF5AB89D28D6557610BCF1448080A640533A56FBF5C422EA200B65AA29C388FEE3CC3CD015A1CB2BE4C6315EF949FBD3888607877B086980218F1EA650BE30C6FBFBB17A879A45033EF7631EECB243B72C43069C4689F5BF9DC78A1D60A3785416AA6C3BA9EC6340A5B0C40450A89922C36C02F3B96FFBF4636897C622E38B268276E428C522911C8B7C015DD57B357174B40968F20B98C87216B4208BEF2B4F2647336D1ABFCCB939F3A6A4B5B51092F760D3C27F98E3CDC1AA4EA11515DA642513F4C2B30935841A7DE6932EB4EA7820C79358A7766021B60ED2071F547A1370B789A0AD0929ACDE7506849B49F7D12053554DA227157C7C25295555FA2AB93F167043C83009F455824A1EB81A27DA8B39B015B7247C4FEF343E3585B519221AA2052C93C1C4863B0F940C8E76C0CE562ACBC7F029FC1C54726261E6F66C63360956E06EC631604724100A861C2290BE296A91F5E88BF6942004E9B6013B4AEBC44B05CB9E08114BCF9A92806C7934B373FC444F88D966D1E43CFAD339F1D217BDAA8CE4CC93A5554152091C2C0C5961F5CDF71A5F8AF056D1059187784C153056680174567436D86228DB3B399769BFCAEC1A3F8BBFD6908410608BD11AB2DD9A40A4C47734E579CA70CD18B304D8D72161AB4DDCC7B71836AA01A870FB87AAF9526D09023295498F15051C8159168AF674602010ED2182303C8D8C39191B2C7374961E4B838B381C15F3E5A339D79883F35E991844723C4F47D99AAC517317B69B0C09872FF300B602C4A63833EDD7CE275A804EB1C186EA7E8CA7B63C2C90B7503378F4853BAC5D9B9334E74A68CFE868A3D4A1FC4A89BD967F127CC0EE0A174F42BBF43430B7A849AF37C1EC1AC7DF9C3F9D514756434EF8D61B3A0C5439A394BA974830C3566BB410EC9797C83B9BE7A1141858A3ABE9014EBA1BB35629E67BCBAF9592D9D6B8C2879B06D44EA5B3AD3121B83D550645C079D8E154FAE2B287A7BDDA9437E4CC57CFB4C9CFF1C3FD98B2BF829BD7829B88E47A6057AD7F365EBDB548C23C3AB4DBC0CFD7C3B3C8AECEB9834D9B6C4F325E44628CFE3BAD02BBB687E57B2168211DF44B9BEBA5204678BBCC9157274089D30A59379FE4C0C343CBB8E1D01BB2C388F42053F9A304FBCB1E594C4A0E7BC6B86097AD1A9505462626810ED6442FC9132D2DE95A264AA5CC966748EB76E5373062781F2B7566CF65DAEEF2348D39777B4126B6027C680B0266EA29B55E5861560C871C88630A6A8FFC9AAFA33103B784706D24807398CCF864482F10000D496D8507CD4E5A735FC970483C94C6289C9C15BF6CEF35AD135C723E2E44244BD2EA", + "C3BE6ACB5EFE1049391DE9D3D706922904336603C872D82DC5DE264E6C32A12A990D73C5DBBD1D0C12ACC42CEF07A6F98245D55CC21F5A13EB097FD006E6157B93D211B758699B51AAA33706AB2E4C305E1C40D2C59121D0C4D5038E865A42408D2F9CBF7617E379E3F106CF6EEA8E74A68B6CF9B403FD1B8C4066C5C446AEB857BD09643FF76CD72DCE33B118E7108EB1956A6A39CCCAD3CC3ED11829A937675F1441D0E4D5F557C938341AF6DE79918DDFA0E344179706B260D105DB29563D3C340591369F951FB5081138A49B2FEBEE17925BD41FC06C20F030515D2F8AF7A844A0B32F2EE18EB56EB3806D84502A2C6574D98C1759496E2CBA7FE7B883ABCC3451BB5B0DB291C1D6CA8A2A56765BD7AE65836E529B496D371139DDD5ECB35DDA583346E93F9ED1723CC0B018ABBC738DE8A0BD42176F5BAA9A7C9FBF2BADDDE6EDD30FBB0D37D2DE15C51770877CE80D499D2F1110009550F84F74AAC9892741413A0E762FB4C8E8C9D9AED5397015E436BA0E0C6583B3E476C6A07ED684B9DC8CCC243984886097D9F5736EA73598BFB900A8B041551A4A5904E4E18D939A2F54A71183BD709D8D172C3F774F3AA237123703BF21A3A54A237074811CA30B8D0622D513248475411F0C6F761FA0D95F461E72BE7C1CD227198DDA88FF91538DBD77DA077C5015FD83D71F5E13FB131B43500B744D3BBF64A8AD5BDB960DA4E3DA77E34428A76BFB07D3D67204D3B4B874DA570B422600A42D9D93D9DEDEE43ECE60F14D330DF88C95434EB151AAF17334FC4D7D2E1C911AC9955004B7B2F8A1775FB48EA447677AF7EE415927B4BB16340A8119C347B7BF8D4117EB2CFFF75195F2741B3EB9A84B68666F8063AA33669E1513D4FB6158CA1A88049B644122429D9892D75FC1EC0D8CA7236FA1C6279D7BCF2BE644C2D369DB2359EF8A9D74207DD91B6C9E3871715A8EB3E218FC93F58EDA9EF5D285F2129018DF0228B0A83A3DA68E48658CE6085A7EA497E10A22E214739D4E5D429D6F320995480C3595CB5E01B2D29B51BDCD7DC40C9D1B281FCA024F2B175C9F76A5C63C3FC82B73FB4FFC2D1953749555E316B17D1B2631A33634F47F1C066C14A1046148A38C745D3D0FE29CA0F1CD296D435891470F0373A21360D5B06AD75E7BA4B52176EDC1E5799C7984001A876C1923158E66A0BA1791F8D4B9261AC92BE4A58423B6D60E4E258C9BD9F97741F9724518A5FDF4CE61ED4EF27DC98177EF530D94FD7DEB5E4CC8660E7112FB5AE34A1C4D0CBC08C2AD1A5258AFC787B095D7751E5F8B8F2547D29F21F9392007029BFC1B147300AC72BCD80CEA8DB0F1AA46B136083143A3A493CD467DB93CE8EF11F3E178845BF531DFEA1DE932689A67A02EB911AD720F52DA51AC05FF0B414EFF8FFBFEAAF308DD8553E5CDB6070A2BCF2A0DF13DCA63FB66A6F8301C43111913B18C3434121DEBA46EC9C2B0D73A6AA282B9586009718E3845B67FE22C0975555A37DB47434A26235C2002F7F773364EA36D60E00F15787AB22DC4E4209D77B32BB430355E1DA7B2965BABC70AC49FD5E420AD3EC2CB3C3D5210126CEE1B54697C3B9EF28D801D4E9A5EECF1BB774067FBF0264BDF1DD86E7A97AD35BFDF4D0E998F6F2C51F6A5D37F3EB8765EADC3C0150E21B455A13A324B29D2404FAAA145020C4274424AB856A4961C7E7CA922E26BC835251E40EA897E80229B931F9463A268E845C8F5055F78CE1BD3A0FD6FADCE10A3B2FD5E140161B076690ADEA2CA24387345FB5BED39AC5B9E08A126274E12BD3E6EA188A2EFFA005A99BE2E704310D466E909AE2ECB63776052DD1E1D1F0692E4C04AF79DCC073A4CF80966FBEA05D178A0FA6ABE55C7CE361AD6601FF18C42D5084428853563E0E4673CEE8BE6B339CE703FB8CC6CFCDC0F416B8DF8F12400691FA86F89541C654525F8B286CF06D07F43C95CC2BBFDABE9004A8B40941860318C156D0C9647C14371AEE0417754A3B7827BC83506D20BC3DED4CF53CFE8D37ECC2378CDEEFCA789F53F70A6C011FCA079BBE0F9E40CC9A781F466FDABBC7D0896E2897A06C0AAAF4A83D3D1D2D6B844A2CF1CF88C9CEABA9FF11C1B4E748067A47B5A378FFDF69C776716B7DDFB12988FC5DE30EF7162727BF3D94458BE79194C96A98007F95FC3AC1A2253D21B5EF60846F565B3AB92B8A4002394B2CD2D280F8976", + "E9A618A93B7C7838F9C4A8FE92C4407D3E32ED28BE7B63ADFB9A85C8E5EAD036", + "B31920DC102928F497E08EEB72A41F1753DB939CA30E8A3B48B059544F8DF0EE", + }, + { + 54, + "6F1777B3673C3D5BC2D582930467815B74570074BD920CB169889367016BD95698FF9C51A2E8A97FACB82C749437953A5EB2B1E0B38475745BDD7719C64C7D9A9C927474B973C5B5A05817C0986C321A1E492C4AFBA5B8077B2692C714BD61477334AA1947661D1583D832695136399FEB4FAAC99033601E3BB59BF1D42C7D6B60FB0566DF903271D41E463923EEA3800F3B35D9998669F94477B57AC3D68A02C49A4D821B15933BA67B8D19BA5201983AE3F39B0249C5B86763FCAB12CAB5874F0B185ED6452B7B12F490AE26781811431664361DC5F5C880C6A1FAFA103DB663F0A8934A9465931C397649A662D22A85FA32F3B638BF7B5373D6C242372B487C9BF089B72DC356FA2905C5432D37020291A51DEF99909AB7909BBC2C95B748FBE5ADF36AA84A7266A61956FD269F372A454EFC76FF870B6AC6B58FB03388263A198AC767741B04C804357765EBFA0E8AF6C2D8A0729E939286967373792F4E8B38B3B34074BBA05E095083B4A6EBB089C534BE59903AFC237008E88D4992299D718EA9EBCF064BCF263392205A310206053354529C742D45B534A4EA9BF26527F2258EC72A9AB8BC284F9A7F22A7710174458AAAB07B1A8816BA08ABF840EA62B97A4A6F05E39CB18901DA626C38E8094F8718F5A34D009013F9C27DD0739142D1A19CE0826DEB5E99D2023A0165E5F5AE5D687893A2477DB663A5768AEEF53DECAC8252AA5FD711683840C37AF71FE656B06B0B6FF99585F7DC51908BC253E047101756D9C28D0B8C985A90AFED06428FC665A22C8F72C82E4C64BB53FCAB51877D1219631AE215129730274568D5A745D96146C55A81A2CC4D59F4C12E23987669939E83A03C87BD847CA783182517B254CE140BE1DA3AF8922F55742C5D7C0C002C05C1DA8528E39B028B526F1C5D5E8880F7751C4FB619659B369E893E86E4B4853A62F64A7F05121C7C486E3956B676B3BF9411A79C9C1BE5A5B92B7101AB1685AFC88BC6E455147A16AA93012DA7355C4BB52540CE47E3C7D8705AD767789C5A09E7F856535B39DA06B9B8A984F2CBCDCC083B4B600763376ACE71BAD9FB2F0E1C2CBC0CCE77B75CDB514BA797A6C39046B57B973BEC53F94B885DC937824A78ACD06B135A9D6799CF38CBA5B9C5CFD6FC7CDDCB9110D9A47FC9CAB4413180D38AE85882055A9BE7360E16F4C4751A2BA2EC98BEDCB92E897FCAA4C9C9E06DD82C90D1CA776C1A276A089987AC9983F114E0239FA7F03E49123D0C747E03440EA56B77440B89EEE0318DF54BCE1092A7C352DB29916EA25C790628DCA415BE4210A2627446EC2724B32C3C33C52D230D24449D787BB1BFE16276EA3B95F22D8E180EC406B726B22E3CB90A9B67A6D0674E65A18A431C6C74D4940BBC3E20B588C5A7C0978C6DE4C38E44D69D4CD0ADA2A5A901C78075E9AE113350535B35184343B3C158FB95849953333580A1DBD42AF1F244967294D318AA6C41A06E1B944DF3841AA7039CB503B94428DFDC0936CCCD4DBAC46533B7825452C8020D03B1540F660F08948E1FD0C453E7899A4C368FA07BB635B341A234CAA607E4520D4EB83D0E748EA7D73BA7186F9A51780CE81E90574C44B55E57E448A63868A8A740D2902DAA3CBD15B2883B012FCBBCC79C71B380F2042A92C487965E3A58BF53A20CC878BB34F474A5E1493C23B11996B8ABB2BBD84024D06A2B7090591C828676703686588FF1E9473BE2CCD5C73C55318E12C892C15A4C25A9B2D96965A44880FE7AC589E8C839377B792B0D3E6C885749264328BAC0750CAB9CA95AC3124501C66D3AAC4B8833CFA62AC82930B55C6FC81CA9EB9830BDC821A8B09B36963792887694997A573AB25A587A6629407F2902A257A229E64F66434351923CAEE32BF0AC0044B1291B015CA70BC8B19CCBEC83368753A2F648A74FBAA24176641951ADAF561286CA0360C411D1E7B437110D55C42EBB0853DA52511D918E28E97586AC1DBD4A5C01DAC272FCA4134582E941A44CA179E4C47751115151D72846451C36E74921BB76F0FBA7BBB5CD1DA6929C1702B6A75564CBBF8D26A26AFA92095C58E70091A5A3AE828A3820B0A6CF52AC55D384BA1B8F39B7682E99472FC79A526A96DCD6197827BE23930FE9D357FFF7904FD40002043BAC3AC585F1CDA4EB7F95C7847E0CF96267594737208E496A2872ADF1AE515BDEA873D987444A", + "82E12EF983A6A7B25B4B52755910633B5A975CEC6522EB2EA5506B0268A8EBCC85CD19955340B98064212C582460D54229E771E5871CF0976C7B822E33414F4907324C8789F4B583D8890C6E9AB4AFEC879F44108CF28CD1B5BEB07871B591636E534999910C79E2801C88A93565B36C15696431119D9963A836382A719D8CDA2415156B97A950D057B945E26E7118C6715A79F0507A9D665DADD1A8CA32AC93449B18B15094684F754086B267C6A8898F46A18105BB1E83B19FC3B836B2B0103168108017B7AE1BC4CC672DF797A8D75ACE81F4A0C83C459993C9414727FF2231FD297083617C5A6B9BB4447D6FDA8663525CD7E636E8379AA6965BC8C68C1D47565D779673ECA900584F2268964F4357B7B62215C081E7005A7E728FF95216A052A148A702ED603952A679FFB8AC886B6CC328C3BB225A82556F9A39ACCD538E4CB0ACD1485FA9E19291957C19B2463BB077127C66B5B757A5EA097F2CB4120CA3BE99C6C1EA178ADC02AF3AA53A5C3EF8507298064CC6A4BD5D239ACF5216496689A734629012C0DFD39D98F50DFE4A55A851CF0CA6797DA473880570BC2B1BF2D5378D2A2F05277C2400C31546BD35B185DE3CACFBBC24AD92BA306589C511AA38812677E5111F4AC1E5FB4533E43540C7A65512881C884327707D5A44A299B16C85A88A70B6CD1076B15765A9F0477D6253175EB552911A157BB097DA28B8B690C87731C84A3CB340370C21279EFE0B600A02C7F16C82AA912AA0857876C96699DA1C75C35338E30F3A0C9152D5AC74538CDDEAC13F34976786950A382023C01E021A134C7B7FE01092A80679A05725F1C2BDFB3B3555C6481D03AC82455FF2EA1918D81FBFB758B8522DFBE364C2831BB1C90BBFA9B6B15B0D5BC6A699985DD2A18281CA6C244017A4E5B25A42AED2506B18018AE28A043145967F61552360005C1B2D8E8CB14B18C06AA8669F8BB9D1E5209CF60FC340C5B499656A783F5EBA632989C292146953130F8FF71F9AC690AF131EEA650BE40B3B73280802AC72F1B888D5BB327984794748CB4DB8B0BB9A8BB4BA400C933EB489CDED98BE72CC814A5B0C5CC1067A5C382175B0268757418A59A8DC12D2DC1A7C48BEFE3A1DB4FB000FFC1B3E807ACB33C74EF97FE1475432222DCCF22B0FD405B4CC90BD4217181CA5614ACDF3F148AE8B1919B2CF1EF89A99E411CAE23B246479E581431D12676E893124C1703AE727D4E0BA393583CEE97B62F5B09F6B2669F06199C5248D8B330474547C797AD352B3C07236FDD8099B847C29716ACCCA954583814822234EF90CF8C0C828AB406916CE3FD8892DCC076DD5605BB9AD42F44F0FD7A97112C67C514524B73379862DD04147C205B5FFF76636111579650D3AB02981821AC8097FE0B70F85BA109C7C598F6417AD7C402F6A250A1A11A1B226F651B9EE542267C597B1F571FA4B1B84D41EE6BC36C74B4E850154381816B1E9B97DE021474267F7B40B5FCB7277436CAD1582327981C2603D75718E2D0B7AC85A768C35C9E6CA8BE712A153FAA627C270CFB479AB0A3C27BB341A1071FB089F23F31346045863B641026ABB7D380CD4A3C20D4CB8D5B6472AF015E6322B23D90DCD148B20332046BCC62709842B4383AC5236856333153902576A5A119503B654823CD6664A566809B5ABBA1B8467696CA9DB01729A31DA877070ECC7861903FED84760B0CDB63647DB530F4B2C4353163593CAB189399A63A2C3B2E35F2628C3A662ACB8FAC86869AC59778C34BB57E2B3329517CCF9088179C905EF798C93C70F6E479D6EB69EF19142F2E24A53E95D41B175BBA4CCD038718F612A5FDB6194245046CCBC3E784877F23B7595436C292A181A62A8A58A24654193CC6AE808CD8ED2B3EA4B4560D467E0C94B6AE0AC0855B76C516E84F909F3F38CCFB2B143964A238846BE289B8094B41394BFE8A510BC571DF0087BBE71546B8BAFF40AC08F32A7F8015BB2CB678CFA2B18D66D4C26CB47257C7A6282608B587F098571D0BF3084957619771D1664B159984CA52AB85BB8AF3027593218853545F0393C71171D2916AC3CCB0F2CF5307BD6C75937CA30405DD370A7AEDB4FD68B2550312BC6E23FA75407A05BB0603C5105318F96F338368328D382B61EE63F1E20AE848748DC70326F1777B3673C3D5BC2D582930467815B74570074BD920CB169889367016BD95698FF9C51A2E8A97FACB82C749437953A5EB2B1E0B38475745BDD7719C64C7D9A9C927474B973C5B5A05817C0986C321A1E492C4AFBA5B8077B2692C714BD61477334AA1947661D1583D832695136399FEB4FAAC99033601E3BB59BF1D42C7D6B60FB0566DF903271D41E463923EEA3800F3B35D9998669F94477B57AC3D68A02C49A4D821B15933BA67B8D19BA5201983AE3F39B0249C5B86763FCAB12CAB5874F0B185ED6452B7B12F490AE26781811431664361DC5F5C880C6A1FAFA103DB663F0A8934A9465931C397649A662D22A85FA32F3B638BF7B5373D6C242372B487C9BF089B72DC356FA2905C5432D37020291A51DEF99909AB7909BBC2C95B748FBE5ADF36AA84A7266A61956FD269F372A454EFC76FF870B6AC6B58FB03388263A198AC767741B04C804357765EBFA0E8AF6C2D8A0729E939286967373792F4E8B38B3B34074BBA05E095083B4A6EBB089C534BE59903AFC237008E88D4992299D718EA9EBCF064BCF263392205A310206053354529C742D45B534A4EA9BF26527F2258EC72A9AB8BC284F9A7F22A7710174458AAAB07B1A8816BA08ABF840EA62B97A4A6F05E39CB18901DA626C38E8094F8718F5A34D009013F9C27DD0739142D1A19CE0826DEB5E99D2023A0165E5F5AE5D687893A2477DB663A5768AEEF53DECAC8252AA5FD711683840C37AF71FE656B06B0B6FF99585F7DC51908BC253E047101756D9C28D0B8C985A90AFED06428FC665A22C8F72C82E4C64BB53FCAB51877D1219631AE215129730274568D5A745D96146C55A81A2CC4D59F4C12E23987669939E83A03C87BD847CA783182517B254CE140BE1DA3AF8922F55742C5D7C0C002C05C1DA8528E39B028B526F1C5D5E8880F7751C4FB619659B369E893E86E4B4853A62F64A7F05121C7C486E3956B676B3BF9411A79C9C1BE5A5B92B7101AB1685AFC88BC6E455147A16AA93012DA7355C4BB52540CE47E3C7D8705AD767789C5A09E7F856535B39DA06B9B8A984F2CBCDCC083B4B600763376ACE71BAD9FB2F0E1C2CBC0CCE77B75CDB514BA797A6C39046B57B973BEC53F94B885DC937824A78ACD06B135A9D6799CF38CBA5B9C5CFD6FC7CDDCB9110D9A47FC9CAB4413180D38AE85882055A9BE7360E16F4C4751A2BA2EC98BEDCB92E897FCAA4C9C9E06DD82C90D1CA776C1A276A089987AC9983F114E0239FA7F03E49123D0C747E03440EA56B77440B89EEE0318DF54BCE1092A7C352DB29916EA25C790628DCA415BE4210A2627446EC2724B32C3C33C52D230D24449D787BB1BFE16276EA3B95F22D8E180EC406B726B22E3CB90A9B67A6D0674E65A18A431C6C74D4940BBC3E20B588C5A7C0978C6DE4C38E44D69D4CD0ADA2A5A901C78075E9AE113350535B35184343B3C158FB95849953333580A1DBD42AF1F244967294D318AA6C41A06E1B944DF3841AA7039CB503B94428DFDC0936CCCD4DBAC46533B7825452C8020D03B1540F660F08948E1FD0C453E7899A4C368FA07BB635B341A234CAA607E4520D4EB83D0E748EA7D73BA7186F9A51780CE81E90574C44B55E57E448A63868A8A740D2902DAA3CBD15B2883B012FCBBCC79C71B380F2042A92C487965E3A58BF53A20CC878BB34F474A5E1493C23B11996B8ABB2BBD84024D06A2B7090591C828676703686588FF1E9473BE2CCD5C73C55318E12C892C15A4C25A9B2D96965A44880FE7AC589E8C839377B792B0D3E6C885749264328BAC0750CAB9CA95AC3124501C66D3AAC4B8833CFA62AC82930B55C6FC81CA9EB9830BDC821A8B09B36963792887694997A573AB25A587A6629407F2902A257A229E64F66434351923CAEE32BF0AC0044B1291B015CA70BC8B19CCBEC83368753A2F648A74FBAA24176641951ADAF561286CA0360C411D1E7B437110D55C42EBB0853DA52511D918E28E97586AC1DBD4A5C01DAC272FCA4134582E941A44CA179E4C47751115151D72846451C36E74921BB76F0FBA7BBB5CD1DA6929C1702B6A75564CBBF8D26A26AFA92095C58E70091A5A3AE828A3820B0A6CF52AC55D384BA1B8F39B7682E99472FC79A526A96DCD6197827BE23930FE9D357FFF7904FD40002043BAC3AC585F1CDA4EB7F95C7847E0CF96267594737208E496A2872ADF1AE515BDEA873D987444A01D0CE8C13AE285B2ADA6369EE7787704E4D0038701F4F5CF19355434FF81DD4DB2BB4DA511A3564740C5A712CB3C888FE2ADCD764B98BB8E6254B19352AC307", + "83828F7ED8CE90AE917CA0FA823E1C6FF2EB556D8CB35E0079839BA7301F3BAEEB76A210A0E02C9939DAE1EC693CEB1A53359D6840EAF6DB845E2E693CB4B8CC0CFA1597CE2B207A63E3AC71D8027C865665F417300592ACB7F6C9FF8C975EFE88474C1ED682DA58457004573719C966FDD0C7990B897E02803FD382BD07DBA3475BFC6084A8EACEF108E5C8BAEF614E9A100E4B25E6E3B1131197CA090288C3752D2A93A251D8B235D46750BA2DC49C3D0BFB0B945D890977490ED4EBC4508D4D94456595244E76405665081C176A0F3F227ED755E1BAA0739086424F74CE179D8FCD28852B25FD51ADED1F32775B47F6DD4CF8DF007E103BCBBDA9748E279576CAC724B02D90EE31E9C57B418DBC77456009EFF657B8C68AC2CF046598223CFCD8AD81541B08AE0DF43C91CA500AB76A2AD90B1EC235AC6261377E20A7A016F13ABE2A4778E8FEE7D2BA82A910B89A11EFCDF57399075B220CA9425BADEAA36827C8F7517788FE5B0EFE40A05678E7822B4D9EC9B9E10F57BC856C5A46484F5FD4E01206B9DF9CF0B00DFCF9B79BB1F54098D33DFDFE1F89377792FC1ACBA6B75E92F6EBCB4248451777F5EE9FE18E88A099D0E1CF25F12B8186498164A31626A934DC6286FB50099B9B9954D078D6871DFC834AE341133A5B11D8550045558F5392D802FB197C95DF29F019565503110F480F76B86F7441C05F914531AD367D0F9E98B8B45D38AAB4707A793735C7E1F73892BF98DCCA383A9A67A5F1C2B70EE545752DCDC6D713A6BE95970668589CB81FD8AE42A497BB3831048583EC6A13A7087D55D6711D6A9B3D0EC929966F88A6DBAA2984EBF94F1E867794D0A4FD1C8D1D64BDEB83D8886D55228191CE3912FB566DC3FC4426CBE875437908BD60455E9EFF6334AF6F199FAB332B78800960325F74EEA6E984AF9299A077A7786B72816C78F5E75E8674D4849D360561FC8A080B5194140BC38250BEDDCAD5A5347D1B3E8F7EC1224D8DD5D32A411377E1B7626A4FB20A2C3BECE55D8F822B833BCCEBF515B7414D2CDF62B000E9CCF44C1FC00B96EF2F2DC4CD571D4FBEB791B7D38F8AAA98D652E37922D9364D76BF39CFC689CC1C205E78ADC455AB6CFCCF7119D000462469370DB5AD81228B1590DF4496016DDDF0EE417C74A904C8AC2DDD92D60528BD1B5C213103C79E1D6BE56C323FC497A805ED8677C9BC90181B9048E2676387AE5238760A9C950D6C6A11DD87B0DC732E1B6885E67A73A4E56DD126BDA20FA3F3E3D03377060E5244355922FD6019D6B7986D5CCBAE2ECAEAD0E8ABF96F547C26E8EA2545B8B2E2B9D160BDDD1826F475F8762E581565F509BC16D1D98C80602FE584E4C1BA4CDD8CCBDDBA81DA82316C9E4EF185D6EFB837DEC43E1772D84BA4C5E162D5059BE4B0767981D7B76F6C9FCEEFEEF43E420630FBF297054CDE48F0350CA9E3CB7741534CD3BD3BC220BA5C85B7EE2A1BCC71F02A83225B1468DF77F7F91F85F86A77307F6B069C4C2215F008964A22E19B38D6652E2EECFAB164A9606CE58BD1D90050613D7838336A2283F61AFAB5CA6F5DD25524E3950E66C308A1AFE06D455A507EDC0944DF946D569CF23DF42EB7D6D622CF5220510083AF3CC5AB1E386CE114A577E48C5E328BF131672BF6D13CD4B89AEFD4E3EAF462D1C06ABAB8B45564F103C8B6CCD088994B4FF240D3CE10AAC295D353F42E0F4B045EBED695E10767B15830F71BCFB26527830F52322D1B3AAF37779B2CFDA1B1DD002F568C8901EC08C432F7089AD6F48ECDD71475E789003B45E1B42BD3D65D956A57104EAF9AD602C1416AC52BC8152FD2F109BD66A3E745F9DFEE26F46484134E959ABD78FB60D32638E9A52F8B0AD52241D38C5AB19B332672D8395A36E2B25F11F98B466214AF284A958C243B134CE90CC122AAD5F64AF9AF05C9624044B03E032561C7847800355ABD323CE63C2B0435CCA43854A30E273451CAE82D753BC0CC78344B36040A512EF5D5928B91FBFDD4BD58E4F7CEB34D8F198C507EBD94611192C0675DF40CEB683295C69EC537EB31F80B151E2D65A96AC25D110C6FF0BD92E99CA3174D5EE54D86DF3CF98B98B29EE9837344FACDA6227F809E6B70035699AB0D122EE7B84681B97899B654D85E65DFB207E816E4BE94527EC73007E54E04A3F3D6D84C737ED3A28E51E4867F219DEBE251F728DD4ED0C77F", + "A67BB0D879441C09933FDADE33695D4B18B75501AF9239ABA349430DB316BE67", + "C1F32099307F1A17F58BA32DAD0151736D831F7A3ADD0647F7D2320228D06AA7", + }, +} + +func TestEncapsulate1024ACVP(t *testing.T) { + for i, tc := range encap1024InternalProjectionCases { + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + m, _ := hex.DecodeString(tc.m) + + ek1, err := NewEncapsulationKey1024(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey1024: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + k1, c1 := ek1.EncapsulateInternal((*[32]byte)(m)) + if !bytes.Equal(c1, c) { + t.Fatalf("case %d: ciphertext mismatch", i) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + + dk1, err := NewDecapsulationKey1024(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey1024: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + + k2, err := dk1.Decapsulate(c) + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k2, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var decap1024InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + success bool +}{ + { + 96, + "9D44513CA81A19B9619485AF92642AC01785C277AD4DB73FE96921BF98AE77A43CBB8B576439CF73317795C7130E76851B0670C12B2BA2555CF8F0C541010C438507913609579368BD922E506869627316D3655F20A62B4C8A7BA33A21DF71A964A9225BB81FA84ABFF9B81FB26A64BD65C4EEA5C955B33D2077A3EC9BC99B6A6F9339BB0926856111910701CCC6EB7502193397AA5C3E773FC453991B444001F474CB18AC7957696A643911555DFACBB03CF9050FE86F7E30181065AA486021D4C36B4C12CF34241852FC6CD322531107B038E46760C59E112149BE905C1915B22B396F814C83A3A1A17BA9B337B0C4D3213C79400037FA80BB3143914B56A8E45DE6348325004D5BDAA461DBA04FAC2E57890F1F256D9BE984E13B3200035552ACC6FB319E756948D7363510D9813561CEF17599BBE584CBA99E3D6564D53C30482A148346BBCD1BA4F56017AADC1A6DABC127F23CB595405B930D41185E56F51418A895294C21470097DB0B1E826619D65C0DFC2123428C88EEA040F4346A83F46173D45279F239D55600F1EB105A86A1FFB61738D6283A03B889E7238A31530FF869DE598235ACCB3E50B80FE59BDB2641F2855C38C643571A629C0B2B87CBABE6C6B0BDD7B2BF415A0B90266564B183E362CD8474EDF93C2D780F69973765554B6D50C7AAF23F4B79BFD8F8973B827C9A92625779BE873A92A9D9361EE20C31559DEC154E8BB5592B85C8A1F64CC13B7058486947C1929E3C6C7AA375E6720C41E03FA43045B549A05B97027178BF7CA62EAB0B123CB232A3E4CBA2F4BB66980977A78A7202A25527325C4B5682F79B3614680D9B9370C888301B133FC7BC74441A8FA44979C311C26182E01674CAA2372C681D53A019F7E1213115257E4C9FBC90478EC760786B6770693F5EE149AE60CC7835C0844177D8C04E54096A40491F8F6352A231C8E3348F4A02558503C18E2336C982226937AF63A15693025D85422D695737D649A04BAA617B6524FF8853FD664966302E29FAC6162848503908FC0BCEAB9710A806B81536CC78A332EEA70F04F27C6DF9645EB516B7D97B8F84C8E8C2C5ED87C0678795884A893866B853A9AC0B301C0320AE07C54C9D06709E66A90D69724F4192C6859DEA8054BA294FADCA6DB075233CF8AA159144A48C0D8CF4CD51A78A9D23A805E632BFAA587D66A7DE052829FB9FC23C6F66D6572020C60166A220C5432C24886F8553BB7214676AB135535D0CF711845C0F654774F591144D498CB93013E5676146361071E047452CB31AC8CB1F19B35A009CD12C0B0A7A84B6D983C388890E64408FE68F950584A917695B3304B580B3EBE45DF29B2685B02695356CDCA01869F4BE00A9930D384C5A7C8EA17B7033382DC0DCC806DBAEE26222B94C0A775C0220BA3835659F002C09CA37A8B5870B7D2521271B643496394157C621D44276EA0CBDEB0A01C6094B0C6183C92A9638BF1694CEECF66A23CC7C6D0664D4D09B58F39E6CD1CE4D125B08C66FDF0514A3534F026CC4C2517F9C137288232B08BBAADC635EA6024A8C4667E8DBC0B56556DC565D5C08C6963A33959B67F5A32A851B621A8B31B8704C97D0499FFCB6DA2A54DAB35E2CA31C722856061B3DEEBC7D6E5BBB64A40FA5CA1AEBB928BC3437DFA449486225941948A7228D56D7A60AE535A30A7A04925733F29A3E242F99876E7981502FC00258E67A1367778539C1E8529FFB707E13C1446099C6E681380F9A2D2F90BCA121009B822C1854B185A51B0BF61BA0B56C6FA002A5B675DC0A0752451F5C92CCDE666569FBCC683379FE6A850440346A3112888548D47B0468D7481A840946E53C0293C7696BBE88A914506A49CAEB3E233BBF00CC94AC74B70BA2A57D4A032BE3C1724382FFAA002DD7C9F025189CC385C1A24C10AC716376C98B5356AE9AAF75FC931E342F5986A146E3855F6873CC8BA3CB86BAD732241C313AD17883E86C5E2F5486C092BCB4F718B83213C95272D134A2A745A4D6309C266431980C8A0F86CB0CF03247FCC25E2072D5574009D4B61DE9608B29B44488942CB27FF3749A9480408E62AA47A0425D74CDD276B26495B0956BC398AABCD1FA51D5C342C5C13677D46BBFA994FE633F2BEC53C0B6BC50090ECA590CE5F6B30A9436A741AD68C7A9966BC9E7E3D0A64ADBADDC6AB2F71F57AE0AB344F614619AA6DAAAC767", + "F1B84483E2A3740C62F7B50CE3588848FCC3DD4851D8866250DB6EB10352C7AB582F537F25C0205CE97AF7D5A0BEA7B2230C37D496A47BE92C3657C924497C3DF90BC3B547C3A129C16532F3584620D71F99253EB0275682B75007B5A1387268615708C210B9C8F370CE8304377A535B8CCC0E550DF7521F18536A0AB4AADA863DFC81BC1044B37135B42584A8F7121DDA4036EC869566D551E3EBCA35902372580CE55A3B58934D8E445980453FCD1B9A1C85B73748A98FA837CF677C23268AB2EA42BB642198CC4631753104E9121E3832D34C7CEAB049F7D22F425C093FB6AA070ABB49781EBE1BBD55F663957341E432818169CFA8515C9F044766C397C191C129355B244730339B8E6404CC63B71A1E93349DDB5C2F995F61EB2508B96D51522A3532C14DCB095AA92F2E16404F1B2328006AE9EC5FB9627975B2C764432382A22076F701BD9A5E281909E3F8393DC3671CF26574B412A19B04D8372C5D1550961B38C6DA7B4B0538C978A4D3D8B61007193B1CC8D773777042A46BF496A1895584091B77A498D164BA4BD1C9B6D49A262AACA8CC03136389D480CA458348F9B2B67840180F014892477D1589C89D42B0097A72BFD9C215808B7317922AC360A8D5B1E3689535C45246948501DA8418D0464708CF8FE7A18566920E086EF879BFC154277E9BCE9B7906EFF0B3B7C33CCC09424C41A487B624A692805C5003B7E98058FB2C30D762981CC164D337C8B830552724AAE7B850625EF5556707520FEB633868270E0B40245FD07239B70C8FF00E9236409832551D252AFBB860739BB6F2427B42F0A08FC264EAD743F1E16E592BBB32B99308FC969980B8C4C685FCFC96E5B1BAB05A343075B327D658753B2065693FAC5258D6C149260183B7AA2F100B0564A6553836373A082D1E2843F140984B37ABC75A36A65B0162F958275ABA3A4119641709DEA1028FA620C322BEB210A68634CF93968C5B80C6AAB3A137A14440FB7E65C570CD366404853D9EDA9FD513A20332010298AC6AF9C85049B3AE4368450B02C598105EFA6B7A65820C659FE220B7B831A62DE7002719A7FCD18309AA154B2C3B28932B85822062B16189862F9E89CBF48C6FF612165F529424E34A72728DB92564F3A4135D3B35A4EB5D113890BAD49CFD4678600927C81586558818FD72CD048BB5A6682241081BE2B8B6E9077DF172363D32AA81CCCF5F4BCF2DF0324A2AAB062ABC2B2ACF591C59B56BBD10238D49202803D31A8C33B39DF9A2BD35CD6F8AB6CFAB65717B327148AAE2F598A6851FEE9A642115B7CA392DB1B273BA6A0D6EF2CFA010259B95957195BE2F93A3FAA68C703C7192AA6E7466BD766C620323662943038E349028208D1ACC70BC396D75D4526DC53CC50C4B546CA24969163E06B6246A542C1C776414058B458494F804510817F7DB8F55F9B32E85352FF6A8864359DD39621426711733A929391248D31A7110C80819795601CA211B87AE4323593A5987C89B24C55039DCA23B28A477416974699D27D6219F943937B9C44C472041BB9729624C0A2358CF908505F8C270F6CA12DCCB15A63E8BF88F4C431B5137BA0F6C90BF209832633B0CBA8BABC7617427479701287C35AFC06163B0429B155B91D414CA16D23FFCBB17FD1C3DCDA8262FC3798B1B1B0843CDAAB6C2609422419C54AD94783EEC42D06BB60F844B58F30427908156A660CC5AC63A5B0B2D8BC496BCC1AFB0CE4555C15956B134565F6C5B3386C57013074400E268CEC74A79C08112F734E316C969471389E94EBFDC14416C106CCCC8927B97DA76086B2C5FD76891A62C5521137DA3C30614361984058214B3507EE932682301F45CA20ED97BB607150AF70AD5DCAB4B5B0C3528969AC029C40CB484788A4568A654C32D4224A22E6A5D6F539F0B94AFDDA10BAC1563807686C48C07FDC622C9F153FAEBB85528C53C0ABA383BA51DB5925670BCCDB3C5B5D2C7750A2E9A83B51D1886C70534C8465E9086AB7851541AA4A6B0E96304CAB8B452C355F52034E2481F426F85B03899DA6B0546BA427AC751C84461952A9DC0A93A706B174733D7081CE724AE7CF04F08CA5B92A6CF0495A10DF2B682FB16806B9CDB6157D6FCA8C8A5A8E8970AA96434B27BBB77D41B1C4821EA6A8F870CBB7422499D44513CA81A19B9619485AF92642AC01785C277AD4DB73FE96921BF98AE77A43CBB8B576439CF73317795C7130E76851B0670C12B2BA2555CF8F0C541010C438507913609579368BD922E506869627316D3655F20A62B4C8A7BA33A21DF71A964A9225BB81FA84ABFF9B81FB26A64BD65C4EEA5C955B33D2077A3EC9BC99B6A6F9339BB0926856111910701CCC6EB7502193397AA5C3E773FC453991B444001F474CB18AC7957696A643911555DFACBB03CF9050FE86F7E30181065AA486021D4C36B4C12CF34241852FC6CD322531107B038E46760C59E112149BE905C1915B22B396F814C83A3A1A17BA9B337B0C4D3213C79400037FA80BB3143914B56A8E45DE6348325004D5BDAA461DBA04FAC2E57890F1F256D9BE984E13B3200035552ACC6FB319E756948D7363510D9813561CEF17599BBE584CBA99E3D6564D53C30482A148346BBCD1BA4F56017AADC1A6DABC127F23CB595405B930D41185E56F51418A895294C21470097DB0B1E826619D65C0DFC2123428C88EEA040F4346A83F46173D45279F239D55600F1EB105A86A1FFB61738D6283A03B889E7238A31530FF869DE598235ACCB3E50B80FE59BDB2641F2855C38C643571A629C0B2B87CBABE6C6B0BDD7B2BF415A0B90266564B183E362CD8474EDF93C2D780F69973765554B6D50C7AAF23F4B79BFD8F8973B827C9A92625779BE873A92A9D9361EE20C31559DEC154E8BB5592B85C8A1F64CC13B7058486947C1929E3C6C7AA375E6720C41E03FA43045B549A05B97027178BF7CA62EAB0B123CB232A3E4CBA2F4BB66980977A78A7202A25527325C4B5682F79B3614680D9B9370C888301B133FC7BC74441A8FA44979C311C26182E01674CAA2372C681D53A019F7E1213115257E4C9FBC90478EC760786B6770693F5EE149AE60CC7835C0844177D8C04E54096A40491F8F6352A231C8E3348F4A02558503C18E2336C982226937AF63A15693025D85422D695737D649A04BAA617B6524FF8853FD664966302E29FAC6162848503908FC0BCEAB9710A806B81536CC78A332EEA70F04F27C6DF9645EB516B7D97B8F84C8E8C2C5ED87C0678795884A893866B853A9AC0B301C0320AE07C54C9D06709E66A90D69724F4192C6859DEA8054BA294FADCA6DB075233CF8AA159144A48C0D8CF4CD51A78A9D23A805E632BFAA587D66A7DE052829FB9FC23C6F66D6572020C60166A220C5432C24886F8553BB7214676AB135535D0CF711845C0F654774F591144D498CB93013E5676146361071E047452CB31AC8CB1F19B35A009CD12C0B0A7A84B6D983C388890E64408FE68F950584A917695B3304B580B3EBE45DF29B2685B02695356CDCA01869F4BE00A9930D384C5A7C8EA17B7033382DC0DCC806DBAEE26222B94C0A775C0220BA3835659F002C09CA37A8B5870B7D2521271B643496394157C621D44276EA0CBDEB0A01C6094B0C6183C92A9638BF1694CEECF66A23CC7C6D0664D4D09B58F39E6CD1CE4D125B08C66FDF0514A3534F026CC4C2517F9C137288232B08BBAADC635EA6024A8C4667E8DBC0B56556DC565D5C08C6963A33959B67F5A32A851B621A8B31B8704C97D0499FFCB6DA2A54DAB35E2CA31C722856061B3DEEBC7D6E5BBB64A40FA5CA1AEBB928BC3437DFA449486225941948A7228D56D7A60AE535A30A7A04925733F29A3E242F99876E7981502FC00258E67A1367778539C1E8529FFB707E13C1446099C6E681380F9A2D2F90BCA121009B822C1854B185A51B0BF61BA0B56C6FA002A5B675DC0A0752451F5C92CCDE666569FBCC683379FE6A850440346A3112888548D47B0468D7481A840946E53C0293C7696BBE88A914506A49CAEB3E233BBF00CC94AC74B70BA2A57D4A032BE3C1724382FFAA002DD7C9F025189CC385C1A24C10AC716376C98B5356AE9AAF75FC931E342F5986A146E3855F6873CC8BA3CB86BAD732241C313AD17883E86C5E2F5486C092BCB4F718B83213C95272D134A2A745A4D6309C266431980C8A0F86CB0CF03247FCC25E2072D5574009D4B61DE9608B29B44488942CB27FF3749A9480408E62AA47A0425D74CDD276B26495B0956BC398AABCD1FA51D5C342C5C13677D46BBFA994FE633F2BEC53C0B6BC50090ECA590CE5F6B30A9436A741AD68C7A9966BC9E7E3D0A64ADBADDC6AB2F71F57AE0AB344F614619AA6DAAAC76772B1D32CDE082B6E240351358470809889513B4C05C4611B6C02F4E0B4AF9C2890699250EAEF4F0DD68C72720BEB1D19804203EBAE8B8903D98F25903A764877", + "BF5EF354B5EE7D74188232802D1B421F0C17012ECF2364E4CB996067AB6270BF034E5FB475BD2EB17C1708E4AD606FF23D38D27C1F4E524CC9767087F7F042F09B1C080B055F27CF879779D129823E4A491DE0F9AF496F04158DE90637547262D3086778AB268665CDEBFCEB85F3C5E02ED837AB43DDD97B456921AE2E03C88CCDAE7757A4773B1A207CC0B11B170F19FD70F25350849CB6C39AD12DC69352B4C4A6A9619CFD41EBCEC4E26B3B0685EDEC45D87879940C4C6E5C43FAD20C65FFF64F676847C4A63E7AE6144404332220CCF51B84C26E726DE976A2CC3AE143BC99B657B5F16290B37E43F8680B6BAED4F033FDF189376882DFF74864E55542CAEECB0B55B3FE2BE91EE73A20F74D5AB984F868CFBBA63EA7F6086B19D624278553378C58E3394FBD1FDB7EB86D919FD99D1B79B59BBB4E5AD0EDF7474DB392F2DAEDC3910D02E636775E313E7D14014027CFA561B66D0B249B6574A35CA45F2CF9AA63C2C71B970BAA3DBC0E0AF1C429FBD8D6E91D67CCA4EF9755522CD0FD49216F8D2BEFC9E37E7066315027BE0DC35460B4DBDC64C07EAA19C5A135FE9A0CEE287CCE67A66BC8E541936477A71157180EA185165E41230DDD35408448D9EE281287D6C03A9FFF128E7B3EF71512CDFF126109392FC44977BCEEDBB12C213E4A40F37AA7559A85945E4A14F6715CAE676F07B5237EE46968AD41A6B0AB4B2A0E558688326B558E6EA8FC925080DB42629CFA1F2BC13BE050476D9F0B2614E9EB0485CBE16C678ABAEC1ECBBC82B506A183EE8B66AE938A57A36EC1023D3981A38AB7E86FB267F722479751B0D01092C3D4F267419865225B1E0FCD50DB4E92172D5BA2DFA8D27F7E7F3971C116581FFDE1EB7C97AF66448ED55101A774393159BD058BE12640AC403AC8F62289938F332206AD1C82D8E0C969D7EBD0DDDE9B6A45E3B5B1CF2A6EBA80F91EF87E347B8EF4758EE8654566BA7DB32CAA8D79C914D291EBFAF7E9C6ABA393972F09A5C8821D435EECCEE94EFB9341561E367A15F5ECF2E4758A0D3558D122907FBF1BD0CC21419C9E6D1157F2A41E3CEB5C9C5089666A81D45D28CC30053CB05EF23235C78B121C5CCBE5E72056A792721FE1427A0CAE8891FC8AD94F80A4BCD1AA1287225532C6EAD21C8332AF19F1333095901F83EAEC30BEFB9CD35F2F056452F682211F4AA7E4FD886AB72A52DA710B4B19ADD0722B8CA090C349CC20C04020AD8284D24E67038AEA537E2F42009B24474EC61223D312AC332C189A696E8047EDD788EF78C3A999BD2724F56FA0BFF16A5F4899DC401C6C55573B954DE01F6C9928E2B1504DB33822623B2CCA44D2178AED441F1F420952FE61F1161EEE907F318E1C693BED9496DCAEE654151CA576EDBA2FBDC12714FC37AA2A3EE7BB167DE1ADE78F205C9E92B4E58A2D8A3D7C8788A167AB1B0EEE549C292BFB25B847B0BB7506A69C32DE245EF8D95E625E89A5C20A3D9823C0C021F1B703F2ABD31B0ABB5912802BE89E530A021A83F65EE94A17B091841661A3B63DD2812AA85A52E7A87B497FA61AE16BE69DD68EF016FB19904E8728B8647B4CA88ED38B90870A1BA32A2014C1887BF9390196B504F950C983546C4A692BF5022BF54664328B4C82D0085C8A399C96D13780F4FF9FC11EAC78F2C689CCFAF4483195ACEE717BDEA3EC75021E1691F53878EA9CCDF1D68134C4D8AEADB1D3DFD241424B2EF0636A35173DE770D0556FDBD7852B74A2D9F0D1D8DB59B4A634B1970E3A4037794A2151970B045AAA667F50549AAF71411792D613B1424AEB3748CCB1C844109D0FE6BC9E10E50FDBEA5FEA833BDAFE4E9D9910E366BA08792ACD7B2FB525C5987E16F1A7651B591C1503C7CE66D6D1A230B90C82CD7B503C55BA9D6951FB3C9C626521EBCDAA0584A05589889D018644E6ECCCEF120BED7079C2E1C66C0975B0BA01C29C0F7FDA9E3141449A1B6548A6B7A6FAD868C0A45E7766FC2D637A2A8A3AE68EC7FF3917C512B51EE5F9D54372B42440892F3DA9E4CD713AEA0311EEF7A5CFD8E441DAC5781827D9FC2C86E09D5C680C43B997CFB2D38D2A2019DBFBC144D2C442BDDC12DEE0C6DF9DC101E6BF0EADBAF2A89CCC0FCE10ECE108ABFC2BA288E64D130B2D72642F0456EA24C8B2BA88AD24E5D71778DDF85F8C125BF4710C0D333E4F7D547CFD4C6282BBF9A81BA8707804EA", + "74E9A374CFD12E92610591E05E80F256FA9BC85146F678C0FD538ACBBB765A6B", + true, + }, + { + 97, + "5CF432212464C32C44A8CA8FA6F6620899B08E968B2F047B42B00527901974337D3613C1BC278EE8D5C254B4898E1088D4A8C09EE175969407C01B11C5F8239C4A0455DA5DD6876C18160944054F69545A5340C5E2EC8A378B1824D313D4001F20FBA7B878AC5F490E0EB65C12D0ACFF68C9F9E54170F4137A4C206834BFEF81651C61BF3A6736B981A222994D3571B1B802B5D759847E915A9BB0B1CA894E055B1DB0FC5D7EE39C5695C10A3112D4E2A0364036C1824C7F32627E81176E659AA1B6CA4EA3CD6591CB7E86CDCB57A6440B6D92B08A628C378408ABF7B93CEC59AA19EB648B39170CA87AF41389ECACBA26919DBD3C4870360A72A398DC7CBDA62C26AE6A0419E5229DA7C77C725FFE9AAFA9EB0E5BEC8B1CCA02FE97379DB20143E21E1AA8BBB55B724BB668C10932BA9C58A24536CA9CAC675350C7F72157B0AF497BA4B16425F52069EEE144FA96A40DE99A1FE10D95F71102FAADF1A8A0FE8038B5A49C04C952C97C65685965E18A0BA8B805AE2839F61278B76265082329F6CA4040242A760B6CFD683D52D6BD4E1156BCD181D4C16FDF9C75426A8000B6952E2A023D3A59844B72F3D9BEEA35BE4F70988871C6079A2F49474599C9A0BBA36B8CC41865363B42554D9722B9CF7A5BCC1812889BCEDB1B4DED8B48BB42695D83BE31A543367819EC75851E1B920BA69AABF57007CAB1426C56EDB6B6F6832C95C0834581BF6240674D336D2437B820E84040BAA93D2056C1C3B2F9A0A6536C19F43A1D0904C188421805451BB91C908F159B1D308071D3886287C2CA56263C60A43F67916A3A79B2FA0B1BF897E941514E78968276508F3135D8A9B8E0D781FD41C05EDA9F2C4A9B4FD33B1C80BB690C59BCCC8CED95C1DCE72136125128507F30A465AE10AFE44CB764697B8DC1AD2CBA07C73BB888C9A559407A3D0636A4134A3E162853493C25770D10847A72956FE3CC01778049B46142AF4B4AD1D900B44A149323190500899D2801AA5C031A0C378F5CA3A778C8B7B688EEA26CD720CD8858A07DA46C176C08278B7C5D47AA6AA92753C75BDFF72E88708E6627BEC0335331D38604E84EC712B0D3EC8714E56FFA8807F40B8CE6C3723369883EDB462AD9BC972206A16C78DAF8C1428344E9C76B161A818BF8835A0043621B3841F80CA3F02970F7882B715EFF117B80E125ABFB406BD75027F9AD34EC7E7B629C2D834C90225020017CA511C8E83CAB0F98C739EB030E172A67586D90A5827E063B9C7B0705BB61F5A6067D102776F18950C3C2540198D916B7D64C803EEC3701799DA2C176AFF38C26004F3BC18144A3501D80B580C1442AFCAB6A458C1BB49E5A5728EB2818B6EC539F5C931F9743EB34B889401D19D220CF7822DB6906006B59A5CC90992691A2071631A96B6D8617F7E863C44A610E72A34CAB05D68281D07088D23580F0A87C870684D74C66D1827BA2702024098377AB359F10CF21B7C5EA9A77ECC121B9D81E52B4CFB315CE0076757EBA100F87A3CA1B28785C46B2B2856160213B09B3B0844055515085F721EE47356E31599D0071214C8B1EA44374890838D1C6636383CD9376D131CE1139277911B221A5CFA182ABE535AFF8995F573B55CA3C08CAFBBEEBB92EC280BC351735A5891997972104993F53321E70E571E86250CCF96010FB59FED89755317A0BAA5F5AD1A6CACAC5CA4612F2B27E94D6B61DCB138A661B28B03091C173BF96CFBBE00CC94223328B55F0B848E27497BAECC6A588C24EB34BFA771C94B4C3B6F87A54458941691C6945470AA68552B77DE5245CDEDC8A457805CEA024034C5731C296E0A468BCBA19D0DC7EC3A38615B10E1D7CB9B61315786A2AD737B384E511AB0CB300976324287063A76B589B88A686A276CA770AC9B79A0B0420692AF8D28AFE392135A62ED20C953DBA3EA0B1B740715DB8084B16661CF9F497BC8BC509E9A9BF5713769CC309090ABD3BCD49E69A09B6BD01536883B5ABF87622A93566B51B1E0C8A0A191A0F90EA5995852BF0EB8B3F15BBAAF55B82D900A74507D167CED79B5D51B5A7791055AE835D79C1B0FCCC0B213664BBF35D9B604356A23E89F9510AE2B8EF6BA3FA52387AC92019A773C4F26DB9F44801F00A2BDA3C14B2953486A7E5605E1B523F894510FA8BB85A080E589A4538F3E0C08FF714113962C5A6FC29172509B3C1AEC2FA41707439", + "630859B0E9292AF46619B7312DE45D6D4B01D4A4BF075372B8A345E1CCC66F3257FD3A5B47EA046A168211C807672C13BA4A48AE742DEFB760CC7446F086C0A8B439641A24EAC5CAF3A730CD3215BDE8B419CA04FAABC3FF0CCB8BDCC8AF1495886B6D33B01D25B032F7C3282AA9259E5178D5841106797A5D559882CAAA17EB695C397BBDD297A5E24A9E00532D355286C09197FC0D186666C5D5360E42B1DB865B1BA950B7D1C2CE46B64AC43D89404CCEB3CEE397175237B618D4112917535F250F2AF42E1A05282CB7CE65C73117F934E1B661CEE4763C375820F09F87916615865E30E146298B525B730B1D89AF469509A0F06FCBA5CED2405DC210AA39FC55251A80A8A0037389CA7DCA5D9A10B777B6919B6C1E805588E3F20849E7B2033C5B781B0545A84E842961041891F91A5E5D4C211B86B8ECF91DE0AB42919B1A5B2245EDD54689AA169E61152DC94FD9C1B80E1602F3E69F9BC38455C93FA2757E14A6369E942E8EC9173C9BC9168B83233902EDB01FAAA66CABB796793C53DCE32C1F81C329215EE9002D8D09494C8301218A379E1B3EA221BF2C36A927E4BDE0F27CC72735FC4B3E7C181F07270A8903460C88B5BE499B5763B79BCA1FC964B75ACA1F2649AB34771D90855C03AB8262F99B7859651FC49AC5119AA48A8931E19035C011A2FA7EC4464DC35B8D7D3392EE0B710A98B3E565CEA3925FFC9202DEC07042A12C677126ADEB25EC34BB0343A1DC311D55F464263C0E32287EBD5A6735682ACA478DCC34B5BF119D153A2C26BC4A47E743B5721A0A1B3DEC00196A006DAD826849C1146D01B891F690D7F116187A966A689E4475B6724445ED17CF1FF4026C8B587B600FCCF148D47852C546A70BE6767855603C77258CB4BCFA1CA310D533171287C5166230C759430B5B1027B8F7C1BFD8051DCF29B090423C93E3301E39ABE8F3C145735D5B9A83E5DACA1E013BDFC51ED05A66DFC8CD32798C928627C98393A1DA003C797BCAC402D3F1C6AD5B8F56626D7954A7C587AD7C7223D622A58169B826456088AB50D4A48EF959124271479E6AC7D979675E222ACCB2BFB8748AA9416B0C67288BA75963196819BA8B20B116F79955AED72735C08BE4297F28ACC8DF25215765C17F9A775310699993610A490F2368A87E9019CB60370E1749721B409B398363FA49F0246C08948674E3670F32525A43C337EB9677760264A05626021C2DE7C180935D3532285905AB71F3B94A259DCAF6CBD62BA5F7129AED0270EE86483673693EF90B84AB39A3CC10FD322D7A989E527920526A94DC8CA7AFE677376625B41AAC4BC4027A316323C877C5F5811888925B81A76EBA5D496923012B37FE6C2827872B81E65339AC8BFC57AE68A3C900C026DDA6AEDB09A3CB98CC2A809A7218B190A5749C42AE4F353124809D78733DC52A89850159AB73415988320C7A093C01D03F4492293856A531A4F013950E30C7A548BFE0466987C339DBF08E9A0B4EDECA083C399F2E6971CF3AC647FB5EEF401759E6C292F038DD732847545A63E43E17579C818C3DE888895F3AC712DC6C767158BE311F24E44FCF91ADB2663BBB0B1ACE801495CC5ABCC208F0DA634046503C694646E9368E43CD98A02DA67B1138EA5FB5BAB0DDC87F85E0A2F7B7308AC26B72427BF7F07DE8B5C304A60B23AA50C2E05E68A9206C3859CA017034814242F49C91A734353C0C17D075E31412245C131CE863E654927337AC046B1BA83214336C32CA8BB179F671E010922080BF1C2891967017459788E4B69131651BCE03911AF8AEDD6570ABEB17727BA25C719E69CB226076780BE183FE705E1539480C147773E3C313C359C3B13EEED49139B27AA78736F41283005CB40375C2DBF28D2DB5A767F0814D661B7E483048E465EE012E877760CAD3A25BA95356C087E596540A603DB8BBB45CF05D0D6431E4B5646D045972FA81524B9476F07F03BBAB400150E4A840853B768B0C02B1D75EB4748E3248BCB7CC037EA70F882299EDD18470443D9FB7A479D83A239809C166B6E7BB61ABD1BDBBA7AEC9B2BC55320F17FB72BD842D52DC8315A8738875ADBC254E5AF369FE32CF8AB5CC45459D2F7433D3777312F17171E3B073F01E0D175935E7B870676D73C1015DA7A64D6333E6601A94902B5CF432212464C32C44A8CA8FA6F6620899B08E968B2F047B42B00527901974337D3613C1BC278EE8D5C254B4898E1088D4A8C09EE175969407C01B11C5F8239C4A0455DA5DD6876C18160944054F69545A5340C5E2EC8A378B1824D313D4001F20FBA7B878AC5F490E0EB65C12D0ACFF68C9F9E54170F4137A4C206834BFEF81651C61BF3A6736B981A222994D3571B1B802B5D759847E915A9BB0B1CA894E055B1DB0FC5D7EE39C5695C10A3112D4E2A0364036C1824C7F32627E81176E659AA1B6CA4EA3CD6591CB7E86CDCB57A6440B6D92B08A628C378408ABF7B93CEC59AA19EB648B39170CA87AF41389ECACBA26919DBD3C4870360A72A398DC7CBDA62C26AE6A0419E5229DA7C77C725FFE9AAFA9EB0E5BEC8B1CCA02FE97379DB20143E21E1AA8BBB55B724BB668C10932BA9C58A24536CA9CAC675350C7F72157B0AF497BA4B16425F52069EEE144FA96A40DE99A1FE10D95F71102FAADF1A8A0FE8038B5A49C04C952C97C65685965E18A0BA8B805AE2839F61278B76265082329F6CA4040242A760B6CFD683D52D6BD4E1156BCD181D4C16FDF9C75426A8000B6952E2A023D3A59844B72F3D9BEEA35BE4F70988871C6079A2F49474599C9A0BBA36B8CC41865363B42554D9722B9CF7A5BCC1812889BCEDB1B4DED8B48BB42695D83BE31A543367819EC75851E1B920BA69AABF57007CAB1426C56EDB6B6F6832C95C0834581BF6240674D336D2437B820E84040BAA93D2056C1C3B2F9A0A6536C19F43A1D0904C188421805451BB91C908F159B1D308071D3886287C2CA56263C60A43F67916A3A79B2FA0B1BF897E941514E78968276508F3135D8A9B8E0D781FD41C05EDA9F2C4A9B4FD33B1C80BB690C59BCCC8CED95C1DCE72136125128507F30A465AE10AFE44CB764697B8DC1AD2CBA07C73BB888C9A559407A3D0636A4134A3E162853493C25770D10847A72956FE3CC01778049B46142AF4B4AD1D900B44A149323190500899D2801AA5C031A0C378F5CA3A778C8B7B688EEA26CD720CD8858A07DA46C176C08278B7C5D47AA6AA92753C75BDFF72E88708E6627BEC0335331D38604E84EC712B0D3EC8714E56FFA8807F40B8CE6C3723369883EDB462AD9BC972206A16C78DAF8C1428344E9C76B161A818BF8835A0043621B3841F80CA3F02970F7882B715EFF117B80E125ABFB406BD75027F9AD34EC7E7B629C2D834C90225020017CA511C8E83CAB0F98C739EB030E172A67586D90A5827E063B9C7B0705BB61F5A6067D102776F18950C3C2540198D916B7D64C803EEC3701799DA2C176AFF38C26004F3BC18144A3501D80B580C1442AFCAB6A458C1BB49E5A5728EB2818B6EC539F5C931F9743EB34B889401D19D220CF7822DB6906006B59A5CC90992691A2071631A96B6D8617F7E863C44A610E72A34CAB05D68281D07088D23580F0A87C870684D74C66D1827BA2702024098377AB359F10CF21B7C5EA9A77ECC121B9D81E52B4CFB315CE0076757EBA100F87A3CA1B28785C46B2B2856160213B09B3B0844055515085F721EE47356E31599D0071214C8B1EA44374890838D1C6636383CD9376D131CE1139277911B221A5CFA182ABE535AFF8995F573B55CA3C08CAFBBEEBB92EC280BC351735A5891997972104993F53321E70E571E86250CCF96010FB59FED89755317A0BAA5F5AD1A6CACAC5CA4612F2B27E94D6B61DCB138A661B28B03091C173BF96CFBBE00CC94223328B55F0B848E27497BAECC6A588C24EB34BFA771C94B4C3B6F87A54458941691C6945470AA68552B77DE5245CDEDC8A457805CEA024034C5731C296E0A468BCBA19D0DC7EC3A38615B10E1D7CB9B61315786A2AD737B384E511AB0CB300976324287063A76B589B88A686A276CA770AC9B79A0B0420692AF8D28AFE392135A62ED20C953DBA3EA0B1B740715DB8084B16661CF9F497BC8BC509E9A9BF5713769CC309090ABD3BCD49E69A09B6BD01536883B5ABF87622A93566B51B1E0C8A0A191A0F90EA5995852BF0EB8B3F15BBAAF55B82D900A74507D167CED79B5D51B5A7791055AE835D79C1B0FCCC0B213664BBF35D9B604356A23E89F9510AE2B8EF6BA3FA52387AC92019A773C4F26DB9F44801F00A2BDA3C14B2953486A7E5605E1B523F894510FA8BB85A080E589A4538F3E0C08FF714113962C5A6FC29172509B3C1AEC2FA41707439B37161E8A5BD109953D75BD81E062D36D81EC8718B0FF11F835FD02EBB0E37D1AF05419B0CB3DE1B514404D1C0DB9017182CE71D7A0E69CB141A94174CC758C0", + "13E45134547251CF55515976985578ABA6CA97FC3FF6DEED8E15AD947117FE7D2F2DACD7E681BB899F5F39AADCA87326085F01FBDE655E7C8A3711C9EA5F6604A547D3D019416E46EBBF851BE3C2703A3241188104EC340BB548E5E37B65F2272C104802AB743C88908D299EE6B9F1DE6F8E0038CC9C4AA567341A4984A8F101E2C2B2795D29557D84D61FE9A8CA937C2123F02BC595BEDA11431C1A2A7D45EC5730638BACCF5FE8397F8F3893C729208138912B846803DEA9D70425F52F07CA7B0BC60B09EA741BC737E60B7C27833BB1C7F2A8D650A7E5ABC1A963350CA34C852134A650254389F04EBED26D33F0BFD24877DAB9FA7173B12FDD6B52CCFD5AC840FE04E2381781A626C814B58EE149FEC67A5D978222AECF323E60AD10077FFDAA62960326191D9513F5E44B22D318D51DECDC1598CFEC5CCE77EF12114ED335837A9D999CA572A392B735A1E10808801687F66B8ED5E10D5E0E7561A1CB07E411CB1D2EAA5A55A9DDD146B661564CAE0BDC4FB175E508C43F6A41E2FB31A38F6A6750AB9B92C69B19CE9741733B77B49DCA86CA6A899D39087F285003358D5C8ADD8A8A5987B8B0AACA4661DAC7301383D80B1EBD29098BEB447E0159F06D72A414F0E28410696D71C7D717967196A22160D9B36615240D75998B0F1177332EA3EB3EB6F9B533B939494716A935DFDCC862EC0A25DA2908831A8DAF5030CFDD0262D0CB5DECDB4B468A3A75401D7878B9714F4C4D0C07538FE7DA6CA3FE9AC5FC829787CAFE2833740A6204B85E951E6AF2D5DF448F125004D85A212900DABE0361336F6284A8E3196B06AC45BFF78A10759D33307C4F6861641A8A515B2A56892B4640B0FD5622637ADD9AF0BB1C3F742A7803D2634236C69431C0A41D99E64A8B454652F3FA507CE74233CA4F49DEFFE91A16A08AAECEC3EA8DAEFE6F0981D2B4158AC43E6E10A8EDA0B112CDBF42EE0E5D552B7E523F006B04FABB3CC97D2FE955CA1A6445A017B6C434268D8E556DF27A6001B59B0ED67557B0673A3CB00959FC70ED07E9EB413BB62F05E7EE6D289215D267ED39ABD9D8D93B0AF375C308C3D08C1DA812502F79B36ACB07F0B97A6A0A127277B72C63394BF35054B9603CDEBA40D9809B8A51637D12B4CC826961B95394635843B4BC3E721670AC7E70FD6FDF9B7533E41A1674E2A189DF133441E7D09762E58B6492873CBCB3661C62C0ED26D1C4155C7DB5D24B08B85476CD9BD4139CFAE689DBF0B6DB7E1E0EA8FF81A8E506704DCF068EBF64F50A8C97D3C127CCA57EBEADAB37E2137E74C61927ECFD24897BCD207B475422AD6B68F9A6146FF320C7AA0B2BBB9C8129DB1DC03E094CEFE88E780DFBF4775E6464F77B5D2BEEE49DC074755110FEC2F43F728F866B0B51407B6AEED48DDEA08490CA786CFC1395105A13CF2A8D654AA7583786064C5AB4C3F432D1FB0E4F4F03F9E7580F839F42F1B72EFFA5DC5D0C90B2C21BD8B93AC7D1EF1603357DBF40DCA8AB551063E2F9C0600BF731BB54D21BBDF15A210EB7E5F2B8AB02468348ECAAD15992535B5EAB1E7C486B6E97968EAE3ECAD37CE9748573673381ED892627CE2F2BEC6D709390D6DA01394DF9B42AC9508C76BA57369FFF42DFFE028596651010D507AD065239E9E00E8C797ECB6ABA2B6DFE80B724B5F9826F0642E4BCA077A8A661CE86593CCF8C685FE690224E4A3F41D15115672FA485C362D641498D655A7CB1D696711DA7F77BA414D528B1BC861199BD0562832BB8032DB885786DE6C60170F33DAC20C2618945F4744F6C6FE1B10A2E24E0443BD60B1F40D39735625725FE20C18EA6C0192E9AF47ED1EE9D23DE3B83098FE40D36D2D17986FEC683FB5411CFEB8B1A5A61E990FCF6B3BC8E32E3B1F1A4BD2C3A86C089C38B3B1266A1CCEF53B663E0F1D28A2B63F11E1E2F86BF43E7542D1EBA891DD978C8F573289933A9B182FE4CD8CA072A59C4BA15D0ADAD86F7735307F24EE01D1789666A7123C25F46B38CDB72B43F86CD5A82B97EC8916ABDCB0CC03D1751106807FBEB21D1503C3D23EDAEB255F30F252A8AE83B5C02BD34DB15A34FDBAAC51D0E60B2A415E1BF0E07CD2453C9021BECFD13C73734F7F6BFBB4145B27F53847873A948977401EB8680ABAC4FDB1A5E8D1A94E6D1A8A50E4BF2CFDCF198D804B43B053276758C51E3B0A20FFFAE94B30705A66E5F586396", + "0178BDCD5B5412B1C06884AD4CA3EB43607566114B599A22323C508578F1CCCA", + false, + }, + { + 98, + "06725AC358433FC95A1269AA9247169504CFF462C01771B40583CFFC91162AF4062FF70B95E2C66EE373191051215B9A29222119385EDE2C514E018A52D847E0E616752A5717F9AF8FD6C5A4001FF21497EE765EC0D040A1D16CE9EC559DB5386C5C6B77B62A341B532FD13A7AD960D80B01FC2751B0398BE7147D688B7732107871E8C71EA1B8E0ACC8B93623B9E3848BF811FD25B1AB902048191BB516CCE4564CB1715C1D1BA7E3A6CA8153951C663B8772CA3058535958758461AF57805604E49114B2751EA13760E95985003AD06C86710B4E7A231D6713CACD75309077C4B92B288C109780F27663B8389594AA84C014D54AA1692C70BEA5C36685B6C66A7D11FB0B94DBABB71C1C80C95BC9277AD0871354123C0A231D255AAFE43B2E10B92FBB99390D00AE3FA68BD938954D78BEC3C08A31A1A47C03BE2C0747F4658FDE4299A58877271A0532C46DF2DBC18C655F23041BA820AA2BF37EEE304A72A587D15A1B26FABCE164B7700C09910902A87516A41054E24AA30623206FBBCDD57612B799B3DAD37D7DBB4A8C43CAD3B0C3842980C255C0C0582D685067BA837B9C2B0997A98B57B9AF212139DC4047A572484FC8106669036DB9951E460A42A581FCBB099F015705203C8E636965EA24BCE902706C3108D7B86A410A8D808544DB34E66A6417017CA41607CC938F4DF27E621C8716F834753B959FAC7D83C76CD80C167AACA2C00B794C416D4A008DECC84163522DDCC22FE5F519485AC5B2389149A694C86B08ABCB071BF50C9C93BBE7FC565D35919714336B413F08004BCCAA4E390C50559142FE35B65D56738B003E38E59712A193165C10F0B99C80C7BCDAB7102DF68085B546581852D42683D9D070AC72672CEA16CDE5C1239136D44CC1BDC92A91098CAC6C4B66B87546B71CA0B882A102CB50E0A0530C4732228508875DA38688585357ACFA74566C48641948200048B6FB59064B620A0754BC2901B4E89BD45C47E24BC9CDC7B81375324EA28429A3C2F273611A2BC2A60B773BC21AD2353F7DF804FFF062FC426E604A894532A051B13D97120DCBAA9F97588F2F96075614BFB6C78D43610A28044CB49969CE2A923D2B6845748EEB8B7E087CCC9AEB16A4F723A4C5924787AC7212C50B488CA99B99D5F90C2603073737A6A68B111D548C66EC62F79134D17C3D3F8BACB1AA22FB3C9515E423A350CC32ACA6C29CC621486CA1992D32735962C9819BF204594AC5AC92476C418E3121A187771020219551FA1BF8A9BF5373286999A7F701835C19CB4119A8B6AA7CC2323967E90721653AA9D8620378C1BC1AA205B13BD298AF5C406ECFA8061A9493FDEB69A3714196DB126277AA9BE11077E96CE196AB2C51202CC66C20F5C354A1C95B46726A6B2FE31702633200499C9AB779090B8A6E8D9B6B1A9452E94A698FB396D87978083C867D0B79B9016ACA4906EC109C1A291AAFCA8FA77C24E3A254083AA4CEA3A370A9B01D3A10D43B487E8496075840E9901E8A0845E9B550AAD7C2F7A35D56D19FB923B130574EA25B256370341D18872D158858148BC0EC3900DD3DFC1770D2D4A790307B2AD4C60B45919BAC9DCC913C66A277ADEC19197A5EB0FCA94076515A1993961322A8D82E86B286F4013F541B15AFE8BA9538B3F6F257990BB2C37901F4A961F53191B0BC4DA684C585338D2EB0B05A373BD2BB97F7B4061295C639DA13E830BAF22266C9326DEFB4BA732A392589593A31B5106C431CF5A985DC927E5567049082B62430541829C37C9A1ED968E42232EF50441B3480C3737CB0B57F2A7A5A48687F73D148FB89C7CDC95424CBB42A186FEFAA51C20C840493360EF05070454346784EEF826C34F15FCAF9B50FB47ED0467666A4BEFF465283454A24245ABF870BCAA89D70D51334A2942F774908DBC61693070D58A6997B6A1F6B2AD1D2931C27C74304321457BA3666153B169207B89786E40C2E050B560A37503141F67420A5B05C88B54F4BD2081920C314761FBB60C85D1443E93089DCA26007D3402FA918D522C85873C80F1A041AA63C0BC65C6F86178C61C5C732225980370BE51C7F065FB4C45E3C73BE30F4A3F31BA63039577E775CC0E2B62E8A1331818BB5943A637697ADB5751E16892DF3272A638051E009CFC0831632CC388D5C54CBA8FEB8607BEE47D52B8810EA13B378160BE4DB04A59B794CA91846", + "4659824AA22A4F3454BAD00ADB48C8CAB062A10470D83A74BD6A09441B5CB1D3AD148765ED18AA202CC5841901B929871E085F3F0B4FBCA649384176E962C9CF4022DA73A66D25ABC24CB5BB196099541F238518432955D2803E29279C7108CD2CB87832386368EC9A8F07CEFDF2B291A49B164055D78840EF6256E1E6A8E4ABB87D7137FA5A2A6082B5773C2C33241B00DBCC6C60561B3C66D3D5CA3FC66044A5AD63033011FA720425A874B805B0E9A8EFC6B0F701790975A7ADD8A3E78C7FFD872274E1CAAD0A087CDB742BD440931C5E43DC9B834A6EAC3C00E96415F0407C7E75CC9A074FE04517BA85AC694771D1457C87755DC6541E05E328F3E328EA87BDEA798506103A4F68C038827E1A357097B9330AF68D39C15FA6532943F6A8BA3ABB33738C31E0A96BE3BB353BC9C955A2E2B89692F4C4C1B520849CB762F8021A408FB6BBB9D1683D7657AFBAFB8D0E185B6BC03431C6C52BC69432FB347BECC48975A015A078CC691DED516BD5900226F2B7CDE6728608808FDC0D86F02011796E4A973A0B57A3CFF25A8EAC1CADBA93CA6397E00AA5C9E979DC90C3B7F729EF9C894A4739FF69BE65EB313DB1C1834C097F75B085C16C3AEC8FF946963312B82D6292CFD658D599410F871EDF522FCF952B6343A7EAB52C59C0B1E7822E6AF63994D3896774614E6091B6132D7F8A4F05619A4213037F6BBD5CD4466593A9BC7729929031B80A8906F031CB4C3498B93294F4B769C661D2B7330511CA43E8A87A817781729E45AC219D41306FE9B63E288D724B82B2A07ADB351108C15FF3FC67A9819403DA49F29659765B6D33D18D3F0A65CD5A4B2CB58736914DD7B74ABB85A852D1BF67A1B03DFC2B5A73710249526B5835EA7CC9F17059B761886D82B891B65844678148678876D8B9D765252E0A5E8CB18A4F9120F01954FE5208905AABC7D1163300B6B2F0BCBAD81C692808FD3793A2047C033066FB55AB924245193C68F01064EF610D7DA2CE64F474988B037FD79EAEE8554EEAA077209EC51849006886264574EAB71BCD93A2A735B44E4632C0D43F39142810D10CF58812436964D5BB4487157DE703A56186C3BDE12A5F074C6D947FEB740403C27D8BE7A2AF097F553947628851E909957E428AB6E43B5DE535F8817B234244C0930E700530BFB2827C4C1DD9570D8DD86A8CC2352F97A0E1AAC353E761CD7226D6F62EBFE95EAF280EA77A237E9063CA9718C5227A9A092587CCA109062679F84F890164BE7B1DF041969648272B21C76F78A02A7617F22CB389F9640FA75E0C957431696081E0AFF3E1B71A100FB68C0E4217A706BA06A1F52C1503AA8D06A28E67BEE5CA9F5DE5B1F4C2ABBB18BCF03694C5A70812F80982B28B7336B093C56E3C042E9551AE34980EF8C7489A541DD6F6BFBB0BA25485468AEA70AC373E3F80BA883A3346614E6BB00A64B7CF0D9BB7C1B8BD18B838757673CAC9663A88BCD3E4AEDD04945DA02AAC359E4129B42665A3C284AC94C75F97D932E442629995666D93B8C43ABA34AC420F8035D529C3FF5B04A3551EA9BB75184057F1D657A4A181655427543B7DC63546C8B0CF1FF9057336B86103239BA991102968EF841632306EFD994ADDF103D80A5FC34881AA54C217460B7A6BAA753820EE8B39C6553E88575152A103B26376C49A815FC17813EC0BC82A86668B965D641C92E02292E4065E2477D2927917A197063B9F27B0B996D90AE75314DBEAC9DBABA71A1713D616BDA831895D8A4BA187C0390A979C1A7ACAB23061B7A02192C2DB372062A42BD9BB10898571F183119D0C4E72227D2284B45390A516A23502374576C906D13509DC20AEBEE88EDE13603BB0404C52549667C0CA9BCA084837850BA4A2F93B183017E0192B76B14842644FC891279349224FC9C60F3C0595A93FD00276724493684A650FC00A9E029C2CB8900DCC363F495AF6D5A5E058CB522823D9113A68433919C5ABEE896DD0110957D7CB64858AA2C76E4CD47861C21EE65AAA38AC7B7BA262EAAB2A4311935AFA35DC31696D948964B596AE12356023835C4643F158127BD321E18B084558A607CAABA52A952EDB817C176BF1A4473E2558E23A5B7DB8781DE064D6E3BF65088BA8B20BDB0B629DCA6658309C4923398A8C82D73A9FDA7C6306725AC358433FC95A1269AA9247169504CFF462C01771B40583CFFC91162AF4062FF70B95E2C66EE373191051215B9A29222119385EDE2C514E018A52D847E0E616752A5717F9AF8FD6C5A4001FF21497EE765EC0D040A1D16CE9EC559DB5386C5C6B77B62A341B532FD13A7AD960D80B01FC2751B0398BE7147D688B7732107871E8C71EA1B8E0ACC8B93623B9E3848BF811FD25B1AB902048191BB516CCE4564CB1715C1D1BA7E3A6CA8153951C663B8772CA3058535958758461AF57805604E49114B2751EA13760E95985003AD06C86710B4E7A231D6713CACD75309077C4B92B288C109780F27663B8389594AA84C014D54AA1692C70BEA5C36685B6C66A7D11FB0B94DBABB71C1C80C95BC9277AD0871354123C0A231D255AAFE43B2E10B92FBB99390D00AE3FA68BD938954D78BEC3C08A31A1A47C03BE2C0747F4658FDE4299A58877271A0532C46DF2DBC18C655F23041BA820AA2BF37EEE304A72A587D15A1B26FABCE164B7700C09910902A87516A41054E24AA30623206FBBCDD57612B799B3DAD37D7DBB4A8C43CAD3B0C3842980C255C0C0582D685067BA837B9C2B0997A98B57B9AF212139DC4047A572484FC8106669036DB9951E460A42A581FCBB099F015705203C8E636965EA24BCE902706C3108D7B86A410A8D808544DB34E66A6417017CA41607CC938F4DF27E621C8716F834753B959FAC7D83C76CD80C167AACA2C00B794C416D4A008DECC84163522DDCC22FE5F519485AC5B2389149A694C86B08ABCB071BF50C9C93BBE7FC565D35919714336B413F08004BCCAA4E390C50559142FE35B65D56738B003E38E59712A193165C10F0B99C80C7BCDAB7102DF68085B546581852D42683D9D070AC72672CEA16CDE5C1239136D44CC1BDC92A91098CAC6C4B66B87546B71CA0B882A102CB50E0A0530C4732228508875DA38688585357ACFA74566C48641948200048B6FB59064B620A0754BC2901B4E89BD45C47E24BC9CDC7B81375324EA28429A3C2F273611A2BC2A60B773BC21AD2353F7DF804FFF062FC426E604A894532A051B13D97120DCBAA9F97588F2F96075614BFB6C78D43610A28044CB49969CE2A923D2B6845748EEB8B7E087CCC9AEB16A4F723A4C5924787AC7212C50B488CA99B99D5F90C2603073737A6A68B111D548C66EC62F79134D17C3D3F8BACB1AA22FB3C9515E423A350CC32ACA6C29CC621486CA1992D32735962C9819BF204594AC5AC92476C418E3121A187771020219551FA1BF8A9BF5373286999A7F701835C19CB4119A8B6AA7CC2323967E90721653AA9D8620378C1BC1AA205B13BD298AF5C406ECFA8061A9493FDEB69A3714196DB126277AA9BE11077E96CE196AB2C51202CC66C20F5C354A1C95B46726A6B2FE31702633200499C9AB779090B8A6E8D9B6B1A9452E94A698FB396D87978083C867D0B79B9016ACA4906EC109C1A291AAFCA8FA77C24E3A254083AA4CEA3A370A9B01D3A10D43B487E8496075840E9901E8A0845E9B550AAD7C2F7A35D56D19FB923B130574EA25B256370341D18872D158858148BC0EC3900DD3DFC1770D2D4A790307B2AD4C60B45919BAC9DCC913C66A277ADEC19197A5EB0FCA94076515A1993961322A8D82E86B286F4013F541B15AFE8BA9538B3F6F257990BB2C37901F4A961F53191B0BC4DA684C585338D2EB0B05A373BD2BB97F7B4061295C639DA13E830BAF22266C9326DEFB4BA732A392589593A31B5106C431CF5A985DC927E5567049082B62430541829C37C9A1ED968E42232EF50441B3480C3737CB0B57F2A7A5A48687F73D148FB89C7CDC95424CBB42A186FEFAA51C20C840493360EF05070454346784EEF826C34F15FCAF9B50FB47ED0467666A4BEFF465283454A24245ABF870BCAA89D70D51334A2942F774908DBC61693070D58A6997B6A1F6B2AD1D2931C27C74304321457BA3666153B169207B89786E40C2E050B560A37503141F67420A5B05C88B54F4BD2081920C314761FBB60C85D1443E93089DCA26007D3402FA918D522C85873C80F1A041AA63C0BC65C6F86178C61C5C732225980370BE51C7F065FB4C45E3C73BE30F4A3F31BA63039577E775CC0E2B62E8A1331818BB5943A637697ADB5751E16892DF3272A638051E009CFC0831632CC388D5C54CBA8FEB8607BEE47D52B8810EA13B378160BE4DB04A59B794CA918460F828896B3ABB156C26BC2E37F4C2DA1608C54971206892640A8B7FE278D8A82B65351EB0FD5E4BA7378EF3C5D2A5DD4084824D6C383D2377D36484624298A81", + "5CCE7410F68738ECED68AC00E2FF78963EE06325D7E030590584A30F9B740A2F7A5CEF919F384D1801827E677C2C155089EDDDFE74471E319B7A96C74A751368354A3C5C62D517CE7C20415E6E2A9121277E039432B99244D95283B9222AAE6F675B1FEA9E9AF71D60C82B8AB74628C2C0A626E8FB7FEEB3A60B97D85EA7BBE77B3FCC85FFDCA8C4501BF7B9AF179716EC483066FA92530B63BC0FA3C296997967FA1B3D98667A350447D59B8FAD4EBDC24F80C25A525022E1CF75F497BFD0889711ACAE5B5FBA78FF0A1CBBD47A352A8709085AE4ED8D06D05A9388FDB106DB6DCD93BF21407771A66B26C1FEA1ED2D15AE345F4D8131E7F2523AD95DA91D6A143AAA08ED15577DEF0F274AE853B41AB63650E7E733D9D7E9B3643FE4CDBE607E5A7DFE75AE57BE8C91932FD5D726691FBEE44D8F83DC94A78CA87369AB35371523462D3254724CE245FA33068606AEC2B7144706A18F34793AD298229E073511EFF4062182DAA6BCEE01CD388629ABA8EEEB105BF0DAA0120BA9FF27FF95EFE6FD4842342DE27AFEBF8972CF100FC3977B2F40E851B7DD14C02E9EE9EF0A7000882718EC2055C25BED85DDDDA8FA7379A904FEB36CA89E749A54F633042FD87DD70BB261058E64F82B5DCF19CA59233EEB364BBEAEA9B71B5E475C33D811D60E7608251F2B4DFD3AF9337FF03B9956BA7F53312E1955E9A1719C3CFAA7CCEDF8AE7CD90FF4F6129967295CAABEC97644108B3C5CFFE65E55ECB93394BA5F56E94FC2720A66023A4CEF20FBE9A3B7CC6684E7ADFAC2644E7E6A735A26D5BFACB56D03BB37474DBE73E11390DA4243162EBC5F93C9CE7CD5AAAB419239C89CA62B7AB233C7688803A32751234D7313DA0A888F41C9FA98C4486AB1D474EC933993BA7C882BF6A5B459B2F8CC606C9F6445FC8DC6CEB24BC376FA9BADF1F2449346E671A584BE000278A631B42DC80A6F723F64D273BC264527247F9B6220371C05FD3F729527FFD87A7C1528A0CC6BB2E5F5E75B009B9D8F92C29CAE035CFE1C16D7B66D32CE3BFC8447BB247F62E46B96A187CCE43256C5E1A0DE4B339854D281BD0AE36D248AC25DE1419BD2E3831BD9E1F5856CED3AB7B1BC9A48B33E24DB0814AB30DB03A25BA58B4A6CC39EA45556D02C6CDF9990938C002ED43EE740E8CE2F9C9C198D287E1225A495B5A054534705D6C47E00CC997539B63433A248D6331F70888CA4FBB01084DF34A8ED1934600FF0052C2C3E25103C6B588277E0073A6DDF31772DE96C54417A75D7E1DC92821B464C5418342D96E10551C81103978D7AE960933B054B7EDEEAD4601B670B86E080B6EE898E5D225897D683AB9006F1BD97A5B99CF95A9E8EE4C7CA4078D6D31F8A3C3EE51DCAE9E87CFEFB2FBAACC39DA57579699981F637B7755393093AE7C3292B748BC26DE7B6AA6E1E06A8BA4BE5AE0D7D8EE929F533EE1306A0C59EEC0EC5A54407118E2DB2DCC2927A176B425AFC0F6AF16DC9503DFB8F66D55990FB9116E7E5EDDE67BA3F9A42746A3103BCD0E3E688639AEA6E63965524997131F9F3BEB988B1AF9D79C5948FFF35EDBB579229659F009C8E29E9ACF753907F4126FD87C8FB1395CC1F0B796E7FE67959C034030333FE6A588FBBFA4536BCA76C69C1654F1BC08914A67BFBC229D8DC1E8FCFAD86F30312F544458818335E5D912E127524905C1590E198F3474529B28F2DEF383CA2A156D808A0766CF4BC99F1730BC846DEFB1C80523639C6CCC65C252491BC37242C0DE5F1F3B0CDAFAC8438195DD2F4AD66CF7EEB47C5D37043B40B5D102BE8CEF12710EE0A3FCD3A01E30A0FBC7837E97CAE4D6AB4A57F9F126C4DE3F8FD05DF3387F3AFBC4D33AA708AE4EF76266C76B6C9C3FD7FF9205146E56CFE1BA1275D57035CCB742B4B16E52F8983AC52A7EC8736ADE8CC998B82AEA49718A9399D2F7B388D16300956ED7A42854B712BE4857FD0FB85609930106BFFD6B91989AF5CBC8A3578DBCDB43A109E0D149F54E03F5B1FE3660ADDE9B58EC6133EC3724371D1B3D356FE0B7ED1FE1E7FDAB3D1DBA4CED7527D07296E59656A678091FE69E2F569AC15DB85B47A0D8CA9F81295C96F894128C52EB1F2BA3124D4D174D58D04680EE911EAA4029CC513F80523DCA639F6AB2649B5E93ABAB36DC1B8FABE40762F842606813836C3A5C011B37FB8B77EDBC1FFB118B3AB641C0616", + "AC32764AABF8926EC41754181B2CFF101B907C0D60FB92638214AB9164E24F2F", + false, + }, + { + 99, + "1023C20E2CC659E9B02355287DE1643CE2155E736FDB64213825B8C5A849E261C309B69558932170C5675A53CD4F62898593A8792535A0A06187EB688C1AC90B957719BB7B3933002A95324686741AF6998074AB9CA116ABD10D3CD482BCDA03710233FEE6296C718F3EB846E3B32FCEE59E2697ACA6C4905E86260038A0635504ABEAC8B656420DD22954A62AD05CA47BF685010CAF64A020B6782C77BC8DE7FC5BE33C119452AA518C4AE5183E6AE3AE195C07D3E4372E7B77B31A737EA6017EBAA1E9E1218970B1C26926A3F758D2301E2449B917D1192987613651B404D98E8A652ADA735E92D34192B6261A52C3A56672642757669A5716A277F7DCACC7D503007D805E950DF9765643349125310742042D4AEC4CB375C8F0C043C82A72CDC73FAE372DDB301F0C684A02C94D9C415E7830255C60CAC599392FC2A63294A2FB22B72009495E3717862AA7D37C5E8A0C0A89E990F16759C54AC9D7D6911EC0BBEF342C13CB988A12452355A8EB57734D1C5B42E485E5E70C98B792C0F0A4FB0B8DF0743CA6A94A936A80A40231FEB69F688B82692356135C45E2D6B3B346B05721BB570A9329428F86074B50930AA737BEDF2C0F3D4320BBCC4CFE2AA06E8820D38B09DC477C78385AF19595F34A807E3BC558228A06E629F479603CA8B8BE49CE24A48D68DCB2D183C83116BEAE4ACCA444A60A844749C6A76BBA67B8B60E04AA4852C56A462550A74B7A4A00CB5662B735BAB6F27157D8C1801A3963F8A1CCA0269D61D273E2B6B1527A1A0B683845E68D1E50C573C3841631300FA39336F3C9AF58A2ABBAAC45274DB4CB139FA7952D7323B658496C7584B291B58A17437EA217FD1A3720CAB173515548D045F7212A93A5033F525C6AB13FEC801ABC4B22177CC409EA1A37B10B48F29A9D631962552F35D291431C6E947B664B164317F010206BBB5023542B6A9CA1E1B877F0269A77C93B7C6E7C65755847328CE2809B74A0FFC76990055D02552919144758673D6F5090DD69ADF3A283B2E0CFDDE3460C8796E7EC4F4ACCB3A9E8872EC744D705C47D3511DDA09A7E8B3867C4385FCCCBD04855DE3619D1E20E3875861A88A40F672D12179BADF5C81FDC7A18E7A5F158866A932401D057CFA363087506C6E64DE22B1DFBE3C4E0634A3CD868389A262F1261FEF19E7B818F53D703AE9B537973849D26B97A8AB1E3B2252C0143E5B3388C577B718561F9A8C19D090D17E943A8D1503B26CB67E8A7FF6C7046F5AAA1CC33EAF011C4550ACEB88C5DD2CEBE12CC0D535E14330D2445A951FB528B108CCB7922562734C59CCB4D3552C354192E8A033DB7AA0FB89AE216689054AE0DA4A8C57A0311064E7C66C15DF19836F80B4FA23825235B6043C6B8B7937D295203B9002F697BBE8859481BCFF00C5E169030D9853EB4B2B924E657A1B424F34C36D0E29A62797A4A43282D69C30C512E6B53C4E39C55C5FB3C19106F14B06E96271CB4A6CE070964368432C721BBFBEC8375434539696322670709A832DA9CA666488C06746987B78D9F348C4B4576FC90986453BA25C7931D61BAE6A31ED5D2845E86366B0C1051233CB259720F615C22E0B62DE3C06264418E1A7DCEB10803224B0D963B3062B86453457BA804BE059499AB849DEC619C101BDEFB5CACACA5B85AB045CB21CA3528090C945D432A581CB6644B7F36D88E1E0290DFD83474B6614DB5377829A21640588FAA7F0E703D0B5651178A215753141913A62E59A61AC8522CB38A705824F2918BE534529098BBBA0760DD7689AB981AC342AB0278B109838FD798C12F71C6E5B419C261CB1AC99B173AA53C5613ACC6BC41A3CBE46CCF4EF3B7F4F49940648AD6AC21F16910FD91084B2B6590B21267002492CAB7E129C64430BD4CA911A5633364E30473B3BD86945134A93A8362C206D19AB62CC96EEB4012A8C218900576A5AC77B288124A9EFD8AB362844F7D873E21B84629B01797676D25533D0C21B4400242CF06CA4A9339E8C373E28CB2CE75B12D54CF70789F5C561A5231052149607E2C17E5D0A019B95B8AA161F39A6C93E7CAA803AA437A5F34E9962D520F49B72EF34C9D7C8015A154410C29381F417ADB2698F23734CB29570853B7A962225735A85D66C0171378E1F0A52E75251B512ED061082E959E04623B0F06A5705032C33129C953A6EC2A75CC21E114F6985BFF79837B", + "E3E45344B293B119586B15BBDCD8C1DE07392B64A444B7C6ADF10FF2089257471550166042016834CB592B8689B3F55E638798CA89CAE92AAB3989A24476142CD27CA4A747D134B7F8308A928890C52767D839535ED880B90C8C004C35FB5B261EB3A6B689786E044E3C470CD6A318D8AA9BC686814B655F430AAB78276BC1893ECFF095C95BA22E9B720418CDCB65A89B6C7170971C3A78CAC6B895D0BBBABC6925F0089656D6539DA9763A107C666AC7C4F01BFEE69B6B76CEF8009F8283852EDA1DFC6CBA2B053785D79E70680DAB36096B4A851EF48949297A419125C91290A105C28D8B4C33829B7075CD4FFA323E537268372E43345930D657EDBCA5760A7E67E442D284A20B1AC7003838241C26286C41EF628DEC929746A3496AF199612C42BDF17B74BCBEEF631E2E973E8A2CA3460C20220BBD7B726ACA0C89B8ABA792621FCE1C45A5C59C0D04988DE71D027209AA4C96245974E4182042E353897817C5C6309343C2E2208F6A2BC88583049E1A9596456FB81B7F67B83C8564CA78A3B0E0FA3F46517B8C63ACD3DB2F326CB8224C8740F2347CF828E1079B8041461BC209F90A7C6256CB00FA3803375EC56CA53CE81DEFD4322FC39A5740C894C31408B88DEEE21F32F42ECFC70E8C9CBB7473428FDA0AEE13A7B549861768CD1777AD08D22992FAA0CB5B9C1A16B60032028EC5055F296FD2A83D7A5583D0D5C2A8FB5BC6560F04673457214F49D715AE466F8D3C6094C676A8B38DF4F663637A472799AFFC236D3499726B6737DADAAD964351A1295193056626E182267A0FDDB85B2DA667CD4A159E592C6A83404273AFA25C398CE3BA70EC0A36249D1385052CE41625DACE34AA42B9538544B24242AA28F8D92FA0116E52F859BA4343FAB36CCCAA6FDEA1CC2928CE3C09CD121008E4B4A965C84C6073544F06D014642BB8D106D36726241197D7ABCC1D0B165FD566ED1C73AC57B10B79CCA6FB3A04691EF04465980B58BDF51E99F4BB836061A38715640C658AE55D9D08B071A566320A8E5D120BC433A873EC21DFF814D6A65D81B19BED6BBD5D52896FA4B4C532A6CE74793384A7344BCBB87720B385C87AE12E771C1152E495BFD9312872384472BF0EC39AE1E82BDA834802524630EC03810493D55C832217CF444B29FC9A5618646635EC767AF696F0F43A388CCE21B22BF13C9431406EB0C72CF7574188872A4CF553102B5353003D4CD256FFC8ABD3C59B743ABCAB18C0B31C41A277658F3478947AAB9027163F9B211C69C13E703E9F9A1CC6229501934B90B8745C8777CDD355E808CEDE632A1B009BE178454CB985597324149670FF2836A4085357776E418061488AB066868B6144CE41FCAC8F8B8C6C975078C19A9C0517047C0EB35772AE06A72A42BDF6435809823938A2C62ED00CFC78BA27F35C74F0A353D255508456D64008F62137A31B1F869C46BE6768FB47C37D542F77E28ECFC69265320679142FEDF0C870C1A8CCA981FCD3B61FA7289E9C16D8A416A86C951662AEDD096BC3475AD0C1AD82213082503D29D8AD8B71397955A3E2294B5205BB7D438F614A574C5086636049537C6CE0A24A242B54C723AD72F167C45923161193196BB0C55111F42A758A83253A396537946DB792C0D0C3647D418041A84F029107BA5171062349E0B45F9F34359E6C6E37750329B7B5069B375A147ADDACB1734C8CA0E0A6F7CB1675073209C9BCDDC8B4C8576E82E5A55B0115C18860A1939E76D8174D7960702AA381925ED9317BE85879CBA90832DA65E1D90AFEEAC77BAA46391225C910960D7262D842A4DC7A860B8C7F79E494D393C22A323A7B64604CF05C7D8907AF3B2159B70B20E83DC1D654A08641CE7566A0BA5BD9767A8583A0564808950526A9B4401667188A89604DD618EDF521DCF89063771EC3992C784C293232280CF8B68B5248066A476CA2482942BE48DC44BAF79A6DA37FE0E32744EB92B2E74B534B26D84A0E2B5073C09A0AA8810EC1204D9A02A6E4F35EB8809B67B6427D772143B91E1AEC72DB582CAC5715D0481698598E2740491451BE81EABD97F4CF838B4DC1268ACA4B8AB4CC67C8F09B8E0A0847922AD0280EF69226AEE95459010C512B6745E1C0F77A8BF315108530595503BC7A080D32A1596D7A51CE524C1023C20E2CC659E9B02355287DE1643CE2155E736FDB64213825B8C5A849E261C309B69558932170C5675A53CD4F62898593A8792535A0A06187EB688C1AC90B957719BB7B3933002A95324686741AF6998074AB9CA116ABD10D3CD482BCDA03710233FEE6296C718F3EB846E3B32FCEE59E2697ACA6C4905E86260038A0635504ABEAC8B656420DD22954A62AD05CA47BF685010CAF64A020B6782C77BC8DE7FC5BE33C119452AA518C4AE5183E6AE3AE195C07D3E4372E7B77B31A737EA6017EBAA1E9E1218970B1C26926A3F758D2301E2449B917D1192987613651B404D98E8A652ADA735E92D34192B6261A52C3A56672642757669A5716A277F7DCACC7D503007D805E950DF9765643349125310742042D4AEC4CB375C8F0C043C82A72CDC73FAE372DDB301F0C684A02C94D9C415E7830255C60CAC599392FC2A63294A2FB22B72009495E3717862AA7D37C5E8A0C0A89E990F16759C54AC9D7D6911EC0BBEF342C13CB988A12452355A8EB57734D1C5B42E485E5E70C98B792C0F0A4FB0B8DF0743CA6A94A936A80A40231FEB69F688B82692356135C45E2D6B3B346B05721BB570A9329428F86074B50930AA737BEDF2C0F3D4320BBCC4CFE2AA06E8820D38B09DC477C78385AF19595F34A807E3BC558228A06E629F479603CA8B8BE49CE24A48D68DCB2D183C83116BEAE4ACCA444A60A844749C6A76BBA67B8B60E04AA4852C56A462550A74B7A4A00CB5662B735BAB6F27157D8C1801A3963F8A1CCA0269D61D273E2B6B1527A1A0B683845E68D1E50C573C3841631300FA39336F3C9AF58A2ABBAAC45274DB4CB139FA7952D7323B658496C7584B291B58A17437EA217FD1A3720CAB173515548D045F7212A93A5033F525C6AB13FEC801ABC4B22177CC409EA1A37B10B48F29A9D631962552F35D291431C6E947B664B164317F010206BBB5023542B6A9CA1E1B877F0269A77C93B7C6E7C65755847328CE2809B74A0FFC76990055D02552919144758673D6F5090DD69ADF3A283B2E0CFDDE3460C8796E7EC4F4ACCB3A9E8872EC744D705C47D3511DDA09A7E8B3867C4385FCCCBD04855DE3619D1E20E3875861A88A40F672D12179BADF5C81FDC7A18E7A5F158866A932401D057CFA363087506C6E64DE22B1DFBE3C4E0634A3CD868389A262F1261FEF19E7B818F53D703AE9B537973849D26B97A8AB1E3B2252C0143E5B3388C577B718561F9A8C19D090D17E943A8D1503B26CB67E8A7FF6C7046F5AAA1CC33EAF011C4550ACEB88C5DD2CEBE12CC0D535E14330D2445A951FB528B108CCB7922562734C59CCB4D3552C354192E8A033DB7AA0FB89AE216689054AE0DA4A8C57A0311064E7C66C15DF19836F80B4FA23825235B6043C6B8B7937D295203B9002F697BBE8859481BCFF00C5E169030D9853EB4B2B924E657A1B424F34C36D0E29A62797A4A43282D69C30C512E6B53C4E39C55C5FB3C19106F14B06E96271CB4A6CE070964368432C721BBFBEC8375434539696322670709A832DA9CA666488C06746987B78D9F348C4B4576FC90986453BA25C7931D61BAE6A31ED5D2845E86366B0C1051233CB259720F615C22E0B62DE3C06264418E1A7DCEB10803224B0D963B3062B86453457BA804BE059499AB849DEC619C101BDEFB5CACACA5B85AB045CB21CA3528090C945D432A581CB6644B7F36D88E1E0290DFD83474B6614DB5377829A21640588FAA7F0E703D0B5651178A215753141913A62E59A61AC8522CB38A705824F2918BE534529098BBBA0760DD7689AB981AC342AB0278B109838FD798C12F71C6E5B419C261CB1AC99B173AA53C5613ACC6BC41A3CBE46CCF4EF3B7F4F49940648AD6AC21F16910FD91084B2B6590B21267002492CAB7E129C64430BD4CA911A5633364E30473B3BD86945134A93A8362C206D19AB62CC96EEB4012A8C218900576A5AC77B288124A9EFD8AB362844F7D873E21B84629B01797676D25533D0C21B4400242CF06CA4A9339E8C373E28CB2CE75B12D54CF70789F5C561A5231052149607E2C17E5D0A019B95B8AA161F39A6C93E7CAA803AA437A5F34E9962D520F49B72EF34C9D7C8015A154410C29381F417ADB2698F23734CB29570853B7A962225735A85D66C0171378E1F0A52E75251B512ED061082E959E04623B0F06A5705032C33129C953A6EC2A75CC21E114F6985BFF79837BB476B6EBC850461C28D4AE315666DDCDF4FE185EB612397E63569AFA884E6819429BB325104E4D5A8A83A27713B50E8BDC063091B606A298AD8F4DEE24EF2483", + "B352D5A6E2EADC640865E102615DB648C648EA3B1646DDCEA1611FE6F08C7EE36E6A84E3B389BBC59CD67559E3D304AF8DC139CFE8C262134BEF89A8B2DA20F1FAC6F1059D7F49C7D53AAFEB1EC9D507A1CD171D129FCC74E757995B38023245AE175AD9E0DCE6407567AE0734676567C62E019E94A033BF75593BF8A58B907750C561218C15FE345FE27D0439331988BD61C2755D5147D8ADBA19E11B25B3598F512D7525694150444B714D900F85965C870DA505E087ABB2FB6FBB1439A0E74664F8087D341C7ADAA5EA54AFF98D725B235640D89119A0CDE2D2DB1E08C9DACD6365DE79B754D88EE9F75BC4E3357507AA652C84D5EEE042BCE3B250AB16C1E9995D2FC65F097FDF3F019378AE0DCED07EAA0DEFFDBEF2CF7F66C3522ECB7BF4CCDA07C354931A6DCF7F759A41C5DDC17A5927C8DD452325DB023B632D1D5478623E641C9845725E4031D777BDB3E886EFD091D2A654959EA841A394B2620166B0E39D5D53BF98F37F6EC45FDC10236B8704771E518FD0C9581D81C0A7801DF129BF7D8EAD32F9BF8290F90D3CFE1CBC0CD9CD0AEED6F37DF04ADB354DD18CCBBEF4100FDDE5102BFD9965B3A373E9CE47CD4208A1C6CDE562D20DBFABC45B553DC0411E6EBE45219B16DD0BFDE0F5BA8239923FAB5CC5F796BD563B75BC10D45217C1BFD05BDE23CEDE44304F3B4644CE3BD6719C258387D2F684758C6E2D89BDFFC498EA518CE47A7E188E87F64CBBED10D27732E651557855827BF8F5014E5AF3E4FF06510DEC0F7F48BEC66C9731AD5CA8D11D5C1B6B3E6C4D1024B41EA4899919927F3FBD8A3F8E89CE54BA83C8DA840611CB16A439E004D486E08B8FEEC8B0BA65EAED6A1A08FBFC4C2D63BB6A7EA0EED678197736877599AA96E8F27BFDBB9A217E97B694DB2B604F4F2CBDCE7DBDF349960B8E9A036317F7339DED78ECE6CBEA9148636AEA5A2BA7F2B84CDE87BCB656A0E4B8B5351407F9094686909B2E7DF85E425352B7F95F7ABEE4243863F9C134E1836AA281D70755F66E6A4A3B6E8A2F7765D436129F719599A0C01E9C9798CC778FC771ACD5067E9E9E1EFBA1F04C367EFCA76E9C32E1145E49A6AD2D9E2B025FDC0EC71F510B2D61C83CB7D215CD306D9EBF2D6083F19EF8ABB7B3E46A728C5A25A5333E48EB2192CCB026BAF143D62478E23953C131DB0FCEDFFF17637D685ADFCF3B994ACC831607516DCA2695283DAD5E10D3B20F9F7366F0076F227EC2448D840F32CCA52D364A1912D569BE14E74A6DC4B4543FB3411BC0C1A3BABDA8FC6F0C4D20BB36C8B5A7FC8F4104A10779AC8375CB174E1366DB213917DC4340570192C57431067C0740A86BB342DFFA8E36BEC9BF554B36839B7D1F572C12928A197403B6B7814D8BD83EC23FF35E4475C8198D7C8EB6B9AFDD4A9DA583F5F92C5C4F048594ECDADD26057BA4C989E08C8E800A9E8A7DBFF4494A33FBA336BD60789D6CAA3277823462421CAF63C2937E16CDDCC5CC0668E49F75BFB96E0293482ABE5FD188DA961EB1116B3B2D2155725A8E52D25EFE3C2D6965A2EB62A20BC640B4E172ACF6DCB2E85EA99A3E0AD711F0FB45084F3DC3C433179C4C6BE311A1909DC097C911C666F78678FFF71D342D1661F07A14AFA2DD8A6D66AEE7A4C5C4CA2B8B77DE19B53E31DC61415937B676A13BD1EADF11A7F7302022EB41E147DE8FD1C2D71DB80778B1DB2D378B211EA04F35087FCE5A98F3D2F40DB13132C4E1B3B50241A70A44E5829DBB562E350FD01283CEB743FA0C5B72A8A5835B9FDE77AA23FF6F016EABF57A256C572AD3727455A10311FD7D1DA6483C70810AA47860436CC4216A90460B0BFA8B98D49FDB3433D0EEE65356D00A05F027C4A0C9409AF72D0E1C3E89E12A383E2AD5956C5A7DB0164613467EE7404BE8025D99CDCB59D9D701B119B16F3E462978850E8DBA8AB08B88E2BDA07DA68ABD89711A086B825298C8AAC9166D6936D3E4F1E09452853EFE6700D126D17BCF6F0D82434CD9F153FAD3573E20D35BCDBAF35D664309A2B9FA21996CAA29C9B11FAA3A22467DC1E5C9A986E225D199732B5B248A38587B2D0E441989170509E693C1966DA3DEAEE35C927DB6EFBBD4486C6EC570437E3879596A1F7A3EC3F4879782F2F21CDFBA0945F99BACBADD25BF1F25B6C5B6BF853901DAB0793419C543CF52525D759C1B0E899EBBD0E994C39EC7", + "90DEDD24590FEDFF42A27AB44173E0711839EC9D15728D7A0C191CE6CE5CBC68", + false, + }, +} + +func TestDecapsulate1024ACVP(t *testing.T) { + for i, tc := range decap1024InternalProjectionCases { + //ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + + dk1, err := NewDecapsulationKey1024(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey1024: %v", i, err) + } + if !tc.success { + c[0] ^= 0xFF // Corrupt the ciphertext to force failure + } + k1, err := dk1.Decapsulate(c) + if tc.success { + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } else { + if bytes.Equal(k1, k) { + t.Fatalf("case %d: decapsulation should have failed", i) + } + } + } +} + +func BenchmarkKeyGen1024(b *testing.B) { + var seed [64]byte + rand.Read(seed[:]) + b.ResetTimer() + for b.Loop() { + if _, err := NewDecapsulationKeyFromSeed1024(seed[:]); err != nil { + b.Fatalf("NewDecapsulationKeyFromSeed1024 failed: %v", err) + } + } +} + +func BenchmarkEncapsulateInternal1024(b *testing.B) { + tc := encap1024InternalProjectionCases[0] + ek, _ := hex.DecodeString(tc.ek) + m, _ := hex.DecodeString(tc.m) + ek1, err := NewEncapsulationKey1024(ek) + if err != nil { + b.Fatalf("NewEncapsulationKey1024: %v", err) + } + + b.ResetTimer() + for b.Loop() { + ek1.EncapsulateInternal((*[32]byte)(m)) + } +} + +func BenchmarkDecapsulate1024(b *testing.B) { + tc := decap1024InternalProjectionCases[0] + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + + dk1, err := NewDecapsulationKey1024(dk) + if err != nil { + b.Fatalf("NewDecapsulationKey1024: %v", err) + } + + b.ResetTimer() + for b.Loop() { + if _, err := dk1.Decapsulate(c); err != nil { + b.Fatalf("Decapsulate: %v", err) + } + } +} diff --git a/mlkem/mlkem512.go b/mlkem/mlkem512.go new file mode 100644 index 0000000..9302b44 --- /dev/null +++ b/mlkem/mlkem512.go @@ -0,0 +1,412 @@ +// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. +// Code generated by generate.go. DO NOT EDIT. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/sha3" + "crypto/subtle" + "errors" + "io" +) + +// A DecapsulationKey512 is the secret key used to decapsulate a shared key from a +// ciphertext. It includes various precomputed values. +type DecapsulationKey512 struct { + d [32]byte // decapsulation key seed + z [32]byte // implicit rejection sampling seed + + ρ [32]byte // rho, sampleNTT seed for A, stored for the encapsulation key + h [32]byte // H(ek), stored for ML-KEM.Decaps_internal + + encryptionKey512 + decryptionKey512 +} + +// Seed returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey512) Seed() []byte { + var b [SeedSize]byte + copy(b[:], dk.d[:]) + copy(b[32:], dk.z[:]) + return b[:] +} + +// Bytes returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +func (dk *DecapsulationKey512) Bytes() []byte { + b := make([]byte, 0, DecapsulationKeySize512) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey512) EncapsulationKey() *EncapsulationKey512 { + return &EncapsulationKey512{ + ρ: dk.ρ, + h: dk.h, + encryptionKey512: dk.encryptionKey512, + } +} + +// An EncapsulationKey512 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding [DecapsulationKey512]. +type EncapsulationKey512 struct { + ρ [32]byte // sampleNTT seed for A + h [32]byte // H(ek) + encryptionKey512 +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey512) Bytes() []byte { + // The actual logic is in a separate function to outline this allocation. + b := make([]byte, 0, EncapsulationKeySize512) + return ek.bytes(b) +} + +func (ek *EncapsulationKey512) bytes(b []byte) []byte { + for i := range ek.t { + b = polyByteEncode(b, ek.t[i]) + } + b = append(b, ek.ρ[:]...) + return b +} + +// encryptionKey512 is the parsed and expanded form of a PKE encryption key. +type encryptionKey512 struct { + t [k512]nttElement // ByteDecode₁₂(ek[:384k]) + a [k512 * k512]nttElement // A[i*k+j] = sampleNTT(ρ, j, i) +} + +// decryptionKey512 is the parsed and expanded form of a PKE decryption key. +type decryptionKey512 struct { + s [k512]nttElement // ByteDecode₁₂(dk[:decryptionKey512Size]) +} + +// GenerateKey512 generates a new decapsulation key. The decapsulation key must be kept secret. +// See FIPS 203, Algorithm 19. +func GenerateKey512(rand io.Reader) (*DecapsulationKey512, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey512{} + return generateKey512(dk, rand) +} + +func generateKey512(dk *DecapsulationKey512, rand io.Reader) (*DecapsulationKey512, error) { + var d [32]byte + if _, err := io.ReadFull(rand, d[:]); err != nil { + return nil, err + } + var z [32]byte + if _, err := io.ReadFull(rand, z[:]); err != nil { + return nil, err + } + + kemKeyGen512(dk, &d, &z) + return dk, nil +} + +// NewDecapsulationKeyFromSeed512 parses a decapsulation key from a 64-byte +// seed in the "d || z" form. The seed must be uniformly random. +func NewDecapsulationKeyFromSeed512(seed []byte) (*DecapsulationKey512, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey512{} + return newKeyFromSeed512(dk, seed) +} + +func newKeyFromSeed512(dk *DecapsulationKey512, seed []byte) (*DecapsulationKey512, error) { + if len(seed) != SeedSize { + return nil, errors.New("mlkem: invalid seed length") + } + d := (*[32]byte)(seed[:32]) + z := (*[32]byte)(seed[32:]) + kemKeyGen512(dk, d, z) + + return dk, nil +} + +// NewDecapsulationKey512 parses a decapsulation key from its expanded NIST format. +func NewDecapsulationKey512(b []byte) (*DecapsulationKey512, error) { + if len(b) != DecapsulationKeySize512 { + return nil, errors.New("mlkem: invalid decapsulation key length") + } + + dk := &DecapsulationKey512{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey512(b[:EncapsulationKeySize512]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey512 = ek.encryptionKey512 + b = b[EncapsulationKeySize512:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + + copy(dk.z[:], b[32:]) + + return dk, nil +} + +// kemKeyGen512 generates a decapsulation key. +// +// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and +// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save +// copies and allocations. +func kemKeyGen512(dk *DecapsulationKey512, d, z *[32]byte) { + dk.d = *d + dk.z = *z + + g := sha3.New512() + g.Write(d[:]) + g.Write([]byte{k512}) // Module dimension as a domain separator. + G := g.Sum(make([]byte, 0, 64)) + ρ, σ := G[:32], G[32:] // rho, sigma + dk.ρ = [32]byte(ρ) + + A := &dk.a + for i := range byte(k512) { + for j := range byte(k512) { + A[i*k512+j] = sampleNTT(ρ, j, i) + } + } + + var N byte + s := &dk.s + for i := range s { + s[i] = ntt(samplePolyCBD(σ, N, η1_512)) + N++ + } + e := make([]nttElement, k512) + for i := range e { + e[i] = ntt(samplePolyCBD(σ, N, η1_512)) + N++ + } + + t := &dk.t + for i := range t { // t = A ◦ s + e + t[i] = e[i] + for j := range s { + t[i] = polyAdd(t[i], nttMul(A[i*k512+j], s[j])) + } + } + + H := sha3.New256() + ek := dk.EncapsulationKey().Bytes() + H.Write(ek) + H.Sum(dk.h[:0]) +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key. +// +// The shared key must be kept secret. See FIPS 203, Algorithm 20. +func (ek *EncapsulationKey512) Encapsulate(rand io.Reader) (sharedKey, ciphertext []byte, err error) { + // The actual logic is in a separate function to outline this allocation. + var cc [CiphertextSize512]byte + return ek.encapsulate(&cc, rand) +} + +func (ek *EncapsulationKey512) encapsulate(cc *[CiphertextSize512]byte, rand io.Reader) (sharedKey, ciphertext []byte, err error) { + var m [messageSize]byte + if _, err := io.ReadFull(rand, m[:]); err != nil { + return nil, nil, err + } + sharedKey, ciphertext = kemEncaps512(cc, ek, &m) + return sharedKey, ciphertext, nil +} + +// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for +// use in tests. +func (ek *EncapsulationKey512) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { + cc := &[CiphertextSize512]byte{} + return kemEncaps512(cc, ek, m) +} + +// kemEncaps512 generates a shared key and an associated ciphertext. +// +// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17. +func kemEncaps512(cc *[CiphertextSize512]byte, ek *EncapsulationKey512, m *[messageSize]byte) (K, c []byte) { + g := sha3.New512() + g.Write(m[:]) + g.Write(ek.h[:]) + G := g.Sum(nil) + K, r := G[:SharedKeySize], G[SharedKeySize:] + c = pkeEncrypt512(cc, &ek.encryptionKey512, m, r) + return K, c +} + +// NewEncapsulationKey512 parses an encapsulation key from its encoded form. +// If the encapsulation key is not valid, NewEncapsulationKey512 returns an error. +func NewEncapsulationKey512(encapsulationKey []byte) (*EncapsulationKey512, error) { + // The actual logic is in a separate function to outline this allocation. + ek := &EncapsulationKey512{} + return parseEK512(ek, encapsulationKey) +} + +// parseEK512 parses an encryption key from its encoded form. +// +// It implements the initial stages of K-PKE.Encrypt according to FIPS 203, +// Algorithm 14. +func parseEK512(ek *EncapsulationKey512, ekPKE []byte) (*EncapsulationKey512, error) { + if len(ekPKE) != EncapsulationKeySize512 { + return nil, errors.New("mlkem: invalid encapsulation key length") + } + + h := sha3.New256() + h.Write(ekPKE) + h.Sum(ek.h[:0]) + + for i := range ek.t { + var err error + ek.t[i], err = polyByteDecode[nttElement](ekPKE[:encodingSize12]) + if err != nil { + return nil, err + } + ekPKE = ekPKE[encodingSize12:] + } + copy(ek.ρ[:], ekPKE) + + for i := range byte(k512) { + for j := range byte(k512) { + ek.a[i*k512+j] = sampleNTT(ek.ρ[:], j, i) + } + } + + return ek, nil +} + +// pkeEncrypt512 encrypt a plaintext message. +// +// It implements K-PKE.Encrypt according to FIPS 203, Algorithm 14, although the +// computation of t and AT is done in parseEK512. +func pkeEncrypt512(cc *[CiphertextSize512]byte, ex *encryptionKey512, m *[messageSize]byte, rnd []byte) []byte { + var N byte + r, e1 := make([]nttElement, k512), make([]ringElement, k512) + for i := range r { + r[i] = ntt(samplePolyCBD(rnd, N, η1_512)) + N++ + } + for i := range e1 { + e1[i] = samplePolyCBD(rnd, N, η2_512) + N++ + } + e2 := samplePolyCBD(rnd, N, η2_512) + + u := make([]ringElement, k512) // NTT⁻¹(AT ◦ r) + e1 + for i := range u { + u[i] = e1[i] + for j := range r { + // Note that i and j are inverted, as we need the transposed of A. + u[i] = polyAdd(u[i], inverseNTT(nttMul(ex.a[j*k512+i], r[j]))) + } + } + + μ := ringDecodeAndDecompress1(m) + + var vNTT nttElement // t⊺ ◦ r + for i := range ex.t { + vNTT = polyAdd(vNTT, nttMul(ex.t[i], r[i])) + } + v := polyAdd(polyAdd(inverseNTT(vNTT), e2), μ) + + c := cc[:0] + for _, f := range u { + c = ringCompressAndEncode10(c, f) + } + c = ringCompressAndEncode4(c, v) + + return c +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation key. +// If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey512) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + if len(ciphertext) != CiphertextSize512 { + return nil, errors.New("mlkem: invalid ciphertext length") + } + c := (*[CiphertextSize512]byte)(ciphertext) + // Note that the hash check (step 3 of the decapsulation input check from + // FIPS 203, Section 7.3) is foregone as a DecapsulationKey is always + // validly generated by ML-KEM.KeyGen_internal. + return kemDecaps512(dk, c), nil +} + +// kemDecaps512 produces a shared key from a ciphertext. +// +// It implements ML-KEM.Decaps_internal according to FIPS 203, Algorithm 18. +func kemDecaps512(dk *DecapsulationKey512, c *[CiphertextSize512]byte) (K []byte) { + m := pkeDecrypt512(&dk.decryptionKey512, c) + g := sha3.New512() + g.Write(m[:]) + g.Write(dk.h[:]) + G := g.Sum(make([]byte, 0, 64)) + Kprime, r := G[:SharedKeySize], G[SharedKeySize:] + J := sha3.NewSHAKE256() + J.Write(dk.z[:]) + J.Write(c[:]) + Kout := make([]byte, SharedKeySize) + J.Read(Kout) + var cc [CiphertextSize512]byte + c1 := pkeEncrypt512(&cc, &dk.encryptionKey512, (*[32]byte)(m), r) + + subtle.ConstantTimeCopy(subtle.ConstantTimeCompare(c[:], c1), Kout, Kprime) + return Kout +} + +// pkeDecrypt512 decrypts a ciphertext. +// +// It implements K-PKE.Decrypt according to FIPS 203, Algorithm 15, +// although s is retained from kemKeyGen512. +func pkeDecrypt512(dx *decryptionKey512, c *[CiphertextSize512]byte) []byte { + u := make([]ringElement, k512) + for i := range u { + b := (*[encodingSize10]byte)(c[encodingSize10*i : encodingSize10*(i+1)]) + u[i] = ringDecodeAndDecompress10(b) + } + + b := (*[encodingSize4]byte)(c[encodingSize10*k512:]) + v := ringDecodeAndDecompress4(b) + + var mask nttElement // s⊺ ◦ NTT(u) + for i := range dx.s { + mask = polyAdd(mask, nttMul(dx.s[i], ntt(u[i]))) + } + w := polySub(v, inverseNTT(mask)) + + return ringCompressAndEncode1(nil, w) +} diff --git a/mlkem/mlkem512_test.go b/mlkem/mlkem512_test.go new file mode 100644 index 0000000..f0c73f1 --- /dev/null +++ b/mlkem/mlkem512_test.go @@ -0,0 +1,336 @@ +// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "io" + "testing" +) + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-keyGen-FIPS203/internalProjection.json +var kenGen512InternalProjectionCases = []struct { + tcId int + seed string + ek string + dk string +}{ + { + 1, + "BBA3C0F5DF044CDF4D9CAA53CA15FDE26F34EB3541555CFC54CA9C31B964D0C80A64FDD51A8D91B3166C4958A94EFC3166A4F5DF680980B878DB8371B7624C96", + "1CAB32BE749CA76124EE19907B9CCB7FD30F8B2C38DC970E81F9956C97A8BD3C6E37B07C29E60BB2B75C5258B572626A859ABA89DB3AABC571424618B26310278B8EC4E76ED07A10B864AABF37BFC9F364731050631421BFCB1C3B9153D4316A95089197A027AB80B39C362CE6D97EFF422244FB81AAF67354F03894CC25B2707939A4A06D302C59D106EB743678DAA3F1D1C3F46B03F0DAA0641835A548363180744E6B6235B84DB9A4628279A6EF7231499208E657A3F9BB6E3F782606B79FC9A38723576FA80898E8A6887D94C3ED774E46A86CFE705B34C6B5535865329C5A4A820F9114CE9A9C68495C726368B9E073CBBE627A7DE419F7F7B4AD221576F91FB1E66CFF9651BD6C25E3CC9CA49A570CF041E457658072B684E714BD6A86B3D05C7597A729E12E512C8D7E5B5C27049EFB0AC0E085B1B88347BFD314B4E4AB4B8875A489ADB8C9AE28008BAD36AAAD24683563BFAF19BDA8677AB7BAC7E33C3087B84A45246A2AB1AEF397750D386ACDAC63C87506166A0FAE18773F530E74545D54BC670DD7353B75B16373CB8A6269AF37097EE1B1640458153132AD80AB64F7A599B8670E301205043C136C56CA5A06DBFABA3204671C1B237B18824555B5DDB206A74ACE637005B363238378BDA5E198AD69B85CBB399E8B07CB899F9E93CF6CF62FCAFC9E4D77363CA2149E92197F2133223799C182CF5F182ECD35B5FDFCBF0A93A1350198F2F244F3216B442A22FDDB2F4F3BB8CBF0168D0220AA725A0E287DF08079A1DBB8747C02F0C2C829759A5D95B6237522E7F71AB5669390377E03A93AB7FC7E9FD6BB59C1B9B8CC966141B0BA6796E66829D6A403B1F5816C8557EE8841031B2ED6C6CCFE8A55B39B9273F8BA050B1B328C7B9A238A7B8324F16A7A474C0B5721B9C8246531E19208838356F3337768BCB3397B4E01CC26175B67A66DFCD11F07B295C20AB484A60E7D086DB39B8301845B654A484462508FA78506357BBBF42DBAC157BCA7769C099A0B1894D4A17256F504EBE50BE284656F653845A26B00856D76A8A5A166CD09D04705261EBAFA20687A1068D9B9E28326848B67A0A3994D2DBA4B7A8F623B901E17CBEE610847A2903301C58287", + "C8D473A9EA0987F356FB309356A39E766633746B8FBAF03D588495E4B1CC87E4B283C871514182D3E1844D9ACB4FA5948BC38D85E05478BB699E41170FA593F7D74226D4597655C5DE80AC8C79181EE750121685C71797D3B5C240676637C7BDEBA61969508C3FABB4CD97CC53C529EBC993C4B883B2B52F42789F64C022620B624B8107E0E55FFE29280BB005DC66A83A0A219A0080F8E656729B29CBF8AA7866358694C94E477E499A055C8A8ECAC7338F461897329ED02B1EF0F0AB627B71B5322692268578B11E5EC203E674251C0182C10C775971247537358C0651EF21704CC68B57D5CF61C627C0AB30DE5743FB027B40D86071531F0C8B67092BAC18361FACEC1BFC53C65D512B3911156A991F462C8588D638526389E3CAAC808809EB7B50ACB83A10142C954B9AC938B983133D5D024303790DF848B7FB06AE4C751DAEDBAF08728A96C481B5A84B53FA37DEEAC6174C745F3C0821553CC88858C3DB392570AA388624CFA98E7232970B746BCF301642D92F3E452E3BF99E24329C470A57B31AA765C8952333A334102605BB8099555230287F07A813109C427E692393540FAD769831756701B481611472A060CA1E7C34F9412371C7658F4C69C9C0BC956404B88AB27BC89ED7F751E51781D95A1D0DCC476E7A8FD4A54272B5A9679B0227176D481035A3864FD747B52739B8FA801713BC0757347BC96A022487BFA5A3C0E30B9FBEAA6838553D9111351FF49A73970263B39CDF180F163972AB993C1630A89A06A2FF69A274F27A819C3727E2244B23163B320DD568B227B6589A402A5E16333C70C4D3264CAC8A80BDC64705675788CC20FE083635D25C89F387F5B39580737F06287F236B7CCCA4CC3A69AC0E84B33B20BA7661B8BA9B575CC555DC820481FA9941A8C259D962273536B36B0BB1E434B18A63BE405583925CCA14001FF9AA28EA7FFF525ABA151944607056F6CD5052C823D00F33651BFBF428257B388544654E9A14F2E595BB4CC5D5929A03F62BC2DC6C6B885D86969B298A06F0294CAE69337944C4AEB5B22E126AEB02BD34083637299D2C41080481C61CAB32BE749CA76124EE19907B9CCB7FD30F8B2C38DC970E81F9956C97A8BD3C6E37B07C29E60BB2B75C5258B572626A859ABA89DB3AABC571424618B26310278B8EC4E76ED07A10B864AABF37BFC9F364731050631421BFCB1C3B9153D4316A95089197A027AB80B39C362CE6D97EFF422244FB81AAF67354F03894CC25B2707939A4A06D302C59D106EB743678DAA3F1D1C3F46B03F0DAA0641835A548363180744E6B6235B84DB9A4628279A6EF7231499208E657A3F9BB6E3F782606B79FC9A38723576FA80898E8A6887D94C3ED774E46A86CFE705B34C6B5535865329C5A4A820F9114CE9A9C68495C726368B9E073CBBE627A7DE419F7F7B4AD221576F91FB1E66CFF9651BD6C25E3CC9CA49A570CF041E457658072B684E714BD6A86B3D05C7597A729E12E512C8D7E5B5C27049EFB0AC0E085B1B88347BFD314B4E4AB4B8875A489ADB8C9AE28008BAD36AAAD24683563BFAF19BDA8677AB7BAC7E33C3087B84A45246A2AB1AEF397750D386ACDAC63C87506166A0FAE18773F530E74545D54BC670DD7353B75B16373CB8A6269AF37097EE1B1640458153132AD80AB64F7A599B8670E301205043C136C56CA5A06DBFABA3204671C1B237B18824555B5DDB206A74ACE637005B363238378BDA5E198AD69B85CBB399E8B07CB899F9E93CF6CF62FCAFC9E4D77363CA2149E92197F2133223799C182CF5F182ECD35B5FDFCBF0A93A1350198F2F244F3216B442A22FDDB2F4F3BB8CBF0168D0220AA725A0E287DF08079A1DBB8747C02F0C2C829759A5D95B6237522E7F71AB5669390377E03A93AB7FC7E9FD6BB59C1B9B8CC966141B0BA6796E66829D6A403B1F5816C8557EE8841031B2ED6C6CCFE8A55B39B9273F8BA050B1B328C7B9A238A7B8324F16A7A474C0B5721B9C8246531E19208838356F3337768BCB3397B4E01CC26175B67A66DFCD11F07B295C20AB484A60E7D086DB39B8301845B654A484462508FA78506357BBBF42DBAC157BCA7769C099A0B1894D4A17256F504EBE50BE284656F653845A26B00856D76A8A5A166CD09D04705261EBAFA20687A1068D9B9E28326848B67A0A3994D2DBA4B7A8F623B901E17CBEE610847A2903301C5828747D4B351E049B7757A2545602C398D2EBCF7C28804BCC8862E270D7324AB435E0A64FDD51A8D91B3166C4958A94EFC3166A4F5DF680980B878DB8371B7624C96", + }, + { + 2, + "00C7E4AB727C202A92869BE37F8A7CBAE63A67463C3A54000F1A3290B987E96F3764A9B01F8C411BEB6DACF298CC30841FEB65A10D6DEBA9780338B4970B983E", + "C4A019A8941B1C639C0C6698AAA051E3B9C78BF7A15005A92AB3C6AB30AE974682F517B96F743889696F6F75BDDF6C9A588963AE04477FD52396C3B904459A3DAA39D5939F33817E8F20CD70860915F3B59FB088FFF98C0AFAAEFE352344681A249BCD0529B3578B4123D35C88E14F67616B1E19B28D027AB7988680CB3B1C257257D36FC3A2B81E18B1F918AB9EB650874135AAD478E3F5B34526856F81A3C32541781127F82304FE86BE29A70D489177FA5B9292882BDC58B5756043C558CC50D666D888C70762C71FAA5A0014A1E573B080016BFB17172D8B1B563C64AC39B536D568DA1853D00968334C412A26AE9272B0606A98C6199E96417A946B8780988445BC867050B5CA9904B6322DC059993068B02C09C9C9C2B00E719F45FBAF183B02CA258C95239E5A4993376B7BC47A8D2751C75C59B3AAA90640379ACB0A66B1ECA226D95E377CBC47B7210422BE338283404496D8A29C8553417E38818547ACB6213D7892248BA7746626B562CB1209871A058A92E85248B37185DD3393D7C95C9C36159CD646A7713B47A60EF33825F4F7CCBBC07B4AF88C25A553D11C3D637859818387013806D5A24914FBC7E6D58977674598CC57CF632148C6BFBE24878F318E1471B08EBC90DB01978A837A86C36C3ED14E69F7C771C32939B7AAA28498A038B558F75779D5C2C0736003C91864206E52A296F949058069ADC15033251890309A3F94D97A2716992C7745F8A991D10653E6F480F7A912C6619B0BD85926F8A2A9885112ABB1C3604EFA6B84301A2ABD0A5D6FC29C8B16C98AFA817B41ACF90C9776D603FC7A6B8DF22377E26105D159D35C1089ACAE7B7427DDB6B01E1CAE79FB8AFAA6917C16BB4DCA544B3CB121D00F4921CDF10BBF4437588351623153C916908BD5824684DCAA5B4C44EE9B1063405F723CA60EB52E0B75B7BF67BFD6D34F5AA94657B3AD1BD4CC8D0566BA35C501A78BCF8183D5A52D2E5C4E606AC09C29ABE5A539A280AB838B66A261BADE2028C1D1A9C352B4C6F1919BDA72C2C213D3EA9586C73E0E2C3486D9B199FAA0EA8B039EE9C3EE3B8E1380CE3C0E2BA2E81C9745BD63054616AD78EC8AC8C44B08CC4A217CA320DE7476805E", + "B0618E21C291A7D7228F2B5624037B382628A90A89D0213D54E20CA6989BF887A5F0E03256F660264724A5518830B5BA681153C512454CC47A3CEC3E49F3B300A928B05316155C0CF2E069BD6084B52C30422878064B6CB3535FB5485491E6874AA00D820770D0E55A6CAB40F3F4117E764EA43C62A3912943B6096CC41FF87ACA0C140AA37A8C49170E355B60CBF16399A00508989A316CB99426BCC9B9054AE7B46DD3B9CE703CA98C4E8FF6759D2006E66114027B92DCC40F9B2C7402A7C9EE7849955BA9FB35543427701F4C9267F65CD37640265192EB085AA5F02DBEB7125D77145E9B1BDD330E2E687E50789F9D814F4587C3030A5FF95724E62AA84490ACA3C2C76AEC24C29C82385597A9A4395506B21160BF5360133BF709F016817AA5CA266A9556BAA4CA71050B0A71400B97AF4C07782711EC290ED3799C91967AA5E9000BC670F295B53DA415EF8BA631F178F98C747AFC5673D61FC6C280FEB37467D09C80257FBCC02C6D14A48AFB72C7499AF802614D24352750CFD7956B9C77BF92B561AD4365E3666DCBE8033793BEFCC9C5AB572838B4BF459CB1A9822D410B0C6CFB5CA55723FFA555A197657E9A7DE72BCB76995D2E0BAA71409301B07AAAD1A092C3020C1032628109D11C1D46F50A739CB63497BA48BBA556E5A17F629E1D965C356809A3456BEF572234064EE79B9D68169628D8AF01DB25B138B27C6129F493522E40B81A91632CCC41B772CDC2211C10D07422272DD0967C9BB52C52832BA807C7642587C608A0748A32B8766A2C3345AAB527700C49AF887582A049D490B053D34B9A9B038C63AD829969FA706C93B906736A897C9C7187430CFC980116B73E018C7F3FB6420C8046EA86977FAA904597313555C4700594225CA3C9B61F789A59CC350DEEE23D37D5A585672446560C6F38278C0C595B61258BA9430393B1171AA24A530BA37999ED395FE2050A15D9AE74751177890137333B01850BD1535788946817EC1FC604AC2C01C6E23720236BB329172AEFE096B5F93252DC6BB6D82C369AA14822AA92667578CA1238958DA72188D0BBA63DD7B6C4A019A8941B1C639C0C6698AAA051E3B9C78BF7A15005A92AB3C6AB30AE974682F517B96F743889696F6F75BDDF6C9A588963AE04477FD52396C3B904459A3DAA39D5939F33817E8F20CD70860915F3B59FB088FFF98C0AFAAEFE352344681A249BCD0529B3578B4123D35C88E14F67616B1E19B28D027AB7988680CB3B1C257257D36FC3A2B81E18B1F918AB9EB650874135AAD478E3F5B34526856F81A3C32541781127F82304FE86BE29A70D489177FA5B9292882BDC58B5756043C558CC50D666D888C70762C71FAA5A0014A1E573B080016BFB17172D8B1B563C64AC39B536D568DA1853D00968334C412A26AE9272B0606A98C6199E96417A946B8780988445BC867050B5CA9904B6322DC059993068B02C09C9C9C2B00E719F45FBAF183B02CA258C95239E5A4993376B7BC47A8D2751C75C59B3AAA90640379ACB0A66B1ECA226D95E377CBC47B7210422BE338283404496D8A29C8553417E38818547ACB6213D7892248BA7746626B562CB1209871A058A92E85248B37185DD3393D7C95C9C36159CD646A7713B47A60EF33825F4F7CCBBC07B4AF88C25A553D11C3D637859818387013806D5A24914FBC7E6D58977674598CC57CF632148C6BFBE24878F318E1471B08EBC90DB01978A837A86C36C3ED14E69F7C771C32939B7AAA28498A038B558F75779D5C2C0736003C91864206E52A296F949058069ADC15033251890309A3F94D97A2716992C7745F8A991D10653E6F480F7A912C6619B0BD85926F8A2A9885112ABB1C3604EFA6B84301A2ABD0A5D6FC29C8B16C98AFA817B41ACF90C9776D603FC7A6B8DF22377E26105D159D35C1089ACAE7B7427DDB6B01E1CAE79FB8AFAA6917C16BB4DCA544B3CB121D00F4921CDF10BBF4437588351623153C916908BD5824684DCAA5B4C44EE9B1063405F723CA60EB52E0B75B7BF67BFD6D34F5AA94657B3AD1BD4CC8D0566BA35C501A78BCF8183D5A52D2E5C4E606AC09C29ABE5A539A280AB838B66A261BADE2028C1D1A9C352B4C6F1919BDA72C2C213D3EA9586C73E0E2C3486D9B199FAA0EA8B039EE9C3EE3B8E1380CE3C0E2BA2E81C9745BD63054616AD78EC8AC8C44B08CC4A217CA320DE7476805EA1CBE5BF3592B77207A8C0C8E3FAA7EF66DEB7C18CE8CCF387A76B39CB4ECB723764A9B01F8C411BEB6DACF298CC30841FEB65A10D6DEBA9780338B4970B983E", + }, + { + 3, + "99683B23ACDA10B8FE4A877FFDAFA3974CA82812119B26FBC3CFD8D13268BCE951DA028C3ED5E25860B2F5C7334317DFB9F263FBA3C4AA9CF579F98F33706AF4", + "08C9A217A9BB0BB90955E2A5513488D9675316D1086C7774218649373C5EF425003EFCA50CE8B2A9349847C011CC421968F990010969BB879FD0D91A28074E9FF06988521EDC12B3AA1A466B955D481A095BBB15AD0130B11953176B5573E379D4E42609C6967A339C3B7453498CBBD4489F54DA0B8ED42A24DC6AA00515F7586703F1C612C6A57AF2B6CABB1991ACAF980104B17A5E1C097A67C46637E380DB91CE6FB612EA35C194200EFB96029B980FDF2B3D36A0561392A89C788676B48A771CA6A47229C0907355317E9EB88903F17792048FAAD182E2C065E30B63FFDA51B69554B0344E27895C4D924CA5FA80BED6AC0A372877454F98C77C4C699D9259BBE30A810BF163CB5817E9339C17151F9678BDA7E00BA1C965F1FB6193753CFC964D82EC36528490DE2539D830CA20FCB232FA3973511D40B1334FF2AC016CCB271CB06432BFD3BA5C7060785D2C3A32AAA4C0762EE873A91955657F49C7B2972106EBBCA39442A3C9490D7A4805F39DAB8A29E08B89A2F80EBE678B200A3DBB376511F62F1A77383F32B6EAB75F462030F75C95719517A4311FD25977B31537C804904B2C0297B63499A0965009B926237965158E9B7963F4A41574306EBD66C80BA4C3B5BB2BD77131C79794985B0EBF0CC351CCBE5BF92611E793C8308F0A8B5CD544556FA0C4DF25656554A1C5D43E79751200B3546929B7975538493B71B2FA40EA0747FD784D6177554D18340A940C89A40E36C84186E9748E1B478C16252E729F84536050E7C448B14FB8687696497412A5A2F8929EA6FB67FF9119331908BB24195D03ABA2C21439191E2539B0DAB826082572521181E69575AA31000FA627D3E80C5EB8BFBA688F69595AAE845AB9AA1955C01CE84674EE16682A1816725A3A8C884532BB9DBA6CACADF9C48446C2D1AA94B46930A180109D3197CC7398A7659C20ECCA63462AC508687314859C08038B5BB7FAE5C2E03AA7F348629CF423D491A5B45791A8A33BDAF101CE05A40C48CFD27930BE98762D79017E109040CB5E7CD66E49F5BFC84744627643E6EA2BEC647FE2D216663352E7D90067DB55732D97E0668D8B44A04821F740E4A04265E2A7AAA4EAB8734262008F55", + "1ABC8DB637C626CB70D4CA58323140DEFABBCE522D4F55614B512B041C98DCDA87781770279A2A63E985538CB6E1EB800637269CA0AE0AF075FE91AD25A5C264C51AA3245B987C679EFB92BD0CA6FAB6543E04513DB5777FA98FE495C8695B91BC123F86C55F9632017DC82D4AB79A67DA61B167044979CDD8C4CB57749733CC8B5C4C344F475AA949386A97C8EE666C13C53F43296AC7D76837C6C20EC28D6A550848EC67648284A479646821A66F0358B80A85C73009045C9A17F2ABC556676F838DFB250A35D1073DA307AC83B20DD7A6E3099A2CB423FD07C25619405E86C2D6C3B447EC8201572099ABC17476A2E67B4C878918FFC0A59D21175A32096247A1FBE922050A029B5A88D11430B5A02DA8864B5C637204075DE7986AEB3089F007CA3784331FF33715DB9D436C8A81B203310900192718F8E2342376C042AC92E51C86BAF22A6467332789AC7C4424E619668C57876E053EA8E0B19B78714C22B4E4F32D0F0917ECB64E8DCCB25BC5379B60A43B1AC81CB763429B0F6C89C01BB44F5B184EAED62720CBB8823C3B80A6AC4CF8047D3C625D24B36CE7C8A87A637FFC4704E7181F341477FA139314A5EC91329829C80A09C2D965108A8CAB974A297B495E0C9779E2740F94E361C9D1C4E81185EFA37BB745566797CEFD8B0834ACA9F774A13E799E61694C14EC9CEE903FEC595C4DC853740379485B8F28F04EA2304423B3C74530840F3041DF684846A89B5B8C1619F04356EB24ADB79803D0110114BE9EA962B84A0D936500B9DAC99E1A924FA1515C2CA7F3C745B922BA58458FFF71A18A990B33923C40D2359FE58ACCD6A548B62A8D26A5A50CC439E07BAEF230D2161367C3AB46619497BC5F9DA7430969BA48D9CD1DE500B9A76B017600B999CBC2A828298B94A88AB6CB3738CD6BAF3C299AC6AC6029B8289F4A276F6C6FAA20B7393940B10B7650FB430430AB12BB29EBFA6AB263A2AF1AA880C730B2D655C2C67066D7A06064B91ED90F7BBB559D0B7B20C4721EB907F469A0AC9B975816A0F3038AC53040637A9FB8276F33C93097B579E8B7CDCAF1214F175108C9A217A9BB0BB90955E2A5513488D9675316D1086C7774218649373C5EF425003EFCA50CE8B2A9349847C011CC421968F990010969BB879FD0D91A28074E9FF06988521EDC12B3AA1A466B955D481A095BBB15AD0130B11953176B5573E379D4E42609C6967A339C3B7453498CBBD4489F54DA0B8ED42A24DC6AA00515F7586703F1C612C6A57AF2B6CABB1991ACAF980104B17A5E1C097A67C46637E380DB91CE6FB612EA35C194200EFB96029B980FDF2B3D36A0561392A89C788676B48A771CA6A47229C0907355317E9EB88903F17792048FAAD182E2C065E30B63FFDA51B69554B0344E27895C4D924CA5FA80BED6AC0A372877454F98C77C4C699D9259BBE30A810BF163CB5817E9339C17151F9678BDA7E00BA1C965F1FB6193753CFC964D82EC36528490DE2539D830CA20FCB232FA3973511D40B1334FF2AC016CCB271CB06432BFD3BA5C7060785D2C3A32AAA4C0762EE873A91955657F49C7B2972106EBBCA39442A3C9490D7A4805F39DAB8A29E08B89A2F80EBE678B200A3DBB376511F62F1A77383F32B6EAB75F462030F75C95719517A4311FD25977B31537C804904B2C0297B63499A0965009B926237965158E9B7963F4A41574306EBD66C80BA4C3B5BB2BD77131C79794985B0EBF0CC351CCBE5BF92611E793C8308F0A8B5CD544556FA0C4DF25656554A1C5D43E79751200B3546929B7975538493B71B2FA40EA0747FD784D6177554D18340A940C89A40E36C84186E9748E1B478C16252E729F84536050E7C448B14FB8687696497412A5A2F8929EA6FB67FF9119331908BB24195D03ABA2C21439191E2539B0DAB826082572521181E69575AA31000FA627D3E80C5EB8BFBA688F69595AAE845AB9AA1955C01CE84674EE16682A1816725A3A8C884532BB9DBA6CACADF9C48446C2D1AA94B46930A180109D3197CC7398A7659C20ECCA63462AC508687314859C08038B5BB7FAE5C2E03AA7F348629CF423D491A5B45791A8A33BDAF101CE05A40C48CFD27930BE98762D79017E109040CB5E7CD66E49F5BFC84744627643E6EA2BEC647FE2D216663352E7D90067DB55732D97E0668D8B44A04821F740E4A04265E2A7AAA4EAB8734262008F55BA5847B144C55CB6F43370E75F26C97B32E62B786D7F0E68D590846B65A9D85551DA028C3ED5E25860B2F5C7334317DFB9F263FBA3C4AA9CF579F98F33706AF4", + }, +} + +func TestKeyGen512ACVP(t *testing.T) { + for i, tc := range kenGen512InternalProjectionCases { + seed, _ := hex.DecodeString(tc.seed) + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + + dk1, err := NewDecapsulationKeyFromSeed512(seed) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKeyFromSeed512: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + ek1, err := NewEncapsulationKey512(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey512: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + + dk2, err := NewDecapsulationKey512(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey512: %v", i, err) + } + if !bytes.Equal(dk2.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + if !bytes.Equal(dk2.EncapsulationKey().Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + } +} + +func TestGenerateKey512_Success(t *testing.T) { + // Provide enough random bytes for d and z (64 bytes) + randBytes := make([]byte, 64) + for i := range randBytes { + randBytes[i] = byte(i) + } + r := &mockRand{data: randBytes} + key, err := GenerateKey512(r) + if err != nil { + t.Fatalf("expected success, got error: %v", err) + } + if key == nil { + t.Fatal("expected non-nil key") + } + // Check that the key's seed matches the input + seed := key.Seed() + if !bytes.Equal(seed[:32], randBytes[:32]) || !bytes.Equal(seed[32:], randBytes[32:]) { + t.Errorf("key.Seed() does not match input random bytes") + } +} + +func TestGenerateKey512_ErrorOnD(t *testing.T) { + // Simulate error when reading d + r := &mockRand{err: errors.New("fail on d")} + _, err := GenerateKey512(r) + if err == nil || err.Error() != "fail on d" { + t.Fatalf("expected error 'fail on d', got: %v", err) + } +} + +func TestGenerateKey512_ErrorOnZ(t *testing.T) { + // Simulate error when reading z (first read ok, second fails) + r := &mockRand{data: make([]byte, 32), err: errors.New("fail on z")} + // The first 32 bytes will be read for d, then error for z + _, err := GenerateKey512(io.MultiReader( + bytes.NewReader(r.data), + &mockRand{err: r.err}, + )) + if err == nil || err.Error() != "fail on z" { + t.Fatalf("expected error 'fail on z', got: %v", err) + } +} + +func TestNewDecapsulationKey512_InvalidLength(t *testing.T) { + // Too short + short := make([]byte, DecapsulationKeySize512-1) + _, err := NewDecapsulationKey512(short) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } + // Too long + long := make([]byte, DecapsulationKeySize512+1) + _, err = NewDecapsulationKey512(long) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var encap512InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + m string +}{ + { + 1, + "ADB031A018722F19C25829150A8B94297C9519173A2C908AEBF76F86C9724A0354300C6AB58A90C7B18A2E828B5FD2CDEACA9810EC00FD8CBB22AA6AE641B5824055A91B27C1F73518953913FA2037D295F42B7E34AABE90D72A2453B215D886EBE28779D76F4830B01A73291B5A7186454A91D665B9E6C04FE808F76AC25E298B97195999F866DBFABD571B59549787F5E316A0C65F3E9132089B219E11712D245AF955ACBFA36D37BB2DFD570ECF90941BD369E8023B2FF65F04D5408FA84531604A5EACCF3D62221429A1D6B1BE5FEB04EBA0BB698B6B6AE4CF72317D96E99B0A840AB55C275B9979BB231926D7140FC9B8C8124B87532E854113532A809C714D492746BEA7C30AA869DD780B77D0BADBBA9D9FE6A09780B1EE7076F5E3CAF5F470D004CB53098BFA340248835EC782B34D612C53701259D2583360935D8B5F25051BF196214D1CC6B33A8A47F41A65B5A143DB3651885214307B0D938F9D253672A4587EBC6D9743A3AFB35A3098149B27C3EA0BAAC5942D9A92856EB0447D1709ED70A673AA1FEBA5398A821FA21B215FD78866C29B1E75C50815CC3F093B7CA70717A10CCF8C1880072230163008AA675A17759937CA672A56BBA772C527A5F9F12D53267419559A7A1A0A3003B64D34ABC8F4294210B140F517BEE86F393BB577E6A41EB64A4EF633C8BA941314528B67A24F4829E99C96BF92022307A763A9086F7C597340C8FBBB0689DC1D28800BAD710422A9334A406B203101A6C97A3D00AC6C095735E22CBC60B3A0760C8B0268FB5BB3D7D0543BAB599FD326608264EA477EE8F8C178898FE7387F15A6B40B13B069D3B574967F8487309495420F20B23456CF6D010AB19CABA9B27CAE79B09D7084D1863359B40F205556456AAD5F92A5A36B49B737866901CF6FB47B59A9C5783C2C5FD95087DA417F7410708290B619B701852B051272AE0428263C28CB025E1C1103B2718A0DDA5FC30394600879AF509B62C57B88A018FEDB09642872591582ABB4AC1408A5750902589406E2479D3BC5AD8016610D7BA18D984EBD951393FC2B83446D474AC8E2A634F66F181976B8C14F6078DE84113D8E728FAC101A23C86300D795CAE9875436CC", + "C1C9276205D0DAD6447DC94A5E2C36E754499B42595E75BFBFC15B8AA2615204A33EA62DA190398B341384C1BDD7B9BC547A976594BFA20C20BA9754302A0FF17616C83A3997C9CBF6F7031D9A5977BC58C1058FE15C3101981578B48B09DB07B035C220753C0766992C035FFF0836B3120B06D40200C6484DEA3618491FC446A2C95B94332512AEC61A8453B2251794A0929859E03925A31B0DABC9E9F88E30B2C364F9858CDA1D7A525F913B51BCDA41BB8635F9202253EA4EC1B7882E541A9A1411B66034212209D7F7B08B960832395280628742728B3BB0037AE07FABBC7CB89289DC9996B5894E203C5F24E77EF33B77AF513D3203B32F7258D663BA48A49B0249CC837AA35966C4CFB5774E96AB689613B6565C17DB0273268CFF2A2F391A018FD972BE165C1FA3A6B6587830EAB75D6B4FD3750EF7820727E9CC5D3869219A690FA58A966C80715AB23D7B042111733B8A3AD70674415277BF0C70F225B7BD775E639CB88FC06005CB135F752E02C66C0A197AFF56544EE13A0F8C2119C4B410168F16D779CD7590B77CADC18269B1AB84F1C562FAA94342279BBFFA58961032F794562D288D2F9683F00C06CD5A7B923002729947814811397487C4972579604288695DE054576F872501F02D98399A4369B636AAA84C04A9C87B1B3A3A7DE79B6ECBD49AAB705652CBABA6A1C91153277726203784BBDAE16703FC77F5AB273AB7376A033A15F3CB19872AA113633291416A85A5ADF9BC164BA9E32C991D85CFFE9B391EC06055B7BAA5A59053546EC6DA129896BE628820089B31A1ECB6AC91C5184428D4C582FA9A4841A6B66D500657A456D378BF6EBA41F9EB57CBE8227DE06751FB9AE058981A105604095E0F0943E79C715ABA8ADE8A2250D600BED71B42E7CCC676909CA78EF9823B60C14B4FE565C63237E9E10EE3761D5B677F9AD809E3E13301C601E1F36B5860C3828B9E7634C3ECC53541CBAA5460AC94270C530C617E9866097C20BFF5B879F642BAD352A2E6254CA4184C1022DD8297CF2CBCDC6292FAF5BB9051226D489D8A5979F5C810DC9819210B309E3A71ADB031A018722F19C25829150A8B94297C9519173A2C908AEBF76F86C9724A0354300C6AB58A90C7B18A2E828B5FD2CDEACA9810EC00FD8CBB22AA6AE641B5824055A91B27C1F73518953913FA2037D295F42B7E34AABE90D72A2453B215D886EBE28779D76F4830B01A73291B5A7186454A91D665B9E6C04FE808F76AC25E298B97195999F866DBFABD571B59549787F5E316A0C65F3E9132089B219E11712D245AF955ACBFA36D37BB2DFD570ECF90941BD369E8023B2FF65F04D5408FA84531604A5EACCF3D62221429A1D6B1BE5FEB04EBA0BB698B6B6AE4CF72317D96E99B0A840AB55C275B9979BB231926D7140FC9B8C8124B87532E854113532A809C714D492746BEA7C30AA869DD780B77D0BADBBA9D9FE6A09780B1EE7076F5E3CAF5F470D004CB53098BFA340248835EC782B34D612C53701259D2583360935D8B5F25051BF196214D1CC6B33A8A47F41A65B5A143DB3651885214307B0D938F9D253672A4587EBC6D9743A3AFB35A3098149B27C3EA0BAAC5942D9A92856EB0447D1709ED70A673AA1FEBA5398A821FA21B215FD78866C29B1E75C50815CC3F093B7CA70717A10CCF8C1880072230163008AA675A17759937CA672A56BBA772C527A5F9F12D53267419559A7A1A0A3003B64D34ABC8F4294210B140F517BEE86F393BB577E6A41EB64A4EF633C8BA941314528B67A24F4829E99C96BF92022307A763A9086F7C597340C8FBBB0689DC1D28800BAD710422A9334A406B203101A6C97A3D00AC6C095735E22CBC60B3A0760C8B0268FB5BB3D7D0543BAB599FD326608264EA477EE8F8C178898FE7387F15A6B40B13B069D3B574967F8487309495420F20B23456CF6D010AB19CABA9B27CAE79B09D7084D1863359B40F205556456AAD5F92A5A36B49B737866901CF6FB47B59A9C5783C2C5FD95087DA417F7410708290B619B701852B051272AE0428263C28CB025E1C1103B2718A0DDA5FC30394600879AF509B62C57B88A018FEDB09642872591582ABB4AC1408A5750902589406E2479D3BC5AD8016610D7BA18D984EBD951393FC2B83446D474AC8E2A634F66F181976B8C14F6078DE84113D8E728FAC101A23C86300D795CAE9875436CC17073137259AE58DA42855C429CB69DE93930C35FA796F80C774DC812AD0B9F6E2FC8C99012BB954AB3FDCBD6F6522CF7A0A59C511B330C6B72C0493BA70AA3C", + "3244E86669E69F0F238E3CD7F03EF31C4D3CF48CEF726955F06EB5099367310D5D9FC70D48A573458837319BD1691D1A699A68F7A9A8DB73D03620E9E4BC4B088E5E9C5E3638EB3354F6EF3C5E7AE5D57D0571F078E174CFBD6EAE2FD76DC2BED5A907EBA531E89B1BA8D2A8EBE7B4CA0DE96BFF28D278A70549AA0635BE50096F297F7BEF92C6AE9C11C4204CFF07E0598F14495AEFBD207B760DAD34FC0AD8F4000A1911F89FA3B59410C8151B9A8914AA71269EB7E2C329586D3C08F3F10939A497717CCFA3EC5082D46750905CEB703106C2D3E5CD71F138704A20898B5F80F5FDA03C08F8894C2874DE32DFF5C27EA0437A44663C0D6F6B85332AD0F5A0E48D1638BBD281797AF1ADED5C5F1EB87D4723E17BCA439EC469489A371A402EEEAADF1A1BD7C7DA409E9A6414E744167DF13AA1ED9EBDB354BC0DD04190DBA3EC48E5D1DB61C54FE881F8A1DA32EB512F2423EA7F9015DC8C2C3D5B5FEA438A88E6C877A6F4ED17FAB8918E53887996D23956502ED9D3D07BBE8EC899AF55813D39CCF6C2700AC8805517317338655A221268E654839C49D83344A1DE0E75FDD63549B7D57258601C1C74B0FDEF80CAB109C54393A7669E4BDB5CDD3BC21731C1E467784DC6A165194487A94FDAA9C177A0BD4AB009B7D7BBD9EEBDA386492F7903CA7C4345A41271D8B6816B1AC0841B8DB7E2D518B3A2B70386CB5BA159A11FC50420F94C001E1F8F0268A2E0A4A12485C08D0BB696CAC92C8866DE78F18BA7C0E5C4C2F450EAB9E2B126DBA80EF70FFB611A010EC3AA9FBCFB2058C2491B331E63AE27321C0098B49C9F7BD409C70DA376A338317217AF310788772E2A95D1BCC29355B486E3B1FA11753C7D39802D183AAE86C3CAD2EB4E70B3C679E47F01D7FDA48B629E5B8AF315847D20BE7A64EA4A16AB9B237F00A9DC659E01735290902F243E866129F120CF3EC01CD668A9827AB419B7F9994A305782C6CB82801C4DA0B9032034B890A761182E4108EF016AE48AE32ED05544EFC7AADC9D219B4E2F7E892EE58130B7413AD2CD6B5E04CEB2593E06165E37BC8BE981EEE1C638", + "6C832560DFE97BECADFBB340EE31AE868A73120806EED02839518E5627D32968", + "E8D6BAC09B25469BEE582A7DEE9BD21890CD3F0AF4D7E19E30B3E24E657C149C", + }, + { + 2, + "CE22B227E25D5B5422E53ABD78D6B80AF94A4BFC15AC6A39C56602C1B29934F456E23C9FB9BA568E4C2B3E7B8D6AF7A8AEB4A7AC94A57453CE6E05AC505B1F6785BB024B6EA9744E6C7874472AAFFF7B0F58A5ABFD2424F9E3B8AB9A4813A19FCCD2CE35E1C45829BB5888A9D6C14E351618F72B9135FA0677B91FD0EB84F997C5FFE7C45FB03067238FB5C880469079CB13620DEBB9A4E5247E08247B1C588666CE43EB6377C33741F2372CD14FC9D6BD9F53997FD92A1059AD5FC261EE89561AD26EB7C88A95370CFEA4A76A9CB5DF32996E5C060AA1CB8F39B8AC9956BD037AE3C750F255AED4B56E44317ABAC0442BF484F43896B2493A53568A6123444FC50E6A3B81A6223BE973902CD64C03F233B0B638538B57BE5A43F11BA31B93CF15D32844E48AAFEA0DB277A19E9A395496366D9C9AD2341BE34C58941692A1DC7570626202A5185903466CEA91B7078C357787967493569760C348ABF7C223EAB1365F455C85306ECF8BB951C0727FB94B89790A75D03782B568589B194A80C049DA498347C50B26447F5C8334B70C9EF74EB65C1DA2429B11942D0D57BB14415C6442973F079B1881AD9D5C71657757AB2A2650EC252292ADD1AC2967B9CA1158A6D2E5BBD55211D24874753CC673A8A689B8021010BFA2A8938F3B6F68549C326A221EB86BF7D98A8ED708BBC95AA6D51F8CF94ED374A1C0585F36BC7F22CBA513449942363F37D167CFCA6A38588147F101E26B5F818C2C67B515D7802DF57437BE9567D77A5164B4A061AA0EF019CF1E9024FAC61F43812B47846185C4BA2B4A7A3CD51CDACB558DF8101C2326EA8B4313C91C043542D7B53EF8B83AE02AAEDA2749B2913CC65487D408C463233750A4CF3913957F270753745CA8450BBD113877C3A9D370643D5BC13D0B4D6D979C9BC197BCA91EA0DC004575C7F8CCC08DA501356A4197AB34F34554DD461FD9C3A94A37C90FB864BAEC31D26B7E0D83853C48C896018D67113C4BD723362B87DA964C56027E820B6412282BA7A5819E7A9E4DD27E8C778B8EB8AC7C21BEFEA4192A9468BFC13BC08B079042152012250A0E9261D5B0B540A09464A54EF02AD0F1255ADC57F034529127DA186DCE9527", + "D910660105C412B3698D043171C1CC9AF2CC65AA019765034FD94AB6F11613DBB80223371970AB05EB3F325686B6A771EE3324DB5BCD32726B0BC760FFDC486AE473D31521E3EB8B5B5A9E0E7A1DA8104E39752D2A32BC08130F2902C8ED33C8A9E6A670AAB329D170A171AE7450686A0CA980BB8C4203AFCC3985C5E5130AE598EFBA572C316149FC9AD4774FC154A7E12662389849C27A64B0B4A53193A83F50AD6A3A4BEC9A5707F09FBEAA360DD1837E217E1AF335DBC120B5C61FC5260B81408AB9793C431CCB0C9624FDBB687B0699DBC93445363E7A4BA9A644630E4B9B1D03BFB7DCC18AD3182E6891D33927FB9188739043D774C06830C48E84286C817F4259340A366D4EE0141917924551C822ACBEBE57BBDFF58774E780F82B9E4F375CA1D14C344A401759A0D8277494EBCA2955753D7B4A01B3B864E66ED2B644EBA073F8805F8E07208799871E7777EFF5A539AA57D3C55AA9844C3EEA456EF5877A5C1F637993C1021307F8C97CD1165AC837078877691B0B8B7B05295C8F7E7C15406636E18B9E3E98C1F0C522A292BE59B974939C293158290A503BD84456E73351C6495284F46FDA0544D9BCBB54189658975AE1E5BBF4F0410FE3355A33AAABD6335026B55CB2CF2B21CA581BB8A2055EDC918BA3570128E392BF10C55A0BCB97C977ABDB8784253908900D6CEC33B2428FEBE6A426274120C27ABF1AC0FB81C622DA0DF7972E201A28D788A761D9242E63B68A7A7B2E67840BF76382F2159E2C425CA69BB1FAB1FEE66AA0B89E4AB8A2781649676124301CAB6BC4B790B1C26AF36C90B2691C50157D153424AC22444B13BF297AEFFCA74196B2F6A7456A80213B381C6D776296A7173B6A28CE16C41AFA657C7911EAD7354F2770C8FB1E06878841738B51C95371D690A8BBA1D5AB098B9041F2A3B9EAF07DF6DC315DA273BF0503DAC3190B27A0CEC78E3FC4456E51C9DCB65C702A777FB0727266AEF47B2A7EF61BA475B60CB2CC30C039D3CC87A05AA72A252EE043CBFB173BA13015BF08C382CAAAC1367CEE779D0AE6BA0F5435B9241496A581ACB36C3ED212CE22B227E25D5B5422E53ABD78D6B80AF94A4BFC15AC6A39C56602C1B29934F456E23C9FB9BA568E4C2B3E7B8D6AF7A8AEB4A7AC94A57453CE6E05AC505B1F6785BB024B6EA9744E6C7874472AAFFF7B0F58A5ABFD2424F9E3B8AB9A4813A19FCCD2CE35E1C45829BB5888A9D6C14E351618F72B9135FA0677B91FD0EB84F997C5FFE7C45FB03067238FB5C880469079CB13620DEBB9A4E5247E08247B1C588666CE43EB6377C33741F2372CD14FC9D6BD9F53997FD92A1059AD5FC261EE89561AD26EB7C88A95370CFEA4A76A9CB5DF32996E5C060AA1CB8F39B8AC9956BD037AE3C750F255AED4B56E44317ABAC0442BF484F43896B2493A53568A6123444FC50E6A3B81A6223BE973902CD64C03F233B0B638538B57BE5A43F11BA31B93CF15D32844E48AAFEA0DB277A19E9A395496366D9C9AD2341BE34C58941692A1DC7570626202A5185903466CEA91B7078C357787967493569760C348ABF7C223EAB1365F455C85306ECF8BB951C0727FB94B89790A75D03782B568589B194A80C049DA498347C50B26447F5C8334B70C9EF74EB65C1DA2429B11942D0D57BB14415C6442973F079B1881AD9D5C71657757AB2A2650EC252292ADD1AC2967B9CA1158A6D2E5BBD55211D24874753CC673A8A689B8021010BFA2A8938F3B6F68549C326A221EB86BF7D98A8ED708BBC95AA6D51F8CF94ED374A1C0585F36BC7F22CBA513449942363F37D167CFCA6A38588147F101E26B5F818C2C67B515D7802DF57437BE9567D77A5164B4A061AA0EF019CF1E9024FAC61F43812B47846185C4BA2B4A7A3CD51CDACB558DF8101C2326EA8B4313C91C043542D7B53EF8B83AE02AAEDA2749B2913CC65487D408C463233750A4CF3913957F270753745CA8450BBD113877C3A9D370643D5BC13D0B4D6D979C9BC197BCA91EA0DC004575C7F8CCC08DA501356A4197AB34F34554DD461FD9C3A94A37C90FB864BAEC31D26B7E0D83853C48C896018D67113C4BD723362B87DA964C56027E820B6412282BA7A5819E7A9E4DD27E8C778B8EB8AC7C21BEFEA4192A9468BFC13BC08B079042152012250A0E9261D5B0B540A09464A54EF02AD0F1255ADC57F034529127DA186DCE95274FF27A9AF1C9074D3439797A96CC512AEE5D09BEA36A6BAE4C5D1A75943D02C56290C092088A99E4C5A6ED16C739BCBF33E640CE33D7DE20714305EF61BA2B96", + "C3D565942112A5B216850124DF528E6792119D688EC12BB9438F83AAF57DD05B1AD9DBDDF482949BB880E4F76FE77AAEB0946B4A5F5A2BBCC8E5C055360B765049D57411E2D3DC201611BEB7B665B7F92453245A668147D1308DE2991F57581E207FA8860890FE3CDB97E35482C38C2761C1CFBF1075F2DA1EEF3B93EBB98B448780183D61E1A70E6EC407669B3734907C5212C6915A368535F6D0DD5C31B0C0800E3374B69B53D2E497BCA88EA5E8A4266A31DDA6D263F7888A85E5E648E79862F0C5B4CDBBD4DAD72D758532553569E16EA6C73FC2F82B65D074F710663E5D17A5FAD1B05152F05E9005714CB395918E59B275BD90B6B9AE588C7984B481E459CDBFBC0E00F0422C7C8EECC783EA6CA1BEAF970E6BBCD54061210FF24FE504D9756C79BF746531EC958244623F89ED87AB48B6CCF28BA18E43298B7E5C3F3EC2E3B669F427762375DA779D206CC889337429DF1A1AD02E0268B8F9F479A573067C9493C1A65CDFA7E7DD2FFA7001423D39C0AFF53910C404E2334420493395EF2C8C2E1F01D2D2F82F7DB8D2FCD30ACD1679D943A9E705AC35F9D10BA9464D6EC52FF86610DE36C79B639B5634776F68203A1D6FC8FD892BDE9F59978C35446C0D4A684108ECCFFC631FD09031932C7435C5EA2E89A25FC08F0196980F2B940CD4D4429AE195ACF14AD8042C09D39A3EE7DC1D1344A8BF5A530BF7C7270482E408FE33E6584C826D2495A5379948F4E90CEACA48757572B086A02AE005EB238C28EFA1D3E9343866C2B5865565FED01CC54D1B1C16148435B0B39F73676FBA06D0FD57B287CA05C7FD0F97B69E62B1A8B2457EBBC8CC4CAAC82EE1A7FDC30649DEDC7129146C6E44E1EF1CCE2BE004E13A276F3E06E8D2801D3EC2616C81CEC6439C9A3827337E20705BAF6F30941673CC8E773188333A76C1C2B04F2D259BB61AEC2B72F846F1C7B2D50582D641F49E61AB4F0828DC732676C3208123F990B9384364A9607905FF155E0107B51624ABACF1DCF48EB93A39E117D9B802EAFD59732D6D7E94E9C7F33AB68908DC87ACAF80EF4BEEA4CCB535C3559519A7E81C", + "F833AEC5841D7F3CA0B188221245745D66B374ED8C3C04AE3522ADE7F973F8D5", + "971B2D3905A306927C18F0F72D9F58CD3FAC6238C902C3AE679449F6CDB91235", + }, + { + 3, + "F5496665F95ED5240696B112B542863122ABC8E23D1F647E1FD74464F167A58B69BDC21A4AF7991290292B43B6EF692541C144B8E72E004A5C1BBC02528B3F02BCB6F2751B06454C9EF7324C051243268C936CB19FE1CCFB29C85D50AA963B4137484823753A5037128D00BE20302796F2920E0948BF9559EF9CA2E2240F40A01CFB4B0C0C54A476FC75980369CBE976A6AC423C8087B94417B926B706B0B5573B16A14B2BB06A167F75A050D200CF20AA3B6B91A9C9893D08A9264599E6C0563F031EFC250B0137983257B80BAB1C43A010E7C53B52A095D9A19F3FA64D2B6C181B580E14669FA05C4E18BB2924D221F36AB92DE8BAAB514C1788CA5AF83A90929784506AB8205765433D7F1693873631E3605FE72A4B50211D807A4EF3D80110B6009046258B97A5DDDB9D1FD15A0346118C7290FD07760A54C83092C806FAB456467CC862682DC5906ABC53DD0AA0BCA5A23D2B85DC711A88AAB092E47C2CB3B69D85938B3C4A262168A3741B5E4CB8DC227DF7C71EA83230A05A2625D04F9CD774A76BBD9E9BA3099C4462F97E30B56942EA48B8566225A45549C21004C89B66900B6A7898232A385839396338A773651F72060D04B1994C282B6CE733C946A97D6B202BE92458A0AC9EF092AF1336EBF46CC1A99D735C4B8AF04AB7607093122C54764A3EDA4676062A35261BBC67C688E9A053F830BAD186FDCB10BCB42E1AEA2AF3D390B0568882246646003CAC4C291D494F14649C778A293803640BB2A59343A114C13407C06AE68665F4414747A19782440C49B6C814B91FE03019576440C8F361FE256F2AF70EF807ADBFB275304606A793A2CDB984BF043703F71C233C6B92257D66916B52C41EFCF115E03100C292055F0BC89D2102E9755203065836E963F0058E52C18CF8190D97829B2661AAB25A2AE89993B8F1756A86370B4884F0C70F60CB73D74C98BE394396E97427650493EBA03ECC2C1A6BB638488BAFF015D5D0CE17403B664517BE6B19F0B17A4DE2B2976415307334BC715B74B8A9EC5BC41A0A8A0C6CC4EF6420D1551C260026E6A58A80434FF58776044492E6A9DD1B70197DF7A3701EE36CBC3EE64CD5646DDEFC65E8B3AB813F85297148", + "FA17500FF65A5BA64AA1796700C2C6F5ECAEA31C7446F35A3FC1659440A554D85CCDE8A0BD2075ABAC60A708B423AB62DA6C18411B6FF1E25C66A92F0DDA925471AF9E95CAB8877B4F6A6F7887CA1CF84F76A9CB7B28052A20C112C88277A57D8F3731D85CC2B31050632240A4C435C09C81409B6CB73CAD79F2B9CD2085A8FB4F3B9862F6205E82501C7D964CA1AB1FFF9A9585D67636906AA5242C6E342522BC3FFCDC744780299FECC8C62C930AAC34CCD55FC617A5D42A63BFF1CC8D50BC60662A9DF4B6B96183F02B95693236564782B4C7CFFC2A8938A132D1C45AC4313E2D775A882864EEAC9C6142AAF4955DEB67304F96C77D1CAB886858457A19CF03235351922DD65E94607085D4C32868AF3966B0B1C541A0412EA113AD360513C4B5014AE20CB3B6311D07A4A4323FED5C650307856B7578C64211A8C259A29AA52ED869D9D74EF318CB10F485F7769247D3B7B4D000E41967C3C7140FAA37C7C79C927BA7B5042F13F674EC1B9C61A929DD59BA88BA3F25AB9C3AF3C38056A23B35854AE4246C3167F4317BAEF0C53B385B2F45616F363E02B9492D064A0C67430383047E18B550F79B4EA996FFC76669DC4EF604B7FAFB9B0D6C54FFB658C740459DFB21CB984D9B336111A9AE82C0CF1D0321D9809AE9E0AEF1CA0F68B1A8607C678CFB4695122C5988C3EEA38DBAA864A8DBBFEAF32A96216AD1CA7560A34D1F3A55203B5E9F847045481203FB40C6136D2D630ADCD9B17A056F07713F4D34421922C05FB96E6E577D90D5B62D6B0960603B569647CF385C7B333680012876C0A49C9853C953BBF8B483839B483D211C340035CCB99969323852F05F03729474F90BCDF44F52FA0399AC66CA811DF1776541B686639119E956203A369DA8D9958D9187FEF8C99BAB0B40E277D256856C45BE55C0BEF6C699245C42BC1050E32C76B335454EEA94692223A1DA8955D1359AD2C69441924098C074F9CD32932A6793359C9A6CC0F7340E798CA3DC046FAA9BDA791CB0E5A80C1414C0E21E94A290BA01CB87981791ECCD4F0610E9792789D16CD26AC1FB7A312E636F95B06BF5496665F95ED5240696B112B542863122ABC8E23D1F647E1FD74464F167A58B69BDC21A4AF7991290292B43B6EF692541C144B8E72E004A5C1BBC02528B3F02BCB6F2751B06454C9EF7324C051243268C936CB19FE1CCFB29C85D50AA963B4137484823753A5037128D00BE20302796F2920E0948BF9559EF9CA2E2240F40A01CFB4B0C0C54A476FC75980369CBE976A6AC423C8087B94417B926B706B0B5573B16A14B2BB06A167F75A050D200CF20AA3B6B91A9C9893D08A9264599E6C0563F031EFC250B0137983257B80BAB1C43A010E7C53B52A095D9A19F3FA64D2B6C181B580E14669FA05C4E18BB2924D221F36AB92DE8BAAB514C1788CA5AF83A90929784506AB8205765433D7F1693873631E3605FE72A4B50211D807A4EF3D80110B6009046258B97A5DDDB9D1FD15A0346118C7290FD07760A54C83092C806FAB456467CC862682DC5906ABC53DD0AA0BCA5A23D2B85DC711A88AAB092E47C2CB3B69D85938B3C4A262168A3741B5E4CB8DC227DF7C71EA83230A05A2625D04F9CD774A76BBD9E9BA3099C4462F97E30B56942EA48B8566225A45549C21004C89B66900B6A7898232A385839396338A773651F72060D04B1994C282B6CE733C946A97D6B202BE92458A0AC9EF092AF1336EBF46CC1A99D735C4B8AF04AB7607093122C54764A3EDA4676062A35261BBC67C688E9A053F830BAD186FDCB10BCB42E1AEA2AF3D390B0568882246646003CAC4C291D494F14649C778A293803640BB2A59343A114C13407C06AE68665F4414747A19782440C49B6C814B91FE03019576440C8F361FE256F2AF70EF807ADBFB275304606A793A2CDB984BF043703F71C233C6B92257D66916B52C41EFCF115E03100C292055F0BC89D2102E9755203065836E963F0058E52C18CF8190D97829B2661AAB25A2AE89993B8F1756A86370B4884F0C70F60CB73D74C98BE394396E97427650493EBA03ECC2C1A6BB638488BAFF015D5D0CE17403B664517BE6B19F0B17A4DE2B2976415307334BC715B74B8A9EC5BC41A0A8A0C6CC4EF6420D1551C260026E6A58A80434FF58776044492E6A9DD1B70197DF7A3701EE36CBC3EE64CD5646DDEFC65E8B3AB813F85297148937D2E5BC317BA84AE3F9C11E41AD14F26504C8C010F2F71E5532767E255E3452E61FE4FA73C922F49DB415D0543153DF506CEC47659F7C1187210C669F25186", + "573CED7759FA816716C2B6E4A3E0089F25CD5A8F3972932C5EC8C501BB948D7A2A126192DBBE683C64A0D8605F36100A7B4B30D54ECBA0C849FF10AE4304CC41337045E60A003AE14280235CBB0866FD0F60F7B832142A9FD2B11A884B53B144E2E4F9C8F5F95A8C19DE0E6A5413FE1CE157FCC638C02A1ACDBFF56301FBCA71F3C8EED4203E649B7764C69B9E4FBA2782F00C7DFF8E0E09E9443F3C9C48E13B4010619C570895D3870C91E87CC74792CCEC7A9463E231D01ABCC1D63A5092F59129A1393D44D811C6CCCD9084565EF031E4B30DC56F849F8F3641AB824F1A598BD7963070B334A0E1A81DE64008577E28348D3E452EA14815D52EAC8ADE4E1F1C2FA5F3B95EBCDCDAC0EFF04BE97843B130443870EEF5B4F5A11F6FC048FA7507862B4C872B133484CA639D34A27BD0C2CF9DCC63D760E27EB50B7890EDB74F8BDCB64B4AC252BFE5B4F74106823134DBB7CEF4CF29D8812B15BC82035804D7ABD0B5D88870B59337EA720035123FB5D3471C96FC8D7B386095D828D4C71D1588080242657BDF6789A7CDC227FFDF5462B59D76D8CC29B5FAFE9099638532E1FE5D3C73E1A6C0A37DD36E00CC2DAAE2EAED9EA6D3D39A7AC6522F66C27207887A447199C98A6B80E9822F0CF23F29BBC402C5C3533A0DC3DCD181613FE1367F734D25C5EEF731D069302584CFFF7DBA2F7023A216E0946EFA4ACC3969E99B022B3C56FCE869C746E0D9F4CC528306056EEB7E32CDF523C63CAA84B6E543F49791ED6A0867A7D6C62C64AE560EEFA3BE94CA3FA7F6118C6D35958E29CBD44E0AD682DFFA302EC398B1ED9D72DB649B1518A50116C70314CE4590B8004205EAB8B6AC68AF5CD932C24CFD02BF9414C3B3727E4FB138E984D0BB1E7E4CBAE823294E7DEFFA4D8CC075D2A8F9A891A31FA3D8665D029872DF083DF75D7E47BA24FB3EF7CEFD4A6B09E496661903E784C450BCAE25C15DC1BC91F75146839E884F564F5233BB5CCA8C2F723037B68F596A9DC509D40A33AB192207D53CDF22F592D7F516524A2FD474AA9D14855C4FAE1E028DEAF78237BDC8420B91035D60273385", + "4161D690B5985FF27B6C385CD3810477523EC7BF1943B743224DB59F0277EF6E", + "A9E1CAAAB831808A51837DFEAB3669CC6C6E5C8C44AB3ADAB32D245C13CC812E", + }, + { + 4, + "B449808E533DB80176A324BCA4DC33E161CC79DABD92E8667149CB23721C2A8A1A251755D3F879D744B13A2C14E685ADAFA298786B40D0559D1BC50C3B82BAF860BDD004AC5825759C6596E45BB46B4647EFC4C7D0321A8149CC6A71323C05330BC984E0C703F23437603076228BBC4B7182263A1DA83B9EDEFC7584D2326E973A1978C0F046801F884E1DC0498583B5BD098E9C49BBA5C7A6630C412D02966C2A80C63587BB5A438A45BBA9621A071ACDB4C91118038F78932A552417DD43821CE7C995B95FBF295482F2905637C5217970C7F1B8BE0B87471C4146467102F08549EA266D8A8536898C7538440DFA60FFA9B91A3B3097655891573DD93A8CCBF81C2D4C10D6B0CAC0B92ABD918428A66DF9A2B7DD440101D83DD88150E4FA6B66AA5FC1A1CA9854245FA72B8C7980546C380A68C84DBBA2F08621FF8727095B0711F9307C876766FC95724CC4558741AE6A23595CA94CD4B9CE303E25846BDBF3192DA867816B1F12E62BFB7977712101CAB11A7F90C9F6820CFD07412F254F8E8B2A1E51C5F4806630C764A04C70D8D56DD2C072610B6333F58E46D782C52B7B44C4A17EC45B1ED03E17CAB6D0F203A7379EE0615EA2E48553231CA6C88D7398908205D038950AFEA34C727917C5F2ABE4AB4DCDF28AB6811E50D07930FB292CA3048442A8C45B37B66C653239A9EEBA2C0D9462C5114D577515C0D6663FCBAC93C3B3E91610FDF4242B506F5815839C929CEED957D0E45D2597BADE830990C9362D9C2B4C08AF4640757D0B954A142FC0058168BB8BAAD6A47250C7A1B1CE9187674D614D0712548D90C5237B0F740A800A015CA9B303CC4416CC8A4DBF84A0CAA49D36A76A747A42D1CB12036817CED21E3F487152A29509F6B4CAD80145322F1EE07D91917282B63E79419B659590AC62481F76161D996DFAC02290100D51249879C1443CD786A37C9A16611C980880C80124A6D493E6F35C7D4B9BC996090ED3482BE37E1F4637B5C57BF295605E949392883AE845CA143CBEC523BAAF09B27EB84A1C57798EB2BD884217B91887BB88C5A561B91CD4A3150806DEB20CC37A81F190693D16F71B6F62EA0CE9A3DBAA109888F67A0DBAA6809811E5EB26", + "5DDA60E497BCE1B357AF952F87EB170CF7CADCB452250382AFA60BC2B83C4A94CD1CE1CFB2DA011D93793DD0907BE8CAA121A585C20A1B1253025B8974E78AD297814A7523A6D7CF25478FA46141C22C1ADD5834A164575153784486AC46D48F9DF19650E0CA0594523CCA83C09CCB61808032FB7FD06B899A1BB02C978E1D565E144B0FF0E598514775CCC98C730309B82A9947B60DED9A74D9D20696971979C8449F31AA4C3B32D3A682FD1C5217059261169A1613789AC38FE371825EC910239A11682ACCC0A239FAF3159630A1B7D95B647686606100088B246473BD216184D4B97E3CD717E72B370E769346431B4D2A3632B133D394B7C3A3A3AF7871BA091B52027D5EF935B2A2771DB352DD34C959C0C83A22514EFBB99C639301E4C9B6792CA220B43AF14370C1CA8C9814AD839EEE629397E9C102EA6E716C0757604E9D229AE2D93D93BA69C10325A67404DE7C857C492D14A16DBD383314785C2A572566A067F613816ED044A857B310A601C9B6698900498F603ED15C33B6F30535548BBA2B6CD22146800952477469E9CC404383A53013487D9985C293617AE97FB1C33E90B229865620E746B9B7D699565C57ED0ABAADF88BD6D4B6F3B39C458C30EC77AB5E9188AE63245D5942531A043FE297336315102AA715BC014A2A21992AB8511248435ABBC408B54DB4583EF1133AD2A16779190D6807C3B46CDED9CBDCCB069EF576B98804BFF92F2752281C268908465B0CE3B169E4A03DD036485138C8A4157B0B2F463BC458D3A1EEAB82864C01160631DB973456AA431C055A4AA2208CE300B7F2BFAFBC6EA7A1C53E729348444E445571A6FB1A6EFC1A01318CD3C3505D74C86C2667C8C262BB527646C170AC9751513701890BCDCE6B179C721BBBE4C71A528B7C294D5E634E80A523F812631EC55702A1649F02283FD071C771647661B92AF7C227C133997169C3AA31CC078588000F0AC397F9D47F39F9557D92CCD2676AF80072BF5225A4AA0D3C8733F9B133061CA93857291658ACAB204884D900700CB1E1886FC9F5746FAA0C68D08C1B77B278869F12CC8ACC8A85B449808E533DB80176A324BCA4DC33E161CC79DABD92E8667149CB23721C2A8A1A251755D3F879D744B13A2C14E685ADAFA298786B40D0559D1BC50C3B82BAF860BDD004AC5825759C6596E45BB46B4647EFC4C7D0321A8149CC6A71323C05330BC984E0C703F23437603076228BBC4B7182263A1DA83B9EDEFC7584D2326E973A1978C0F046801F884E1DC0498583B5BD098E9C49BBA5C7A6630C412D02966C2A80C63587BB5A438A45BBA9621A071ACDB4C91118038F78932A552417DD43821CE7C995B95FBF295482F2905637C5217970C7F1B8BE0B87471C4146467102F08549EA266D8A8536898C7538440DFA60FFA9B91A3B3097655891573DD93A8CCBF81C2D4C10D6B0CAC0B92ABD918428A66DF9A2B7DD440101D83DD88150E4FA6B66AA5FC1A1CA9854245FA72B8C7980546C380A68C84DBBA2F08621FF8727095B0711F9307C876766FC95724CC4558741AE6A23595CA94CD4B9CE303E25846BDBF3192DA867816B1F12E62BFB7977712101CAB11A7F90C9F6820CFD07412F254F8E8B2A1E51C5F4806630C764A04C70D8D56DD2C072610B6333F58E46D782C52B7B44C4A17EC45B1ED03E17CAB6D0F203A7379EE0615EA2E48553231CA6C88D7398908205D038950AFEA34C727917C5F2ABE4AB4DCDF28AB6811E50D07930FB292CA3048442A8C45B37B66C653239A9EEBA2C0D9462C5114D577515C0D6663FCBAC93C3B3E91610FDF4242B506F5815839C929CEED957D0E45D2597BADE830990C9362D9C2B4C08AF4640757D0B954A142FC0058168BB8BAAD6A47250C7A1B1CE9187674D614D0712548D90C5237B0F740A800A015CA9B303CC4416CC8A4DBF84A0CAA49D36A76A747A42D1CB12036817CED21E3F487152A29509F6B4CAD80145322F1EE07D91917282B63E79419B659590AC62481F76161D996DFAC02290100D51249879C1443CD786A37C9A16611C980880C80124A6D493E6F35C7D4B9BC996090ED3482BE37E1F4637B5C57BF295605E949392883AE845CA143CBEC523BAAF09B27EB84A1C57798EB2BD884217B91887BB88C5A561B91CD4A3150806DEB20CC37A81F190693D16F71B6F62EA0CE9A3DBAA109888F67A0DBAA6809811E5EB26F1A56A1BE44CD860B6AB94C5A96A136A642EE8AC85A139AEE1DEBC92F7C415797F6828F70EC49463EEB056EE33762A2209E588FA1843B235FF7C77AE0A3A6C02", + "D2EA52F54453BCBF2DE9BC50A06B67FEE98090961FC5BCBFE683C371B5B8902DB0F4A64B309C87609562D42E04DDEF4196F0339ECB2AE2224D70FAD4134F696B538DC3FAC029C7E7F7423A0B72D65B66B1C350AE0E0F5263944E5B117B62A0AD9F55C301EA46EAD5B0F568548F4C67DB306C2DD9D7D390E60DEDABF330FF230793F18B1E8C1AB3F54B299E75F6ABB6119A38685F4C31B34559731419D978C85A8B40182C53BD0D9C26830F0EF765BD80DDD30BA2DE9CB26A7E41B94EDB69F40442371B54D35D891852B723BE176F43FE8B59E5D8EA7D7C7ED0638414D5CCE5F990D746F45331E1D80C2EF2E58086AD3D855ED717AA53C37DB52C253C124A1DC9D04AD9F126B4AF2FDED47926019B49E96842D41E77121BF977DC8728D3638FCE43BAEA29D485CD0CB6062F4EA8547E760781538523745E4477F22E4D346F07D26A5E445C55451F8440370FE2362741CBA3818B779AA5415CE934EA3D96C3A139CB76355DC84299D6795BB36402A7B964A5310B1B80A35D01CD14B7B5BC885A217D66FA94D829C724BD8939337F3FCFD899045F4C4A2AA4E3A2BED4B76436F7492C633B48F2440315B5AAD00C338D6E854B46C01D87741ADC07FF0C2E72F95FD8484CAB327F504F3FD7292070C0EBC0C4099129CBE2239B66AD4CF6A5CBD8689CD1F6C8A486BF2FDE9AC1A97FEEA82A27DF9ED44E8CBD2E727E845FDD0DFA48EE6CCD62BCB1603E48E5EC3756566EDBD1023AAF453C63053D647D77D984CC6D37FEB793B3C0602D2BD2A4ACAF67CEA9618C62ED0D70934CA63C830230D41D0D68A8FFBCB3D50330F9B0871846066F3E53846EB023E115485B3B5EB9F354191C9C310FB77D28E23FAF43EEE86DF8EECAEC1CC6DBF6E7C62DA537D2FAB0E43B21DDCF57829151FAA7F5D2004D40750DF420A96CE55DB30200FE63655777F0F6B6011B58FDD232A6B31195D367699CBFA3AA40FFEB742A8816FC83DC21379DBC001432634DD7D40F6356F2093F184E610AA6E64500D976B1FE1EF1F49DEA5B50063ADA2814840D101D3933E23AD6CD8E8910191CA4242AA91D5EB06046C7A671B19C", + "7528672FF512CE81FF014D5C96834AF516FE383FA424D4F7F143310808CC7E91", + "4DCF56F1BD29253C98117410BA20609FA9F95749986751D68E9AB639F4F78D80", + }, +} + +func TestEncapsulate512ACVP(t *testing.T) { + for i, tc := range encap512InternalProjectionCases { + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + m, _ := hex.DecodeString(tc.m) + + ek1, err := NewEncapsulationKey512(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey512: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + k1, c1 := ek1.EncapsulateInternal((*[32]byte)(m)) + if !bytes.Equal(c1, c) { + t.Fatalf("case %d: ciphertext mismatch", i) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + + dk1, err := NewDecapsulationKey512(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey512: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + + k2, err := dk1.Decapsulate(c) + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k2, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var decap512InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + success bool +}{ + { + 76, + "70EC19B39348974173A550A5C52A248E191D46640799A4365F3C254757350B65764984180A9ABC9048679D6A84C61B7405EC82A403B79569C024CACA55D79BE23C943C159DDADB17011820C7D88D54071FC8AB142727BE747BBD253A26E92071DC34716F5037D1988A889302621A8465D08ABFD2215D97575D730C9495617EF3B69D0C7AA79965CA985BC147797D9688116A20F9626BAB1997FCEAACBFB439D655A566B0A4BD08472265439C00CE487955EBEA094AD00A6A5AC6FB9359F3C11A6E4B3B6C714F70F8A7B8B6524572B34B758DA270753B697415A964170374548B9D1FE43FD80C62A4D951E28182F2848B044A8352E11F5198547B71215E6287DFF13A18287148277883926396D9A07C0B0139577E4923AC41C9C31F0B6067C88B94CBB9921513ACF0AE80D7779D72C666B256B72A777B56A2D12007797B7BE6657650EB42B1060AADC5157FB819EBF707E51637CD8469645196765A2EBFE8483D62C5E2422C27BCA84E5A0F69F45609928A7E51948A9464D5093CF463A39C26447126C684160F3A4298EA0B5B0973A15403CDDF286BEBFA587FFBB5BFA7692A404ABE3BB14014AEAC418900E4588676A5E7A21553F50037262E3DD6253A8BBD985A679F638F709545AB322F6DE71192FB60C0A368A3B06C8CF5267D792D6C2518EA0A27855635E3E68891C141123A1F8C80AA5300B151AC9AE4342334265C457CA8809891EFF12B398A8465F25508836EE3075C898822BA7675AC359D80E85FF84112C8502932016E24531FB24AA0F744B8252B3D89B4A41721BFD779A826AA46F00720B9592A12307B27EB2B5502987AB477161B72F31BB8EF2C2F4EA83823B72CB16532768508835852A701C3522A7B71DA1CD8B5CCA512117990709797C8AAEA3B300B79A28460D22A2C3527CBB8529AA667064FA2039E22AD5ED740BFA353EB04B74A9C9328266561A76A12EC2C33766697DABBDE92A712B81712D2C4E1F335AA642774441E917650F8D92278A8C0444214A774B7996463527756935171D9FA61D0B50454F74ED50831EB3138DB7374A35387BBCB23E40388BB9228C3A66CDD2222524D7CB8DAE2E70FE3D97847AC35824F5D58B54DD943A440DBFF421642", + "54301038DA5911366D16C417BFD96AEB85C5DA4AA45AF32F88A700BB9A973AB62684A052D793379B3C2B253B01A4E512AB34C8D8A76F5EBA5F1B28451A66632872C489F3CA606C6B36336895C032386BB35E98B43C8136350616CCF82A4C24816F4354B7C197940A62537BB36F2B8DE32A6AF6CAA100845FB62184B82B29DCE2662CA94D93FB027DB3C8EF858D4223540E79AD5D204C99637FB66718CD04782E149A07354D6FB2C742A9849506BDDECC0548367421FA2B8599443C74192D6B4B67CC5E16F8880087AC578ABCADB5492CC1AC6916CE73AB2858797134676260B22B28B4A8917C5ABD876EDA706CE381AA4985827D492A736652DD6C1DADD10ADF045DBF416158B0384A991D8501BAA9A714B82788C263C16858C81B978FD353126222A46E1587718872BB11513CDA3A88B6547BC464CD612548E581DFE106382428BA8452A86AC6ECB50B7B3C1CBEA571513C63B8741A2CB82C5055CDB2CB00CE9036C220A25C1BA21891B520D435342136D0C15AAFC12DBCC810E9C256D397A3D23A6287282AAEC038649151886A19F9B583806839EE86A12D131FB3E6A1CA1C9CC48626037B5F3DA74C8A025CC61A76078B07D00AA6DAD32C4147CDAADB4DF644A8DBF4222C47AEEF774BDAFA5A48258BDEA84E7B8B6A5CA09243589EF7F1C249A2B344068FC8D874C33B6C4C2782EA86AADE7C10EFDCC93FB993147A3B636127711195C121C7506759389373F896843991125D5A0AB8FB34595166A9109DC2D19AE2738444716C9C21BD06DC8145458ACBA30981A95F8E141BD17B17A3F645E9B63747E370E8D873937A858048614F0645F4B461EF769D2A696706C2811E756163E4746BF00CFFC556AD7A2F0804A6BEDC6A59B1546727C602A99397C50FF818721D5319F63B058D03BC9DE57C1DB74FE14A0D083C25B17886200633D230946DE0A148781735555CCE797040BB479FA733C2A808EE1CB9A285CF863CBDB47172F3A68EEA614736C73C71FA4AD82C6CF13C5CD3179AD296BF3BD6815D13CBFF75A3A99B4824B7560227BE0BD809BD43AED7D951ECDB7273719560DCC0E8C13F70EC19B39348974173A550A5C52A248E191D46640799A4365F3C254757350B65764984180A9ABC9048679D6A84C61B7405EC82A403B79569C024CACA55D79BE23C943C159DDADB17011820C7D88D54071FC8AB142727BE747BBD253A26E92071DC34716F5037D1988A889302621A8465D08ABFD2215D97575D730C9495617EF3B69D0C7AA79965CA985BC147797D9688116A20F9626BAB1997FCEAACBFB439D655A566B0A4BD08472265439C00CE487955EBEA094AD00A6A5AC6FB9359F3C11A6E4B3B6C714F70F8A7B8B6524572B34B758DA270753B697415A964170374548B9D1FE43FD80C62A4D951E28182F2848B044A8352E11F5198547B71215E6287DFF13A18287148277883926396D9A07C0B0139577E4923AC41C9C31F0B6067C88B94CBB9921513ACF0AE80D7779D72C666B256B72A777B56A2D12007797B7BE6657650EB42B1060AADC5157FB819EBF707E51637CD8469645196765A2EBFE8483D62C5E2422C27BCA84E5A0F69F45609928A7E51948A9464D5093CF463A39C26447126C684160F3A4298EA0B5B0973A15403CDDF286BEBFA587FFBB5BFA7692A404ABE3BB14014AEAC418900E4588676A5E7A21553F50037262E3DD6253A8BBD985A679F638F709545AB322F6DE71192FB60C0A368A3B06C8CF5267D792D6C2518EA0A27855635E3E68891C141123A1F8C80AA5300B151AC9AE4342334265C457CA8809891EFF12B398A8465F25508836EE3075C898822BA7675AC359D80E85FF84112C8502932016E24531FB24AA0F744B8252B3D89B4A41721BFD779A826AA46F00720B9592A12307B27EB2B5502987AB477161B72F31BB8EF2C2F4EA83823B72CB16532768508835852A701C3522A7B71DA1CD8B5CCA512117990709797C8AAEA3B300B79A28460D22A2C3527CBB8529AA667064FA2039E22AD5ED740BFA353EB04B74A9C9328266561A76A12EC2C33766697DABBDE92A712B81712D2C4E1F335AA642774441E917650F8D92278A8C0444214A774B7996463527756935171D9FA61D0B50454F74ED50831EB3138DB7374A35387BBCB23E40388BB9228C3A66CDD2222524D7CB8DAE2E70FE3D97847AC35824F5D58B54DD943A440DBFF4216429146E2AC9383962420545163D6F82456E1B93E22A1B2E6875ADA12D4E194AE93EF5C3485EEBBE1BB13C560480DC3471CD950EB300CF2D18F38CAE7575B133526", + "9068502093766BB27635F12F3569794C54227CB1828128AEFC5B715CDCD1E9080D59FB218D17EA0D212D158DDB5ED0FFDB4FA9401F4F23387D32AC8B788CFB7A319114425138744002648B07D5216A3EFB4964BC72E98A6EA2939FAF372CAB44CD5D8A929F66C41D644118ACDE5DA2F09B87F8A1F41F55924A7784D8552790CDF256958E35324381902D9A006FAE02933B017A8E55931B6A0CC8CE3B5723D85DE4C4585FAEC0BD80986224CDAEA443556EBF8BCFDE162C258B9E0AB00C2B9DE0190384C61988BCF362BD0493D40D276FFE4873811EF2851204626342921BFB6A75EB6079F58C030AB1D9C1844078E61C29DB88B5FDC463B7AD3F770E1CB8B526BD9B9A5AFADADAED0368BEE0FFABD9ADFEB0FBF6E6DC7A36115BA47A292D454D7A31F5601BD8BD5435B2EF464A474E37B12B7794F356F905FDBEB248B44003F2B43B925CDB98017A68A15B8B90E2D6DAB1B72AC2921CA92F55B3453C2865DECC094E77EC1E70F99A14CE22BBBF7D3C25F1ECBF96478D84DB4EB1F5E077777214CDA31165C2790172EF778435B56B712E3C5C6B2FDFA3B40B45F7065731EC1E33A8FB300F9FD1EAB14A77E5D8367329E0F834A76E889EC2C8F80E5C1098055F2D517EC381A01F37B1AA3923894D90E1A25A8F55D3DB782ADCD644A1B8A168BBF263C77F34B1A3388E76528FD4F91BFDD7D6499EF99CF663964421FFBB6C17CA9456A2E6A3681298628FA728D3FCFB3BDB65A22E7CFC962FB83007F249D543696A8EFBD9A3DBC7C090F2C82B38E76ACB653F18E78407EFDEA120AE61CDCC8C28CAD984D776B69FB201BA3E154F3C87F53CF84DEF777E50BE420DDFB9734065B8D541F983E69E7FB2B48A186BF8338F3234A0B785B2BA63AA875B28EEE98843C48F60BA500E93067F283155A21905836AC33CA8B06790DD800DD000CC42171775A07F704229FB6F9E5123ED032148DD0EC616530B98A68BE3DBAD2A5D24FFABEFD6D78F4484C8A9969DB7480F54A3DDAB445D3C6C489A9E296B612591A027D624032CD1B11452FEA69A178006E8429BEAB1FC089098BE7EA3D73518F3F5E7B59843", + "32FE0534E517EC8F87A25578EA047417EC479EECE897D2BA5F9D41A521FAEDCC", + false, + }, + { + 77, + "770AB5FCCA728F659556C212F9925F28493CD3C2A60AD743830769F9B387422AC46385C88F4A10D8C43764F5200E810D20AB9A7AEB5C8B100CB751C55B301976D7BD4F0A1DE7D7CFBF0379CC6B9844B16749B8C6659824B65C2659A430914B322289B88D51324772C4A507CBAF240436AAC23950088E41973FCC487F57BD1D14321664AFC6EA2B66403C881509BE445933528A4F97C2A5B86F1CBC08816484F9432241A09E16D6B6C7068F0356505227216127AF9506250E59ADA3AB4611D7BA4A999621241A3D8C4FE0E7CBAE2492B6F44E6AFCA27F33940E3233F5A382549B9DA2C52B9BC99A8BDC1E541B274987A5BC4A272F330F725856220961C659CD9BA534716C5A113B53CD243D5C907FD6E1B2CDE69090B40314B7CA85F2185F9401FA758C3FD8AD91441A6DCB88B7350B753CC06AE5A1FBCB4DF61CAE1DFC7E60C28C7787654A068DD06821983AAED658C110BC19C2FC61E76CAA65D9C0914887A77571B3E63CD63A437028BC03DC8F69346585D023F01465E430220607948619975FAC993703CF60055D9AF28EAA1A2CE412322D11497DF22137380714B701DAE205EF308E2EB8A1B0B09ADB896BB8AB3F34F9BA2695056B7785FD51ACD043CDC1919C6B0335D7E7BDD1C43693582ED5E71D534163A48C45796B43E98BCB3DF47E075B8AE3E96A640CA659747B287C4714113DDCCBC83D37C54022CD321409BE74C331900CB17A72DC27BB6D4401D974B4E236B82F894EF5DC9A7A29091AB475D584B52D9672DDBC8D63B0BDB51554709041CC07CDEE435F39463C04BA0DC2B4C085AAC7822AA4BA50CD470A75C53A7C1318839DF4086D8A8D3510780081112453134F15601F810895B287195866FC237669F5B212C002F62B2130952B99BAC4E0AB002FA915520A715A41B1D541CB54CB7DD0291655F14FD2D0B3D802C9D6CB569FC815B69B1A0787463104A30AB34E7BD732FEEA3C8CC8764FE199947513889942B7C110482C9A1477348C3C3B491060A78B8299490E4DBABD22B054929BA77EE31922E1439D15B530A0C307E72C2F9C568622301DFA261C885A6F66399DC79E593486B11FF236B8367BF6864A1A596448FC251898A3C89CCD0B551131182654", + "3B5879284A33A6204C06F84BF91843CF9B23CD8256E3D23BD1012325686138F40E435275298A614D30950D98B00F59AE6A04BBC37510D4DCBE738B90530B455B048DF4F4AF191B59DB8A3D37C83190D425D40014775B507D43A2E2204B9AA6B6241057663A782A411419F0A0E1E8A4D7F5995B197114E832C7FAB5F2D69923D53A46F07C403038C29219EF228DA1746ED27978D9723D09EA6D6F32856D8B5589382AF4F32D2DDA0EADF342ED3248EB0CCB7B9424FCE6432F8A3892A24D610B9F35B7A4D63B918203BEF239C07277433D8122EDA503C3D596B7C670893051286044EA919BB4863AB6A7CD8D43255C099CE1DB832B109CC24C1B15BCA2F5383F03CC7263775AD39A90DE6A1E1CBB4EE8683295353FDE05478CFBB4C5249500FAA9D35B264E36494F009D8CEA06AC096FF3965DE5980B8E8485149B6912BA7B8E935341755AC67499647391A181158F3AC719F3B36CA0624B6C6F26800904609759B0C5D4270ACCAA01159C4235685D1B079624576C73F498A3F9A37B5090FD6010960C3EC9D7A1D95C3749DC3E225B800D2B8F3B597090F0B83CB315B7A43FB69151AD2B2EF21C8E3D6028A650716BD96F466BAEC316A230232B59A97D371C3DF6D2476C51C64F48271EA38AAE9C8EACD1A0F9318DFBC273D382CDFC0043B0C47B35834EB2069A0E306A53826C49C7A69AE3442947C37ED01F55E9411146870DA50194E778CAF71A7561429D1AC02AC362FAD196CE1331BF2A00001DA290F2B136B92B576C2831717908682412462AF101C3ED3685180112D41A73BB164553F9B79CA71F0932693A630E4209BEBE7BAA7AEA295150B1B827716F47464204258F414BDB6C5FFFC42249C782494A4268F60BC5C195AE9C1EFD778CBC15AF2003B3DAB7336AF5037BFB4EED702EC887AF43EBA616414914B08493D53ED9F89EC2805EA1B40B634610B458236A2ACA1610565350234E9B23BE27166FB8ABDB441138F44E79D8541C3AAE85FC553FB884CCA95E6C84325DF29E112B6D863444A52721F5A5A6D0DB5A65564A545633CB121A927136408C16763191EB10270E6A1604B486770AB5FCCA728F659556C212F9925F28493CD3C2A60AD743830769F9B387422AC46385C88F4A10D8C43764F5200E810D20AB9A7AEB5C8B100CB751C55B301976D7BD4F0A1DE7D7CFBF0379CC6B9844B16749B8C6659824B65C2659A430914B322289B88D51324772C4A507CBAF240436AAC23950088E41973FCC487F57BD1D14321664AFC6EA2B66403C881509BE445933528A4F97C2A5B86F1CBC08816484F9432241A09E16D6B6C7068F0356505227216127AF9506250E59ADA3AB4611D7BA4A999621241A3D8C4FE0E7CBAE2492B6F44E6AFCA27F33940E3233F5A382549B9DA2C52B9BC99A8BDC1E541B274987A5BC4A272F330F725856220961C659CD9BA534716C5A113B53CD243D5C907FD6E1B2CDE69090B40314B7CA85F2185F9401FA758C3FD8AD91441A6DCB88B7350B753CC06AE5A1FBCB4DF61CAE1DFC7E60C28C7787654A068DD06821983AAED658C110BC19C2FC61E76CAA65D9C0914887A77571B3E63CD63A437028BC03DC8F69346585D023F01465E430220607948619975FAC993703CF60055D9AF28EAA1A2CE412322D11497DF22137380714B701DAE205EF308E2EB8A1B0B09ADB896BB8AB3F34F9BA2695056B7785FD51ACD043CDC1919C6B0335D7E7BDD1C43693582ED5E71D534163A48C45796B43E98BCB3DF47E075B8AE3E96A640CA659747B287C4714113DDCCBC83D37C54022CD321409BE74C331900CB17A72DC27BB6D4401D974B4E236B82F894EF5DC9A7A29091AB475D584B52D9672DDBC8D63B0BDB51554709041CC07CDEE435F39463C04BA0DC2B4C085AAC7822AA4BA50CD470A75C53A7C1318839DF4086D8A8D3510780081112453134F15601F810895B287195866FC237669F5B212C002F62B2130952B99BAC4E0AB002FA915520A715A41B1D541CB54CB7DD0291655F14FD2D0B3D802C9D6CB569FC815B69B1A0787463104A30AB34E7BD732FEEA3C8CC8764FE199947513889942B7C110482C9A1477348C3C3B491060A78B8299490E4DBABD22B054929BA77EE31922E1439D15B530A0C307E72C2F9C568622301DFA261C885A6F66399DC79E593486B11FF236B8367BF6864A1A596448FC251898A3C89CCD0B5511311826546E56B6967EE923E5733561D5A4BF940CAAC4960BF60CB769A40E396BFC370F094A00986D708AC731B420FDC11FCB071BDA0786A23F80269341AE270B8ED6844B", + "30991222B8EA47530F7C703D85BF4357F61F47615539781920EFFDF067172E32EF1BA77B21670ECA074C4B2401BB591B21CA0F4BFBA9F8BE4A26A9DE2ECEAA8303A91073C0C91205DAF6DDB17D35104969C5036BA722B176F6A3E6D92E1E5EDDAD9A6A3561F7E5338BA2B163702E297F9C6F27C5BCB7975139DFF287B739D2053BBC4307946B89DF3D9C963379B932DDBA015A6EA396E729996F7FF573A0C24040DE323E60B95B2197C89127661DB35D44588E132742B62949EA45D3E8527F0B2B71295E0943F1FA1F87D3B3EF11F840B59E2BBB10AA22B687FF23D22CDA109D5CE33F3527FEA041579793530226009D48CAE3E499FD0ECCD036D04B8DA21F939908E53F5BBBE41DBACAF3A7F9F5839D479BA0909F0DF0B2C8CD7AE8B11F160B16EBA19656744AC38D9AEE3A31E698380B3B9483E3A5F3C3B3767C519ACBB515706B1F192B16AA7B1E0B8178F28C65CAB578368DE5BD0DD5691B659293B3B212A5547E60727F69B33D3938A301572FDD931F5F71E7C647BF9CB4B3A8B294E2A17CF504319278648E59DA78F0FC5BBAD5ABC37551C30AAB853CC50DF796F308EC99D56A2348954EDAF7AF6E4D62FA6B1BB6FAC370226F47F1A91E2BC6731875C09CCBF8E635745DEF1A607F15BA774E7A1FCE8822C07916D352BF24DE6218350C5356D627411F884623496620500337654DBB8048D58DB94BCD8BF18ADFF7EAFE9DC8687156F426379FF0D57B880B8F86FD94861CF865DB231B9ADF9FCC53A7D8E5BEC45EF2EDEEAF2109F35A365C1287AC81D18EF302C9313B357870DB914E2E8300440A0C44E3940FAB6B35F1BC4BDF9B7A54EEC634897F1F715A334E553F2AEF6ECCD13966364CB942CA7C91A90EE2ED924DAD7F7A0907A56323BA787967F687E1C8BAB45976E20AE14301139E989E4257EC9F87728F4AC56A5F0588D96908FF7DD901AC4FBD8AB336EAC865377DCE7C22B4E8193F17769E1C1D6A2365D21715F014E9634834EEC80E4F6C97FDFDA6559BA2F88F81CA57A03AED25A0D818E7823BD08713E1667815A5E4776ACE6FB5658053E6DD38A01AA0AA9819802BD83E", + "6621D11567D58EAC3CDFBEB9C69DB0E7AFC4C97C252D98B4770C5F6AF98C83DB", + true, + }, + { + 78, + "22664886862835E6188D53C21FDC5EAF7849E078832E004CE79A7BBF122E6E59885608002C170077E98440039870E03FCB890D94001A98482EE6644097FB2BF39A93DA8811B951C6B905229A40BB97727EFAC92D266C4AAEC5623C17C1982A25D4859E362A030D191BD9E11D5CEB145E7482FFDC38ED17AAD859BE642072F55B4EDAFA4AC438B3DFB793A910BEA792780A801E70D2AF76283B60238DEE9A63011C343A586BA54CBB0C77CBBB3C8470C09F1A9C57A42436D3ECB89DC0CD711591FC674878A8317BB833D8DB10D217CC02883A1FBA2CFE3770F46721A7C9AC0045BE9F14487CE6992CC0CB1871CEADC3BA0627CFEBEB974B457D3B7953922CB0F5134918B006CAC669FB0B180BA00FEF56912B99A2C23B095E0A1F65F5611C879D4B519FB7E27153284725749C8D321178BB83C33AA9B547129E780B6C958E69192090FB3E2D69AF3B44A923276B4D5998A3349E5E291291B613B579800B12BE0805063755032A219916A5B9807A7A74A0A833F14F2F940465D970EFC7B7F8D93AE6D554A61085BE277DB5709AF4F1AE3F331B6C8900B24697978B7F69F1B3800B3606F23D90A683E44995F4A1451FA4322E03BA4E32120C5B6E6F8218B67A42D2A54467D672297B4F5CF51131180261276548F1ADD6C985415C15C3D00D83108BF34A6077EACCF7789865341B9594AC3C3781D15336696325C811842335AA43E2146B9176EA251ECCEC7F3E18C8E7F11CA7B09AF0CC14995515D80AB090711520D9BC8CD7353187BCD7D2BC4FD19911E11D904959617B4A30EA844E6292CDE42E46D2A013635C1C38C2D42BCFA68C928B6196B9EA75E6626DC7B17849205838B4C794511DED5B19CDB76CCFBC175F6B94E4E5CC5FC3C940C836C7F86BCAB3BAA860CFC3F1C8C088ABC684A243DCA306B27C2200AD1A31261B4C3C163B99E3022D8F79C9B4989AFDC32F5D93134FD57AE2A7B8381319E9FC2266F457CEE24440307417429AA259CF7DF5865A290404D73FB638BA6341316D8AA69FF605FD23453C34BC015B915D723815285FC200966C03CA84828A37579C86418D5433748669A56C447FCAF3F7A27F8BE433449CF232282A0FC0F55F6387EC0CDBAF615987A153", + "65BAA3F5C74319F488D378CD416C312E8B5FB189A3991741B4035E1E15B261E350004A4DA438725888AC54BBAA2C9406E303CDB2D9765A0352A22A28BF80CE51CB4234397119B3C5AAE2324BCC0FF6620605373366A474621A949B90888A12BA91FCB68CC9C42070B2AFCC54095236497583DEE7947A3258A6D33672C2B1FE8A02EA9CC34A045C91C5AD3AB72973888C46B8CC383015DC12476551BADDE250D2D84E7B06B7D5984B55A6918023C41EC9648CBC706BC61EF3C041C96822265C092DF204BFA25A9A16754B165AEE58607F2CB9BAFC05F548164C96483A846C3892A6553422E99B1D0099B946FBAA4C320D8187BA9928C574B448613B18EC7BA7AD5C4042642FD04260F5E5600E22AA69051642D0A2AC2BBF5142B69A69B4865AC9FC8B25F71B93F8CCC2C601717D85AE5267116EEA467F54656999CDB365076CEB052D4B4698B361C0B9B02B7BC10129972AC64B1CC65EE0AAB59D401AB41A245BF10F0D58393587CED798786EA87A56F48121E3CF0420BDB438A4E292CB33B6B371D8850D290C8C93627D2B428566014A82AAFF496E809BAC64538A2A52B6FFAA0852574D8A648A5CE81A17471EABF3CBE2F34B6E54C01CE079CFF4AF0CC60F820C64BA30755B20CD06420BCB9156055624599C1F8B8292A668468BE0836FC47383EB8B74E2118E918FC499C64E146276B17E5E015BA85863D9920D8A299F90CB09C178018B895CE6F0828246247CD0B9C86B4F885350CA6167E90953E9397B5EDC0A71866C087A9A8C1A6A3E895BAA78303337479AC256BAD692FC0A9ADB58C2A152A9E382B11956AE30263E74AA7BF1D79B3B98800B7C82E81A0C3AA2CBF4A8CC1D13B3ACE8B3502361ADC694E7684278164C810642771B04085122DD408228606B939550892B01A05B347B2135B5568B4E13C250BC1CD14396A3B756450BC864D7C9346CA4879178DE1A0D5DD82E09F9A938B40C37C4407DA787DC7149875C3E927BA166528054001FF103BFCFD2C1AB933C17E10F1F12779AA44207C960BCF7330D415832050713B438C0482651A5B10B850A828964EEB26196A84AF4AC2922664886862835E6188D53C21FDC5EAF7849E078832E004CE79A7BBF122E6E59885608002C170077E98440039870E03FCB890D94001A98482EE6644097FB2BF39A93DA8811B951C6B905229A40BB97727EFAC92D266C4AAEC5623C17C1982A25D4859E362A030D191BD9E11D5CEB145E7482FFDC38ED17AAD859BE642072F55B4EDAFA4AC438B3DFB793A910BEA792780A801E70D2AF76283B60238DEE9A63011C343A586BA54CBB0C77CBBB3C8470C09F1A9C57A42436D3ECB89DC0CD711591FC674878A8317BB833D8DB10D217CC02883A1FBA2CFE3770F46721A7C9AC0045BE9F14487CE6992CC0CB1871CEADC3BA0627CFEBEB974B457D3B7953922CB0F5134918B006CAC669FB0B180BA00FEF56912B99A2C23B095E0A1F65F5611C879D4B519FB7E27153284725749C8D321178BB83C33AA9B547129E780B6C958E69192090FB3E2D69AF3B44A923276B4D5998A3349E5E291291B613B579800B12BE0805063755032A219916A5B9807A7A74A0A833F14F2F940465D970EFC7B7F8D93AE6D554A61085BE277DB5709AF4F1AE3F331B6C8900B24697978B7F69F1B3800B3606F23D90A683E44995F4A1451FA4322E03BA4E32120C5B6E6F8218B67A42D2A54467D672297B4F5CF51131180261276548F1ADD6C985415C15C3D00D83108BF34A6077EACCF7789865341B9594AC3C3781D15336696325C811842335AA43E2146B9176EA251ECCEC7F3E18C8E7F11CA7B09AF0CC14995515D80AB090711520D9BC8CD7353187BCD7D2BC4FD19911E11D904959617B4A30EA844E6292CDE42E46D2A013635C1C38C2D42BCFA68C928B6196B9EA75E6626DC7B17849205838B4C794511DED5B19CDB76CCFBC175F6B94E4E5CC5FC3C940C836C7F86BCAB3BAA860CFC3F1C8C088ABC684A243DCA306B27C2200AD1A31261B4C3C163B99E3022D8F79C9B4989AFDC32F5D93134FD57AE2A7B8381319E9FC2266F457CEE24440307417429AA259CF7DF5865A290404D73FB638BA6341316D8AA69FF605FD23453C34BC015B915D723815285FC200966C03CA84828A37579C86418D5433748669A56C447FCAF3F7A27F8BE433449CF232282A0FC0F55F6387EC0CDBAF615987A1537E35FD4ECF80F07D398BD1A4F057647C17838F8427212A33DC3D3E7052AD61DB62ECC47A3A06302A8383E1A465EDCF1BF3523A84F5549859A8CA5C0905DEBA3E", + "596D8F70598FC6837434DBC18E9891D67735460FE00248E49E07EEDCD2A36C07B37ADB63AB0DD98294B799CD8CE664D09F567A7B52C2BAC89F32366101983529D97586951B9EE52A1B48B51D87F47444D6D3A0F3F5A7063B621C6152ED5FD7A1B06903CB88D2817CC000ACD3C81A6F236CF4A268FEA9388E61EFE62FCFE21D93B8D872E31AC4C84AA8F6A66C419BD9D03EBD11A00BBB3AC07DC806A32783AADDE4B41FA8743A330CA590F5E2076F738A147999D8AA983E67E8D8CB663EEC7A693CE9E48016BDE8511484EEB22232142F6778B0AC3096F14EE5C0131C966171B3CDA96F815D8A6A4668B20F65593543B19656E3C7315523E2FBF3936D050DAFF6E260CF196A2CC83D1C5A8949D1B38E331083565E8683CFB4485D256B7E5CD128FDB516745DE86E402A67DF21FABB2B64E804225ACC7D0435D83261DB47EDC234BE5497469740C2160C793E84EB061D8612093B242D0396588333F4A3CAAE8A456F7D8F29A790CF7569530EDAA4FD2A93F9CAAE4FC4C6CD1D873153351D082B3C9FDF7C644C36D168485CB8C71E96258A3750A89BC3E4EFCB2C81130D7B2CEA9A3277F3919C193B69677DC2BD290F473DE11562974D4AD9E4CA19598E63CF0442763B1A17E00E58207FF2403FBBA79B393C885BCFADAF31AE41FD124CEBCD1AFE38A05CD7B03CE692CC4984C5EC72EEDA9ED85F8575E96CB06569B938AD6E8E78316B3FD099504D1B254D8BC1379B77D61A83D47270FEA6E2455CBAE336A2AAE8FDE479FC405D2F1AD2F54ADEC49B9C2999BA2694C42F88E8AED706DDB9CFD9E9A560FEFB52ABBEA30A09128ABC1780D8099736AF4D4F208EADEA47A1700F2D8C7765C497ED9E23E3A73EA0159F4F7BFB137ED5E6239737743323CACD84E172CD0B451D7F3232BBB6A61AA267D7F5A06A285A2FEEBB13705C824AF5E760017604AB15FB2D68E21A71198B78A6A7969012403AC2DCABED2DE26D65FC5877878EB1456F9F66DC09D66F7F66C127828E1C6E92D8BBD50D2F15AA16AAE3F38759250AF9E02EBBE9DDBEB16F7523FBD8B1473F1D2A3AC7C7FF8EBE0A5D8B764B00D190", + "0F138749C455FE4615FF58200C7D4CE24FFD8709C305C53C26AA9E340E8A73EE", + false, + }, + { + 79, + "2F29C94A9C9351A321F262343BB7777A8B46CAB624E3F4BCB5704258B462F9EA711146AB34E748A1F08D000249FD73655CE748552909B3AA5546B54A72EA5980B957636B05EB14AA616C1B8B5AA78A5279A46B23B513132AFB85C963409E40CEFCB74F71C75298A670F7C0632C302444C7CE74F643E27B03AF93A76490374632BBFAD0C2AC84BA2BE4579C32237EC701177312B68458AF3B039F431C2658BD54041398D94F39848199C8763A2AB170C421853C7FA19C0A9ED89B665B13CE3553F6C8BA4C745327B3B10B0CABBEEC2D2C11277F359E86547D1E901F0BC44126F3242850AA7A3A00E4FB2923049205EAC160C24CA1B32280B0C0CB788857A54D223548C7505A5DCC5D29D7189595BB5CC070AA23149D376AADD75DE42A611E157C00B797735A3FEA3B59E0EC46E58A0FCB48606D033CF8D75E6592CC89448732D78B30B754F3AB7066F85C57F45E7FC035A7F08496225171373818F765FC02817BAB6FAE432CF0EC38C0779ACE7231041B8047869227DC01202419B83A7B8B87A74468036D9A2CF0C5892EE9CC85F89A32039A0D3A219D0365C600125BCC15CF607C737732D7AB5A27367ACA464571B0839DF379A79880F5B7BFF1FCC4AB69A047166800204C733B3A72FC566C2C280788CFE36A1FA9F63189B0382E683A4ED417D755A20B9895652C1E26B628BF19915D06B8320AA997531E298926F0F268C97C936B1896CC51050411338AC8088CF4A333C847384C9DC766CDB1BA531199C08B0266F1D24331E941224B2617EA9D1EAC33024939041007B5F840100B7E5437306D2A72A3B439E3D355817C78EF28332123797466A0F873A33AA76BC0B42E33C109F3EC8733785C2D03B4E64C3A5A1988C1C4947142CAE75965A610AF5D9849D6056DD9A45E8A8705C04306850B13FA295D36164F486B3440F62951F79BBA12747497C31167BE94D1A9D424373B4B377AE18299C85EE375B0312004D71C107E3ABB12CCA8AA50935ED1AA64477D866BA940C6441D44B006D22AF62960F62478BF978222DCB90E454CCD33BD9AE06F3EB34F5B576C4C9146A4514DD33258A0B6562A5EBC6A8FC1B5F15C9CDDF7EE64417FB722AE52086868BC807BF19B472AF32A", + "D01B6395019BCB1318A6670DBA709588C0A734256B3CCB44E6F2A002B91B9DFBC68E18C1957B3EE8C5464C00215E41907C7B6C1E0C20F62B294E8686627CA3181AA1377C25B5F79355B31BC0C14776621A014302DB2119F9BA4F0AD653F9B4AD562BB14760802AEC8B10348403289C9ADA8C1F39A4CBDA9648589ECFD09DE6AA0BE97AA8F0A13644F23D8E505E1076B415DC078B659E63A50D04A53E680980B2E90241435AA0810CC76016F0010E7CF8B2A9A0B1F0A75CC068CAFB4A5AFCD74CBADA6456D83A4D460D978025A89B379F2294D61BBF0A344611635335CC05DD24BC800C48845930F90538C5854D9862B059F952A25B3D2D6BB3E70141EEA769BDB31F42467C16E6509DF8A0A24477916A28B623A555E47079C61EB7B7A5431085AE7457F696812CC49D0DB242FB42B3A58A6B405289974BC34DF24C8E09C63BB81F109468B112668CBB408AA13AA1F2B4EB79A419ACC2B3C05807679E8EC60951B266D621319EBC289F8709E903478D150448FB00B142BEAC93BADAB7703D5CC4ECD78A05121B4182501CE62E4A3BBA65CA6C57156622CB624F844172B0CD3B376E1E008A25E0A8BF982927E154C55A82EC276DA312C2721570F835C51C131E7A011D26F52A90E5BAB0B813EA41C7BB3BC7FAC789775286B1D115CBB865BF96324FA99A3180B7D1A8B7C2B539411302EA62020611359659C2AE0062B1C52C83B7B94A831EC9036A79025689A70C14C44BF590A914A3325392827F6820B96961E7CB2E88B56A1F706C6027A18BD3AB8998124D8C73251A8CCAB2771A630BA448318562061755B6F271063100C4DF788030113F5A8768EEAC11B31A1F0A81C5D14002242409F35B2725AA68FC4087A22321F122B672E101052C3EE59571A9B71C17976C3220C070C0A4641B73B007CD51A5561BF0AC24B58A1DE1443EB38B794822C4148843746C44E6458FC92F1BF4289E3178CBA74521D792DD297D93D15FED878F9021B8F1DAB90D6259C354CC9B4A383A58800C42547F9A13A4F809F5C05EED58C6C98A878FF432ADBAAE218A9C283CBF45D527ECF8A4B867ACD7576957D1C12F29C94A9C9351A321F262343BB7777A8B46CAB624E3F4BCB5704258B462F9EA711146AB34E748A1F08D000249FD73655CE748552909B3AA5546B54A72EA5980B957636B05EB14AA616C1B8B5AA78A5279A46B23B513132AFB85C963409E40CEFCB74F71C75298A670F7C0632C302444C7CE74F643E27B03AF93A76490374632BBFAD0C2AC84BA2BE4579C32237EC701177312B68458AF3B039F431C2658BD54041398D94F39848199C8763A2AB170C421853C7FA19C0A9ED89B665B13CE3553F6C8BA4C745327B3B10B0CABBEEC2D2C11277F359E86547D1E901F0BC44126F3242850AA7A3A00E4FB2923049205EAC160C24CA1B32280B0C0CB788857A54D223548C7505A5DCC5D29D7189595BB5CC070AA23149D376AADD75DE42A611E157C00B797735A3FEA3B59E0EC46E58A0FCB48606D033CF8D75E6592CC89448732D78B30B754F3AB7066F85C57F45E7FC035A7F08496225171373818F765FC02817BAB6FAE432CF0EC38C0779ACE7231041B8047869227DC01202419B83A7B8B87A74468036D9A2CF0C5892EE9CC85F89A32039A0D3A219D0365C600125BCC15CF607C737732D7AB5A27367ACA464571B0839DF379A79880F5B7BFF1FCC4AB69A047166800204C733B3A72FC566C2C280788CFE36A1FA9F63189B0382E683A4ED417D755A20B9895652C1E26B628BF19915D06B8320AA997531E298926F0F268C97C936B1896CC51050411338AC8088CF4A333C847384C9DC766CDB1BA531199C08B0266F1D24331E941224B2617EA9D1EAC33024939041007B5F840100B7E5437306D2A72A3B439E3D355817C78EF28332123797466A0F873A33AA76BC0B42E33C109F3EC8733785C2D03B4E64C3A5A1988C1C4947142CAE75965A610AF5D9849D6056DD9A45E8A8705C04306850B13FA295D36164F486B3440F62951F79BBA12747497C31167BE94D1A9D424373B4B377AE18299C85EE375B0312004D71C107E3ABB12CCA8AA50935ED1AA64477D866BA940C6441D44B006D22AF62960F62478BF978222DCB90E454CCD33BD9AE06F3EB34F5B576C4C9146A4514DD33258A0B6562A5EBC6A8FC1B5F15C9CDDF7EE64417FB722AE52086868BC807BF19B472AF32AE511FAA0E231976722B8C8DA3A15116AB0407F3C7209D421D97969800A933F9742B5A904C6C1477F268EDD8106D88618B002F3A2D0FBB72682CD37B51F9BEC47", + "3856C757D2A5FCCD231A1669ECE3832AF80E5AE832F3B1314ABFF54CDBB94ECBE156D3E93FC4705C46D0E662468EFFBB514DEAA24C384DF4F0BB2B3442D6C67926B5D4EBC6DE170AE0A420EF7319F3A52B6F7726CD47F743B86CB5AE06610FE485932D9B6EA9CDE8023A978DBD0DB5C5DD30AEC898A042C825F0143A32DE320EA97CC36B1625D652342F716F61446EE2355435A9FC043DCD68C6BA0C15AF81A50EE394CFAF9558C6247A6E83826FB48ACAA574F26FF137676348EF1FD80EACE7A20EF6A5150CA1F094A9C31794E3C20CE6CFA71AAC394072432CB4442C6BC7D9AACDF493949CB4E9F33B1946ACFAB8ED5C7D978E43DF907848996F9CDF2CEAEDCF4CFFDA1F18AB30EE1F68F505B4C3A1CCFAF492944506101FD2432667B4D1DBA381C3401BCEB2FCD93CEE7D506E63D97081D4721A453BB96009CD58122AA3B2BADC079EC0358C6C8051D58DBBAE2DB9EA7A28668A2B2FB35D90A542212C7ED4C149416B5CA79B9CCA8B608062A0F3E8E6801D86C8295BD1BA1A078935ACEF1C9DC52F126E83B79927A361167C4DA101FE8C42C20E3AD167D28A73E22093E12EABC39A1C0B3A295C3049104E1E9772D3A4A76D829D9BAB22958C14C301488287936EADE7FCBF43744E71B35A23DF0BD07D2D89C468CE3F8E1BE589B202D74B2D4DECC09FB919EFEA56166CF385C40E113FFFC570178044ABADE35172614DEDF9FA93D91AD8218655C8519D5871986C16320D845D5A5231DC1E04335A26AAD213341A5946680FE9F22CC7BBEB660EEEC1CE9BBC92D497A9ACB21642F9CB52D19AC26B774FCF8817350E301E77CD294ADDA779672597BAD395F4913A45E0346A41C14C0A473928F89B92A7075B63E7929E1FD45077D0401356705DD9B6B75D5044605017A9139B19559DB18C3B931DD71A532C861AF40D18D15C63531F1545D337BA1A30841CDA6AB7D6EAFAA4798E4E6980F7718EAE92775DD8460569B05440DCE3789E6D743E695B5B1BAB0C9DE9C666C1595BBEDF69E80445DD53DBAEBEA126E45479CD6472D5F97840475EB1E52F7984ED651C07B82D584A07D9099C353DDA", + "ACB0CD384DA7C8A0E5187025297286AD154E9E37448D9D0E798BAE24FA7A6727", + true, + }, +} + +func TestDecapsulate512ACVP(t *testing.T) { + for i, tc := range decap512InternalProjectionCases { + //ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + + dk1, err := NewDecapsulationKey512(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey512: %v", i, err) + } + if !tc.success { + c[0] ^= 0xFF // Corrupt the ciphertext to force failure + } + k1, err := dk1.Decapsulate(c) + if tc.success { + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } else { + if bytes.Equal(k1, k) { + t.Fatalf("case %d: decapsulation should have failed", i) + } + } + } +} + +func BenchmarkKeyGen512(b *testing.B) { + var seed [64]byte + rand.Read(seed[:]) + b.ResetTimer() + for b.Loop() { + if _, err := NewDecapsulationKeyFromSeed512(seed[:]); err != nil { + b.Fatalf("NewDecapsulationKeyFromSeed512 failed: %v", err) + } + } +} + +func BenchmarkEncapsulateInternal512(b *testing.B) { + tc := encap512InternalProjectionCases[0] + ek, _ := hex.DecodeString(tc.ek) + m, _ := hex.DecodeString(tc.m) + ek1, err := NewEncapsulationKey512(ek) + if err != nil { + b.Fatalf("NewEncapsulationKey512: %v", err) + } + + b.ResetTimer() + for b.Loop() { + ek1.EncapsulateInternal((*[32]byte)(m)) + } +} + +func BenchmarkDecapsulate512(b *testing.B) { + tc := decap512InternalProjectionCases[0] + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + + dk1, err := NewDecapsulationKey512(dk) + if err != nil { + b.Fatalf("NewDecapsulationKey512: %v", err) + } + + b.ResetTimer() + for b.Loop() { + if _, err := dk1.Decapsulate(c); err != nil { + b.Fatalf("Decapsulate: %v", err) + } + } +} diff --git a/mlkem/mlkem768.go b/mlkem/mlkem768.go new file mode 100644 index 0000000..4cc5042 --- /dev/null +++ b/mlkem/mlkem768.go @@ -0,0 +1,484 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package mlkem implements the quantum-resistant key encapsulation method +// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203]. +// +// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203 +// +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/sha3" + "crypto/subtle" + "errors" + "io" +) + +// This package targets security, correctness, simplicity, readability, and +// reviewability as its primary goals. All critical operations are performed in +// constant time. +// +// Variable and function names, as well as code layout, are selected to +// facilitate reviewing the implementation against the NIST FIPS 203 document. +// +// Reviewers unfamiliar with polynomials or linear algebra might find the +// background at https://words.filippo.io/kyber-math/ useful. +// +// This file implements the recommended parameter set ML-KEM-768. The ML-KEM-1024 +// parameter set implementation is auto-generated from this file. +// + +const ( + // ML-KEM global constants. + n = 256 + q = 3329 + maxBytesOf64Mulη = 192 + + // encodingSizeX is the byte size of a ringElement or nttElement encoded + // by ByteEncode_X (FIPS 203, Algorithm 5). + encodingSize12 = n * 12 / 8 + encodingSize11 = n * 11 / 8 + encodingSize10 = n * 10 / 8 + encodingSize5 = n * 5 / 8 + encodingSize4 = n * 4 / 8 + encodingSize1 = n * 1 / 8 + + messageSize = encodingSize1 + + SharedKeySize = 32 + SeedSize = 32 + 32 +) + +// ML-KEM-768 parameters. +const ( + k = 3 + η1 = 2 // eta1 + η2 = 2 // eta2 + + CiphertextSize768 = k*encodingSize10 + encodingSize4 + EncapsulationKeySize768 = k*encodingSize12 + 32 + DecapsulationKeySize768 = k*encodingSize12 + EncapsulationKeySize768 + 32 + 32 +) + +// ML-KEM-512 parameters. +const ( + k512 = 2 + η1_512 = 3 + η2_512 = 2 + + CiphertextSize512 = k512*encodingSize10 + encodingSize4 + EncapsulationKeySize512 = k512*encodingSize12 + 32 + DecapsulationKeySize512 = k512*encodingSize12 + EncapsulationKeySize512 + 32 + 32 +) + +// ML-KEM-1024 parameters. +const ( + k1024 = 4 + η1_1024 = 2 + η2_1024 = 2 + + CiphertextSize1024 = k1024*encodingSize11 + encodingSize5 + EncapsulationKeySize1024 = k1024*encodingSize12 + 32 + DecapsulationKeySize1024 = k1024*encodingSize12 + EncapsulationKeySize1024 + 32 + 32 +) + +// A DecapsulationKey768 is the secret key used to decapsulate a shared key from a +// ciphertext. It includes various precomputed values. +type DecapsulationKey768 struct { + d [32]byte // decapsulation key seed + z [32]byte // implicit rejection sampling seed + + ρ [32]byte // rho, sampleNTT seed for A, stored for the encapsulation key + h [32]byte // H(ek), stored for ML-KEM.Decaps_internal + + encryptionKey + decryptionKey +} + +// Seed returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey768) Seed() []byte { + var b [SeedSize]byte + copy(b[:], dk.d[:]) + copy(b[32:], dk.z[:]) + return b[:] +} + +// Bytes returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +func (dk *DecapsulationKey768) Bytes() []byte { + b := make([]byte, 0, DecapsulationKeySize768) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { + return &EncapsulationKey768{ + ρ: dk.ρ, + h: dk.h, + encryptionKey: dk.encryptionKey, + } +} + +// An EncapsulationKey768 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding [DecapsulationKey768]. +type EncapsulationKey768 struct { + ρ [32]byte // sampleNTT seed for A + h [32]byte // H(ek) + encryptionKey +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey768) Bytes() []byte { + // The actual logic is in a separate function to outline this allocation. + b := make([]byte, 0, EncapsulationKeySize768) + return ek.bytes(b) +} + +func (ek *EncapsulationKey768) bytes(b []byte) []byte { + for i := range ek.t { + b = polyByteEncode(b, ek.t[i]) + } + b = append(b, ek.ρ[:]...) + return b +} + +// encryptionKey is the parsed and expanded form of a PKE encryption key. +type encryptionKey struct { + t [k]nttElement // ByteDecode₁₂(ek[:384k]) + a [k * k]nttElement // A[i*k+j] = sampleNTT(ρ, j, i) +} + +// decryptionKey is the parsed and expanded form of a PKE decryption key. +type decryptionKey struct { + s [k]nttElement // ByteDecode₁₂(dk[:decryptionKeySize]) +} + +// GenerateKey768 generates a new decapsulation key. The decapsulation key must be kept secret. +// See FIPS 203, Algorithm 19. +func GenerateKey768(rand io.Reader) (*DecapsulationKey768, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey768{} + return generateKey(dk, rand) +} + +func generateKey(dk *DecapsulationKey768, rand io.Reader) (*DecapsulationKey768, error) { + var d [32]byte + if _, err := io.ReadFull(rand, d[:]); err != nil { + return nil, err + } + var z [32]byte + if _, err := io.ReadFull(rand, z[:]); err != nil { + return nil, err + } + + kemKeyGen(dk, &d, &z) + return dk, nil +} + +// NewDecapsulationKeyFromSeed768 parses a decapsulation key from a 64-byte +// seed in the "d || z" form. The seed must be uniformly random. +func NewDecapsulationKeyFromSeed768(seed []byte) (*DecapsulationKey768, error) { + // The actual logic is in a separate function to outline this allocation. + dk := &DecapsulationKey768{} + return newKeyFromSeed(dk, seed) +} + +func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768, error) { + if len(seed) != SeedSize { + return nil, errors.New("mlkem: invalid seed length") + } + d := (*[32]byte)(seed[:32]) + z := (*[32]byte)(seed[32:]) + kemKeyGen(dk, d, z) + + return dk, nil +} + +// NewDecapsulationKey768 parses a decapsulation key from its expanded NIST format. +func NewDecapsulationKey768(b []byte) (*DecapsulationKey768, error) { + if len(b) != DecapsulationKeySize768 { + return nil, errors.New("mlkem: invalid decapsulation key length") + } + + dk := &DecapsulationKey768{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey768(b[:EncapsulationKeySize768]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey = ek.encryptionKey + b = b[EncapsulationKeySize768:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + + copy(dk.z[:], b[32:]) + + return dk, nil +} + +// kemKeyGen generates a decapsulation key. +// +// It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and +// K-PKE.KeyGen according to FIPS 203, Algorithm 13. The two are merged to save +// copies and allocations. +func kemKeyGen(dk *DecapsulationKey768, d, z *[32]byte) { + dk.d = *d + dk.z = *z + + g := sha3.New512() + g.Write(d[:]) + g.Write([]byte{k}) // Module dimension as a domain separator. + G := g.Sum(make([]byte, 0, 64)) + ρ, σ := G[:32], G[32:] // rho, sigma + dk.ρ = [32]byte(ρ) + + A := &dk.a + for i := range byte(k) { + for j := range byte(k) { + A[i*k+j] = sampleNTT(ρ, j, i) + } + } + + var N byte + s := &dk.s + for i := range s { + s[i] = ntt(samplePolyCBD(σ, N, η1)) + N++ + } + e := make([]nttElement, k) + for i := range e { + e[i] = ntt(samplePolyCBD(σ, N, η1)) + N++ + } + + t := &dk.t + for i := range t { // t = A ◦ s + e + t[i] = e[i] + for j := range s { + t[i] = polyAdd(t[i], nttMul(A[i*k+j], s[j])) + } + } + + H := sha3.New256() + ek := dk.EncapsulationKey().Bytes() + H.Write(ek) + H.Sum(dk.h[:0]) +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key. +// +// The shared key must be kept secret. See FIPS 203, Algorithm 20. +func (ek *EncapsulationKey768) Encapsulate(rand io.Reader) (sharedKey, ciphertext []byte, err error) { + // The actual logic is in a separate function to outline this allocation. + var cc [CiphertextSize768]byte + return ek.encapsulate(&cc, rand) +} + +func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte, rand io.Reader) (sharedKey, ciphertext []byte, err error) { + var m [messageSize]byte + if _, err := io.ReadFull(rand, m[:]); err != nil { + return nil, nil, err + } + sharedKey, ciphertext = kemEncaps(cc, ek, &m) + return sharedKey, ciphertext, nil +} + +// EncapsulateInternal is a derandomized version of Encapsulate, exclusively for +// use in tests. +func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { + cc := &[CiphertextSize768]byte{} + return kemEncaps(cc, ek, m) +} + +// kemEncaps generates a shared key and an associated ciphertext. +// +// It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17. +func kemEncaps(cc *[CiphertextSize768]byte, ek *EncapsulationKey768, m *[messageSize]byte) (K, c []byte) { + g := sha3.New512() + g.Write(m[:]) + g.Write(ek.h[:]) + G := g.Sum(nil) + K, r := G[:SharedKeySize], G[SharedKeySize:] + c = pkeEncrypt(cc, &ek.encryptionKey, m, r) + return K, c +} + +// NewEncapsulationKey768 parses an encapsulation key from its encoded form. +// If the encapsulation key is not valid, NewEncapsulationKey768 returns an error. +func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { + // The actual logic is in a separate function to outline this allocation. + ek := &EncapsulationKey768{} + return parseEK(ek, encapsulationKey) +} + +// parseEK parses an encryption key from its encoded form. +// +// It implements the initial stages of K-PKE.Encrypt according to FIPS 203, +// Algorithm 14. +func parseEK(ek *EncapsulationKey768, ekPKE []byte) (*EncapsulationKey768, error) { + if len(ekPKE) != EncapsulationKeySize768 { + return nil, errors.New("mlkem: invalid encapsulation key length") + } + + h := sha3.New256() + h.Write(ekPKE) + h.Sum(ek.h[:0]) + + for i := range ek.t { + var err error + ek.t[i], err = polyByteDecode[nttElement](ekPKE[:encodingSize12]) + if err != nil { + return nil, err + } + ekPKE = ekPKE[encodingSize12:] + } + copy(ek.ρ[:], ekPKE) + + for i := range byte(k) { + for j := range byte(k) { + ek.a[i*k+j] = sampleNTT(ek.ρ[:], j, i) + } + } + + return ek, nil +} + +// pkeEncrypt encrypt a plaintext message. +// +// It implements K-PKE.Encrypt according to FIPS 203, Algorithm 14, although the +// computation of t and AT is done in parseEK. +func pkeEncrypt(cc *[CiphertextSize768]byte, ex *encryptionKey, m *[messageSize]byte, rnd []byte) []byte { + var N byte + r, e1 := make([]nttElement, k), make([]ringElement, k) + for i := range r { + r[i] = ntt(samplePolyCBD(rnd, N, η1)) + N++ + } + for i := range e1 { + e1[i] = samplePolyCBD(rnd, N, η2) + N++ + } + e2 := samplePolyCBD(rnd, N, η2) + + u := make([]ringElement, k) // NTT⁻¹(AT ◦ r) + e1 + for i := range u { + u[i] = e1[i] + for j := range r { + // Note that i and j are inverted, as we need the transposed of A. + u[i] = polyAdd(u[i], inverseNTT(nttMul(ex.a[j*k+i], r[j]))) + } + } + + μ := ringDecodeAndDecompress1(m) + + var vNTT nttElement // t⊺ ◦ r + for i := range ex.t { + vNTT = polyAdd(vNTT, nttMul(ex.t[i], r[i])) + } + v := polyAdd(polyAdd(inverseNTT(vNTT), e2), μ) + + c := cc[:0] + for _, f := range u { + c = ringCompressAndEncode10(c, f) + } + c = ringCompressAndEncode4(c, v) + + return c +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation key. +// If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + if len(ciphertext) != CiphertextSize768 { + return nil, errors.New("mlkem: invalid ciphertext length") + } + c := (*[CiphertextSize768]byte)(ciphertext) + // Note that the hash check (step 3 of the decapsulation input check from + // FIPS 203, Section 7.3) is foregone as a DecapsulationKey is always + // validly generated by ML-KEM.KeyGen_internal. + return kemDecaps(dk, c), nil +} + +// kemDecaps produces a shared key from a ciphertext. +// +// It implements ML-KEM.Decaps_internal according to FIPS 203, Algorithm 18. +func kemDecaps(dk *DecapsulationKey768, c *[CiphertextSize768]byte) (K []byte) { + m := pkeDecrypt(&dk.decryptionKey, c) + g := sha3.New512() + g.Write(m[:]) + g.Write(dk.h[:]) + G := g.Sum(make([]byte, 0, 64)) + Kprime, r := G[:SharedKeySize], G[SharedKeySize:] + J := sha3.NewSHAKE256() + J.Write(dk.z[:]) + J.Write(c[:]) + Kout := make([]byte, SharedKeySize) + J.Read(Kout) + var cc [CiphertextSize768]byte + c1 := pkeEncrypt(&cc, &dk.encryptionKey, (*[32]byte)(m), r) + + subtle.ConstantTimeCopy(subtle.ConstantTimeCompare(c[:], c1), Kout, Kprime) + return Kout +} + +// pkeDecrypt decrypts a ciphertext. +// +// It implements K-PKE.Decrypt according to FIPS 203, Algorithm 15, +// although s is retained from kemKeyGen. +func pkeDecrypt(dx *decryptionKey, c *[CiphertextSize768]byte) []byte { + u := make([]ringElement, k) + for i := range u { + b := (*[encodingSize10]byte)(c[encodingSize10*i : encodingSize10*(i+1)]) + u[i] = ringDecodeAndDecompress10(b) + } + + b := (*[encodingSize4]byte)(c[encodingSize10*k:]) + v := ringDecodeAndDecompress4(b) + + var mask nttElement // s⊺ ◦ NTT(u) + for i := range dx.s { + mask = polyAdd(mask, nttMul(dx.s[i], ntt(u[i]))) + } + w := polySub(v, inverseNTT(mask)) + + return ringCompressAndEncode1(nil, w) +} diff --git a/mlkem/mlkem768_test.go b/mlkem/mlkem768_test.go new file mode 100644 index 0000000..ff9c51d --- /dev/null +++ b/mlkem/mlkem768_test.go @@ -0,0 +1,355 @@ +// Copyright 2025 Sun Yimin. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +//go:build go1.24 + +package mlkem + +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "errors" + "io" + "testing" +) + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-keyGen-FIPS203/internalProjection.json +var kenGen768InternalProjectionCases = []struct { + tcId int + seed string + ek string + dk string +}{ + { + 26, + "A2B4BCA315A6EA4600B4A316E09A2578AA1E8BCE919C8DF3A96C71C843F5B38BD6BF055CB7B375E3271ED131F1BA31F83FEF533A239878A71074578B891265D1", + "5219C4CC17C35A828F3E21B2AB7496805C99EE041FCA0158A3314F07D053F364C887A6825958A625965D4885C2CB355E83A3C1BBB15446F891D2D24F145632CF06A5EE1A278CD3064A79AD53193853E4CEA654448A4297CEA3C9E87561629680F588953B858074292ED31C20DDD983E805D07BB9CFADD823C7900B604286C0184738CA04E0DA8289540E329605EFAA5960AAC0FD0760006C1F1993426CC7BEA22BCBB3CC02E099B828E82F94045DFACB1D9FB315582B20D1B41476FC43AC4680647259FE9B51371223446C82E0BBAEA132913E2B96EA11950C450F25854EE4FA4921193C8F1C66D61B8265C7072B046F0C532141D51D9919C80733C1BD3C5A6D77CCB3A1938C95C1E4E866D1D65C78297B3B32CA3C4143E6A215C609A36A1B13BAA17981D42B7FF4C715AC806DC491560032A5A2BB30E476A266C6E4A4A065D9698DB08132608136082689B1B648B49063C98324706A43876507FC690893900F8166E1D52ADCC44848D864B8BA0933B32DAA63435F11065915C5D5D879EDCC136FA8515B0260B9536C316120B4904921805521C0232DA126E2A9C5323976ABC73B5AB892E59B01A194B6446C1B73217FBBA855AC887AFA58A8F15A768EF968D775267E050150C7A8237C1024F9421C210D97907B2A144736E3B58E01C48947B1C62655E380256491CEBE400F52493D9033A29D6C9AE80B33E8C38584B30A31A37B15E85F5E82B73157408A5399A957190CBA905F1713C9ACA53AA32923CACB8269436A56BC02932189C7139D8463D0DC540621875E9A7DFEF020E7E83696C7612BC2A7DE7148D7075C31F257766301FA5A06E00582DCE15C6FB3195AF43078D79B110B5DC0789BE3A32132737FEB247DF8D2216F272C3D5BA700C52ED7E3A2795CCFE3072CD1D1B533939AC58A065A8A9FE85441ED291D43869E25307737A5155A095C5056A204326F944A9FC8F7035C931A61033CC2DB6ECEC08AAE0045A6542080C1A7BBD699902A2ECBE3A1F9AA95FC6222C6C6AB7BF96EDB3450E029B0CE104DBBFA0A49B8044EE1BB63B33BCFAA8A6A450194CC577D490A549B6C42D6816F444AAE8B8A4F62AFFD17664E957E2BA245A864BAB8D28E82CAB9FAC3BCD707AAC19A5EE58A3925DC7059BA1B9E7A798EC987E1924342C54E5D935A6699B6D9AA78E4738B0CEC6C57D8228C86881A14A3B57065BBE5653BD92B5BD5A9888B1B1E9B017ECA1A788CBC3A283A9F7B00D3F55B138CA26C33CD2CA3BA632A0735596162AA6C9D10968F53305D35CEC4395CA0489FF631A3EF661264A22ACB535DCC1313CD6917BFE3B5B8FC3B8016CA0DFB151623C92F95701DCC4140459B52EB796FDA60FB429651052D2C16B550FA035CC1374C87A439077713769C728B766BC75A609B70BBC14AF84A3C70519C3211ACAD58BF7C14C15F1AA86FCC1DF55580A2F9BBBC31B6B0EAAB379481CE9966F7FA6487C611BA5B3E8F8307FA35CD5248C8AB351B63ABC4BA005871A97751F01E5143302C757A65E411AF7B26F22076C7A1CF7E2B0055E80D7116CA2B3056BF8754BCFA9095D0CE99715CC4512F10125C5A500DABB7C11F5B0408377900621BE851B7A3602576650B84191749F30AC635A9E2212400769DE8E1915F26BA198AF8E53DACB598711738DA8C583A388E027A59", + "E6F634F0A3771ED789D6842321E147B6010ADA7B6B0B105949F90AEBECCA062C494743AD3BE35A23C9BAA0E4CB83E65C9EB8BBC42433F0AC671ED9544A37C0CD704D1C24343ACA7BEBB538C7051186BC624A9A98F536874C3A15E145732A591ECE600C78028426BB8A7AF22757296EB968B5E3553721DB6BD507A5BE5742F45AA3E134383499400BDAC0C8607FAD647F41A2B547A631F971224C93570141C844662F96D9A3A32B6DB1C93F2A2B82E6B370B7D91EB9F9B8D551C8C2F3CEA6C2711379969467ABF7041848BB05E619A7B63CC2F9DAC97C05457911470EC7B4CD380528D695604B6F042394524C5C77415C3CA15FA64A9E16B240ED12CFF0A48AF72743916C469593B89D8510BFEC58A0532948D80D32A11FCE265ACBE6C21751507FD72366EB5EDD650344268757A715F27B28BEB4CF80347DCFCA34BB92A046955AA39590C2397A223ACB42B94B938329CB6A9C4253137531C54D2100966B9A8C96110A412AD2DC4480FAB04D05B93087AE8E4A1616526A48F38CAA920C6F495CA9E488776551630010C46C375DE52AA5EC23CCE9BCCE6663F1678B9F4325D1A961D551A02B046320F52272382DA8D565563A4CAC7419830B6C5F95629B3441727CCCB4892BA765B212221147076872361A0A31AA7C50403976042DA5486D287D143083646A90C6F1CA4037868E8478952829FD4C7E84187894F28CCD58170CF107FE74AF5A0CCFC849A71EEA4860080100310259FAA59C477C7002BD93385B8DA14E57891585253AE0CAA66BC3A33F052512788828916F0AC3C9C4FA6E31AC4088BC6A47F29386E467FEA414EDFA9194D30CE0770D3ACB1C1162312ACC64FF2793A2AC4E990726D1739E467A4BA8221169137AD1069EED5112EFF648F2353CCA4B4F8469CD29D05F2684236AA3AB3B019E6635AB53AC456D2C9D83152554655F8D6B4F75E23870677F492A2BBA53913B535A0B54113193B4122A5331585EDCC42C9361A08C173F73E4182F85B15DFC6F795269C88A19C4FB7D7D33AEA8144000827C57E73AFE87C6D9B646B1B1BE560672C858531D835BC3DA21B1407DB74A9D6B729F5BB6CA1AD086F3C2461D78A29A21238B0434594058BCDB125EE56F3B606D8C7A17CA5C3D6F876A3061BA5B65B97F033D3889AA74302D1EA268531354A707756EB60E621AC959E5AB5875790E6477D5C91A3C06593B976010F054AC6B4D54D452E5302E8E332CA6457CF2527DDFCC3B5462C7171105F9E24C8D299CA348CE9DB678E041A135893E16AC9CCB2BACCC202F7BF7B3DB1C8C5D567FE3E3C233024C75D369D7CC1093B2C2EC9094D020A8782ABFBFB89FF199517B0A4BB81C7455A64D9C07897877422D6ACCEEFA16EA242E0CEC8B5EF20C85E82E2DCAC3A3030B211A600B520B7D66B58AC317B0F65518F20EF26AB5B7D8AA729210A92B12FF115B43540509156CC247B4BCB34C313B3A38228D4BACB7E1E934FD40CB42322B6E618670E17A725CB37CB20210E000678393DE6A5D40786CBE019DAEF867F2A0B8F1827F48C0C04305357B82BAB80CC144A6997228A846D9CB49E32B865858A7601F55CB8127DA0BEA4437F017502AC8709E428309F6A252C69042C973BA104A5219C4CC17C35A828F3E21B2AB7496805C99EE041FCA0158A3314F07D053F364C887A6825958A625965D4885C2CB355E83A3C1BBB15446F891D2D24F145632CF06A5EE1A278CD3064A79AD53193853E4CEA654448A4297CEA3C9E87561629680F588953B858074292ED31C20DDD983E805D07BB9CFADD823C7900B604286C0184738CA04E0DA8289540E329605EFAA5960AAC0FD0760006C1F1993426CC7BEA22BCBB3CC02E099B828E82F94045DFACB1D9FB315582B20D1B41476FC43AC4680647259FE9B51371223446C82E0BBAEA132913E2B96EA11950C450F25854EE4FA4921193C8F1C66D61B8265C7072B046F0C532141D51D9919C80733C1BD3C5A6D77CCB3A1938C95C1E4E866D1D65C78297B3B32CA3C4143E6A215C609A36A1B13BAA17981D42B7FF4C715AC806DC491560032A5A2BB30E476A266C6E4A4A065D9698DB08132608136082689B1B648B49063C98324706A43876507FC690893900F8166E1D52ADCC44848D864B8BA0933B32DAA63435F11065915C5D5D879EDCC136FA8515B0260B9536C316120B4904921805521C0232DA126E2A9C5323976ABC73B5AB892E59B01A194B6446C1B73217FBBA855AC887AFA58A8F15A768EF968D775267E050150C7A8237C1024F9421C210D97907B2A144736E3B58E01C48947B1C62655E380256491CEBE400F52493D9033A29D6C9AE80B33E8C38584B30A31A37B15E85F5E82B73157408A5399A957190CBA905F1713C9ACA53AA32923CACB8269436A56BC02932189C7139D8463D0DC540621875E9A7DFEF020E7E83696C7612BC2A7DE7148D7075C31F257766301FA5A06E00582DCE15C6FB3195AF43078D79B110B5DC0789BE3A32132737FEB247DF8D2216F272C3D5BA700C52ED7E3A2795CCFE3072CD1D1B533939AC58A065A8A9FE85441ED291D43869E25307737A5155A095C5056A204326F944A9FC8F7035C931A61033CC2DB6ECEC08AAE0045A6542080C1A7BBD699902A2ECBE3A1F9AA95FC6222C6C6AB7BF96EDB3450E029B0CE104DBBFA0A49B8044EE1BB63B33BCFAA8A6A450194CC577D490A549B6C42D6816F444AAE8B8A4F62AFFD17664E957E2BA245A864BAB8D28E82CAB9FAC3BCD707AAC19A5EE58A3925DC7059BA1B9E7A798EC987E1924342C54E5D935A6699B6D9AA78E4738B0CEC6C57D8228C86881A14A3B57065BBE5653BD92B5BD5A9888B1B1E9B017ECA1A788CBC3A283A9F7B00D3F55B138CA26C33CD2CA3BA632A0735596162AA6C9D10968F53305D35CEC4395CA0489FF631A3EF661264A22ACB535DCC1313CD6917BFE3B5B8FC3B8016CA0DFB151623C92F95701DCC4140459B52EB796FDA60FB429651052D2C16B550FA035CC1374C87A439077713769C728B766BC75A609B70BBC14AF84A3C70519C3211ACAD58BF7C14C15F1AA86FCC1DF55580A2F9BBBC31B6B0EAAB379481CE9966F7FA6487C611BA5B3E8F8307FA35CD5248C8AB351B63ABC4BA005871A97751F01E5143302C757A65E411AF7B26F22076C7A1CF7E2B0055E80D7116CA2B3056BF8754BCFA9095D0CE99715CC4512F10125C5A500DABB7C11F5B0408377900621BE851B7A3602576650B84191749F30AC635A9E2212400769DE8E1915F26BA198AF8E53DACB598711738DA8C583A388E027A597CA0C2CBBF4FBF28DE8C479D4473C339D96B89C34A4E5FCBCF7728BDFB43B945D6BF055CB7B375E3271ED131F1BA31F83FEF533A239878A71074578B891265D1", + }, + { + 27, + "6DBB99AE6889AF01DA387D7D99BD4E91BACB11A6051B14AECD4C96F30CD9F9D9360557CADDFCF5FEE7C0DE6A363F095757588C35A3FD11C58677AB5E8797C2B8", + "BC02284C2B36002A8E002311D6FA3ED17088D6157E76867ED2110B7E50A9F0380893253026D833BE84A75E745CB9291F4E513EB41C68053575FE27893159C353DA6FB2C87757476A269331BB52268F39362DE69968F410C8167FB7195B6DBC3280B9AA5E28CF60071D2DD54323897701591931B6CE4CA0B087EA160A5A067C53665EDA6019E243A86883344041E3639126A2016F4321E3F39507E55F97BA0EF5F447E707BDF62A7609922DBDFC283ADB3CB7B10A083746BDC3B077BC1CEE06CE666B027BCB6E743B974D495D2D0183ECA61369EA52DD4BCDC1A54A8A08B8286A984143559554CAB503CCA2095D5AE54782B2586A778D4D3B6631114D7F543F467076B0CA9157729871DB0BF80065B9824835143B556419AACB9D39BA836E07274D1920682592046C83A37B8795603410B360C1E6B8FAACA706F8C613F97A1A82AB0F28A6239866D834BE02680B542A84ED50B4E98586231657C97A2B3121433914A17C20928E6734B45A8622CCCE69C71D367923447264CC72C26FC4986C26882557B342229AFCF7A8A8B716FEDB2FD2060708F99B6A826CF69551D0A90B4E7B9EC96C0B01F02481804124EA56604585F2B6284C205BDB795906A5C53508BA0627611DA8656600CA36908287A00FB8228B26863570977FF958B315A867488CB142B51377199BBDC2403B3679E34CC476C8929059503CB26838958934AACD0D4B67F69807B70855538C25E246569AD400E4F276CEC425D1229A8485AC5AF650C35A2F1730876FF943A4F6AAF0918EF5B543717298D6AC2D0F08C431D5876D669E2A64A0C6658947FB682B476E2CA04DAA743188200C6F79BF9AD23D78364625CA84D73C70258183AF2CAEB483B2C1F5B24A59B57911149FFC7442B8540561433014582ED6A361210E69575555497498508B86522DF2AB3FCCF64B19A9B8C84B32B8F681F51B97965294CF400CD08643FE9551117940D12338B7E9AB47343036AAC3DA4001428629AE8C362B1977259483B4156E5EC510436158664831BABC05807883B4832D2819305AC8CAA1177115A914C789C864574974F22058F2AAA36A0A31F465B4C52F34C70C28E47D35A1255FD31511A419EA9450A3A2ABB6AB68A544CA3E58CD398129F3A275305AA76AB42643725D3AC97E20690750DA9D340739C8D00A7B710BDE654732AA7C95D3304D7445D5E16C1AE64F99EC98B886AA10B3324F61105718BE0CC25B3A4A3A41C1653C0A255001BD51724BC843A1C5DA4A1350C0A40AC5258B17D725652E6CB4B5606D3CE877FD010ACF776D8A5145ACA06EED684082368C48BBB0284B8F502569E3342AE08619649B700C343E74243DE0A18803DAA6A6D2CBFBDACBACE53A4A5C10F7E7BCD6294B388C982FD794F3377EA9414C4DE70643A82A2F7A7A1B145AF0B1394A4B9399083D0EA8A35AC2BBC5D45D3B06AEAE511DE342AF6674CFA962A1E126BEC7F1252F892CD565596B3099BF60B332D58929C7B1ABB9CCC6D315D36930CAB08527F2776F76992AB977F615CBF8C1773DF0941DA095920806D36AA0F0884256807FF10726C12128FC337B299206050626A18116FE29215DF3817F1BC9EDDBBE2CA26ECCE15A325A9E566345F32A64D868AEBE124F044B0610E9C45A440D855C8F5AAED9A5E09369935779F1B56B9E", + "54DB18751A289F68B462D90388629B33088C2AD32A834380B9E7AAE7DB510DC26747A7C958ABACC04944C0715195420DCB58BB0EB30D9C933DF4994BF242B2FB2C723AEB926A7B3700A01F0A94A50DB845D234B64D790014CB912D887FEE572558EABB46E1C11F41BBD2615BA8F75F8D3CA6611991025A3393F5AA186B809C5BC04CC59A36057A0F91CC77825025C30F9229BACF75013A586C31A124187671F15C6AEAA9A802A065223850F683638D759D2DD6C14F9C7262E9CEBFD9585284528B686400358CEB3198238AA8F8469886228D59DAAC5F9AB6CD753F3C27BB61F06D4F66C9B64BAD6BE1B461F4940802991BF07A8E9144CFD9A854469163E62FD76528CDF9CDAF559A627040CEA414208744AC8754FD8B13060A7E314B15935B5965558B85585F3AD9C2D59B1137B8B191FC5506C23568F6868586C41C22BA0712BAA24757E4E5BAA4758DA57B6C925766149B768ECACBE7391F52773CC3F89C7929AEECDCA343077584458D3A57AE43304112CB903D7A762B23C46DDC2D26394E4F2B5FA1A2416B493F44D36278009D87303BA6AB340483A5E8962C03D486E5B77EC352ABF1B3C758424165C9C696F496202A71E36105C381B4B8AB924C9AB69A01639412152FDC59B28A612B66704CC64DAB003542C541008D77EB86A7B8F1201DD77BBCDCCB37100DD7343BFD87679EF4B9C62023C186AAC7D3C235CA642B594D806B65872ABA86603D5BAC3D71C5329A89A3AE7591D8354DB5B4C354F6B0AE603BE5373E12146F25493E73A7BAE75C952EC412FEF18DF2E77A210695EC7969FE564F204C9496497F1F0968ADBA11613749EFC524BEB3519295C4A05570DE6344D9632F8FF74482A612F5E7C0463B4D6895A44E964C87B930E57B7033A253980009A6712C382CA66487902341207E2331EBC6C904E6AA53A57724633626124574E4144609CF9D5B6727877FE6C53526EC6742D889CD8B045B22C4DED3637228BF3FBC1216C750C449BA5EB0645FD8A7B9F35E45DCBC4AF4C960C522DB339E16A7881F9A551894CC19565B9F329719B9A6CD81838EF2A8ED2308148601092A20C712C3B393896E47712FA42C2B197514821DB28524AAEA7DE87229BF62401F12B99C38C8C039A7D212A5E404866D7A8288A9B1BE63A0A6C980FF39C3BB2C6DAC7464E05B657AF585C6F0620E63A640146D2E9C3C12D05328F343BB409C7CCAAEE0000C7C8921C31C2DBBC3B374A51A1B135FCD2740081740B06C413A41A519F1CE18916DA0519D132943FDD392D5B855441126EB238AEC989CC2D663B9F575D790407A5265471CC32BC6993BCBBD063C4DF69C8ECAC96B8539BA9A3BC532955825EB7F3DEB4351385EF6C53AB81CAA7A92AD1FC839C7DB1065FB5F53B17DC5C0137ED1BA6D9BCC5D037D1B96909F98CB3F74B5F4CA1AA593C50E39A55D363641453F2A7A1481D4BE66FBA27DE5088783466D0CCC82A8625C91814D966281B977276715886313A83BCFD00659A295C62AC970A904C31F118E35A900E3CC711F9C8E5D73A970E13F574A3030188B2EF9203EE6A92FF7096E97998470B77446B293AACC15432FE0F207659844C9B29C87C751D4080656B2951116317B194CB686AABC02284C2B36002A8E002311D6FA3ED17088D6157E76867ED2110B7E50A9F0380893253026D833BE84A75E745CB9291F4E513EB41C68053575FE27893159C353DA6FB2C87757476A269331BB52268F39362DE69968F410C8167FB7195B6DBC3280B9AA5E28CF60071D2DD54323897701591931B6CE4CA0B087EA160A5A067C53665EDA6019E243A86883344041E3639126A2016F4321E3F39507E55F97BA0EF5F447E707BDF62A7609922DBDFC283ADB3CB7B10A083746BDC3B077BC1CEE06CE666B027BCB6E743B974D495D2D0183ECA61369EA52DD4BCDC1A54A8A08B8286A984143559554CAB503CCA2095D5AE54782B2586A778D4D3B6631114D7F543F467076B0CA9157729871DB0BF80065B9824835143B556419AACB9D39BA836E07274D1920682592046C83A37B8795603410B360C1E6B8FAACA706F8C613F97A1A82AB0F28A6239866D834BE02680B542A84ED50B4E98586231657C97A2B3121433914A17C20928E6734B45A8622CCCE69C71D367923447264CC72C26FC4986C26882557B342229AFCF7A8A8B716FEDB2FD2060708F99B6A826CF69551D0A90B4E7B9EC96C0B01F02481804124EA56604585F2B6284C205BDB795906A5C53508BA0627611DA8656600CA36908287A00FB8228B26863570977FF958B315A867488CB142B51377199BBDC2403B3679E34CC476C8929059503CB26838958934AACD0D4B67F69807B70855538C25E246569AD400E4F276CEC425D1229A8485AC5AF650C35A2F1730876FF943A4F6AAF0918EF5B543717298D6AC2D0F08C431D5876D669E2A64A0C6658947FB682B476E2CA04DAA743188200C6F79BF9AD23D78364625CA84D73C70258183AF2CAEB483B2C1F5B24A59B57911149FFC7442B8540561433014582ED6A361210E69575555497498508B86522DF2AB3FCCF64B19A9B8C84B32B8F681F51B97965294CF400CD08643FE9551117940D12338B7E9AB47343036AAC3DA4001428629AE8C362B1977259483B4156E5EC510436158664831BABC05807883B4832D2819305AC8CAA1177115A914C789C864574974F22058F2AAA36A0A31F465B4C52F34C70C28E47D35A1255FD31511A419EA9450A3A2ABB6AB68A544CA3E58CD398129F3A275305AA76AB42643725D3AC97E20690750DA9D340739C8D00A7B710BDE654732AA7C95D3304D7445D5E16C1AE64F99EC98B886AA10B3324F61105718BE0CC25B3A4A3A41C1653C0A255001BD51724BC843A1C5DA4A1350C0A40AC5258B17D725652E6CB4B5606D3CE877FD010ACF776D8A5145ACA06EED684082368C48BBB0284B8F502569E3342AE08619649B700C343E74243DE0A18803DAA6A6D2CBFBDACBACE53A4A5C10F7E7BCD6294B388C982FD794F3377EA9414C4DE70643A82A2F7A7A1B145AF0B1394A4B9399083D0EA8A35AC2BBC5D45D3B06AEAE511DE342AF6674CFA962A1E126BEC7F1252F892CD565596B3099BF60B332D58929C7B1ABB9CCC6D315D36930CAB08527F2776F76992AB977F615CBF8C1773DF0941DA095920806D36AA0F0884256807FF10726C12128FC337B299206050626A18116FE29215DF3817F1BC9EDDBBE2CA26ECCE15A325A9E566345F32A64D868AEBE124F044B0610E9C45A440D855C8F5AAED9A5E09369935779F1B56B9E9C9032984AE72B7D5E0732A29EC29D8BBC4252A31454185710D280C223A47899360557CADDFCF5FEE7C0DE6A363F095757588C35A3FD11C58677AB5E8797C2B8", + }, + { + 28, + "7725321C56F925868FF834F5D1EE90A70332AA9283434E122C60A8D474AC6C0F00F6EEC72778E02ACD04BB056113C571982E45018BEAC566EC59953724F38A4B", + "EAD840B81B03BFE75805595DCF0808B83738AF14C758920F80E209D68B9192C5CF0DA397A0BC9A702295B8AC3826DA6F427C9281D937CBE4686F70713B39021CDCB96D71405D4CAFE4068CCDB64643E7540E571DAC0019D3F1235CA8161AE68592223E674C07AC76286C95BB17E78110317840064485D7B16DDB830C807BEC506CDA14C957DAB843781B977A439C612F52A944AB2B6E6A1C221B886097EC4259B0661E550B58376A56F7A62ACCB7A8B94CA02B1797B62497B88B0035623F26086D14122CBC9EB7486B0BC2A8BE25985B2019906977CE971D928347DA5756EBB65835764EEB6B52BA123E61BBC5123645346168597C1194C80ABB26C74EF6500AC6B180C190C28C9041AAACF5371C41373BEDC686BB73B583F46217F31F01B66DCCBC9BD6627DC03250B7E52C690261BE97B42FCCC63EB443952143F5734EF4D336C5311D0B2820A1AA715CBC1A106117BC7159FDF32FDA33C82653727466BB14ECB98FF06E2808CDEC540A2276B34FD633349367576AB50360C88992A675128400DCB07999A2B3978547735BF8A202EEFACE1960AA6649CC08582E92DC928F30BC0E3CA45EF8159F928842CA75A01A013B1A5CD55554274A6D2BC39A4796A129017E7A91A79CE22665940F5F711660ECC5101C230BF927146486AB6546C4E4BB3D8C7D877769B3BA7FE0162397DA4583D30B343862205CAA6D0CBE57000D6F9384D73C6ECB3AB773599B73FAA2F9258EF467BD6266273D1214C103399019751B240C128021F1E698110559B2C56FE32223CDD90D04B766CA425AF66458D8E66BB7029D147CA11E778B5323216C3081D0A6596A7AB5FE886E9C9ACEBA705A3B751F19165916C5855AE29D34D10FBCD68A60C38023243CB1C22D30261292E01D31300569777B099C77F5351FF77C2BA5C09F584CBB6CCB84F11AA264C310FDF253B271CD6450B85F040FF8262F2113141BA3B45E1550D6084BC84A50202B19D23A92CAB5B8C6838F49D99AADD6592B1842EA251F71F3A6707C55C3EC0A3A0314C9A4530F9AC4FBF83B9B73510229B136D5989990B71954BB3889CE54944E01039AC7A9B4EBEC47805A9C51298A7F867BEBDACA71D7AE2012AE2B855CD7C97BA0E14EDE7B8508935D3B0192743B3AE7D3628166AC251A012C59034CD30B8258A159432A62DA317E8A993DC07C8DB73E5B3478D208875D6AC9B64CB36D0B1979AB9D553843BF054CA687B316414E846CB5E305B68C00A8B1C903C7C0CCD03970317C6A1F9A8646B5CABF5112BE01B868402620883E46DAA256228A8092648BA61FF57805E0D03A06C46AD1411CA8E3A95FA73904D3246AF0C4AADA2C582906B54180E9569C86A76B5F88091EB9165F3528B92A8C0549B097FC2A3398A226B224EEEBBE3B3A96066A2C0F884A60FAAAF9E97122C89582395B192172F661B775E09C6662CCB64169BEEC4924E96AB257BA514617161B8F7B316CE0512090DC627D59C4F3A39EAC068554C075E29628383477E5C493150B04D1249D61E6A09E2B323BE503A6512BB119A262B8ADDFB43612654DDF685981063E14DB0DC025520BF20FA3BBCF81D779A98300A4E0A6B6516DA6442968C7BADC129B3D458A1ED8ADA5301E5C3FAF68F13D6DBFA29987FB14563E8C6BEE4EEC9B8AB5F504A8BC1A724E6005", + "C397104AE63A279795F3243F6EAC3A74AA0B414C822A668664826003F7CEDC2667819773E4C3AF895502D22C3BC95A9887F84DE421197861964FC88E3299B16226250F701A1C1C97357884F2A20E8FBC08E84C3ED233B6B261BBD3973ABD6B1F5172C59AEC4DE0BB676417528B43A2688A831C0889AA96113640ABEF499B1AB7B67B355DA2B9A26E0CCAB9848CFDDAAB994B8E0FBB090F5C332E6CAB63D8ACD1D0B80DC68B755A2274FBC524346A93DCB5F27956CDC73B429B71410C7C75E4683440A1179851C77220354C998C47CC29EC4A87A2A44B3C69EBBBB572534B3C1B14F90041E6ECC4400041EB729E9487064E44CDFF33A352904421D8A2BBC34D1275A475C61160440DA4E2785F140B327BA35F247ED1D66EC8A05A44E4298DC72764877BD369B78A287CC7DC6B58132C43841E9B6924AAC38F6D4BC4D4FB2E32FB5E54E0963C54C8058522AF21B7FE435B5EE3801A0BBB21088DEF77B20A466A5EB8C8D3D71ABA4705C0D6C23476BBF9918B6271CC041217B41A0E0EE885EA0B5334F64A5BC0199F73675C0593A0369429818E0227B8A4556EF83AC492D8A572F126004C1E2CBABEF335312F3256A3E6B42B4150FFA21E183588664B465C5CBF14D9A8C58CB33D4955C80939351C1D8A959BBFB210730906C546C60D747AECF15DDF571769524553BCCFE6CA3422830CEBD61C5A3C541E20B8AB9C44061B4BE888A01F429C03128CCF2B2EE53555D42A3F77E04CEEE46B0BDB3A84B4CD3BBBCA9DACA71426666934095A862E94710B862C31985517A49A9C1B175E9C4A63B2C055B2548BCCFB14329B35EBA1CB302972073480E913AA1BA9399F63610220133E6ABD02E47740A56CCF16B620308261692CA0A56FD6554A59C8ABBCB26156F69034C30C2D1B89730627A8036818458D4DA70E4A954572F095BB64ACF346473481A67DAB2023539C3111B205E414A24483632C4C548B6BA9375D758C597C322DFD89288E4B676034B36CB695C571960232B94396B9E08924FDD882429A143E451855F65B3EA661B573878C083EEB20BD7F5A142E386DC8A45AFD000072B8544D74B7FA472FE03C0F36A310FC54C354B0A22F62145C5BAF1BAB5DEEC678AAB4B89F4A49012088F1307FE02421B7B2B53335871984678295C8423BA60F834BAA9642811902B91AA23344B85628C242F183AFCC27F0E18A9C683C9C7484BE82CF6E17C03DD5A558326C6D4974B28261DF45639FB15B18C9BB3F8B2CA0951C6DD2C3C6853DA8402CF1D625CE4C7D1EDC8BE31CC2C761B540F5121F739A4256A47E396C81419020C324DCB29AB577C7995850748A41D0176E7513A8FA9252F9730E44D2220A8BA3641CA4C9B5A2D193415288730D9434BD9AAD79D27B98F16217D52241D0606CF5424B27318346B954E805E0507C87A741F4FB1FDCE454DB18B845E90732724858AB02AF10B7150C3AA11C1FE4C6B836A0065FB724C1D77D1DE0BB2867B26925882B915D4928159CF421A59A4243E990C3C17E1AD792141CB3D2DBCEEA7648FB91A60D8A62C65A9B2C765F2C1582F10513485093519AAD55D2B08A18C6F29B38768859F5150CFA4537DA470B38645A4F8126BD696A6BF9715634B70F3B1DEAD840B81B03BFE75805595DCF0808B83738AF14C758920F80E209D68B9192C5CF0DA397A0BC9A702295B8AC3826DA6F427C9281D937CBE4686F70713B39021CDCB96D71405D4CAFE4068CCDB64643E7540E571DAC0019D3F1235CA8161AE68592223E674C07AC76286C95BB17E78110317840064485D7B16DDB830C807BEC506CDA14C957DAB843781B977A439C612F52A944AB2B6E6A1C221B886097EC4259B0661E550B58376A56F7A62ACCB7A8B94CA02B1797B62497B88B0035623F26086D14122CBC9EB7486B0BC2A8BE25985B2019906977CE971D928347DA5756EBB65835764EEB6B52BA123E61BBC5123645346168597C1194C80ABB26C74EF6500AC6B180C190C28C9041AAACF5371C41373BEDC686BB73B583F46217F31F01B66DCCBC9BD6627DC03250B7E52C690261BE97B42FCCC63EB443952143F5734EF4D336C5311D0B2820A1AA715CBC1A106117BC7159FDF32FDA33C82653727466BB14ECB98FF06E2808CDEC540A2276B34FD633349367576AB50360C88992A675128400DCB07999A2B3978547735BF8A202EEFACE1960AA6649CC08582E92DC928F30BC0E3CA45EF8159F928842CA75A01A013B1A5CD55554274A6D2BC39A4796A129017E7A91A79CE22665940F5F711660ECC5101C230BF927146486AB6546C4E4BB3D8C7D877769B3BA7FE0162397DA4583D30B343862205CAA6D0CBE57000D6F9384D73C6ECB3AB773599B73FAA2F9258EF467BD6266273D1214C103399019751B240C128021F1E698110559B2C56FE32223CDD90D04B766CA425AF66458D8E66BB7029D147CA11E778B5323216C3081D0A6596A7AB5FE886E9C9ACEBA705A3B751F19165916C5855AE29D34D10FBCD68A60C38023243CB1C22D30261292E01D31300569777B099C77F5351FF77C2BA5C09F584CBB6CCB84F11AA264C310FDF253B271CD6450B85F040FF8262F2113141BA3B45E1550D6084BC84A50202B19D23A92CAB5B8C6838F49D99AADD6592B1842EA251F71F3A6707C55C3EC0A3A0314C9A4530F9AC4FBF83B9B73510229B136D5989990B71954BB3889CE54944E01039AC7A9B4EBEC47805A9C51298A7F867BEBDACA71D7AE2012AE2B855CD7C97BA0E14EDE7B8508935D3B0192743B3AE7D3628166AC251A012C59034CD30B8258A159432A62DA317E8A993DC07C8DB73E5B3478D208875D6AC9B64CB36D0B1979AB9D553843BF054CA687B316414E846CB5E305B68C00A8B1C903C7C0CCD03970317C6A1F9A8646B5CABF5112BE01B868402620883E46DAA256228A8092648BA61FF57805E0D03A06C46AD1411CA8E3A95FA73904D3246AF0C4AADA2C582906B54180E9569C86A76B5F88091EB9165F3528B92A8C0549B097FC2A3398A226B224EEEBBE3B3A96066A2C0F884A60FAAAF9E97122C89582395B192172F661B775E09C6662CCB64169BEEC4924E96AB257BA514617161B8F7B316CE0512090DC627D59C4F3A39EAC068554C075E29628383477E5C493150B04D1249D61E6A09E2B323BE503A6512BB119A262B8ADDFB43612654DDF685981063E14DB0DC025520BF20FA3BBCF81D779A98300A4E0A6B6516DA6442968C7BADC129B3D458A1ED8ADA5301E5C3FAF68F13D6DBFA29987FB14563E8C6BEE4EEC9B8AB5F504A8BC1A724E600585E42177479AC364C343E26B4BDD480D14F1A7487FF4C64E37850C5D1B4EFC9600F6EEC72778E02ACD04BB056113C571982E45018BEAC566EC59953724F38A4B", + }, +} + +func TestKeyGen768ACVP(t *testing.T) { + for i, tc := range kenGen768InternalProjectionCases { + seed, _ := hex.DecodeString(tc.seed) + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + + dk1, err := NewDecapsulationKeyFromSeed768(seed) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKeyFromSeed768: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + ek1, err := NewEncapsulationKey768(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey768: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + + dk2, err := NewDecapsulationKey768(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey768: %v", i, err) + } + if !bytes.Equal(dk2.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + if !bytes.Equal(dk2.EncapsulationKey().Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + } +} + +// mockRand implements io.Reader and returns the given bytes or error. +type mockRand struct { + data []byte + err error +} + +func (m *mockRand) Read(p []byte) (int, error) { + if m.err != nil { + return 0, m.err + } + n := copy(p, m.data) + m.data = m.data[n:] + if n < len(p) { + return n, io.EOF + } + return n, nil +} + +func TestGenerateKey768_Success(t *testing.T) { + // Provide enough random bytes for d and z (64 bytes) + randBytes := make([]byte, 64) + for i := range randBytes { + randBytes[i] = byte(i) + } + r := &mockRand{data: randBytes} + key, err := GenerateKey768(r) + if err != nil { + t.Fatalf("expected success, got error: %v", err) + } + if key == nil { + t.Fatal("expected non-nil key") + } + // Check that the key's seed matches the input + seed := key.Seed() + if !bytes.Equal(seed[:32], randBytes[:32]) || !bytes.Equal(seed[32:], randBytes[32:]) { + t.Errorf("key.Seed() does not match input random bytes") + } +} + +func TestGenerateKey768_ErrorOnD(t *testing.T) { + // Simulate error when reading d + r := &mockRand{err: errors.New("fail on d")} + _, err := GenerateKey768(r) + if err == nil || err.Error() != "fail on d" { + t.Fatalf("expected error 'fail on d', got: %v", err) + } +} + +func TestGenerateKey768_ErrorOnZ(t *testing.T) { + // Simulate error when reading z (first read ok, second fails) + r := &mockRand{data: make([]byte, 32), err: errors.New("fail on z")} + // The first 32 bytes will be read for d, then error for z + _, err := GenerateKey768(io.MultiReader( + bytes.NewReader(r.data), + &mockRand{err: r.err}, + )) + if err == nil || err.Error() != "fail on z" { + t.Fatalf("expected error 'fail on z', got: %v", err) + } +} + +func TestNewDecapsulationKey768_InvalidLength(t *testing.T) { + // Too short + short := make([]byte, DecapsulationKeySize768-1) + _, err := NewDecapsulationKey768(short) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } + // Too long + long := make([]byte, DecapsulationKeySize768+1) + _, err = NewDecapsulationKey768(long) + if err == nil || err.Error() != "mlkem: invalid decapsulation key length" { + t.Fatalf("expected invalid decapsulation key length error, got: %v", err) + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var encap768InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + m string +}{ + { + 26, + "F255CE47334283B8622BE7CE76D7354E3C4FE3F6C44F6BB25C9864EE0BAEB5765950D88F438263CE8B5A7A4C0FC4C95F10C477A7521F9BB458B8AA55D2E43BDC86B72F0930EE428B4C5A9C7116310F2AA5CB03AC1603C811959EA9012D69CBCE40B37CD890999CC74FF375C66F048B240363343CB795998856D560F4C712938C79466864D20B0BE95419C9EA6A8E7203A1986D10B606691242CEF630941B116458A41C83B7DC5B06A97C840B116F2CE9CFA87A1C1AA8C4FAC137DE8498E8749B3638404271539B247183A32E7E4413B6400E0F295788084EEA93B4A7653341005672D908C62B64B11B48414B505F3036EE56CC4DA88FEF27B2DA974C9DD38C150090B5B8A29BD7C5975A8A959549044B4DAED52A7FA68335308F40C9B768C5821F78CF068A694978964F597408D09759A19578624C64DC18EAB23082E599EC488DFE016E4BA58977E15B715C612496310219B9B4775CB51C5DF03B934F7473AA58A57C602CF17C5993D30F52D753AC56BACA1A994742BC50435E179A262B3C8EECE1513955C593E7508B945F6E95CC4268CBD45B2504082FB8B23D8906946A74AC2FB676BDBC39DF76B9B8450F49D283C622784565B76B96084DFC099EC2279E5BC13492561B4439E32324B0050C5FE6451974BF0D72750AC58BAC046D218AC397F65532ACC7800246ED1C8094FC807306BF88E2816AD13B06F2898CA87C486A124B618156A090B1058722ABAE389AB5612CA2C2766DDEF98202A6AB1097B392404EA151788528B07544325F851B4DEAA2495138F929BBB4026042B0A8CD3CB0A7D061927A717D4877E0D9A409D6B125361C99090AFDF922A776ACADA2B6A84522134B089D4B428020C83061A87816C6A59263E636B5B2ECBCA6A64E29600948D5B0B45600B8D473A65B450B766D0251B6915898BC3C1C2C53B9679121F1F06CFB9604DE0051FF4B093939C907AB18C2988646A90481BB99F4153611C138BE34BE163B3ABAC44354A774E9CB54FB29903367C78D275467499D22E83A11CA9B8445BE9DF3CB612069222A8715A495D115B4BC2457AB731AE7EC1BD8EC9722CA980958180AC2BD67898F4A72A675106D66981B2E923C0BA40E3234655D00B25D6462591C9C9C7A53491489D57A77B2510D08B95B9C61C1784BA752F4A73023742ECB985DFB37808B16D6C283CD4A06C5A3AC401855E1DABE63F9668BF7A661946B18230A1A5A7C19DA66ED08151E77A624F579D4E44ABE023A1CD33459FCC3F1A6589426634D062D0A75A387A0B7B8D802A66B2106E01264500915B97307C85ECF331BCAC35E4AA243C837876D858AFA8B510C342708B38093B2CD35D1BA68DA0544794D172C6CA8850A7F847B56998D8E0B0A17144FB6F443E3679767CA91B80A6CAA8BB0E22BBAC01C0EAE1604B8A243911672B3748C7F18C531E3783D522039130057198D6F0989E99641AB718DA123710BDB67B3B75EC66BA9CF459FE06C7C4F7959DD7281FF155940B09FB14AA55CD40B963CA3312C05B36A5207C989428C16E5D288ADB18A66F74617CA39DB8AA612D706DFEC884C457AECD1AAB598195B4AC971529FB7A883492235E62112064A0F6FF5BF4F1619A0D03B96B5112009966B2DF7C2F300B6F295DF7FA2C453E1949DF6405309DF7575C7656C245EDCA9F6", + "B8E17544C0493C200F8B15C8973A25BE28AD58442B916C8074439E2D12CB2A4623065658C80017F8475419C9A7FADC988888ADE32B540AA77539C49F10B4A0FFE6A21271192A3908E0583DC8788301C31777D116B5759B5F189CFABA11CBD976067810BF939F91579440FA86FAC692D125811329701381B97175317ADC60BBC7585FC07F65A92B67978684C225B960439C147C06D11D0423896257BF8FC89E3FE8021AC3A6C81192B3F96DA3B250024541D92759BBF77AE36A1C9B1B1AB01B0B7F2B9917A579FC265D05D4360AC547B8D2AC68998181C1A5E3C4284B51B16E3BA69989B431C86BFC91C62DD804F2678EE6E71FD3CA84E9636CE728C0EC80B752807F09EB732B8C9CC2A15B84A303BE1C0087391C4D36A55F36BCB8347365E9CE33980380FB628FF0BC1A6A0FC19C89AAC71304473EBABA772EAA95EE37A28E6665D6345647F72EEAF429113096A8322B3F4BA18FE73C056A7DBE746512A022AFBC59B39422AED579A8C0C738F5BE2D22BBD467CF34CA75768200984739EBE97F31A87F620A3EE13B378739CB0C71AD2F44B20EAAA1A4C61B9CE88F1F84312EB50B34080D83074F16624A7AB7CA236787F7112A455A9DC0BCB8D6FC4D3E52AB9A2373A5338BACE0C528B62A4BEABC2562199BBC1BF98839ED771BEAA68262E8CC7AC51C9DE4615FD34DBEC61D653CA374C4524308C7987B7F772B16FEE02557F32ABB8C721992BEAD8A5DBB838183791B9AC61F507C1DC7C51DD505AC61622B0CB36F7F987B5AD75ED1922C2BC187EEA661DD12794DF5C460D7477669213E733E4D9553FE9850F1A9388CC582F137433D9CA52F074B5320C89B763C97F64D4B9B8F7772CA6D70ADB7D818FA47367B6A91A3418777FC0B67C30FCF4B9D550792E8DC5E4FA8304F317698E7A303B83195D6A49BA51B20FC8F1A549094D62ABF79AE05742686E35945A0AAF7881760B55279341D12599FDEF65B8B512690DC84B204507822754DEC900F30085BC872E258595EF525D141A71AE54F8AD19AFC893B1844761156A04646BCC57BC203D05DCBE015DBA9993A381525204AA9797F94406D9BA1AC96B600405459AD578A414542A598BF81698EA22BB88C6656B2DB7915298A083CBE7F309A5A517CBEDBB8FF9A53CFA4C5BDA3A9843807C0604C25F29C2A135A1BC015467206E2D4AE0B6422373C251E0C39E3276C7782B5CA1969CBD731AF6CCC058324C7830FF97946A89C5D75C8629D745C8BC8A0549A7993088CA0484139E012EACA09158B249488A2B8A07A57253D4AD30BA8A28B7BD3828214942D2B90DBEBC017688C366C2ED8189969334F81452EBD08A3A24A09813A2C98F3C9C2E23AAC4785ACE990E66866C4846892E9558D1121A993BDDBE0540D7791F7F0AE6A994E1C65014DA6C0EB7C4882BAC510B9637A637EF8FC1A42D6150FB437B681601F859FFAF9C0CB89A514098C91E53E13F1BEE032C819060B82692D4CFC60F27C8D746C3C879A1B1749C35AAA695EDA372B7A41272C9598D33064726867041B09C684F55A3118E64EBDC028ABB8C8D6CA6ACE566D2B2C7C15BA3EE301B867C84A7AB75C3C1434C3E551206911F5FA7A870C513A289768867EF120C308FB2DF255CE47334283B8622BE7CE76D7354E3C4FE3F6C44F6BB25C9864EE0BAEB5765950D88F438263CE8B5A7A4C0FC4C95F10C477A7521F9BB458B8AA55D2E43BDC86B72F0930EE428B4C5A9C7116310F2AA5CB03AC1603C811959EA9012D69CBCE40B37CD890999CC74FF375C66F048B240363343CB795998856D560F4C712938C79466864D20B0BE95419C9EA6A8E7203A1986D10B606691242CEF630941B116458A41C83B7DC5B06A97C840B116F2CE9CFA87A1C1AA8C4FAC137DE8498E8749B3638404271539B247183A32E7E4413B6400E0F295788084EEA93B4A7653341005672D908C62B64B11B48414B505F3036EE56CC4DA88FEF27B2DA974C9DD38C150090B5B8A29BD7C5975A8A959549044B4DAED52A7FA68335308F40C9B768C5821F78CF068A694978964F597408D09759A19578624C64DC18EAB23082E599EC488DFE016E4BA58977E15B715C612496310219B9B4775CB51C5DF03B934F7473AA58A57C602CF17C5993D30F52D753AC56BACA1A994742BC50435E179A262B3C8EECE1513955C593E7508B945F6E95CC4268CBD45B2504082FB8B23D8906946A74AC2FB676BDBC39DF76B9B8450F49D283C622784565B76B96084DFC099EC2279E5BC13492561B4439E32324B0050C5FE6451974BF0D72750AC58BAC046D218AC397F65532ACC7800246ED1C8094FC807306BF88E2816AD13B06F2898CA87C486A124B618156A090B1058722ABAE389AB5612CA2C2766DDEF98202A6AB1097B392404EA151788528B07544325F851B4DEAA2495138F929BBB4026042B0A8CD3CB0A7D061927A717D4877E0D9A409D6B125361C99090AFDF922A776ACADA2B6A84522134B089D4B428020C83061A87816C6A59263E636B5B2ECBCA6A64E29600948D5B0B45600B8D473A65B450B766D0251B6915898BC3C1C2C53B9679121F1F06CFB9604DE0051FF4B093939C907AB18C2988646A90481BB99F4153611C138BE34BE163B3ABAC44354A774E9CB54FB29903367C78D275467499D22E83A11CA9B8445BE9DF3CB612069222A8715A495D115B4BC2457AB731AE7EC1BD8EC9722CA980958180AC2BD67898F4A72A675106D66981B2E923C0BA40E3234655D00B25D6462591C9C9C7A53491489D57A77B2510D08B95B9C61C1784BA752F4A73023742ECB985DFB37808B16D6C283CD4A06C5A3AC401855E1DABE63F9668BF7A661946B18230A1A5A7C19DA66ED08151E77A624F579D4E44ABE023A1CD33459FCC3F1A6589426634D062D0A75A387A0B7B8D802A66B2106E01264500915B97307C85ECF331BCAC35E4AA243C837876D858AFA8B510C342708B38093B2CD35D1BA68DA0544794D172C6CA8850A7F847B56998D8E0B0A17144FB6F443E3679767CA91B80A6CAA8BB0E22BBAC01C0EAE1604B8A243911672B3748C7F18C531E3783D522039130057198D6F0989E99641AB718DA123710BDB67B3B75EC66BA9CF459FE06C7C4F7959DD7281FF155940B09FB14AA55CD40B963CA3312C05B36A5207C989428C16E5D288ADB18A66F74617CA39DB8AA612D706DFEC884C457AECD1AAB598195B4AC971529FB7A883492235E62112064A0F6FF5BF4F1619A0D03B96B5112009966B2DF7C2F300B6F295DF7FA2C453E1949DF6405309DF7575C7656C245EDCA9F6D63A7F9914381C8093835899CF0C93056F03C56A1B64382AB764A8DA2E4C50610624F2A4AF5E82AB4B0F15E6CAC2D552CAEF57F3B78AE4551260AC71B007279F", + "4EE24D9E0858B36DC755A9389F4FDBF438DB8FBFDDD2E2A41FBFE7313693E87B2BD86A2A5C95286840A2E477F4AAC12F28319D892C30FE9A120A09713369A17D5EC459C7E5DCD402F9049BF6FF0F7D07A7F18D4C1E3E0429BF6D501EEDD33E114423A5C4692738096101EC79233F20C58A6EA7E855C4E608DA7C9EE086EEAFE296F3214BD0B9264AE18069342AB493BBEE267401FEDAE19D5F9224A11D911505CB3200C65F17C91F88FBE25621402C939F071C48D6BC6ACD207C8C215E2FF23AB5FAD94F2CE61003C99DE15195B36ADE08864043CAEA49D4EA1D550978C49455B06AFB4BA4F0C178D30F953D47F0258370FE8E686802085FDCB25598DE439CEEA186F848AE2E4ACF526E03755D0A89A941A08C5610C96DFB3F47C693E7E899E1DD38DFD9126F3D05751234ABB82D2BBA4D347823B97293AE2B58FD3C71D2216797DA1CA453D600C5A9A6B36CA23B9450894A894A42C40B38871F1E9E84B40A04112CC9A4B6E7B8DE59CAE5D8389687DBB1EA078F3CC93DC5D0788A671530D9FFDE5F2CB6A558AB38E038D59ED1EF1FEE381AFB6DE0A77E1333FC5B0FE3EA2DCDB84FB33795449AC6DC322F4E5E09B2A462D3750BA426C04271903CFE9A3F02221B69E474F24B7DB608492C01AAC3F2FA472A2469B6A8A949A67C567D139D6F40B00DF1AAC1E6456FFAA646E0DEBD79907905F28076F78ECB3DD1A6493A40F3D1F8D89C71AD15D22162264D0AA73581AF7A77EEA43B856370182348034810E60A902604B724E09EA4638AAB039982A64EF02C9C21259A381BFD3BF298EFE4285CB538F79BE01CCEE1A4A903A80132942AC6BCD4425BD197EF014E7DBEFFDFDD5AAD00DA2A5E8DE4FFF1E8E97F23F3F4BE38970AC09CB99D252CF2736059CFDE177EA6B36EA329B9DC541C3DCE61FAC504908857184C936C1A1C4AC0C8DDE9A66B4F3AAADEAB2DBEB2F2E6422A49A6B9C223DF25A34720717253DA6DCFC0A825553A1D2884E01A97060886FC30A1AD50E52C46F6F9B87274AD6369FCE4FDF3FF6A5D62D4AC82CBCECE902E4DE4214B71A2CA547C536380DCA26F63F2BE71B01EAC68F42AD1399D390404AA5539A5995254836FE6CF59C490B13CAC01C69E50CDC0EBA1C2F9978F4223AA38DA33639ABE933B40F6E2751515EFAC9896465041D3922A4B043897083F7CC6DAF1FD87B970BB6D1D5E48B599FE6CD2AFBA5FCE17E4BD4B0217E879FAE34FC227EC132D6F7141A48D14DED57729164A1D2B0BEF89A16244EE114AB4E108648B7E80A7DD502B0DBD0C38AF1D85AF6966BFB8E54237000A3F18C48EFEB93ED62B41A9B341361D0D7AE75D63A3A34DCEE329996AB0630553F38BC0DA148419162A31D386745C818E2691411A7927E49822AAE9EA918BF49D6809290B57C1BA5EC212F135AC0B8EE945C0510CFED4DB5A84B617BA1525E996486728E1DB87B6E99CC9A7EC4399B063D3943FE4EB1933E90365A4916CA8D67D064DF7D5B8DFC51E337247CAC3CFFAEDC5523276CDCB82B130D8E1C3FB6D7D69", + "B2425299020BCF563B8EBE0512F0479941335A75A32B8D10BFF60E5548B64672", + "5BD922AF345AB90F297D0A82EA39527A648E4977AB56242E2AC0ED9A2CC66F10", + }, + { + 27, + "8E8107F8F9701A9B6B01F05D964539605B87CAD36A2797604D3983D0603F7522650312A8707B64F1F4CB12D8886321BF7EA1CC28F3982554A3FFC664B706C76C429785EAC08D8954517315A418A5181A2D882C51623C9E3623545BBA8511C8BC0993880391BCEFF86A1A0717D09079A1C33C8E9C97BB520F273684B2AAC1D4D18A70D490AD0A6627F0537460C095364A42FA08A21AA6D24255F6B2660B1B64839846F6E030814CC58B188FD1D142D4DC3EC5319773817287BB82EE5479E0A1A498E1671D615162D8C5B38068B61643832300FDA1B41EABA31C5B9854A07B62D8B2C591898D92BEE3CC01710810EEF3288FEB3AD9FB58F8D39F76A128EFC764FE269345EC76613AB0A41A746496445FA705AEA7094A4542A8E59F83B380E0BC663247765C3B685B33C01CE7806C80B037D22053E6B155D516CCF2C010F97575527AC7A5C3D1AA659063AF2D0B0D915A1B57C75C707A179DA035A9E1CA3977B91B8330930C4DB5BB2501308747C0A709C34142A3627BCC2952FCA704D749E3723231672187DC1CF61C087A2384FE727BE3AA7BB00240E2395B979B0DFB942D38F3825E72132395348F7C6CBBB800C3C36F8D4818D584C19B124AC9A728F7714760625B5FE877C855BE65B01BBCE106A24A2F7208696E73A22A27522566AADD8A73A050914D362932902B8EC8429E5C15CB427965A87F190625B6452C32896F5776318B15CE2F3060B296A1BB72A4C36B5CFCC97B9668713F85575280C1085B20D8B35D993A27DBECB97DB218B3C297B2E60C8384B05DF2C7234C750669864A5BBF76B9902F00A05BD74429E57C1B24C6BC28B6BE2C9EA5C46213D92C4E29828DC1B10DA786106106C7689014009B1B1C022093AB7CECB44D4998B0838BBF912C81269051655691135B5CD45F84D715F3C7B56DA3314D8783BD0213C0C33038116042D116DCA41226974483C6A4B8A2A319690FC0B1BEDCA3B02925A01C13472AB9A565778D44242D3B1776FDFC15C4F18969623E01A453AFBC6F2C264CFEAA8AD14B39F96B55EEDA5CB8C391A31773CBAA77E8481B5E26ABC8D531430B0931F36BAF28B96FD646AFB71195D58BFA1686A7247F9A69A5D20C0516694E760451199A25A55457BD17674B4CBFC5AA201ED0CCE77172951170342B3091181DC22B7CA70213D1A38B9D757E87961A4CA7BC18E22F0626850A794E3C2A5D5B5001B194486CC0CC880467FCF3B55B3492648980C961333E6C516EC4298A7112EFE459E1E92E821C74D5896CDF229A798529EB5048088A4D9BB105E54C006A5C15A46923909272AFA9B6D5661F7AF16D15A698D213C51D771AD0B14BA641C762A292EC5A0372D63501B5BECBF24731E283DC4246E3652A5544BFE99A71D05974CC2B9DDCB34C0AD9A057BA0DF05C56B8854E01655BAF21C9A5079983D322525049E0811103A101BB5163538C6D27A477B4F956F1967F445A2201D990D304755EEA5482813089A3C4911031FB49B380D13FA2C8A9BA7B439B3B5102B832DC65387FA41B4E7959BCB790ABC51BC30608555A80C4984AF0B80CBD1829FAB6C7BAF870D7B76D55672456FC3AAE7A4FA466CF64B43482E53DFFD1862F32BE3718CE4D21C57B894A3193C3DC8A03A34AA37806B6A35925C365EB01B4835F4AE98AC69080870E7CC1", + "70D81B5F999C92B7340CC167324C63876AC6F212803BEC7657661423D640BA4019D983C2E16803E72AA3D9A83E7EB79076A3A02C9263E55C052ECAC18AE7C97339CDA6BC1AB6261F533B25D831A4FDCB41CAC346F4B6666D6C2BEC0BB158448A374A84F0009550B39BAADA23E0C331A315505B28265719CEA80C8FC1803449346B7EE7A6D215BDA924AE81ABC7CD8666B521C5E76012B84754EF50738297AA0093A9CD4AA4391146B149BB9A90BBFFC1638CDAADCAE019B05A9017E82C9E39A0A8C2B72702AF10FC3C1222621D9B2AAF70302C1A603B704CE79992EB608A16373139D35782966ADBD71A1DD261D4D42CA8117F1AC66793A54C9263137A159C1320512FF19C5CCC855F8B2D6A106186A69B036878382B94D0510E34F01383126A5C3393D9015F36E4CD0CD1458F88744E03C7B3060BD5A3380296860C388769639F888A4FD1EB389F2214F90B68BAB87712493DB9980C92148281584DBD51094C99BE4DE54223F11D1C1AC6EC5139AE66344990CB22392440B556DD8901440545AF815C2DCA70078A6E320B4274E76C6B50073302AC67D348839037A1E1491A79517D6B902E532575475731FC2D3404552BAB025E037B5AB476E2E18B5CB0092B215D31998F9EA5361E9AA4A2CC5E99808BC0909BCD42485E725383824E31394C7BFAA8095C0D6D92973C30A1D4B8AE6BDB72AE28CA6FA132DE11C20ABB0BC6C311A1541BCE0C67F06B43F3607AE0447C6D59C2E2B0233AA037D74CA4BF8174D9EC833506A0F74A03766A4FB2A84957F7CAEDBA27AECA2FC347794CEAB2E482B61F8451F7888E0A94078BDB7D3A791452CA8AF76A86F6822D12A123799C0D02D7BC16D63DB5993A1CA88F29B9A33BD662790BBBADE71242319C75FA29891552FD0BB5E3636C254BB9913A678616CE72E6CB14F82D1449C65E3170EF29947F598A57C508847556940820BE751C538617546B9D25044214382F80A9768FB7A4519B34E3B1B417520B6FB60899E5086168B07246AADFA7C3D5505791BCBDFFB9B18DC3B86F3488D51A9D2BDC3326A0ADD86C2E4A7AABF67CB5FA77BC66482DC20106EB1B40FC3C6CEEE2B46347AC3373AA6490AF74957C75F18510D017F8CCBAEDC50E20F6A619436EEF562657BA42944A50D7BC446EEB1792A47A8BD13C0855BDAAA168AB93B877A8B9BCE30F2403CA80E9499A279E9DD67C8E0B3297C4A13EEA1757912F83D842974188F6122ECF754D8566BEAD96794B372EDE02C77EFAC0AD616B032269FE73ACB4C585DB91C29BF12A07B9BAB21770ED4423E03C09F348C2D9946479F835A183748FB09F630AAC93F22E7E56C7FD47328A0A125CB1B6B58A92BC3CC643685EC769203D4447FDCA7F3CD64FEC5B5636E8C5C2AB8E1C58B858431E7DC163A177A4ADA5C3EB7A45AF9558325A5068748E14B9CEFBEC4C4E751B0D6CB46D6CA1E38605732826299CB44DD1B275E540E82B427A0AB6909BBF583B67CF0A4CEDC168FE1345E7218E7027345DA80754758119630783299CE9E054AF3A42015C12F8B733BC508F45F4AD23260FA7B6C15BF638EFBA86BE2C91EB799D7DB45E80A004D2A3BA403C8FBE30713F8C59C990968B01AE16231B0DA74CBFC6788E8107F8F9701A9B6B01F05D964539605B87CAD36A2797604D3983D0603F7522650312A8707B64F1F4CB12D8886321BF7EA1CC28F3982554A3FFC664B706C76C429785EAC08D8954517315A418A5181A2D882C51623C9E3623545BBA8511C8BC0993880391BCEFF86A1A0717D09079A1C33C8E9C97BB520F273684B2AAC1D4D18A70D490AD0A6627F0537460C095364A42FA08A21AA6D24255F6B2660B1B64839846F6E030814CC58B188FD1D142D4DC3EC5319773817287BB82EE5479E0A1A498E1671D615162D8C5B38068B61643832300FDA1B41EABA31C5B9854A07B62D8B2C591898D92BEE3CC01710810EEF3288FEB3AD9FB58F8D39F76A128EFC764FE269345EC76613AB0A41A746496445FA705AEA7094A4542A8E59F83B380E0BC663247765C3B685B33C01CE7806C80B037D22053E6B155D516CCF2C010F97575527AC7A5C3D1AA659063AF2D0B0D915A1B57C75C707A179DA035A9E1CA3977B91B8330930C4DB5BB2501308747C0A709C34142A3627BCC2952FCA704D749E3723231672187DC1CF61C087A2384FE727BE3AA7BB00240E2395B979B0DFB942D38F3825E72132395348F7C6CBBB800C3C36F8D4818D584C19B124AC9A728F7714760625B5FE877C855BE65B01BBCE106A24A2F7208696E73A22A27522566AADD8A73A050914D362932902B8EC8429E5C15CB427965A87F190625B6452C32896F5776318B15CE2F3060B296A1BB72A4C36B5CFCC97B9668713F85575280C1085B20D8B35D993A27DBECB97DB218B3C297B2E60C8384B05DF2C7234C750669864A5BBF76B9902F00A05BD74429E57C1B24C6BC28B6BE2C9EA5C46213D92C4E29828DC1B10DA786106106C7689014009B1B1C022093AB7CECB44D4998B0838BBF912C81269051655691135B5CD45F84D715F3C7B56DA3314D8783BD0213C0C33038116042D116DCA41226974483C6A4B8A2A319690FC0B1BEDCA3B02925A01C13472AB9A565778D44242D3B1776FDFC15C4F18969623E01A453AFBC6F2C264CFEAA8AD14B39F96B55EEDA5CB8C391A31773CBAA77E8481B5E26ABC8D531430B0931F36BAF28B96FD646AFB71195D58BFA1686A7247F9A69A5D20C0516694E760451199A25A55457BD17674B4CBFC5AA201ED0CCE77172951170342B3091181DC22B7CA70213D1A38B9D757E87961A4CA7BC18E22F0626850A794E3C2A5D5B5001B194486CC0CC880467FCF3B55B3492648980C961333E6C516EC4298A7112EFE459E1E92E821C74D5896CDF229A798529EB5048088A4D9BB105E54C006A5C15A46923909272AFA9B6D5661F7AF16D15A698D213C51D771AD0B14BA641C762A292EC5A0372D63501B5BECBF24731E283DC4246E3652A5544BFE99A71D05974CC2B9DDCB34C0AD9A057BA0DF05C56B8854E01655BAF21C9A5079983D322525049E0811103A101BB5163538C6D27A477B4F956F1967F445A2201D990D304755EEA5482813089A3C4911031FB49B380D13FA2C8A9BA7B439B3B5102B832DC65387FA41B4E7959BCB790ABC51BC30608555A80C4984AF0B80CBD1829FAB6C7BAF870D7B76D55672456FC3AAE7A4FA466CF64B43482E53DFFD1862F32BE3718CE4D21C57B894A3193C3DC8A03A34AA37806B6A35925C365EB01B4835F4AE98AC69080870E7CC14BB77F9E61F9A7C46CE816743B511574AFA4B490BF2BF55221114A9D72B3A08273BECED57093AFA374BCDA7E84F949E12F1BD8E51F4FC3C51DE0A7B314BCC004", + "C0B435307F5CBA65E17F2FF8B2D435874A6D44F618EC5FA2444FDDD281E5E19EF12ADBFF26371451A27E40210635659274BDAC8B6A3377DCCA959D19BB3EEFACDF814D131740F6B7EE6C97320E5E7901A55E3D274B5D5B9557274E566AAA2A8DBF9541BCA317AFF7EB383BC6B89A1A542885249A263B1F33DA8C1B2D034FB0B0E680085437733F4644B8E197C9BC25147E79080F1AFA644A507A2C0D2962B6E9A43C9DE42078AF346FB5CA30447AD74CF93A43846C1ABFABB56758C7233BD410823F88B29E6B492BEDBCF18AC3B8A6801E0C188E044E3353DD858D2E4D0D9AD90CFA137D4149C9C44176E7B2F7A38FBCDED715DE5526885ADA3717D265DE4768EC121C01997E4F9DCBE796D26D047145E24FB62EE5ED154A170624F22DBD89E01D1C7C156D4DFB2C6F6D6DFB7577F561C5D41D5E0AFB1B638B26DB8118C99AE5AC8D827AC7F5072B1EF56FB13EB070C01AFF1C92A97842C9580B7390B40D156017A85E15E7992611D980F5D6982DA0657AACAB159722AA9852833E82E7409328253E925A1E67F6925576C33AC518523AEA22283B8EB7F0C9D6BA161756DDA12052068B6016A4D553175E33D35147A12D0B058F2424B1F499DD18AC9C58EE1B037111ED00989D188871269F9C0A397A293C20BEE7BA7F09C8866152A1410482D3D7466AAAC16B73328FDA74DD670A815E1BEDAC97D90D0F6264C0CC7D10956E9D0E2640EEFD7357BA98B53EFC932B650E24FD7C43C9984545B117E1ABC15E25D72DCBCF4469596585CA642AAF1F14CE711B0360BBC5C8FA08CC092983213CD5CE94F7ECDFC438B360FC46591141B3E6DB212125306C86CD9896D251C404AEF8816DAD9A2762B4518AF3D73FE44C45CC2C85A2C695D5C7209BAA7F5B99B229C9D5B51E4C3DA8D98721951B60813CC011B6016B92CD1D835EE17FF6E4B1BA511016D5816791317BF85F410C9A753A2A31E5C60AAA85BDC0F7ABAB63114F47A3AD35D3FA4C7F3434C4D6F40DC8C5B7A328CCBC8CD7375A690A812C091B3523B0B4725B9EF932911A558470D43FCE3D74F41898C7E325FB244D1AF27803B06259C31049F45BD6AC5B4AA3266D008A56EA2F6952982CAACFB9D228560D366ADA3F30C51594B986676C014FCB09A6671003000B7613A4934CED2D5ED6FDB824CDD22333C7212FF28C03D6FEEA1B824004C0854C8AF138B6FF35CABD555894CF1052199280174BB3112762758E5C9FD235FB46F21A3DA88840B9DDD2C1A8E11B302369D7E8BE8A39246782B57DF531452F9DF9E02AF353E6860A740B983852DEE17DA7995D821F8B02935F2459FB2E0E75C86646972915849BD191C0EA731F4B21E68AA2377006CDFAC3DF9DF98E1F47B8757F2BD3AE409CD7BDF35E9813248E016D75615EFEAA2F4FFACE232AC496B6BD0F2A91641766BF3BD461EE9429A917FD49E44AEDD43E2FDFB9C20868D8B89AB288756152ABB563344A05C309B20A2EBA905D8C27518D1801F6B428109ED7922474019B44FC08E7B84E377E373174655B8F0338", + "E8AF40EF16C053F4CE74A82AFD44AD3129FF72FB6CCE84A4C8F729B66133BCB7", + "F3430DAE17CEDEF4BBACBDECDE7FEE0522D6A9AF90ED76B13B312E7A86E5310F", + }, + { + 28, + "62387A619993B5D9567CE83676994DC54C46D6E380A8944E0DC60DD6312460E698CFA6AEC0AB440C3A3D93F21F65A05B42933CA538108BE5235F8A69B505CC2ED84CC0606EEC5B1449BC5F12048074F05985429AFB6C9FBB7140C28019BF783CE9877749516CFC76969925A09451A277893CE3280AD7DACED8C66615E074D4716103CA5C10757027995F8F98ACD141AFDB4CB1531089525BAA01B906BA4ABD0D6C186ADC3B96691965A883034C53E50268BF8A6CD954115FF64D6C9630CEF8288E6395C2EA011CBB3170F79DFA0759F1D1CDC475059FFCC375DBA3AD9B064230833060C803182957F80EC9772DC235A58B16C38A365176F3147C5B524AA13E2296A36F2964C1638FD8F7570CC572D7D69E2B81985C580A9B8BBFF08A3FCC757FEE6040FDEC7852A32A05D51D8FF338C0730242CA6DB2A4BAEE99C29749A33E1396FC680B6A7589089477DCF44FEA18AA2C925900203200AD14585239F0727B4387530BF68F7E077E62A108581C8127FB1C5DBB5E04582E746894D2516995434D81380FFD374A0641776B218E38C978622A5BE4EC6072E38A1F0289B060C2DEC91E7F763702F17803A58F094250D2E1670C7B90F0778365E8BFEB2B3556B168FE55B854089E52E0A2B8D832DA57AE355091C876AE67B367CA866A1C3597AD4CCF4E5B2FCE0788593141D2C0CB6B3C9B80D3445F992EBFE98F26DA68D03669503A0EC696B430B2217ED9C4B6CB22BB3A91D6AA6C1A2A3F3615BF67C5B420EABE7159363F525E5AF9049D4C0C9ED197DD7B6F8B4C2F6604C8CEA75771A3AEEC0C0E8D1C8A6AE94FC8485206217CF785677E34C458795D6303403F7AA427763C5ADC4DFFC8C2FD422870B876FAAC6D334931F0D760ACF4092A0727F1935855B20698B5C8B2879576A25D9A9B93862079DC02CC30C731FDB7021C9631C43230A6FC6DDA1956F3EA01DCAA64354060CD4C67E1149915A423FCE3166272B2E3B71E3BB509AE978D88859B55E50D338B703E1B1D77D44212E00D510695974A07B8250053B7129889769B4991B5083E05D0054FC4CEA18C0A4291298578BD53B11D174C6FAEBB6E5919B2E7BB7A4CA9C94E45ACC1674D5D994D2AF81CAAC6A153334A1B770937F14DC2696721665F87751B608C921B985215F40EB28BA632533119618A97D0766FD7807318571EF8305175724E54CA261A03B8243B7D1087F70A39A7FCBB0B223CBD11AEFEA9BBA4833FBED98ED48120B3CB507C2C272E7A6EE6C92EE5401B65345182C31901F1C0A82743C2512A392C3B6BB73FADD89131FBB65610C9407C62860B7EF894CD353C02E347424185C27A06209914444FBA9C751CC27D50527531B282A6C503594571CA89759C345632AD2AC24DAE862EE091C4F421194ACCC9F45792B69C12215B878A4ACE975B295C672288472A2B5CB3865C787F7A3DB60608E648A11E6BBCA38456F93CB4CE2C31FF1B660A096832E518B6B7032C20ADB3830E982430EFE661C3E50802487CEEDB0FD8A004A7263483F1B41227B96C207EB1B2131082CD85244310AB6D09D4A73D436BF3E8A275ABBEAD804968F9A5ADF2264F897463D71FB09A797EA500B671C0062C04B911606293B8C7BA6186C2A2DDF074C2A6F36D35796EF0FF1CF8E47273048F82A8F00AA9040D87CB0586D968", + "47D60DEDCB1A694A5F55A066C4E61A4305346BFA8F03BA2FC7E01456471A9C1C02B2F471A3DB9D10056FC048652C545621000DB032386D2ACDFD81119F781CA0D14DCFA78C700B700BBCC55D3A3951BB82FC662F1B26C9A4F360EE266CA49585E696625D9787CEEA807E8506B4A38302969298BCB15A4219460C653A965370D14CD7874E711791CE719880F4A61E086A560457B1B002A9B739292A4459CB415F76A4597C2325599BCF18CB44E7CF3F53091054CD3024543494ADE226BE9D4541930AB14CB82268863B9B569EA528BAF3601FAB2831F1C29B4C4A712699B144CCBC63C90FB1E1779046CBA85AB61E23A97E57477FD84F022B20839B2F967B761CF3916BCB2D088850BFE2BD3CC40DFC5170CD7086F1D61380F338EFA2BEF1950315635BAB527745790495692664D1A55B3912DCF0AC797291F98249EE91660FDC8A67407F47C3B39B0857F1E1A2C0082D3716C0F7D0AD35229A795A3EE41A130BE53BE1173A94D3BC712B9F4CD86D5594A2C87A25642311ED45230B1839DB7050B8CC7D2DE99D90E32D5C2C9DE82BC56FA850886C6B549CA86E480E3373092B5ACB628C497DE3A789167DE055740B9C3960A52E1C54A99CB5B4221B7B1B90BA04E0949B39735F8956C30748C0CA7D6641CA97B87C30E7BF57E02166175E411A046645CF4B1C63D7C383A46A957A74593BA694E2446462231FACCA0BFA3B19AA469022C89B48D436BF4C4411549115D44857B27718754A2E7A025A693C858A594D52AE839465188693D8D7A449F3747CA88B0429795331AC53B565A0BC44B13B7D73FAC5AF606BBBEB73BDA8429136C3EB77928A3879507A6212216E6060095E2294F2C6B708883877D73970D306072B411ACA1A51EC1B64DA86F71098C46C4C2908C9AAB11B4D5BAFF7B205F1A1772454450F2A84723B87BB4A6B7CD5B07300A2EFF9A2A4BABE32320B7D268EBCCA1C42BBCEB9964950B71F507A2070F73BEFE482F4BB4750D2C6868AB1A42C6BD731A85EC036329B122E937B2786961B76060AA0961971C90887B468A23A3CE8C21E2AC2B6C6613F0539AB48946E6920211894CE970E9F671FC3105BFC7A7655A667E5A6775EA970BDD37D289489090A292E8CBB72D78A86F697752CAF79AB6840684356CB2E5D632C64B986A4174B05CC3DAED01F4CD286413CA8486AC310C61797815797433C38CA6352504237486C11CB383AC3C1A957586EF69BCF52A091F10884A515474BC423C11630446E1F7B98D7906FC4295F00473094A01B325A246875A90704B44349A460F6663BD6BE2B954CEA2794DC802AB6160400634726D07B40157A63DBAAC7EC5D371502E51C0A99461B8FD23FE6492684E76466C661C886BDCC16ABACB56E1DE361E6F4864BE32EC3B58558444C612B91351A7AAB3C415D475CF2A86EDAA91BB9B5B7FF8184824B2D9D36509E00348366483B0080883786F2353CDF459100D9AA21B279189C4652F24071C9BBAA5A697B03756EA3AB084407E41CCD2B66B044D83070D49AFD0716FC79AA0820BAD5CCCF26379DF4432E87C4A8C5B85F181222333BC7D7E5CE35252CE42CB518BBBFF00BA03959989370974388B7F0B64D6697B631145E9C5036D8174362387A619993B5D9567CE83676994DC54C46D6E380A8944E0DC60DD6312460E698CFA6AEC0AB440C3A3D93F21F65A05B42933CA538108BE5235F8A69B505CC2ED84CC0606EEC5B1449BC5F12048074F05985429AFB6C9FBB7140C28019BF783CE9877749516CFC76969925A09451A277893CE3280AD7DACED8C66615E074D4716103CA5C10757027995F8F98ACD141AFDB4CB1531089525BAA01B906BA4ABD0D6C186ADC3B96691965A883034C53E50268BF8A6CD954115FF64D6C9630CEF8288E6395C2EA011CBB3170F79DFA0759F1D1CDC475059FFCC375DBA3AD9B064230833060C803182957F80EC9772DC235A58B16C38A365176F3147C5B524AA13E2296A36F2964C1638FD8F7570CC572D7D69E2B81985C580A9B8BBFF08A3FCC757FEE6040FDEC7852A32A05D51D8FF338C0730242CA6DB2A4BAEE99C29749A33E1396FC680B6A7589089477DCF44FEA18AA2C925900203200AD14585239F0727B4387530BF68F7E077E62A108581C8127FB1C5DBB5E04582E746894D2516995434D81380FFD374A0641776B218E38C978622A5BE4EC6072E38A1F0289B060C2DEC91E7F763702F17803A58F094250D2E1670C7B90F0778365E8BFEB2B3556B168FE55B854089E52E0A2B8D832DA57AE355091C876AE67B367CA866A1C3597AD4CCF4E5B2FCE0788593141D2C0CB6B3C9B80D3445F992EBFE98F26DA68D03669503A0EC696B430B2217ED9C4B6CB22BB3A91D6AA6C1A2A3F3615BF67C5B420EABE7159363F525E5AF9049D4C0C9ED197DD7B6F8B4C2F6604C8CEA75771A3AEEC0C0E8D1C8A6AE94FC8485206217CF785677E34C458795D6303403F7AA427763C5ADC4DFFC8C2FD422870B876FAAC6D334931F0D760ACF4092A0727F1935855B20698B5C8B2879576A25D9A9B93862079DC02CC30C731FDB7021C9631C43230A6FC6DDA1956F3EA01DCAA64354060CD4C67E1149915A423FCE3166272B2E3B71E3BB509AE978D88859B55E50D338B703E1B1D77D44212E00D510695974A07B8250053B7129889769B4991B5083E05D0054FC4CEA18C0A4291298578BD53B11D174C6FAEBB6E5919B2E7BB7A4CA9C94E45ACC1674D5D994D2AF81CAAC6A153334A1B770937F14DC2696721665F87751B608C921B985215F40EB28BA632533119618A97D0766FD7807318571EF8305175724E54CA261A03B8243B7D1087F70A39A7FCBB0B223CBD11AEFEA9BBA4833FBED98ED48120B3CB507C2C272E7A6EE6C92EE5401B65345182C31901F1C0A82743C2512A392C3B6BB73FADD89131FBB65610C9407C62860B7EF894CD353C02E347424185C27A06209914444FBA9C751CC27D50527531B282A6C503594571CA89759C345632AD2AC24DAE862EE091C4F421194ACCC9F45792B69C12215B878A4ACE975B295C672288472A2B5CB3865C787F7A3DB60608E648A11E6BBCA38456F93CB4CE2C31FF1B660A096832E518B6B7032C20ADB3830E982430EFE661C3E50802487CEEDB0FD8A004A7263483F1B41227B96C207EB1B2131082CD85244310AB6D09D4A73D436BF3E8A275ABBEAD804968F9A5ADF2264F897463D71FB09A797EA500B671C0062C04B911606293B8C7BA6186C2A2DDF074C2A6F36D35796EF0FF1CF8E47273048F82A8F00AA9040D87CB0586D96885B34A8FF209DDD6CAF616A2883C143BFDF14DB231E98CB119C2656D1175BD0FE1A8F78CBF6F01A3878C8B0C421B83005D834A668E52F982855D5C943A1D93EE", + "FD8CF594954FE5FC75D774B4390EC1678B676770C602B7C128070BD331D260AB21D1F99C726729B6FBF8D699EA133B1665F63DA83F737E938A41B865339D10ECD26940BE601870478E0D9E2FC2BA385BCCC0A786318D9600B5E8F269DEEAFCFCF61D5138FD905ACCB595D239F7643263DF754C55FECAB8F07DA82DFBF590EB6E175F6283879BF589B052B9CA2130B7025C47931A8AFDEF26B3843F6D2C701A7C3075EAF545D07E6280F261CE1EE3EB967F889651430C89693B6853F8B9FC32D758C6947ED409BA4FC00238E490059995A81F7013133C6BF4D508D30E3CEA08C0D84ADC2C1A465FF538405201B31CEEA71CD1568B98B17F4856571150674745C5858D770EA4D91E40148DD056C0D8C938B380FACCCB2743BB2D728C11F665717CA0C271316496331F8A2583FB1C2B3E53D668C3F485993F14DDF234B7AEFDC95FAFC99CAF0F44845E5D7F727B70036F5909C54B56B3CD842C89907408E600C14C7C00AC09540AEE9D25619CABBC3178184F2E9CDF0A3892FFDAA75C7EEB4F28CC9EA0AA8A1F63E4A9F9373075214C7B51D606B79F12E95D7645CD2D0158DF5D70C409FCB01ABDAD1FE33ADA0065F3DE67D49BFE9919C74B8941953B2864AC797A2CC5B2FD887B1C0C9FF1C9203D230EDB5A59DEF96F8BE673D1955422C7C16FB7EADB3DB33748D0413E94A5F85D9A020733629F93574D8687D78AA1D97A10D9B1392166B1E0D0B6F235CB8EB8A5CA56981511B2805D7728589F75E5065E40B911CC7253B67012B6370222BC1EEA263424C307F3F4BDE39EFF597F31787164545EBD1387BF88A717F1B5194947A5CA44BDE7A176D50D1EA7E372D95737606743A714E64CF4C1ABF8A2DF7BDC9A15A8925C502E2DEAC0CB2CF127E25DD002D4D36785A3042E8712943CF3E9441DAFF33855A8F2E1DBAFB723FC789A174ACAA808E574020B252C3E99B0B170FDF0C68AD6039D593B3B3B96E408AF85E7CBE60B275F4C0E3E1C0E793DCBEEE8B8A8BD3BAB92B263C0F6AD2971F462537ED9ABC092CE6FE617B33BF731EDD9DF66D6E626E5F173CBE0B246B3A0D977B28CAB98B5EE4857C7E620525F51D8F6A83668768CFD68E5B8EC2E0A1A17B4BA5792CBFCFECF0962DCFEE00E220B438CD75F672B2BE7E17EA85D4070E12D2EA716D9710E52F9025324BAE8A76DA34DBF531629411CACDD7D5CC982FBF2BD6D5D481CE758A431C6908092B77750DDE6859F1E09620B535857B082E72416F39B7169D57C1151593976F5782609B5CF4BDA74D9C8654E3AA8C420540ADC732593AAAAAFFDDCE25CD9B554B9EA31900A93E06C5CC761F592074425083A4C7746D05B138E234DF6AB7FE0B24A900E03DE7AC166DBEF702E5920D023DC828655609C9CC0EFA2297BB26C439889B099B3C7642323719F6B28E8C7E8D92FC92B69772ABBE5DCC47EF31C37BFCA8EED7BDEF7941A986576001009DBEA7213A096D1A51A3F27DDEB653160837914AF4F960009E541ABD0A669DCC54FD6261D4D3F3371E8BB3B59C8E38EF4EE", + "D5722BD3F338D3A4AAACBDE95E2768BFDCA819F719BADD1C8FAACF859FE65EA0", + "DEAE13A40F060C5A154826D5FDAC7C271E2A7C8BD3FCC211027F4B7C2E67C0FC", + }, + { + 29, + "C17292BC117476F81C319CC2523875F5309794D2B7AA85AC7AFC08AED5B283F5A16A7274F80A484D9292D440B4DD4537C0E37040835ABA5562314069E5262E8218C191686459E80201379760D55054F11D53647135A5A62972C7EC5C9A426A8D8FD9C712B4B510A670CF54A1CE3127EAB80A3F8BB0AA228E5C22A3794767CC040DF13764CB6953FEA762269BC8D5D169619C80E0160D990A3BB41297086110050145D8EA48BF503C4C6414BE732B2BA83F583B3EDE75C486D56949A2AEBC82459526BBA71525301C178B2B0FB5A7C2CAC480473280D50BCBDDA21C876B3A6B99BD32C27A1A081E36BA72DEC599FA976DA3180DEC4A8B134196E5E01122A266CFD79AABBA26F18336DF74593E296CDC0A770EB80F08D3290607AE95867D1FA5A05E4521910304D20320145219F3737AEEA8C5A94A63AADC06ECE1AAD714C25C244360DC49FD79847FFB2D0BA20DE374A1B5F54DA9B3A0EF5824E339BC58B552FDDC72045B1FD8FBA920874DB50961C3D4BF0D1A1212EC58C9720DA1FCCF3D7711EB4A5C205744B3AC0AD571AC8D9C345ABC571E65112686361402393FEA6712DC323D88132699738B89287388388A149638993629F8B59AE623ABC37FABC385D36CADBD6C6F54B97310A06BB5323C42A66F973237A626AFCE620DCB88967BE7C8BAC479D6C1887201BB44E1A75AC351754135A81C799F26358B9C7948B564D6C88DCC075EEE7A256EC49861CB2E8F28422E362F4D6ACE1E94AA6CAA1E15DAC4D6F0683B156A71807CEE810915F4ADF68736387C7089B24355D21465A7BB71B3CCC5989C54C9B7C5B2823B496A2FD23A7A994A7788B91FA14D05306D86125F33F342A11AC53CC8838859CC0740B6CDDB9B2E53C4A2D13391967A5E3197998494699AC84028B751399ACE998E26B818B94CBEF78AC97170990431BA7BA22B8891CDCBF62413D585AD6488CCDA194DF89222699BC77A71B43A80A95336F978967116B7059153CF344B02B0CFD78BAE7547849DD106A3170A99FBCE07067D06354A0E86C6D106A2F513A839660CA2942A51D9A463027EC2E5B574B795D59BC3BFA406B6A99BED62A63B37B27C5B40C45871C5CB503D062E255C48D2835FDF89467250CCC4EAA32DEC2B05C04E555C5FBCE3CDC6C855E6545062B4C8D4599D83717F03124727E45B5383BD3AF08144E7BADF062F1F2009336078A7A62CF2706642B2BDA323C43495CD8C067D8914640CB8660759970F665F8E282C9CAC647D709AD600AB8B67C9DE7A36A1482C2A94AA1E214CEE1A68566649CA5B8553A6404934219BF1C07657C82F48C3BDB00BC0B394327C75102BC10B5756AD404930CA4DC8ABA2486875AFA58BD81B83F08B9FEDF802626518B9441C0049A98CB0C8E5F132A0738ACBF323B3577EC906940871947F856C591047C1C66883F040A6995117B8AB51E5B88207A84E3C7239266B13790C561876C99C64DA8B9465627C59FB1F740951C906B540B592BD6467E16324594974E6933C6E509708B56D41766D4A59597BCB5AB902B86CA3B4F8AC29DD4CA359A8C3CC70563B0CAB3E559C3F1507F54C2FA61A383D217071C4C704514150AACBD9B02000A48DE2605AFC8A142B49B95CCA44A9CEDE035327B2BC1B770BC0C3EE6DBD74FC9D3948A46E4429FF6D3B5CC6A018", + "28618473E6A45C1040379B1842365919C38BC8C99DD01287415A0B9D953DF81C49041609E1FB5743A7B9343B0BC83AA84D110549C97C009D17591808B99A5EC9FB9BAF1C35579BA4A7885BF99325C31182B68B69DE0B53646A1BBD642C02E11E454B5D9D738D65EA6808C2CDA62C45C7007ADD85B71F429B288A02B0BC7BD9A37FF1514872377DF280989B0111F8A0919059B233CAAD62AC17EB74AADFE27A0A9BCA4549AC8FA442DE966AD3D14CE3D6B8318CB427C73801818441AA732B166795225AA6C41C9FA42FCE7B5F3BC67102A828E3B39AFA3AA685849A96D8CA4FAC010BB2914F43A45DE731BD86C2C2C6ADDB511D6E8A8B36B183A7C63969F70A57CA461BF62F42A4322201BA64F8894A3276227836D97B14E2A80D380B418628CB2CB6A6E17647D97AA023874263ACC546F14429D7B6DDB41E96A19AD5B3011AD8C442536B4972A1BD1B428B828016B842A7D90DF1D73106E1C8ED01627C6B02BC362D796870973B96850A709E48967910C4FAD9C7CBBC547E339FD144AA6878B130B8A1D9035959BCA8906278975875627799B342C2FEA83B04D322F4004F0F9ACD5900449DAB8514B7719D3537CA86AC0E77B19737B0B10540B43266A33742897B8B0EF473F1A4C63BD18EEBF45C064921F87218DA36C7D7C27DF489386408C0735C3AAE980571B02D760B0A0D486E9685164E9599A382912FDA21990CC23342283BFB87D168BFBB4ABF42AA762A477F08758D871B8C819A08DC1A3125D188CD8681FB08B45248615C2973A47B155E13375917484DF1B548889508B0CC633CBA71D6BC842097C937441EEB3104C50BD4945DF723720784A7222CAC29CC302B66C7987C8BDA037FD3A75F67F8AAE2318C71E14F9868CDD49833A9520B440607431C562E4B23E7215B043B62EF51655272935FF61BE6393A13ECAB8307ADB2A58531361D4EAC2F58C8A64E089EDD5B5035C45FAE557E3945089EE8B330579BEF2169C2E3C596E08DB3C97463CA4D0D553829066DE23AB97ECB2D242B13222CAF60C3C7C437B85B289B415086DB4711208B5E48D77155D667B0109821A0932AF950D48A7ED8961D7388252CB21C0AA8260DB8A9A1E0277382206DE1C0BB933526C254498778EE14694D01307695030EF17DFBE6287E35613BC301F946924C7C349B7BC62F3407A0804579D461A3CA7D6F318FC7687755966968D73275B79F5874AC3EF936786272449A2FD6E7B8000A58BDC2388803C7EE94A0138B1D51B313EE3503C19A675AD25C8A4081C98A1B3A9201D0A3C9767921491BB703E5097FF04E2B749AD7CA37682C68EDB769AB7C76F6F623659549567563E0244C3ADBBB9706774C225CFF4832C962CE58E09DF6673F8E88A848B270A7892B1C447F28044CB3C231F1692E76549069D6762257048AEA42AC37B0EA930DED6534C6FAC43D80754BF067DDC717D7D541FBDA2F0CB67A97DC2E1D6B6FE79CA738B57029AC54E1EC95CE832F0FE12B6AB2B6BDF6287FEBAE32667DB2669B94F74D5031082C54CB1E6AA60314453514067A6B8D43BBA41B8B1CF1C6686636A5DD47B35302660FFC95EE00085AF31C15DA091922AEEA66A30873C511C45CA3804079269E9690591F2436C17292BC117476F81C319CC2523875F5309794D2B7AA85AC7AFC08AED5B283F5A16A7274F80A484D9292D440B4DD4537C0E37040835ABA5562314069E5262E8218C191686459E80201379760D55054F11D53647135A5A62972C7EC5C9A426A8D8FD9C712B4B510A670CF54A1CE3127EAB80A3F8BB0AA228E5C22A3794767CC040DF13764CB6953FEA762269BC8D5D169619C80E0160D990A3BB41297086110050145D8EA48BF503C4C6414BE732B2BA83F583B3EDE75C486D56949A2AEBC82459526BBA71525301C178B2B0FB5A7C2CAC480473280D50BCBDDA21C876B3A6B99BD32C27A1A081E36BA72DEC599FA976DA3180DEC4A8B134196E5E01122A266CFD79AABBA26F18336DF74593E296CDC0A770EB80F08D3290607AE95867D1FA5A05E4521910304D20320145219F3737AEEA8C5A94A63AADC06ECE1AAD714C25C244360DC49FD79847FFB2D0BA20DE374A1B5F54DA9B3A0EF5824E339BC58B552FDDC72045B1FD8FBA920874DB50961C3D4BF0D1A1212EC58C9720DA1FCCF3D7711EB4A5C205744B3AC0AD571AC8D9C345ABC571E65112686361402393FEA6712DC323D88132699738B89287388388A149638993629F8B59AE623ABC37FABC385D36CADBD6C6F54B97310A06BB5323C42A66F973237A626AFCE620DCB88967BE7C8BAC479D6C1887201BB44E1A75AC351754135A81C799F26358B9C7948B564D6C88DCC075EEE7A256EC49861CB2E8F28422E362F4D6ACE1E94AA6CAA1E15DAC4D6F0683B156A71807CEE810915F4ADF68736387C7089B24355D21465A7BB71B3CCC5989C54C9B7C5B2823B496A2FD23A7A994A7788B91FA14D05306D86125F33F342A11AC53CC8838859CC0740B6CDDB9B2E53C4A2D13391967A5E3197998494699AC84028B751399ACE998E26B818B94CBEF78AC97170990431BA7BA22B8891CDCBF62413D585AD6488CCDA194DF89222699BC77A71B43A80A95336F978967116B7059153CF344B02B0CFD78BAE7547849DD106A3170A99FBCE07067D06354A0E86C6D106A2F513A839660CA2942A51D9A463027EC2E5B574B795D59BC3BFA406B6A99BED62A63B37B27C5B40C45871C5CB503D062E255C48D2835FDF89467250CCC4EAA32DEC2B05C04E555C5FBCE3CDC6C855E6545062B4C8D4599D83717F03124727E45B5383BD3AF08144E7BADF062F1F2009336078A7A62CF2706642B2BDA323C43495CD8C067D8914640CB8660759970F665F8E282C9CAC647D709AD600AB8B67C9DE7A36A1482C2A94AA1E214CEE1A68566649CA5B8553A6404934219BF1C07657C82F48C3BDB00BC0B394327C75102BC10B5756AD404930CA4DC8ABA2486875AFA58BD81B83F08B9FEDF802626518B9441C0049A98CB0C8E5F132A0738ACBF323B3577EC906940871947F856C591047C1C66883F040A6995117B8AB51E5B88207A84E3C7239266B13790C561876C99C64DA8B9465627C59FB1F740951C906B540B592BD6467E16324594974E6933C6E509708B56D41766D4A59597BCB5AB902B86CA3B4F8AC29DD4CA359A8C3CC70563B0CAB3E559C3F1507F54C2FA61A383D217071C4C704514150AACBD9B02000A48DE2605AFC8A142B49B95CCA44A9CEDE035327B2BC1B770BC0C3EE6DBD74FC9D3948A46E4429FF6D3B5CC6A018A390D967C94B4967EC22008DB7FBFF797DDDA62D6ABF1A67A99B82DF587B1294DBB1C363EB427CB804DC543D969492479C0A876B43A9D2F095D4F8FC890096EF", + "6E77D8BF4CD2AE8E84DE27558C41CF8B63DBF07295C86F83DCD9CF4C4E0AC14503AB3AF4491225037291A7F0BF6B51DC2E3966E093FA8B217C35407F2C5408730D2FFBA39E6DDE33150F804A36EFF671534FC99DB0A76C8516CEB28431CCCE0ECB54D66F73CB283DA9635F5E6496BA0D3BFF33F6DE9AC9F780964368E3D0175E64BFC2DAE50544789CAC7BC439642E678361D99FA6DE85252E72B39DBC5F32B3B45BBFFE90D53032B00A8E831316CB1858FF3EC0EC4E22459AF83D8AA09BBC6823DF580723466260351ACD562C9F8E8AEB82587F4DDD7D26644A990C71E2A6385F648ACCB25FBCCB31704753E9C0BFCB71A433A51C3C0DA5118A7A7E69F3C8AB0EE3F7116DD50DC5C18002AE10179093C25340B00506206443D1D8C9210CEA277F7BBAEF4BD5097237B7D0DE65D524DD6C7D467B8162EF2BE60AA304082799D4253785A65F9CC5D75413FD0ADD22E9019A9CFC7D3060DD86AA10A218B050E6C2B12EECD5646C539E3D7D87B06896D3D6AB902EDC8D162AC34C5C6C2DE155D1094BCEA464E90393C63BC6FFAB862CBC4F68E899E0872B52A403D37A21810EA9D0FE376397C6A1848D225DE562A968C16177045F86FEB2F53D19A95E674DE446FA87E0BCAC9EBF77243E4AF6C55B0585CD1ADC300888AB14CA518192F63DE4786AC9FBD83DF4D794932D5027B17F75B32941251A87CB2958B92494E33B5BAB965B212E557B0647492F361A371298FB7C4D424731C28ED389B90EF3EAF5F71E601227F8BD915EBB8063B95911EE50AC3BA994E5AD96BAABAABB32D0E7948BB91468F9C8D5EADE554728CA2794322A72CE728B80C2D8D306B6B5C7C3D867748BF413128B0AF6587CA9424226254C8D2BE7C6174038CC926FFAB6139BF1674DB51F1C86333DB231543169E12A1828D16C11C9A8040C02BFC1B92300C3FE3B38FC01AA7A0E7C73890F934A5DFDF16720A536F01AD67531049E705C2A909E7FAF4979D2E39DF685271026C6F7E91B5A891034059827FB57651F1750388C1908613A8F0E7C0BCC85291C18A52883A1A6FD65B56C8BC9A058F1031916B46AA8309C7239059179D72CD093EA624111DE89A7CED0B3C92975DB293179F6C0324E9CE674125F8B667C87812FC59683F59C07E9C4A793CD840EC6CB728DC1AA49CC78D7F1BB7D8466ED1D55AA45EACF6C6CFA250E4CB9F2B597E28D69F38F2BE852B2DB2257ACFFEE310E65A5CF90F4B3928850D5FB5B5057EDECD31F71A42AE075158C2AD90E5745712134D4232EA5054B81F4A70D3D970BDA0874449CE73A87188BC85D9EEA568C23FCBDF00B8051FFA5786D9B719B3BE00572BC994A6FAA576F283CE683434879DAEA7E9164E42E0D290532FCA15ED478BB1C4EB6D0E43801562E09292DDBBEC0B2DCA4F0F9DA0463FC81C4B2576B9900443294EA5EA8FCEB18AC3177B9F53B7FCC8B68B02BCD8AC97DCFCA990830DCA3DC473AE12CB56B634FE1D7497BC360E16922331D3266B798BFAA8AE75155837EEF39B28A42DD3F706A052292889D", + "63094ADED32451C9C6EE792A7495715B0AA36191F3C0C80E92E9D2BE48301483", + "C4316DD26DF672F2C733AFA75904229B722CEC9C4A5ADAA24C6DAA6D6FA57194", + }, +} + +func TestEncapsulate768ACVP(t *testing.T) { + for i, tc := range encap768InternalProjectionCases { + ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + m, _ := hex.DecodeString(tc.m) + + ek1, err := NewEncapsulationKey768(ek) + if err != nil { + t.Fatalf("case %d: NewEncapsulationKey768: %v", i, err) + } + if !bytes.Equal(ek1.Bytes(), ek) { + t.Fatalf("case %d: encapsulation key mismatch", i) + } + k1, c1 := ek1.EncapsulateInternal((*[32]byte)(m)) + if !bytes.Equal(c1, c) { + t.Fatalf("case %d: ciphertext mismatch", i) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + + dk1, err := NewDecapsulationKey768(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey768: %v", i, err) + } + if !bytes.Equal(dk1.Bytes(), dk) { + t.Fatalf("case %d: decapsulation key mismatch", i) + } + + k2, err := dk1.Decapsulate(c) + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k2, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } +} + +// https://github.com/usnistgov/ACVP-Server/blob/master/gen-val/json-files/ML-KEM-encapDecap-FIPS203/internalProjection.json +var decap768InternalProjectionCases = []struct { + tcId int + ek string + dk string + c string + k string + success bool +}{ + { + 86, + "1F2962ECA97FE4A63D64804B5B259F2025197F0B0EA9996326D24736553ABF113594E122AAC2C62936C1BD75689E240CED9BCE098212067776023A4954376EAA342687195E548160835224283A4606207C6C939376030C91BC3FCD6466810C5FA361488824B35C693DD8FB56DE2412B86A9ADB28384F908B23ABBD28307CCF834D3CB29F3E1AAC65806F4084974CD7037E2B84F645733FD660D3C246076706856B2415B0A234A29A76532DD6D6320BD06AA459768FB067C7B28B8C89258C2354EC5926D23BA388C14E39A63F61D23D705B1B850C58804856934892DAA452007B2ED01C6196995C55C647F3F58BE2D27AF8845C2334AD27687A25E50BB6BC90058B181D7197C5331088B7AC1D18038AA9920145887247C805102B95F57235783A4847130E47566A60469575651F0137608395D010567B7866C8505E6DBB6F6728959C128C382414FA53C461850E7783CCA1B4265008642581A06BB0C410D2A791D0977669188C6231A4A3390DAC1B15243E41D57843C53E309A3DEE5BB0991A85109A716800866259054FE1CC59FB6A96A75C8503517EFB5517E8170376512A53C861FA35AC8C4B5E63756247042690BD67BB8E0C7C9E8F05C5CE48CAB0F1425E329072F87401634211241738C2CE97208C6A6AB609F03EF13387AC6C08A3FA61E53632677507711A25C1FA3454D86D7BFC75E668100D6A6A03086142275383E291097104356A0848724BD95C3CD89CCECE97421BDC67B0FC8AA0E0C2460B4AADD9033CFC60EF04AA6F39566E89793DDC0481109E532566D90525EDE57E8B756FDA7190BB89937DABBE2AF4239961CFDAD37F1E4B5BED5A4CB5637163840716A1CE2F2036975A640B6A7EF308690D66750CA05B9058772828BC458AA9CE47116EF459B01C292010B7449207EDE7AA38AB20F10280E316298E9C86B3B4B94E718B85798D5567C96531A6CF84BC806AB85E156A34447B15FA9FA5BBA5F1087189BB9D4561885B2547DBC5BC081C1A268A9EBEE5CB1D4147545044D11933F43871ADA5495C894793645B0BC472407BCDC5ECCE19F219C98532882381739B94E2366BAE2C543E68116BC30974149E59318DF3336204C22063E4481E688FE2634D790773E243A325AACA07AC65EED5BBB092B6371B519BC01067849E6BA3C5D9B4AF5D073BC84BB472C123F2C72C4B191C85405D63C148B368399ECCC42F208F62E75E14408BEB14101EE4249D2418C359AB05E5A3B60A5465637EC6A31419EAAB9458A285F684404C4A94425FF6806150C7111AD2A77C0616D45CAE9024BE5DE259FFD6C3C0E509DF484F2D219D5F3CB596BB763D4BA6C3521789FC840142A42D510F14EA44B3E9B573A49F9B0879CA5A5E0606639A7C67A7674124E2423339126FB019E00BBB5031A1AB9B3A71190459A16EBB0A196DD9CD5A152179082AFA9436B647A68111439DC325EA777BCA84ADDBD62FDFB61009A54FEB495A166BB35C9C426A678E6E2958BF52138F68711D623B41C7B4A97A241B17B3C37720FBF04BA3C175C96A4CED4C3FD0E4CFBB345A0CF48A7C0870E4353CA02B92A204A9C03A6287BA8E1CFA50B91C2E2674CF93382A72E8B9E0214504B97C18D60351F689392A8E44AA2C57DAAABE4CE52E3CDC2BB436DCBDECEBAE89BD2E392F0370092A66B4F0", + "BDB614FFC9BA80A3BBC1899EC855448E2734BBA74116181D1503CFF0B497C832B75998A5CBA94C76C2CCA25904F574080DFCB7B4E64D5D7547B2C2C5E4CAAE550098BA50BD6DA47EFD10A67C2502A8C7B42F4B2696846FD849706C014A6CD8B828012B4BF41BCE0041E27A4245231B954B958B1400A7721FDDE369C488BE6AF043A8C0A9CD0CBBE132A2F303BB57A4ADFA02CB12717B2EEA905381B110E61A6B506B45392F45313C51756D80B27B9398C1F3F1A74655AC08AB6801F651A1A378DE128F63D4BACE138330025FA3882BAA630C98A070395697D4756D7F0521EDFA065D36A7491B53A371080F97CA1FA0B93E544DBEE4815B92AC2EFB9005113D583A731E17B713A68373D5AF07E32520B1958FD9643B4CA70E601472002141A12424726114AC1FA65801C8659846E8A1D42C53BB0A3BECACB43161C7DB707351485258C4C373754E9774AC09482CE92A24EBE959973A5E12606440777B22471C3EC015F2673EA3B4AB844778EAEA88302446EE790C2E954B29738A90287EFDA113125170488964EA551451955656A788BFE4C58A7AC4A9F2BFA2DB2C91C69DBB332FC4B231DBF1B4AA914BC63C1C37380C2395874EC1C517F0C79DD0B85054668D93CB2922917F9593D49292353C8A0208A26C37738EF146E92A2E10C49904F324AFE1820EFA0AC5E5A4AB3585A097B6D35C88DD57B5AC0241C21A5DE1A581D5416C11CB1AF1B79374C746FFFA4DF658CC6881BCD5C875E925223AE8A56924422EE9091B21BEBE841C37B8506B097E3F3BC20342AE00307745AACED7090547C1882D32C60BDA724CD156ABF8A11DA1BB054A8BAB5C44B474084FA13FE2949310EB64239338BEB36BD86ACC923C4320510220545D8EEC2F77EA364870B58404126B501CAB238BACA52040DA2037D541D574A6E7C30B9877AD540512BE2B41AAF888295B1A38C0CCE4F023BFEA82EC0217BE5A7D6A6515C399ADE1AA911336718524AD916A186FB488DA793D214486F0E231AAB6075C0A04D6406286684D65484B8520438F021FB4C55AFC5A4C35D97F0036480E65A8EF894C938B42ED023A4B966D235479012594BAEA06E6E86DF73C9DFB000F748C8140AB49341B5952F5862EB73E8503B4125997384B11208972D1DA3D320064459B6D714A4151696A843B872626B051D219F8238BFE480182D93F70752319B8AAF7360494038BE5700F2E9B86E83812744989B4921469F8A4B41C682ED329203B00257A950BD5435B224E4699729CE58D3855293C62C44EEA5D2CF96B4063A10C8ACE2DEB53FA79B8A1E77F955A6A48B38119FA6B8106301A25849F9030C86A2889BC75C147C90D6525DC532745391E717C70DB15007423C06E8BAA23277FC896B757536C2BA5A2605397E015A84AB216635B2366C7BF1364CF87D3CC61A31A57A6949AF77AD5990ED54122D4082813754079765C999266C3E961E674C1F7D03F9D258C1976950A120E0E31CF6239362FF38E395935B5E29D40C5BFBBDC63C25855A372C3656C243AA135E8492F3890615CC75B3A25221DB72670E465E1B45128B131395B4F0AE3BB09E94965983FDF6A54AF087AE4F32DD7837AEBF893212306D8B6AC1765758466908A3A641F2962ECA97FE4A63D64804B5B259F2025197F0B0EA9996326D24736553ABF113594E122AAC2C62936C1BD75689E240CED9BCE098212067776023A4954376EAA342687195E548160835224283A4606207C6C939376030C91BC3FCD6466810C5FA361488824B35C693DD8FB56DE2412B86A9ADB28384F908B23ABBD28307CCF834D3CB29F3E1AAC65806F4084974CD7037E2B84F645733FD660D3C246076706856B2415B0A234A29A76532DD6D6320BD06AA459768FB067C7B28B8C89258C2354EC5926D23BA388C14E39A63F61D23D705B1B850C58804856934892DAA452007B2ED01C6196995C55C647F3F58BE2D27AF8845C2334AD27687A25E50BB6BC90058B181D7197C5331088B7AC1D18038AA9920145887247C805102B95F57235783A4847130E47566A60469575651F0137608395D010567B7866C8505E6DBB6F6728959C128C382414FA53C461850E7783CCA1B4265008642581A06BB0C410D2A791D0977669188C6231A4A3390DAC1B15243E41D57843C53E309A3DEE5BB0991A85109A716800866259054FE1CC59FB6A96A75C8503517EFB5517E8170376512A53C861FA35AC8C4B5E63756247042690BD67BB8E0C7C9E8F05C5CE48CAB0F1425E329072F87401634211241738C2CE97208C6A6AB609F03EF13387AC6C08A3FA61E53632677507711A25C1FA3454D86D7BFC75E668100D6A6A03086142275383E291097104356A0848724BD95C3CD89CCECE97421BDC67B0FC8AA0E0C2460B4AADD9033CFC60EF04AA6F39566E89793DDC0481109E532566D90525EDE57E8B756FDA7190BB89937DABBE2AF4239961CFDAD37F1E4B5BED5A4CB5637163840716A1CE2F2036975A640B6A7EF308690D66750CA05B9058772828BC458AA9CE47116EF459B01C292010B7449207EDE7AA38AB20F10280E316298E9C86B3B4B94E718B85798D5567C96531A6CF84BC806AB85E156A34447B15FA9FA5BBA5F1087189BB9D4561885B2547DBC5BC081C1A268A9EBEE5CB1D4147545044D11933F43871ADA5495C894793645B0BC472407BCDC5ECCE19F219C98532882381739B94E2366BAE2C543E68116BC30974149E59318DF3336204C22063E4481E688FE2634D790773E243A325AACA07AC65EED5BBB092B6371B519BC01067849E6BA3C5D9B4AF5D073BC84BB472C123F2C72C4B191C85405D63C148B368399ECCC42F208F62E75E14408BEB14101EE4249D2418C359AB05E5A3B60A5465637EC6A31419EAAB9458A285F684404C4A94425FF6806150C7111AD2A77C0616D45CAE9024BE5DE259FFD6C3C0E509DF484F2D219D5F3CB596BB763D4BA6C3521789FC840142A42D510F14EA44B3E9B573A49F9B0879CA5A5E0606639A7C67A7674124E2423339126FB019E00BBB5031A1AB9B3A71190459A16EBB0A196DD9CD5A152179082AFA9436B647A68111439DC325EA777BCA84ADDBD62FDFB61009A54FEB495A166BB35C9C426A678E6E2958BF52138F68711D623B41C7B4A97A241B17B3C37720FBF04BA3C175C96A4CED4C3FD0E4CFBB345A0CF48A7C0870E4353CA02B92A204A9C03A6287BA8E1CFA50B91C2E2674CF93382A72E8B9E0214504B97C18D60351F689392A8E44AA2C57DAAABE4CE52E3CDC2BB436DCBDECEBAE89BD2E392F0370092A66B4F02AC3FDAD8D8F210A271603D045F95EC94C2A38EF4985F2E72EDDF9EEA90CA3F0FDE3F9D49FA98B44BA6D37FC13524B7FF2435B88410255345590DA7870813873", + "E3C563F0E5A3E2A6FF16EC3630EA56BA17647ED1236BD957021549825E806DE8924E3AF298F5D3CF2D462E31082180D2C2964ED80D3E7D92E5227E01B9B740F1E2D111FB379AF7A92320EF8BAC44A2591C7438B7F916C1508C45106E2EE3629F28FD280D725C382D647C894E60C2B109843D5DB59ABFD52B4B98E86773CBC75C8989239C288A79AB1D6608FA6EF287BB0431391C0CFD73504AB24D10A2F8FA5F3C3F38D9BD48607889E160CAA09818830472038F9F6CC2F7AFDBC45870B434F421743E1E0FD019D5C41101D9DAC014472C4FEEF0A9EF980E7F276CC7F1E1513A64172428A4A091A4C9A6A009EE5621E37B179EBAAC0D122A8BF42AE861EA420473D2C8F9EF3F4EECA11AAF8863BC66310EE5233797134E849A01992149CDF637BE74F0292988572A38AC86959CB67DECF3B3C3BCCCF186F917476DEE06727EA819A51ED1EE6F3228F4A0322032759D74BEF9703BC185A59492A79A67109FD6786130AB0D41F346D59FEAB3F3FEEC1270904B1504FCF463E9B4B40FA9B5266582A8031F0B200954FD080E207C9B40608C499D49FF752D9D1A4FB5F91264085D9E1A4A1A1FC91B5C5B3DBFA5D1EF5FCDF289206F611610B948644CFF3632229AF44D2971E3FD85E88E6936DA82DDD8815EA1A5DD6DD3DFC03259F604FEFC59CD4F9FC6AB4E9D8001A747546455B9629CCDCF5C4058D57C20DB6C9EEBF7C4ECBF76784FC2C9A08B5414F02EE7C93A38201BDFF31068786FBF98654F0A32B188B891BC59D683E08BB2D6906594CD02B232DAE179BD5C10CA0F7323A0016876BD80AE16265AE3A96204665A01758FFA9EAD686D3BC22D92899633520437994D73EEAC9A2E55C1EBCC3D07229B2E5202DAC0285B2BD600437CFEFE98EC02B13977359FE4E51EA9FFCC287C88211BB220A3F0E52D5D97B547B2369CBBF46512DFC304D72EE1664C91BA5E75B2B1D089EA9C02BBB7E390FCB2EE60E56FCAFFC3E3FEEA01FD8D5212783BF71AA94BA94813325A0249E55BD4337B7BD662D6641DEA484962E04DB7B42522837991D1048E9785E3BC297B2106ACA4968DDC1D05BED81DCD061D21DCFDB82D79CE3853E8B2089A8FE150F3F34CA7BF540D8044D8E40CA3EFC66F1778EC0E131F922E28D8B62B4050042064360515B5A88032F82AC618C9356678CC77F3C4B3456C61462232D92094B29A139E975D1DBD2B2E5A8BD6AD7A3CB9EB9BC0C0144B2178EE709B2D8EFDE65FDF3630565FA83910C9C3CC179F4613467294FE778A33636AE26AA9C2EF2D0FF2D648D3F8E2B9E20D434EB8BE0AD4B57E87B92A6D3572D8DFCD38B2DF6D77C5C8C25612EB7A3E0C0791003DDDCFA723841749A6AB9A9A5AFB098E1A82390FEF7A0FED281D7DA116B830524B5B4319F93DB15081136B5B06F8FB289018B47BE3302C193C8236015CEAC96916E39E4E8C032398056DA8C57F7D0F0A0A88B8B91D5D4D2A37CC5F0B8935BA7FA7AC4A0E0D87ED501B6196B7A4014226667F0F0EBC221BAEB63C7947D8677BFD625403F99F74", + "F8497107CE0D948FF5BF6E039E3CC4EBFDF5E9409EF77171A1E4BB2165713A3B", + false, + }, + { + 87, + "6A3A01EA24AE603A99613B63596360748386C8F33EDAD3AD33C5620DBB58FE81B76B960A28C8A893F7930F2583AB0431CB15B6D0EA97EE76045FECB2FEE09BE411B8AB827236C67D65C4B548A43999134D4FA3735629CFBF3314EC955194C8BDAE1B6B32635F27427DF0C8103639CBF7301C4C36B896E853C2377C5C155C9DEC611D1B6BBABB6777B6A95DA79B5456007B30CED6BB4547056C7B08B703A774114B036DC081BC896BED96C8F7C8BEC4EC945CB78E21503F04F849C053A9B7E1CA5E080325972FF9310B12B04BFDDA31AEE5C45E7BA024133BE2B7B389D22D3C9AB4CA34A31FA902571ABD0C267DC23157787B368F1352BA6A0F9D04187AD29CDCBC382DCACE8E31869CC2C7A55097AECC218FA68509D289833988EDC26B23B4CE26954A15041465A7287E372CC6213F7988768D105030DACD01B73B5F2145020287ADB92D63DB8D8E16A231618D4DD97851335C68811789428E8A8280519A81A215774B103981614224E35592647823E633E951646A1912721C0AD5BC21DE84734E285653B0858E5B325699B5E31C027B266E7E82C6473072A9E49B0B8C28BCD163ABCC7A3F3251601B4719F2894F1089FBB37F1F760079C6286525551BE7AC73E52926B28FF3F89A663043CC3C5FE2BC0312C3A0E1206583633F0747013C42A541E5CD53142D19D06B7E930CE1F68485D700BFAC9A71977C37A28CB11C703AA04FE533B58F6826ACC1AFBF25CB7B0A7890C427FE7069120315313C0A45C18C2EEA868243B4F46A03622C911A98ABBD7C2EA3305A5237AE76838350EC1512314A7CFB19549283CF2866F3FC3F429168A461034830BC4FCC2A7259BA6A49B256CA9066086A1BFA64712A7E5EF40BCC758B5A600C2717216839BEBEF900EDFBA4126C89047328A9849E80A596FA930D666099A9978085508F9AE8CD1A1325B4082457045A923410C84A0EF382BEA75420883C4E3E43183E8B9BB02A7030051526055910A835AF9053BFD66B378CB978AAB14B629F88839F3A4A7F985BCFEE440939359C7FF98B668202149045F68418466C45E76517F9F7389A683B47897A608A2F85800F7795BE75799DFD597057FA3F86C84CD52A4140758AE45B51F80262DD56337562A974B942DCD20EA2BC5C52F225DAF5A80BE0AED045A7E812819F646D28B0578D042C8B77C8341B46102BC881A738DDBB43411B43C41813C4C28D75400BAD4902ECE7C899FC7FEEE32C78D70165DBBDD139BA3EB2210EE3C1F6BC2B31C8C73B1A54657346C3D322B243AF797A93E7129B17FBCD3C7B9CE49216472BB6DF1B2F41BA4EC3813EE6DB3898F19643C38FFAF3880101B08DE8370ABAA82D91BFDE31B8F78CB85474A3BF06844A233E65884303CABDD1BB52F23C01512871A6FB50DCD3A2417140DAB019A11BC10176C5C83BB138CC2BB45369FB74995613CCF71426CC45536586A9ECEB63877494C4789E62B33BE19B87AC40357DE584F11B236E389B26A5578232478E56B798FA77E6366EC15046B38B4BEE0255FB6AC0B8B928F9F3849417B7C76A396D0B58DE16C709E58D5C523DCB6C7EF74C60960886926B4F5B472A7D2B419DB06592691B0F7299EE970B8ECA7F74B1258AD17C087C8EA0F1AA37C40C631CFC95ABCC465F6315C54C7531FF559F19B952406FF1BE2C2C", + "DD30BACFE52565345BDB2157888B5694438242E2A2792B5E3696C516F8AD1D893DA3FA1907ABC9F0E627866C30B1BCB4887C7291D0381F2976C690ACFA571184112F11251E38C61DE8E727198515FF2696BC8002000D0C1AD542360652EBBA33F5F8A037F50E146400BF12247B73A7045369272753B2DB9A5B13614CA446634B9513D3A050C155E879537CEC6E93634B3DC067DEDB1A6A378DC9674AF3F09A6726B346F5A1EE069C1356626D7126380745C7B065D5528735B36355A8260F3216DED3CDB1F6780B8BBFA409B67A266F097429C0E808D1F2479617AF9A05712E7731298455611A66C2584C8888B691F23B574A463FA8CDAD2802CB14BF83AA31D5D946ED55097A24CA024696148A3126DB4E69AB82EDC9024C4734FE182E45E8766732750ED443726A578E39484A90721A28CBB0E03DD72931405516F53001D57C4CA4EA2E171CC27AD896F2A8C6A5877C5338374DAC8DF4D762C1D97BC735430B33CF62BBC2D0A0A3FA8401B54C0BB77A46FFE5AD73FC2A5D285A1137052ED028DB79988A4323D0F8105C58AD9FB47BED3A60A8811FA2C55B187AC7DADCC42AC09B2EEA3609A06A68EB47B28025419B9C702BCAF06C38C86C7640629C89D7A486990D62433A66327A687A4CCCFBBEF4C0947966C991573592B78E54E7ABD5A9CD17D6930933936E049121C568CC013D415C23CBB2125F5A3B1FF58FA795B4E16B8C44A94FFCC344D035BDEDC45698A2A205414416A49F4E2A06281C9D254462C070AA8B7974180B3FAF81A7257034B7752EA315A1F4DC64418470EFBA37B228C0E5826C40D31519865D0DD07004B21B0D57872FBA2A00245C2702CF2C8AB91C5A5AF8F40614FBB1279C66C4F2405B4221EDBB8B35FC5EB65707559A29A04939C5990633680771D76E302616DA2A5384D875E5F2C3ACD4C96FA74CF88090B22540478161BBC14B8B6121607239056022EA3947EF661D72B12227889C2E1C266A7A60C014BE19E0328817104027475CBC78CE2C85012017EDDC0F7CEA11C8EA72AD3338498292BEEA4B8A7129D71B1927FAC2A6921DFB87C425F9CCBDE2BDD01C685E3CAE52425070C5C00EC8745FA538E4F96F72D89214E042F19734D7E57952D0833C72276CC93D9991A1AD99AE0CB5886FB83367C967CA65A7AFD022DF404DB0625FBF2C50F0D8812FE2BB1DB4351C7CCA96018A07A6AA62255655A59319AA9A0F130387AC5EB3BA4591A68130523A6D023F57D7801C71BF2237A17FB3590B240FB03037675A56CB85A68EC0C4FBEC98A1C534EAA4C1AFA1B7A012AE871C2EA6B34B3E661A90713C66A0A527676143CAB465205ED21B1E82050343B62CCBF875A2B13D752AA9007426D52661E6E1C42446B7FA632332498B53DC68D780264E1638BE1B24C6323BFF427030130A3091772988789CFC65369C98CF6A6BCFDC99CB477D0D120531C8AF46613228CC687DB38E984C269FA225E61B4B5EDC4246849CE2578BC7519881055D00295DFB560152F435E7A800F5E2BF2299A2BEBA0F0EA63D2B3550AAE2518EE80948E9961BB74D5B5B8C65C2C7C2229158B017B9250B2200809C8466387831C08B791FCA10D2DB3800429B35279310403566934BABE1776A3A01EA24AE603A99613B63596360748386C8F33EDAD3AD33C5620DBB58FE81B76B960A28C8A893F7930F2583AB0431CB15B6D0EA97EE76045FECB2FEE09BE411B8AB827236C67D65C4B548A43999134D4FA3735629CFBF3314EC955194C8BDAE1B6B32635F27427DF0C8103639CBF7301C4C36B896E853C2377C5C155C9DEC611D1B6BBABB6777B6A95DA79B5456007B30CED6BB4547056C7B08B703A774114B036DC081BC896BED96C8F7C8BEC4EC945CB78E21503F04F849C053A9B7E1CA5E080325972FF9310B12B04BFDDA31AEE5C45E7BA024133BE2B7B389D22D3C9AB4CA34A31FA902571ABD0C267DC23157787B368F1352BA6A0F9D04187AD29CDCBC382DCACE8E31869CC2C7A55097AECC218FA68509D289833988EDC26B23B4CE26954A15041465A7287E372CC6213F7988768D105030DACD01B73B5F2145020287ADB92D63DB8D8E16A231618D4DD97851335C68811789428E8A8280519A81A215774B103981614224E35592647823E633E951646A1912721C0AD5BC21DE84734E285653B0858E5B325699B5E31C027B266E7E82C6473072A9E49B0B8C28BCD163ABCC7A3F3251601B4719F2894F1089FBB37F1F760079C6286525551BE7AC73E52926B28FF3F89A663043CC3C5FE2BC0312C3A0E1206583633F0747013C42A541E5CD53142D19D06B7E930CE1F68485D700BFAC9A71977C37A28CB11C703AA04FE533B58F6826ACC1AFBF25CB7B0A7890C427FE7069120315313C0A45C18C2EEA868243B4F46A03622C911A98ABBD7C2EA3305A5237AE76838350EC1512314A7CFB19549283CF2866F3FC3F429168A461034830BC4FCC2A7259BA6A49B256CA9066086A1BFA64712A7E5EF40BCC758B5A600C2717216839BEBEF900EDFBA4126C89047328A9849E80A596FA930D666099A9978085508F9AE8CD1A1325B4082457045A923410C84A0EF382BEA75420883C4E3E43183E8B9BB02A7030051526055910A835AF9053BFD66B378CB978AAB14B629F88839F3A4A7F985BCFEE440939359C7FF98B668202149045F68418466C45E76517F9F7389A683B47897A608A2F85800F7795BE75799DFD597057FA3F86C84CD52A4140758AE45B51F80262DD56337562A974B942DCD20EA2BC5C52F225DAF5A80BE0AED045A7E812819F646D28B0578D042C8B77C8341B46102BC881A738DDBB43411B43C41813C4C28D75400BAD4902ECE7C899FC7FEEE32C78D70165DBBDD139BA3EB2210EE3C1F6BC2B31C8C73B1A54657346C3D322B243AF797A93E7129B17FBCD3C7B9CE49216472BB6DF1B2F41BA4EC3813EE6DB3898F19643C38FFAF3880101B08DE8370ABAA82D91BFDE31B8F78CB85474A3BF06844A233E65884303CABDD1BB52F23C01512871A6FB50DCD3A2417140DAB019A11BC10176C5C83BB138CC2BB45369FB74995613CCF71426CC45536586A9ECEB63877494C4789E62B33BE19B87AC40357DE584F11B236E389B26A5578232478E56B798FA77E6366EC15046B38B4BEE0255FB6AC0B8B928F9F3849417B7C76A396D0B58DE16C709E58D5C523DCB6C7EF74C60960886926B4F5B472A7D2B419DB06592691B0F7299EE970B8ECA7F74B1258AD17C087C8EA0F1AA37C40C631CFC95ABCC465F6315C54C7531FF559F19B952406FF1BE2C2C55A5817F1A9B11AAEC3112029860E6400D99E57DF796A8B775C8C5060D4C3DF45BA1405A293656507D39C8FC3990BD23E6735715792EFF440A9BEC79C7F5FDE9", + "690D3DA51B1D22BA09AA62B4FD6EADFB4C587C34FC434C62225B9D132DEDFA5774B4ADFA213EDB328DFD6056CEE8F96EC1454C85F8037021F22879FF0BD7FE1386BF272021522F774F10C4C9F7A53E18E3A7023DEA1C3BC8BC76EA8FCD2BE2442A655F2E34F3F102919B4C336B177B038A0CDE0A2A49787E89AB005999F1AC733A04B867D557AFEC7502368CF8DED96CDA58FD0C0426767AC2EDF808F8A03B45D72B30D354C9792FEF5213E7928399F09E2DE540F4826ED72F86C02637738ACF28B28CBC8D45D5A7A0F4AC5A1B7CDA2FE6AE3872C6985E61E259311D5201A540B4351DA0E794F3BBAAB916E2603633741A7878BF61B3B344715D6DCD614ECDE5EDEDAB3DCF4DB03582CF12B764179ED88FCF5FDB0A550522468E04A5FCB297EA78C1DC6DB4CB868F7B1D0B195A16CADB15C28DFE7D2ACD9C2399B6461ED22D000F3368E88BF12F32BFE6B749B4665978C297314DB35122C7D04738E38F7B4CD8246FD36B93624CDC7F0BB10FB482479EF1E1FFB2F85094C9806D160467832D604FF7945CE2D6BCDF15802BC2CECB90FA3F109987BBB377B4E0B092B64F712E3605B3B11E3377453538EB28406243C9C182858650F70CA9AAE1B8E00E3699D2C47038F2AF7076CA6C2330542FE680D79C463F60F80192F85F6BFA0AD455712840F0E25DFDE29CB98A68FD2EB4DAE3C536967D5C4615788AD70A5451ACC78A5419979EB35F83E2DEC8A9EF3DF19A73225832FC6EF76A19159E38DE1BD15B6931F4D47C25024AB2DFB0A800EDB9E070E1FB3628B8236AD227AC3F6603F1866C4CFD669F992C50C06C1986868330C4889B3A9F77DEBC702F3A6337A549B1F51FFBB7201B04287DFA6BF3144160DED6206B5AA1F9A03CC0D56FC53EE55C8CD9A23344BCFBE1439904A3FE6A225D3DC16BF0BDE2B53F90CC0064C4A7DA29E93A84585161D6B367063DF1AD2639E94F0F61455225AA72C294936293831B7F221D209D30317FE4D68507822BF667C8175F23ACDB14000715D26AB3303C46D2E9741F03A73C5FF80B50E2750744D747E295A6DC2D6B9C5D12F0D711267F46E61D25388A5AD57F8ED0EF9CF99311A6A14D0EC5AA5BD8046755921930406B786BB015F4C5B1326650CF8A6EA2FFA758C4F3250F235CF4A824807BFF10BF54910A610B1361B156980ABDE86D087FA133CABE34FC5EED86C3332B86A84742ABF33144B8B08E3E22C845A5E0C2ABD786DA7A7E01DEE4558365857B384CC3F398D74E157CCDBEBF8ABA82AB7804303ED27E280BF68AC3122F6881B3F75C0F5BBB69F6C02C4712FF8B9D1822686CBC441A4C81BB54DF79C90E32A2C5464C0453954DE231AA0D01A0FBB868B4467D1AB6035A9F0C85C1C4016F586B7B6E653689331949380A00CC8ABBB265089606F65E324FEE9AF4CDF02B1F6685AFAA05E2DC9AA17AF7E8131D1F1E3D2FFDB9592C9C57CCDD619274D547FE2285A8989DE7926C654DCAED5613CB4DD4A2E0BBF9BAA7DA1A0F7628C924927EC3C339A38E73C219CC79EDB4904F97", + "8B6AA77088CC998C2E6B42ACA57DBF3FAB786DFF66ACB017B2ACB48FDBC73D07", + false, + }, + { + 89, + "BE0412D99A23CB207074F8A203A4421678ACFA3A225FA5C560055E8D380187EC6845DA3BD3611FD6E917DEE3CF2830C52C026BBA8A6A31578B942484F0079E88B2AC05D7C453D73D3716BEE80C698A323D2D5331B046B86AE007AD40CCE3927A75F4894432C7D235B4B6610A792920D5F0CBEA389235784EA91BA02BFBA238A79FA2ACB6B2A91DBA4BCD0678796D0BB4357B4BDD849762F4A7CBC69D163C7874C9ADC92B01EAE8AE616461CC89ADF41C415E484B94BA43FCE7AF7B520A598B35429809C109CD7C048B9C67C70124B597C1979A7A31B7156CEEF849C720B39406A32D1B639D18A1D0E462699181B9523FFF361AC995BF1127A4E5046C32F1A1E102CDFC431B060975A7437EB765BFE9A486CE6A52E3E33CEA47766EAA9BAFC819F248B0520313EDEB524CC6BE7771C75E96AA1096507CB18E9B101CBC21132A7337D78C0684E07C3AB8260E2070A3825EBC625F8851CC3E653011DB01DB07CC6B350A68652E31AB630FD373B75A1099A72C35441B9B6127B99469F95B503C663C9B3634A212936B3023FB4B50E8186619B00925639F52453979DB24D2B8CAACD61094D1B14C9597DF53339105C1A6D89BD6D626BD6C5D0E1BABF903CA8B4B48420A137B729A2EBAB17FB434EE258EA5F4CF9B9B1F58E85E343292981C0D02C4003F52B2F37CB3E7CA65AEB8A7F2EAC05634457581A9ECDC487223691263B696F8BD1D685770256AEA5A94DB60AE05A501835848E2F8CE8BD7CF5D1365E6120E18D456366B19E9F1ADE4C552050A02CEA89B8F99BF89B36C08232E13F79A25310934E23FA4ACA724761E12B32322F2744D93A6E4B0997B019C6857952369035F284D60D70581C593BF4B5749987B2E569941860E1619AD1D524469E987B7261F2274739699935291656D114525400984865B91895EC6C39EC54925BE2581D24A62D7B2785C2C697FD61C14C28D1A9568C9C331E3D749BA6597D9BC94CF391A7E3AA57C52C7ACBA8A6A85941BDC59CF57CA46336B25FB9BA93CCF8719A0057969A764399EF2CC3A81AA1C4534E195151389432BB43EC54A2DB2DAAEE068921A6A275333AF3A66C22E924C9CCBCD298663D6C4048BB09E8785387F8A291B3C15E72CAC44C6360EAB290F633FAAF4B023D000B03371EC885EBEE4684ED39A3BD03A390A0F7FC72F5E9B8574A23098404E7F4620C7A8411370B2AD8813DCC91011670FD3A29C088303725CB2425C783AE71539C75F96C1B1B395BD936C118B0BCA994627F32A6B53A187884453CAC606C39B81937670FA915A36F44FDF00D0AF0CBABA707A9FCC3672123D4EDBC1E7F30692400CE8E83E198A5CB692CED4363D7781095E12961BD27AFE1948C8F7259E336A2531082A2978C72BC67AD30BCA086C51A7BCD57B7B15F64E332C1F91AB6015105893917102530459C218DEEB58F5DC9DA5262B7D8A3159B43154F8597E5C57CF37400A2C560081B098DC2C20F6B5CCECC6BCF5B96184B0F7475076317C2EE5482ED83FE058108F762958E610041963937930C3AA3E6985423A33A07E77613F7236F0D9911AF49344A15D77C02A68F19C661704EF515871659F050251BF4CA9074A1A2B1A028FCB92E8053DFCAA319334C6443FDC1F8EADE4EF0C36949A1D36B440747C8D27DEF2FE62C033D6C17CBCC8C6", + "A7F68B43898F7383B09F0C6EE0D97377D454D528132DA90AC55819A461B58424BB95C7C00A246F04F8B03D3C9D62389AC2B45DD6D43F32072C86E03812909650C52A0104A42696C5E1F389EE4A8775A9A063737A5AB41D8C0C0B8C49C822D7351C1A63BE677D49018CF18311775926746520D650C1C31C0BDB6B570C96BA6B06915815C0FCF6B32F583E80C2B7A84B805ACA39F676426A463E59D6C904042761833CD4C0C0336B00D7E158DC774C5C58BD71458C7C74459682245A03B3C8B8522B7448C3A98432C48495E51E7B401EAA8669707C16F36B72FF21623838B31B496D56B12C0747587325A59509379D2706670C573F85ACA0F426119254D5519D759232C5087B84520F2459148DC6A86184662BC368BC2C83FE0CA7BE156C67C05025A1161BD1380DA39BCA2C99EF727B4DD90D99C21209B573803953129C4DC82A279ACA0A73833C5BE189187546519C2E17EA895CEC1EA7D46594297FF43CBFB7D54466B21F1DD4B72116C10B6198553AA0273CC6F38CA9F9011985012AB4F44C7DC13B00A10F646456D9B29CFDC368FA610C49F1BBF75B5759480A43CACAF99C8BBCB5873CE1CC0D091F6282CB6D1A9FC34459AD0333C88A1C7FA3132DD2B415F809235A101162120CE0BB53C435EED595E01273622CB6A33222477634104A64909A2CA19956D0E0CBDC6A8A611128DF161217856737100205E1B1E9EC844C447E4A4BC538157D5EF37A81F3BFC0F347CF14BD3F75C18F53A777CB0D381B0AECCCABE4005515C911A2C4C7123447DFC93964262FF05A45D969551240BAE60B41ED698AF8E5193BF409862759599C598AC63460F991D47A18A7E7A101964E756603D3A4747A10179CD77FFC1614B5B15D028B7F5794BF7E46B09DF68EEA6744ED117EB241B59095177314194E6C9BFB3049AF1CCE267880E810A855BC5A30177996C22CEBFB58A93331395C52C0809813A2600EB2CE358473DD9A25D8C149F515A5CE10335E0A716D1A09AA817ED6959E18B79ABA93A7CA25562E458A294B767AC5BC860756E2393DAF0507F185C7DE839D2E70885885973B561A660A8E497C0999F99D6C7C66AFA2C1A098A5088685F330969B3526D09758CC8964E3B543DDA6177312507730AEB98C3EA4E4C9478499DD676D2634A7BD69BC2FF8CAEC434BE2F58428334B4014255C4654BCC3A46B737E486C424DC019EF071FDFC817AB7B35362313D7787C73EA9B411CBABB5526441999C6E9ACDBD581987A4A88A421FBF42691A36629A37E7ACA126A7BC68A9BBDCF270F2EFC8E12C328B39A6C80D24496931C012769559A8042F0C2B81158AD77493949987295947AB5CA8EB13B2D668990291CCDD0703FC046C38301CFC350EAB92B9307A685D9BC43E4105F8BB286745E60A4A37E6966F2F7B558F8427DC17A122143C12271A276A0C6D28FF8410C236B68C8CC75D46B403F430716192A47FC7A8BC2CD11528870F7A5AB52A752609D42A05250538E1CC07B4E278CF2977D14F9BB1BA701D771915ED65903BB5CDBC5C1FCFA1D4B04A43AA4B48D946A5B124E3789AF2C34BBAD31A17839653E849E2B917230824887193568FAAC42F533CDF4B31009950EDB065CDC498536A3101876BE0412D99A23CB207074F8A203A4421678ACFA3A225FA5C560055E8D380187EC6845DA3BD3611FD6E917DEE3CF2830C52C026BBA8A6A31578B942484F0079E88B2AC05D7C453D73D3716BEE80C698A323D2D5331B046B86AE007AD40CCE3927A75F4894432C7D235B4B6610A792920D5F0CBEA389235784EA91BA02BFBA238A79FA2ACB6B2A91DBA4BCD0678796D0BB4357B4BDD849762F4A7CBC69D163C7874C9ADC92B01EAE8AE616461CC89ADF41C415E484B94BA43FCE7AF7B520A598B35429809C109CD7C048B9C67C70124B597C1979A7A31B7156CEEF849C720B39406A32D1B639D18A1D0E462699181B9523FFF361AC995BF1127A4E5046C32F1A1E102CDFC431B060975A7437EB765BFE9A486CE6A52E3E33CEA47766EAA9BAFC819F248B0520313EDEB524CC6BE7771C75E96AA1096507CB18E9B101CBC21132A7337D78C0684E07C3AB8260E2070A3825EBC625F8851CC3E653011DB01DB07CC6B350A68652E31AB630FD373B75A1099A72C35441B9B6127B99469F95B503C663C9B3634A212936B3023FB4B50E8186619B00925639F52453979DB24D2B8CAACD61094D1B14C9597DF53339105C1A6D89BD6D626BD6C5D0E1BABF903CA8B4B48420A137B729A2EBAB17FB434EE258EA5F4CF9B9B1F58E85E343292981C0D02C4003F52B2F37CB3E7CA65AEB8A7F2EAC05634457581A9ECDC487223691263B696F8BD1D685770256AEA5A94DB60AE05A501835848E2F8CE8BD7CF5D1365E6120E18D456366B19E9F1ADE4C552050A02CEA89B8F99BF89B36C08232E13F79A25310934E23FA4ACA724761E12B32322F2744D93A6E4B0997B019C6857952369035F284D60D70581C593BF4B5749987B2E569941860E1619AD1D524469E987B7261F2274739699935291656D114525400984865B91895EC6C39EC54925BE2581D24A62D7B2785C2C697FD61C14C28D1A9568C9C331E3D749BA6597D9BC94CF391A7E3AA57C52C7ACBA8A6A85941BDC59CF57CA46336B25FB9BA93CCF8719A0057969A764399EF2CC3A81AA1C4534E195151389432BB43EC54A2DB2DAAEE068921A6A275333AF3A66C22E924C9CCBCD298663D6C4048BB09E8785387F8A291B3C15E72CAC44C6360EAB290F633FAAF4B023D000B03371EC885EBEE4684ED39A3BD03A390A0F7FC72F5E9B8574A23098404E7F4620C7A8411370B2AD8813DCC91011670FD3A29C088303725CB2425C783AE71539C75F96C1B1B395BD936C118B0BCA994627F32A6B53A187884453CAC606C39B81937670FA915A36F44FDF00D0AF0CBABA707A9FCC3672123D4EDBC1E7F30692400CE8E83E198A5CB692CED4363D7781095E12961BD27AFE1948C8F7259E336A2531082A2978C72BC67AD30BCA086C51A7BCD57B7B15F64E332C1F91AB6015105893917102530459C218DEEB58F5DC9DA5262B7D8A3159B43154F8597E5C57CF37400A2C560081B098DC2C20F6B5CCECC6BCF5B96184B0F7475076317C2EE5482ED83FE058108F762958E610041963937930C3AA3E6985423A33A07E77613F7236F0D9911AF49344A15D77C02A68F19C661704EF515871659F050251BF4CA9074A1A2B1A028FCB92E8053DFCAA319334C6443FDC1F8EADE4EF0C36949A1D36B440747C8D27DEF2FE62C033D6C17CBCC8C60DF10A55B73E721350FF5FFDF8C9717103B3F242F12FCF292DE0CE75194E2E59678218BE4EAA47762D1ABE76C755C76249BC0DEA1C5C1625531BD1F86FA0AFDA", + "6C69EB30D9636EBA9EE14BC92EE730720D39E189AE3ABD96367580845A82F27C6C85F7135BC2471553CF1B0E0F092D87AEE9AE24649D6BC910A6C6B432F20993E562C5A1777BD801A7F1C05FD3A9E8DF57A187CF2637F99C6E24B5DCEAA64DB892796803DE9C1B6EB9E297440F7051D668F4781764F84CE499D6E7F6E4086C1F9B862260630E1B3E6C4BE52EC4FD99670A345659E4D865E5DBFD19D6412B4C9D31003D36EB9A1DE2ECD3A495B9E5A44D51BF85610EA867C9F4F1F09E10667A4919207628B2FD91330BADFD44CF491CADA6FA9AECDE5E20CE8AECC2986647F2656869E6201D851AC8176D98DCADC5ABD396B0C4B468871D0EB9332C264AC7550CBA62F77655110C4D15DA73A641F67A7821D7C7EA81CD4DD1BEFC5CC056883E74EB8DC985CA193ABEB30865A59C86167DFB51F96891D604577572D61623950D5CBC20F2752E6C65662950F7D69C4E6D883830326539DBEE1A1E683D74E1B261AD1D825D57FABC3512A405B172BE7AC22479E4D2F834009337726751CA617A95A6E915E50755FD644D600AB670EEB5F1CF097CC521A7EFE8A712E52FC91634EF0A3B87B91370444661674D9F1D0766DE59CEDC70CE0E78EC062F7A50DFA575897BB4A758FC7D49A92C6CDCA5020C0408A7830106529CCD6CBED34B0D5CA2AB68F27CA869ACCFF363BD546CD6EB6ED3589BBAE0D7E3101C71D63220140CE0146DC2A038B4C32749A4B99C3D9666688582984FBCC82F79FEF746180D05B8574FC42BE26E35961E01F2EB634CAC23BDA5D798C7264223524A040C9EF7D42673C7F6B0F2611467580EAAD9FFCAE0291962A98EC23C7351D620EA02723A6493E6354ABE0373FBC77DCDD022025F8C7F94C3476A0E400AA7414F6E50584E14B7DE84968F47083CB228924551191FA1031FEE39CF9A0494A18E34FE269F980A15594A6F68499FB01BCED9DAD187DB29842D928755F618D08B3F44A243257D7EBECADE361D47758A3E3B6E06601E4F409D2208F36397300978FE8173082851DEC328325BA05676848B9198E46966EEFDE2DC72417841E82F9848FFF6BE528F147648F881CF9718E6B1F7E0E20C6A1B37C5EED42DC6B982CB2232802CB71E83123DB105E6DD068BCBBDFA964C9955B2D93B7776EEBA03C5650C631D8F05AD84AD7D8190ACFFBA4977607AB8CB19788BDC7323A7A1D91B7AF8BE6A4386ADB47BEA4B4D93C01665CD605D5677B9A2B75A7BEE386FB1DABA09ED209915616BC544167B97C9E23DE77B757D5B98E63CCA9051B6D882313B4477D10B04128E6FBA7E1EAE23FBFB64C8D091EE233FCDEE91A0EA195CE511C3DF4B24C17B9D36389EF5CEB3BEE6D9A50ED35E3BB72E95040851A4AD2910CE6EC35A17AD1715DA7BD121190876203457836838B0415A53F62F9A6C4CB2B1B8E811FD78023FB5A119214D192B813E9B82B82574D36A5DFB1EA9A03D20ED2DF12A04944FB9BA283E0B290BA7370B5098A328AA70A1455CF166EC6A6DDC0704FCDE258E1F7363B4A46B4AD1B6E749D7CB8F", + "F5D303C2E38227359BA4A64727A9424DE88D41EEC961697385529C4587980D62", + true, + }, + { + 90, + "83E76806808ACAAA120286650F965DEDBC6E00E13563B5547A345981FBB177A6CD6598B358F56C3B272AA2106FC8901FEC0633391BA78DD344D17A219C3AA103A8B078BB3451D690D81AB22B858231850672A92270D95209F01123E6620B043F8B64ABF9252087365E51B8789B76995EE0098CB2AAEEA6760935A9390746EAD04270B91CB08B87ED1905335895D766CB8D229AE939A154A0680C583FD2D0CD71009D8D26A9B6F90C5A463125BC63ED2165BBC12AFD5866518110C4E809CED8932F5149EEA59FE806091906CE6D53AAD7045A69A74540C738B4E948FB470073D35DC6A536475AC8A037A1DB842D742A2D2A41C708C9125EA746D309981B51719CB31A7BA412DD11AD1C1C33C7810F016498EDBA6938D00BE7902E20AC9EFAC7C8E0A22FA5EA711B0ABBA13BBE5138A11244B81AE04C65987B7A5428430980BE57575881260B1780DAF3086C94AEDD0295A793414552B2168300A5A9B077F82FB8A2B697C7CAD06078184896C0D87BD5F8A6A967266E051A2E984B335A88D59285D06775B7F25C48F431A735BC9837AF8383365D8CC06052416B826B3A888314696B7CF67F20C2345CF1113A666A1A63B878610D24744FFD9B8F8A889F3E065ECE25827E137E33E56956FC60CC2423CC6AB8F272AF00179D98E014BE1A2D6DB202399B272CC33AFB2B0F1F502636CB1CB74279D66884F0169E2FE334F268ABDCA372B36084A784B34AD0998BD742263B2119C65527F61C11816724D577B1E77A911A5D9C005F10106A7E8C6292716E27B55EDA339A7A88C62AE166FC324BCB8529C4115969E1C28237977D70C950B31C50D036B0B5584273CCF9B87250666365029F3287429C69943E488C053240B2707EF6CA58D8C840766250853A4BDB938A258B004F0705F0DA8D2D97C753884319D7A2CBBCB357DBCC1134039D205BFDA3306F61C4E28C1271112353B827A2A98996135DBD76B04D4545DDD4B111184B2A787B326A3C0466159114A9BF6C801780BAABFBB578096B73245B3EAA17161530DA777306BA614AEC5D60376288AC7F0E66A4989B93920C37F18A2F91F045DA13A2E7777FB3922A48E9211962A4E9A47BFE467B707091AF86BF8CEB111EFC9745BAA22300C447C28181721AC8A7B51C9470A8F999B7E2268ADC327C160A3F42CCA0C9094DFB65BBAA5CB0153F85225E5C83AEAC55932F19B6A9E077E2BCA1E0221EF4191DD53940063799BEE5B0A5336B6D8324A1A6AC5A865071CAC33C6930974C0A54675BE7392B8227C09EB59706442618F68FCF5977D19C06B5EC6AB8D5C3F5712B96F20ED16A1E170B40219B003A5A453E5B58AB92A786FCAFAE855D3FA66724125D069251CD04277CB1B2065728E7B4437A9954CF711616C25156353194D336F75080BD706ADFFC51D1F7163662B0F84B625226249D15CF7C33206F5249E7325548149421620852F2BECEC6BA28476935BB0F27E181E9508B9838A51DC68EBC70653ED8B5E2D68DD9895F5223A418C49951B5CCCA8494D86AB2E6D773FBB55884E2AC16DC9418100A774548E4A95AF4265772E9A3A082CED78AB81CF15DCC1C24F7D4889CC606A137B29C31A1BDAC4FB050ACB1D28D9C07B3D9973DB9A763044A6F13AF75A46DC7D480380940F9D688DE684BDF867C21EF4BDBF50908DB04", + "37782D0B89341726615D95246DA633E2BCA9D71B477B77150CACB7BB5A0ED4565644A2565EBBC4AD742AC5718F3A63518D3818A3FC31DC5552EE033522622AB75027BCC39C741B3524E51A26D12C02628BC363AA0D908330B356A412911BDB8042768FACE36B02E5126DEB6592590AC5D867EF937A2BDC3A74930C183411342AB5D0E77031859F3877A1E71B9C50200EFA04730D5A223760A62BD4B1F0615A0EA902492404523AC1F485253CC10BAB97503808C565808DEB9B3176532975638923678002E232AD68666539407AE1CEF8129702C8B58E69060DB422C49B67426214F7DB20C2FC857F52B174D6C93BE90B4A58811EE27E453382E12918652CA9F0313A8BECB7D8D7C9EC4830142539BDD92582D00234503D6FD8CE12F806A5381232D6BF0F0391EF8A1B288038FCAA98956C546AF47327009CA65326521A553F2A4E3983B052B3420D20303D378C6AF110B3451CCD5A4AE4AA83F7B2C20BC27542318690FBB2F542A3A51930E04B9FB6109C15F72B825656586C8B76F54F5382ABFE34CF91471922C9BD2D600003156C621579A0C61626A7C2D0BB7AFF95863A89C3D4722916397601630DE0E158AA9B5974A95107E0ABB6A66308123241275613C210495CAD017202508464847B4BE8792D4DC9017BD94FCE873A30C0A587A16CC7941413785AB6969CC90C47235847AD4CB1769835F195871712AA467C5DF1B30D06BB907C6519CA4AA3DC9884262339D4441C078049995BAD49FB8FD95219EA3CCF17C17F97213AD862710A60CAED990D5CF01919764218D672D9B5050AF0524A7AC67B8A38A4FA5C755050F9431584970BF6775F2609559F3807FBCA6EB67B9C67F6B0A0B4B527D563BEB415AC4989473758E31BBDC8F517911B1E431B88B7E871EA33A052FB1D2536032C561DDEB6526E652E1DF338E9C179539C891421AAF6067F550052140B89E5942E42337A1DB8CC3D88760A1498D5F407497925238A79A0B469FB2BBA70D93195F4017E0008FCC65D0635C9FDBB74EC4C4C50455E1E5B893D03764861964A9C3703FBC6A6FA557D34AA5C352D67C370DE7608E83670A48692FEB4CA17B842BCC8AAC7B0AA4546A607157DD2529182703E8AF6818E836635C428E136BDEED6678F962B1CE847D993B5F7B57370390B74261909A3963264A21DD4886A812A44CA2CB82A06EBD7C1280CA993F542EB36CB656A140471195A56C750073906B6115DC17A93E79D7E6037883349EF3B0469E984B8D63BA932BC592A09643B7C675567B676B25E46AEBC61265872CE01F61F75E4AA086B82B2F51364C3065CD880078933F606B114DACEB97AC9B535115F8166B5E9C1E23C1BD6406534A0209DC96063632682185E8E8C6B86FA3B073AA765718C8E42BBCCC16D1137270374C12A076295B0B61060341148BD64BB5553D2AE933274A034CE5CD62DE96118947877A934A28AF22F5F3410722C22B4839DC72AC2DD8222FCFB918A81ABB2422B30313484292695478F521396114A8FF0FBCCFE94A028743A7FC34EB9DBAD283C891E6A18765812FFB658EA58842BBB1232EAABDB332BA111BE36100745A842F8F3B19809C2E3C554BA3B312042C17F298FE7C1910E6774B934C283E76806808ACAAA120286650F965DEDBC6E00E13563B5547A345981FBB177A6CD6598B358F56C3B272AA2106FC8901FEC0633391BA78DD344D17A219C3AA103A8B078BB3451D690D81AB22B858231850672A92270D95209F01123E6620B043F8B64ABF9252087365E51B8789B76995EE0098CB2AAEEA6760935A9390746EAD04270B91CB08B87ED1905335895D766CB8D229AE939A154A0680C583FD2D0CD71009D8D26A9B6F90C5A463125BC63ED2165BBC12AFD5866518110C4E809CED8932F5149EEA59FE806091906CE6D53AAD7045A69A74540C738B4E948FB470073D35DC6A536475AC8A037A1DB842D742A2D2A41C708C9125EA746D309981B51719CB31A7BA412DD11AD1C1C33C7810F016498EDBA6938D00BE7902E20AC9EFAC7C8E0A22FA5EA711B0ABBA13BBE5138A11244B81AE04C65987B7A5428430980BE57575881260B1780DAF3086C94AEDD0295A793414552B2168300A5A9B077F82FB8A2B697C7CAD06078184896C0D87BD5F8A6A967266E051A2E984B335A88D59285D06775B7F25C48F431A735BC9837AF8383365D8CC06052416B826B3A888314696B7CF67F20C2345CF1113A666A1A63B878610D24744FFD9B8F8A889F3E065ECE25827E137E33E56956FC60CC2423CC6AB8F272AF00179D98E014BE1A2D6DB202399B272CC33AFB2B0F1F502636CB1CB74279D66884F0169E2FE334F268ABDCA372B36084A784B34AD0998BD742263B2119C65527F61C11816724D577B1E77A911A5D9C005F10106A7E8C6292716E27B55EDA339A7A88C62AE166FC324BCB8529C4115969E1C28237977D70C950B31C50D036B0B5584273CCF9B87250666365029F3287429C69943E488C053240B2707EF6CA58D8C840766250853A4BDB938A258B004F0705F0DA8D2D97C753884319D7A2CBBCB357DBCC1134039D205BFDA3306F61C4E28C1271112353B827A2A98996135DBD76B04D4545DDD4B111184B2A787B326A3C0466159114A9BF6C801780BAABFBB578096B73245B3EAA17161530DA777306BA614AEC5D60376288AC7F0E66A4989B93920C37F18A2F91F045DA13A2E7777FB3922A48E9211962A4E9A47BFE467B707091AF86BF8CEB111EFC9745BAA22300C447C28181721AC8A7B51C9470A8F999B7E2268ADC327C160A3F42CCA0C9094DFB65BBAA5CB0153F85225E5C83AEAC55932F19B6A9E077E2BCA1E0221EF4191DD53940063799BEE5B0A5336B6D8324A1A6AC5A865071CAC33C6930974C0A54675BE7392B8227C09EB59706442618F68FCF5977D19C06B5EC6AB8D5C3F5712B96F20ED16A1E170B40219B003A5A453E5B58AB92A786FCAFAE855D3FA66724125D069251CD04277CB1B2065728E7B4437A9954CF711616C25156353194D336F75080BD706ADFFC51D1F7163662B0F84B625226249D15CF7C33206F5249E7325548149421620852F2BECEC6BA28476935BB0F27E181E9508B9838A51DC68EBC70653ED8B5E2D68DD9895F5223A418C49951B5CCCA8494D86AB2E6D773FBB55884E2AC16DC9418100A774548E4A95AF4265772E9A3A082CED78AB81CF15DCC1C24F7D4889CC606A137B29C31A1BDAC4FB050ACB1D28D9C07B3D9973DB9A763044A6F13AF75A46DC7D480380940F9D688DE684BDF867C21EF4BDBF50908DB04620C3BA3112D18C0B347C5A0CFE56E720693645641009674E2E2F6E5DA0447A798028E8157DBAAFD5B9370227C9D876818A9A4F7D23D9567BF413F3383F8F1AB", + "F58D542A5D03DD8D622D2D61E9CC759873479DB67359DAB471DE0C6945505EF8EF61734E84EF78D5A36EEA0FE8E6006B0741C415A225834C644AFB29E4CBF207C24F7760024BA27F1BFAB5A9EC712C3A81FC2F232736BA2CA69AA2F9CA7AC17EEB89A193493800951C71812E4E2134680B3BE6FFCE9508C791EB19494D86F9192990D0745AEFED3A83B24A6E7C8C2BA6CB536D2710C49D2C3122D090B95E57C8E1DECDBAD54BF7CC1E76E79448EBAAD77284E35FA03567FC692D2D8E674D7D805899BD00C35615FAC52298BAE7D87C7CAFA3734E95FC8C7EABD0C3224BBDF0F32B61491C1C060DCDD2492516CC4419DAD6AFEF3B96A1504A9F0CE09F1774E607F3E9AC4484F41B7AF8BE402EFB12680AE98B71557562A8C1D3486C5998515D9BF87CD173F5A79BA90EEEDDFF7E0F323D8920C41A80320CCFB2157A0E9148FAFDA7FD68828E5B612B373342872F3FDB0F77F2339D96BB49D2F5E172E0523C04DE0E3C80C89F5640B84FD8B372AFB3319150D7057A9293EA36A53275FC778BE736F468AB4FDDCEF2CB40D42AB8C834B5DC08C1E3B9E7CFEA61373A46595FD2E3DCC164C7B5212F98D3AAD9FE7E08585E0111265ED71F94725E3E4D9873487C407C74B03EE254EC8D9B041D817E6E9D0827626438822595FF14503D0257F04D99352C5BB09DEC0696804260516684308C1B7B60D86A27F724D10080FA4A2B70C90090C9D3DBBB2D681EA2EDF35A73AEB3A77E4338C5631E0842AC53137379E8A653291CDE9CC3421B2417A0C59A1A3E4AF60F455FB01A65E9BCD415B77D6486495D453BA9ED0C79237313D89019FAA1BF89EF849A752389CC2A8D1E677DB68A4E4CD81A2CB9F09E0531C11BB6FCF0FAD67507A86B174F45C033B1988C6B433E9E66CA7CA69A39446FFE2B219C9A9B3CB808C067E781765991665FB40B5780E307B39C370EF01C6000660827D5CAD3E1BBD28BCE05AC0443A2806EFB93CCA9C7ED3C18FA6D58C99C5E6B6AF785636384643BD2E24F6A705012AD7A4D2E6B94CC9908C086F13515C42EFC4CDEFEB2645D106AE385D4014B983DF6A099438954586928D46476A2E70E36147B8F57DC498DC564C62CAB6C00638895D864438E8C3FB075449A7636BAD20A0C8BD433BA1C6B4E3E8E42ABD6AFD23CD6D76A4DEEBF898F699094F1E2EC5E857142C0EC0FE724F040A02CF191D2DDC0A4880D89A0ECAFF00BFD2E26588180EC93B6E3ACE0D45573CBE0B836A9547D12F00BE77B059B0F73FF36827033795F877CD347ABEE7222697440A8EE6F9F9D4747C7DF26C92CAD12747AC6442066CBEEE4B072F5E1809724B462BDD86309A08C96E23D8BDC7006129C41CD1524E9DB13475F8DBB58B9F8EE722FFEFE0628AFD711A1A66CC03F7A1D26A321093179877A1E9CB223CD688F65772C1F550C86FD9025B58F6A1DC9CEC65816EBFB2CB405526F797A500B52EA73AFE592E7A63BE79D6E2ABFE03D528154C0E8D277C844EFA398B346D96F41376FD969356114854C1997D53189404F499165", + "44CC405E3BB0A9F51A180A86213D036ED58AA0D226804E717B9C2A475DE58E75", + true, + }, +} + +func TestDecapsulate768ACVP(t *testing.T) { + for i, tc := range decap768InternalProjectionCases { + //ek, _ := hex.DecodeString(tc.ek) + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + k, _ := hex.DecodeString(tc.k) + + dk1, err := NewDecapsulationKey768(dk) + if err != nil { + t.Fatalf("case %d: NewDecapsulationKey768: %v", i, err) + } + if !tc.success { + c[0] ^= 0xFF // Corrupt the ciphertext to force failure + } + k1, err := dk1.Decapsulate(c) + if tc.success { + if err != nil { + t.Fatalf("case %d: Decapsulate: %v", i, err) + } + if !bytes.Equal(k1, k) { + t.Fatalf("case %d: shared secret mismatch", i) + } + } else { + if bytes.Equal(k1, k) { + t.Fatalf("case %d: decapsulation should have failed", i) + } + } + } +} + +func BenchmarkKeyGen768(b *testing.B) { + var seed [64]byte + rand.Read(seed[:]) + b.ResetTimer() + for b.Loop() { + if _, err := NewDecapsulationKeyFromSeed768(seed[:]); err != nil { + b.Fatalf("NewDecapsulationKeyFromSeed768 failed: %v", err) + } + } +} + + +func BenchmarkEncapsulateInternal768(b *testing.B) { + tc := encap768InternalProjectionCases[0] + ek, _ := hex.DecodeString(tc.ek) + m, _ := hex.DecodeString(tc.m) + ek1, err := NewEncapsulationKey768(ek) + if err != nil { + b.Fatalf("NewEncapsulationKey768: %v", err) + } + + b.ResetTimer() + for b.Loop() { + ek1.EncapsulateInternal((*[32]byte)(m)) + } +} + +func BenchmarkDecapsulate768(b *testing.B) { + tc := decap768InternalProjectionCases[0] + dk, _ := hex.DecodeString(tc.dk) + c, _ := hex.DecodeString(tc.c) + + dk1, err := NewDecapsulationKey768(dk) + if err != nil { + b.Fatalf("NewDecapsulationKey768: %v", err) + } + + b.ResetTimer() + for b.Loop() { + if _, err := dk1.Decapsulate(c); err != nil { + b.Fatalf("Decapsulate: %v", err) + } + } +}