Revert "Revert "external/boringssl: Sync to 81080a729af568f7b5fde92b9170cc17065027c9.""
This reverts commit 228bd6249d17f351ea66508b3ec3112ed1cbdf30.
Reason for revert: All fixes submitted for modules affected by the ENGINE_free API change.
Change-Id: I30fafafa13ec0a6390f4a9211fbf3122a8b4865f
diff --git a/src/.gitignore b/src/.gitignore
index 41e32b9..366feb7 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -7,7 +7,7 @@
doc/doc.css
util/bot/android_ndk
-util/bot/android_tools
+util/bot/android_sdk/public
util/bot/cmake-linux64
util/bot/cmake-linux64.tar.gz
util/bot/cmake-mac
diff --git a/src/BUILDING.md b/src/BUILDING.md
index 4fc1615..e3a3570 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -10,8 +10,7 @@
[Abseil guidelines](https://abseil.io/about/compatibility). If in doubt, use the
most recent stable version of each tool.
- * [CMake](https://cmake.org/download/) 2.8.12 or later is required. Note we
- will begin requiring CMake 3.0 in 2019.
+ * [CMake](https://cmake.org/download/) 3.0 or later is required.
* A recent version of Perl is required. On Windows,
[Active State Perl](http://www.activestate.com/activeperl/) has been
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4cfbcc5..c3992a9 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,10 +1,4 @@
-cmake_minimum_required(VERSION 2.8.11)
-
-# Report AppleClang separately from Clang. Their version numbers are different.
-# https://cmake.org/cmake/help/v3.0/policy/CMP0025.html
-if(POLICY CMP0025)
- cmake_policy(SET CMP0025 NEW)
-endif()
+cmake_minimum_required(VERSION 3.0)
# Defer enabling C and CXX languages.
project(BoringSSL NONE)
@@ -369,9 +363,13 @@
if(FIPS_BREAK_TEST)
add_definitions("-DBORINGSSL_FIPS_BREAK_${FIPS_BREAK_TEST}=1")
endif()
- # Delocate does not work for ASan and MSan builds.
+ # The FIPS integrity check does not work for ASan and MSan builds.
if(NOT ASAN AND NOT MSAN)
- set(FIPS_DELOCATE "1")
+ if(BUILD_SHARED_LIBS)
+ set(FIPS_SHARED "1")
+ else()
+ set(FIPS_DELOCATE "1")
+ endif()
endif()
endif()
@@ -481,13 +479,6 @@
set(CMAKE_ASM_FLAGS "-march=${CMAKE_SYSTEM_PROCESSOR} ${CMAKE_ASM_FLAGS}")
endif()
-if(${ARCH} STREQUAL "x86" AND APPLE AND ${CMAKE_VERSION} VERSION_LESS "3.0")
- # With CMake 2.8.x, ${CMAKE_SYSTEM_PROCESSOR} evalutes to i386 on OS X,
- # but clang defaults to 64-bit builds on OS X unless otherwise told.
- # Set ARCH to x86_64 so clang and CMake agree. This is fixed in CMake 3.
- set(ARCH "x86_64")
-endif()
-
if(USE_CUSTOM_LIBCXX)
if(NOT CLANG)
message(FATAL_ERROR "USE_CUSTOM_LIBCXX only supported with Clang")
@@ -566,8 +557,9 @@
add_subdirectory(crypto)
add_subdirectory(ssl)
add_subdirectory(ssl/test)
-add_subdirectory(fipstools)
add_subdirectory(tool)
+add_subdirectory(util/fipstools/cavp)
+add_subdirectory(util/fipstools/acvp/modulewrapper)
add_subdirectory(decrepit)
if(FUZZ)
diff --git a/src/LICENSE b/src/LICENSE
index 49c41fa..2f4dfcd 100644
--- a/src/LICENSE
+++ b/src/LICENSE
@@ -181,6 +181,29 @@
SOFTWARE.
+The code in third_party/sike also carries the MIT license:
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE
+
+
Licenses for support code
-------------------------
diff --git a/src/README.md b/src/README.md
index 4595e8e..5d116c0 100644
--- a/src/README.md
+++ b/src/README.md
@@ -21,6 +21,13 @@
Currently BoringSSL is the SSL library in Chrome/Chromium, Android (but it's
not part of the NDK) and a number of other apps/programs.
+Project links:
+
+ * [API documentation](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html)
+ * [Bug tracker](https://bugs.chromium.org/p/boringssl/issues/list)
+ * [CI](https://ci.chromium.org/p/boringssl/g/main/console)
+ * [Code review](https://boringssl-review.googlesource.com)
+
There are other files in this directory which might be helpful:
* [PORTING.md](/PORTING.md): how to port OpenSSL-using code to BoringSSL.
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 5cdfa40..477faae 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -87,7 +87,7 @@
add_subdirectory(fipsmodule)
add_subdirectory(test)
-if(FIPS_DELOCATE)
+if(FIPS_DELOCATE OR FIPS_SHARED)
SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES EXTERNAL_OBJECT true)
SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES GENERATED true)
@@ -115,6 +115,7 @@
chacha/chacha-armv8.${ASM_EXT}
test/trampoline-armv8.${ASM_EXT}
+ third_party/sike/asm/fp-armv8.${ASM_EXT}
)
endif()
@@ -136,6 +137,7 @@
cipher_extra/chacha20_poly1305_x86_64.${ASM_EXT}
hrss/asm/poly_rq_mul.S
test/trampoline-x86_64.${ASM_EXT}
+ third_party/sike/asm/fp-x86_64.${ASM_EXT}
)
endif()
@@ -145,6 +147,8 @@
perlasm(chacha/chacha-x86_64.${ASM_EXT} chacha/asm/chacha-x86_64.pl)
perlasm(cipher_extra/aes128gcmsiv-x86_64.${ASM_EXT} cipher_extra/asm/aes128gcmsiv-x86_64.pl)
perlasm(cipher_extra/chacha20_poly1305_x86_64.${ASM_EXT} cipher_extra/asm/chacha20_poly1305_x86_64.pl)
+perlasm(third_party/sike/asm/fp-x86_64.${ASM_EXT} ../third_party/sike/asm/fp-x86_64.pl)
+perlasm(third_party/sike/asm/fp-armv8.${ASM_EXT} ../third_party/sike/asm/fp-armv8.pl)
perlasm(test/trampoline-armv4.${ASM_EXT} test/asm/trampoline-armv4.pl)
perlasm(test/trampoline-armv8.${ASM_EXT} test/asm/trampoline-armv8.pl)
perlasm(test/trampoline-x86.${ASM_EXT} test/asm/trampoline-x86.pl)
@@ -278,6 +282,8 @@
evp/p_ed25519_asn1.c
evp/p_rsa.c
evp/p_rsa_asn1.c
+ evp/p_x25519.c
+ evp/p_x25519_asn1.c
evp/pbkdf.c
evp/print.c
evp/scrypt.c
@@ -317,6 +323,7 @@
rsa_extra/rsa_asn1.c
rsa_extra/rsa_print.c
stack/stack.c
+ siphash/siphash.c
thread.c
thread_none.c
thread_pthread.c
@@ -404,6 +411,11 @@
x509v3/v3_sxnet.c
x509v3/v3_utl.c
../third_party/fiat/curve25519.c
+ ../third_party/sike/fpx.c
+ ../third_party/sike/isogeny.c
+ ../third_party/sike/curve_params.c
+ ../third_party/sike/sike.c
+ ../third_party/sike/asm/fp_generic.c
$<TARGET_OBJECTS:fipsmodule>
@@ -411,9 +423,25 @@
${CRYPTO_FIPS_OBJECTS}
)
+if(FIPS_SHARED)
+ # Rewrite libcrypto.so to inject the correct module hash value. This assumes
+ # UNIX-style library naming, but we only support FIPS mode on Linux anyway.
+ add_custom_command(
+ TARGET crypto POST_BUILD
+ COMMAND ${GO_EXECUTABLE} run
+ ${CMAKE_CURRENT_SOURCE_DIR}/../util/fipstools/inject_hash/inject_hash.go
+ -o libcrypto.so -in-object libcrypto.so
+ # The DEPENDS argument to a POST_BUILD rule appears to be ignored. Thus
+ # go_executable isn't used (as it doesn't get built), but we list this
+ # dependency anyway in case it starts working in some CMake version.
+ DEPENDS ../util/fipstools/inject_hash/inject_hash.go
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ )
+endif()
+
add_dependencies(crypto global_target)
-if(FIPS_DELOCATE)
+if(FIPS_DELOCATE OR FIPS_SHARED)
add_dependencies(crypto bcm_o_target)
endif()
@@ -483,12 +511,14 @@
rsa_extra/rsa_test.cc
self_test.cc
stack/stack_test.cc
+ siphash/siphash_test.cc
test/file_test_gtest.cc
thread_test.cc
x509/x509_test.cc
x509/x509_time_test.cc
x509v3/tab_test.cc
x509v3/v3name_test.cc
+ ../third_party/sike/sike_test.cc
$<TARGET_OBJECTS:crypto_test_data>
$<TARGET_OBJECTS:boringssl_gtest_main>
diff --git a/src/crypto/base64/base64_test.cc b/src/crypto/base64/base64_test.cc
index 9122dee..6905659 100644
--- a/src/crypto/base64/base64_test.cc
+++ b/src/crypto/base64/base64_test.cc
@@ -39,14 +39,14 @@
invalid,
};
-struct TestVector {
+struct Base64TestVector {
enum encoding_relation relation;
const char *decoded;
const char *encoded;
};
// Test vectors from RFC 4648.
-static const TestVector kTestVectors[] = {
+static const Base64TestVector kTestVectors[] = {
{canonical, "", ""},
{canonical, "f", "Zg==\n"},
{canonical, "fo", "Zm8=\n"},
@@ -103,7 +103,7 @@
"=======\n"},
};
-class Base64Test : public testing::TestWithParam<TestVector> {};
+class Base64Test : public testing::TestWithParam<Base64TestVector> {};
INSTANTIATE_TEST_SUITE_P(, Base64Test, testing::ValuesIn(kTestVectors));
@@ -122,7 +122,7 @@
}
TEST_P(Base64Test, EncodeBlock) {
- const TestVector &t = GetParam();
+ const Base64TestVector &t = GetParam();
if (t.relation != canonical) {
return;
}
@@ -140,7 +140,7 @@
}
TEST_P(Base64Test, DecodeBase64) {
- const TestVector &t = GetParam();
+ const Base64TestVector &t = GetParam();
if (t.relation == valid) {
// The non-canonical encodings will generally have odd whitespace etc
// that |EVP_DecodeBase64| will reject.
@@ -164,7 +164,7 @@
}
TEST_P(Base64Test, DecodeBlock) {
- const TestVector &t = GetParam();
+ const Base64TestVector &t = GetParam();
if (t.relation != canonical) {
return;
}
@@ -188,7 +188,7 @@
}
TEST_P(Base64Test, EncodeDecode) {
- const TestVector &t = GetParam();
+ const Base64TestVector &t = GetParam();
EVP_ENCODE_CTX ctx;
const size_t decoded_len = strlen(t.decoded);
@@ -246,7 +246,7 @@
}
TEST_P(Base64Test, DecodeUpdateStreaming) {
- const TestVector &t = GetParam();
+ const Base64TestVector &t = GetParam();
if (t.relation == invalid) {
return;
}
diff --git a/src/crypto/chacha/asm/chacha-armv4.pl b/src/crypto/chacha/asm/chacha-armv4.pl
index 2a9a7d7..06be3a6 100755
--- a/src/crypto/chacha/asm/chacha-armv4.pl
+++ b/src/crypto/chacha/asm/chacha-armv4.pl
@@ -1161,4 +1161,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/chacha/asm/chacha-armv8.pl b/src/crypto/chacha/asm/chacha-armv8.pl
index 7795f2c..02325e7 100755
--- a/src/crypto/chacha/asm/chacha-armv8.pl
+++ b/src/crypto/chacha/asm/chacha-armv8.pl
@@ -1134,4 +1134,4 @@
print $_,"\n";
}
-close STDOUT; # flush
+close STDOUT or die "error closing STDOUT"; # flush
diff --git a/src/crypto/chacha/asm/chacha-x86.pl b/src/crypto/chacha/asm/chacha-x86.pl
index 1d6a4e2..ec1cf80 100755
--- a/src/crypto/chacha/asm/chacha-x86.pl
+++ b/src/crypto/chacha/asm/chacha-x86.pl
@@ -769,4 +769,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/chacha/asm/chacha-x86_64.pl b/src/crypto/chacha/asm/chacha-x86_64.pl
index 6be270e..ab8a1f7 100755
--- a/src/crypto/chacha/asm/chacha-x86_64.pl
+++ b/src/crypto/chacha/asm/chacha-x86_64.pl
@@ -2782,4 +2782,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl b/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
index 1a3d064..54f0b5c 100644
--- a/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
+++ b/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
@@ -2253,4 +2253,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
index 0e32279..5b2b977 100644
--- a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
+++ b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
@@ -2485,4 +2485,4 @@
___
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/e_aesccm.c b/src/crypto/cipher_extra/e_aesccm.c
index 3e18659..e9e9e16 100644
--- a/src/crypto/cipher_extra/e_aesccm.c
+++ b/src/crypto/cipher_extra/e_aesccm.c
@@ -1,21 +1,56 @@
-/* Copyright (c) 2018, Google Inc.
+/* ====================================================================
+ * Copyright (c) 2008 The OpenSSL Project. All rights reserved.
*
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ==================================================================== */
#include <openssl/aead.h>
#include <assert.h>
+#include <openssl/cpu.h>
#include <openssl/cipher.h>
#include <openssl/err.h>
#include <openssl/mem.h>
@@ -23,6 +58,213 @@
#include "../fipsmodule/cipher/internal.h"
+struct ccm128_context {
+ block128_f block;
+ ctr128_f ctr;
+ unsigned M, L;
+};
+
+struct ccm128_state {
+ union {
+ uint64_t u[2];
+ uint8_t c[16];
+ } nonce, cmac;
+};
+
+static int CRYPTO_ccm128_init(struct ccm128_context *ctx, const AES_KEY *key,
+ block128_f block, ctr128_f ctr, unsigned M,
+ unsigned L) {
+ if (M < 4 || M > 16 || (M & 1) != 0 || L < 2 || L > 8) {
+ return 0;
+ }
+ ctx->block = block;
+ ctx->ctr = ctr;
+ ctx->M = M;
+ ctx->L = L;
+ return 1;
+}
+
+static size_t CRYPTO_ccm128_max_input(const struct ccm128_context *ctx) {
+ return ctx->L >= sizeof(size_t) ? (size_t)-1
+ : (((size_t)1) << (ctx->L * 8)) - 1;
+}
+
+static int ccm128_init_state(const struct ccm128_context *ctx,
+ struct ccm128_state *state, const AES_KEY *key,
+ const uint8_t *nonce, size_t nonce_len,
+ const uint8_t *aad, size_t aad_len,
+ size_t plaintext_len) {
+ const block128_f block = ctx->block;
+ const unsigned M = ctx->M;
+ const unsigned L = ctx->L;
+
+ // |L| determines the expected |nonce_len| and the limit for |plaintext_len|.
+ if (plaintext_len > CRYPTO_ccm128_max_input(ctx) ||
+ nonce_len != 15 - L) {
+ return 0;
+ }
+
+ // Assemble the first block for computing the MAC.
+ OPENSSL_memset(state, 0, sizeof(*state));
+ state->nonce.c[0] = (uint8_t)((L - 1) | ((M - 2) / 2) << 3);
+ if (aad_len != 0) {
+ state->nonce.c[0] |= 0x40; // Set AAD Flag
+ }
+ OPENSSL_memcpy(&state->nonce.c[1], nonce, nonce_len);
+ for (unsigned i = 0; i < L; i++) {
+ state->nonce.c[15 - i] = (uint8_t)(plaintext_len >> (8 * i));
+ }
+
+ (*block)(state->nonce.c, state->cmac.c, key);
+ size_t blocks = 1;
+
+ if (aad_len != 0) {
+ unsigned i;
+ // Cast to u64 to avoid the compiler complaining about invalid shifts.
+ uint64_t aad_len_u64 = aad_len;
+ if (aad_len_u64 < 0x10000 - 0x100) {
+ state->cmac.c[0] ^= (uint8_t)(aad_len_u64 >> 8);
+ state->cmac.c[1] ^= (uint8_t)aad_len_u64;
+ i = 2;
+ } else if (aad_len_u64 <= 0xffffffff) {
+ state->cmac.c[0] ^= 0xff;
+ state->cmac.c[1] ^= 0xfe;
+ state->cmac.c[2] ^= (uint8_t)(aad_len_u64 >> 24);
+ state->cmac.c[3] ^= (uint8_t)(aad_len_u64 >> 16);
+ state->cmac.c[4] ^= (uint8_t)(aad_len_u64 >> 8);
+ state->cmac.c[5] ^= (uint8_t)aad_len_u64;
+ i = 6;
+ } else {
+ state->cmac.c[0] ^= 0xff;
+ state->cmac.c[1] ^= 0xff;
+ state->cmac.c[2] ^= (uint8_t)(aad_len_u64 >> 56);
+ state->cmac.c[3] ^= (uint8_t)(aad_len_u64 >> 48);
+ state->cmac.c[4] ^= (uint8_t)(aad_len_u64 >> 40);
+ state->cmac.c[5] ^= (uint8_t)(aad_len_u64 >> 32);
+ state->cmac.c[6] ^= (uint8_t)(aad_len_u64 >> 24);
+ state->cmac.c[7] ^= (uint8_t)(aad_len_u64 >> 16);
+ state->cmac.c[8] ^= (uint8_t)(aad_len_u64 >> 8);
+ state->cmac.c[9] ^= (uint8_t)aad_len_u64;
+ i = 10;
+ }
+
+ do {
+ for (; i < 16 && aad_len != 0; i++) {
+ state->cmac.c[i] ^= *aad;
+ aad++;
+ aad_len--;
+ }
+ (*block)(state->cmac.c, state->cmac.c, key);
+ blocks++;
+ i = 0;
+ } while (aad_len != 0);
+ }
+
+ // Per RFC 3610, section 2.6, the total number of block cipher operations done
+ // must not exceed 2^61. There are two block cipher operations remaining per
+ // message block, plus one block at the end to encrypt the MAC.
+ size_t remaining_blocks = 2 * ((plaintext_len + 15) / 16) + 1;
+ if (plaintext_len + 15 < plaintext_len ||
+ remaining_blocks + blocks < blocks ||
+ (uint64_t) remaining_blocks + blocks > UINT64_C(1) << 61) {
+ return 0;
+ }
+
+ // Assemble the first block for encrypting and decrypting. The bottom |L|
+ // bytes are replaced with a counter and all bit the encoding of |L| is
+ // cleared in the first byte.
+ state->nonce.c[0] &= 7;
+ return 1;
+}
+
+static int ccm128_encrypt(const struct ccm128_context *ctx,
+ struct ccm128_state *state, const AES_KEY *key,
+ uint8_t *out, const uint8_t *in, size_t len) {
+ // The counter for encryption begins at one.
+ for (unsigned i = 0; i < ctx->L; i++) {
+ state->nonce.c[15 - i] = 0;
+ }
+ state->nonce.c[15] = 1;
+
+ uint8_t partial_buf[16];
+ unsigned num = 0;
+ if (ctx->ctr != NULL) {
+ CRYPTO_ctr128_encrypt_ctr32(in, out, len, key, state->nonce.c, partial_buf,
+ &num, ctx->ctr);
+ } else {
+ CRYPTO_ctr128_encrypt(in, out, len, key, state->nonce.c, partial_buf, &num,
+ ctx->block);
+ }
+ return 1;
+}
+
+static int ccm128_compute_mac(const struct ccm128_context *ctx,
+ struct ccm128_state *state, const AES_KEY *key,
+ uint8_t *out_tag, size_t tag_len,
+ const uint8_t *in, size_t len) {
+ block128_f block = ctx->block;
+ if (tag_len != ctx->M) {
+ return 0;
+ }
+
+ // Incorporate |in| into the MAC.
+ union {
+ uint64_t u[2];
+ uint8_t c[16];
+ } tmp;
+ while (len >= 16) {
+ OPENSSL_memcpy(tmp.c, in, 16);
+ state->cmac.u[0] ^= tmp.u[0];
+ state->cmac.u[1] ^= tmp.u[1];
+ (*block)(state->cmac.c, state->cmac.c, key);
+ in += 16;
+ len -= 16;
+ }
+ if (len > 0) {
+ for (size_t i = 0; i < len; i++) {
+ state->cmac.c[i] ^= in[i];
+ }
+ (*block)(state->cmac.c, state->cmac.c, key);
+ }
+
+ // Encrypt the MAC with counter zero.
+ for (unsigned i = 0; i < ctx->L; i++) {
+ state->nonce.c[15 - i] = 0;
+ }
+ (*block)(state->nonce.c, tmp.c, key);
+ state->cmac.u[0] ^= tmp.u[0];
+ state->cmac.u[1] ^= tmp.u[1];
+
+ OPENSSL_memcpy(out_tag, state->cmac.c, tag_len);
+ return 1;
+}
+
+static int CRYPTO_ccm128_encrypt(const struct ccm128_context *ctx,
+ const AES_KEY *key, uint8_t *out,
+ uint8_t *out_tag, size_t tag_len,
+ const uint8_t *nonce, size_t nonce_len,
+ const uint8_t *in, size_t len,
+ const uint8_t *aad, size_t aad_len) {
+ struct ccm128_state state;
+ return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
+ len) &&
+ ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, in, len) &&
+ ccm128_encrypt(ctx, &state, key, out, in, len);
+}
+
+static int CRYPTO_ccm128_decrypt(const struct ccm128_context *ctx,
+ const AES_KEY *key, uint8_t *out,
+ uint8_t *out_tag, size_t tag_len,
+ const uint8_t *nonce, size_t nonce_len,
+ const uint8_t *in, size_t len,
+ const uint8_t *aad, size_t aad_len) {
+ struct ccm128_state state;
+ return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
+ len) &&
+ ccm128_encrypt(ctx, &state, key, out, in, len) &&
+ ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, out, len);
+}
+
#define EVP_AEAD_AES_CCM_MAX_TAG_LEN 16
struct aead_aes_ccm_ctx {
@@ -30,7 +272,7 @@
double align;
AES_KEY ks;
} ks;
- CCM128_CONTEXT ccm;
+ struct ccm128_context ccm;
};
OPENSSL_STATIC_ASSERT(sizeof(((EVP_AEAD_CTX *)NULL)->state) >=
diff --git a/src/crypto/constant_time_test.cc b/src/crypto/constant_time_test.cc
index 59a7bb1..ae80003 100644
--- a/src/crypto/constant_time_test.cc
+++ b/src/crypto/constant_time_test.cc
@@ -153,3 +153,19 @@
}
}
}
+
+TEST(ConstantTimeTest, ValueBarrier) {
+ for (int i = 0; i < 10; i++) {
+ crypto_word_t word;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&word), sizeof(word));
+ EXPECT_EQ(word, value_barrier_w(word));
+
+ uint32_t u32;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&u32), sizeof(u32));
+ EXPECT_EQ(u32, value_barrier_u32(u32));
+
+ uint64_t u64;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&u64), sizeof(u64));
+ EXPECT_EQ(u64, value_barrier_u64(u64));
+ }
+}
diff --git a/src/crypto/cpu-intel.c b/src/crypto/cpu-intel.c
index 98d8d4e..1621ef6 100644
--- a/src/crypto/cpu-intel.c
+++ b/src/crypto/cpu-intel.c
@@ -148,6 +148,9 @@
int is_intel = ebx == 0x756e6547 /* Genu */ &&
edx == 0x49656e69 /* ineI */ &&
ecx == 0x6c65746e /* ntel */;
+ int is_amd = ebx == 0x68747541 /* Auth */ &&
+ edx == 0x69746e65 /* enti */ &&
+ ecx == 0x444d4163 /* cAMD */;
uint32_t extended_features[2] = {0};
if (num_ids >= 7) {
@@ -158,6 +161,24 @@
OPENSSL_cpuid(&eax, &ebx, &ecx, &edx, 1);
+ if (is_amd) {
+ // See https://www.amd.com/system/files/TechDocs/25481.pdf, page 10.
+ const uint32_t base_family = (eax >> 8) & 15;
+
+ uint32_t family = base_family;
+ if (base_family == 0xf) {
+ const uint32_t ext_family = (eax >> 20) & 255;
+ family += ext_family;
+ }
+
+ if (family < 0x17) {
+ // Disable RDRAND on AMD families before 0x17 (Zen) due to reported
+ // failures after suspend.
+ // https://bugzilla.redhat.com/show_bug.cgi?id=1150286
+ ecx &= ~(1u << 30);
+ }
+ }
+
// Force the hyper-threading bit so that the more conservative path is always
// chosen.
edx |= 1u << 28;
diff --git a/src/crypto/digest_extra/digest_test.cc b/src/crypto/digest_extra/digest_test.cc
index 7d07c04..6d02c08 100644
--- a/src/crypto/digest_extra/digest_test.cc
+++ b/src/crypto/digest_extra/digest_test.cc
@@ -55,7 +55,7 @@
static const MD sha512 = { "SHA512", &EVP_sha512, &SHA512 };
static const MD md5_sha1 = { "MD5-SHA1", &EVP_md5_sha1, nullptr };
-struct TestVector {
+struct DigestTestVector {
// md is the digest to test.
const MD &md;
// input is a NUL-terminated string to hash.
@@ -66,7 +66,7 @@
const char *expected_hex;
};
-static const TestVector kTestVectors[] = {
+static const DigestTestVector kTestVectors[] = {
// MD4 tests, from RFC 1320. (crypto/md4 does not provide a
// one-shot MD4 function.)
{ md4, "", 1, "31d6cfe0d16ae931b73c59d7e0c089c0" },
@@ -144,7 +144,7 @@
"900150983cd24fb0d6963f7d28e17f72a9993e364706816aba3e25717850c26c9cd0d89d" },
};
-static void CompareDigest(const TestVector *test,
+static void CompareDigest(const DigestTestVector *test,
const uint8_t *digest,
size_t digest_len) {
static const char kHexTable[] = "0123456789abcdef";
@@ -159,7 +159,7 @@
EXPECT_STREQ(test->expected_hex, digest_hex);
}
-static void TestDigest(const TestVector *test) {
+static void TestDigest(const DigestTestVector *test) {
bssl::ScopedEVP_MD_CTX ctx;
// Test the input provided.
diff --git a/src/crypto/dsa/dsa.c b/src/crypto/dsa/dsa.c
index 288e2c8..51dca7f 100644
--- a/src/crypto/dsa/dsa.c
+++ b/src/crypto/dsa/dsa.c
@@ -558,29 +558,34 @@
}
DSA_SIG *DSA_do_sign(const uint8_t *digest, size_t digest_len, const DSA *dsa) {
- BIGNUM *kinv = NULL, *r = NULL, *s = NULL;
- BIGNUM m;
- BIGNUM xr;
- BN_CTX *ctx = NULL;
- int reason = ERR_R_BN_LIB;
- DSA_SIG *ret = NULL;
-
- BN_init(&m);
- BN_init(&xr);
-
if (!dsa->p || !dsa->q || !dsa->g) {
- reason = DSA_R_MISSING_PARAMETERS;
- goto err;
+ OPENSSL_PUT_ERROR(DSA, DSA_R_MISSING_PARAMETERS);
+ return NULL;
+ }
+
+ // Reject invalid parameters. In particular, the algorithm will infinite loop
+ // if |g| is zero.
+ if (BN_is_zero(dsa->p) || BN_is_zero(dsa->q) || BN_is_zero(dsa->g)) {
+ OPENSSL_PUT_ERROR(DSA, DSA_R_INVALID_PARAMETERS);
+ return NULL;
}
// We only support DSA keys that are a multiple of 8 bits. (This is a weaker
// check than the one in |DSA_do_check_signature|, which only allows 160-,
// 224-, and 256-bit keys.
if (BN_num_bits(dsa->q) % 8 != 0) {
- reason = DSA_R_BAD_Q_VALUE;
- goto err;
+ OPENSSL_PUT_ERROR(DSA, DSA_R_BAD_Q_VALUE);
+ return NULL;
}
+ BIGNUM *kinv = NULL, *r = NULL, *s = NULL;
+ BIGNUM m;
+ BIGNUM xr;
+ BN_CTX *ctx = NULL;
+ DSA_SIG *ret = NULL;
+
+ BN_init(&m);
+ BN_init(&xr);
s = BN_new();
if (s == NULL) {
goto err;
@@ -640,7 +645,7 @@
err:
if (ret == NULL) {
- OPENSSL_PUT_ERROR(DSA, reason);
+ OPENSSL_PUT_ERROR(DSA, ERR_R_BN_LIB);
BN_free(r);
BN_free(s);
}
diff --git a/src/crypto/dsa/dsa_test.cc b/src/crypto/dsa/dsa_test.cc
index 295a7fd..4682131 100644
--- a/src/crypto/dsa/dsa_test.cc
+++ b/src/crypto/dsa/dsa_test.cc
@@ -62,6 +62,8 @@
#include <stdio.h>
#include <string.h>
+#include <vector>
+
#include <gtest/gtest.h>
#include <openssl/bn.h>
@@ -315,3 +317,18 @@
ADD_FAILURE() << "Tests failed";
}
}
+
+TEST(DSATest, InvalidGroup) {
+ bssl::UniquePtr<DSA> dsa = GetFIPSDSA();
+ ASSERT_TRUE(dsa);
+ BN_zero(dsa->g);
+
+ std::vector<uint8_t> sig(DSA_size(dsa.get()));
+ unsigned sig_len;
+ static const uint8_t kDigest[32] = {0};
+ EXPECT_FALSE(
+ DSA_sign(0, kDigest, sizeof(kDigest), sig.data(), &sig_len, dsa.get()));
+ uint32_t err = ERR_get_error();
+ EXPECT_EQ(ERR_LIB_DSA, ERR_GET_LIB(err));
+ EXPECT_EQ(DSA_R_INVALID_PARAMETERS, ERR_GET_REASON(err));
+}
diff --git a/src/crypto/ec_extra/ec_asn1.c b/src/crypto/ec_extra/ec_asn1.c
index 6e21275..31988f3 100644
--- a/src/crypto/ec_extra/ec_asn1.c
+++ b/src/crypto/ec_extra/ec_asn1.c
@@ -159,8 +159,8 @@
(point_conversion_form_t)(CBS_data(&public_key)[0] & ~0x01);
} else {
// Compute the public key instead.
- if (!ec_point_mul_scalar(group, &ret->pub_key->raw, &ret->priv_key->scalar,
- NULL, NULL)) {
+ if (!ec_point_mul_scalar_base(group, &ret->pub_key->raw,
+ &ret->priv_key->scalar)) {
goto err;
}
// Remember the original private-key-only encoding.
diff --git a/src/crypto/ecdh_extra/ecdh_extra.c b/src/crypto/ecdh_extra/ecdh_extra.c
index 1e08099..b8a099a 100644
--- a/src/crypto/ecdh_extra/ecdh_extra.c
+++ b/src/crypto/ecdh_extra/ecdh_extra.c
@@ -95,7 +95,7 @@
EC_RAW_POINT shared_point;
uint8_t buf[EC_MAX_BYTES];
size_t buf_len;
- if (!ec_point_mul_scalar(group, &shared_point, NULL, &pub_key->raw, priv) ||
+ if (!ec_point_mul_scalar(group, &shared_point, &pub_key->raw, priv) ||
!ec_point_get_affine_coordinate_bytes(group, buf, NULL, &buf_len,
sizeof(buf), &shared_point)) {
OPENSSL_PUT_ERROR(ECDH, ECDH_R_POINT_ARITHMETIC_FAILURE);
diff --git a/src/crypto/engine/engine.c b/src/crypto/engine/engine.c
index 875f148..973a57c 100644
--- a/src/crypto/engine/engine.c
+++ b/src/crypto/engine/engine.c
@@ -41,9 +41,10 @@
return engine;
}
-void ENGINE_free(ENGINE *engine) {
+int ENGINE_free(ENGINE *engine) {
// Methods are currently required to be static so are not unref'ed.
OPENSSL_free(engine);
+ return 1;
}
// set_method takes a pointer to a method and its given size and sets
diff --git a/src/crypto/err/dsa.errordata b/src/crypto/err/dsa.errordata
index 6f4bc13..1cf5206 100644
--- a/src/crypto/err/dsa.errordata
+++ b/src/crypto/err/dsa.errordata
@@ -2,6 +2,7 @@
DSA,104,BAD_VERSION
DSA,105,DECODE_ERROR
DSA,106,ENCODE_ERROR
+DSA,107,INVALID_PARAMETERS
DSA,101,MISSING_PARAMETERS
DSA,102,MODULUS_TOO_LARGE
DSA,103,NEED_NEW_SETUP_VALUES
diff --git a/src/crypto/err/evp.errordata b/src/crypto/err/evp.errordata
index aea4a94..771cd6a 100644
--- a/src/crypto/err/evp.errordata
+++ b/src/crypto/err/evp.errordata
@@ -15,6 +15,7 @@
EVP,114,INVALID_OPERATION
EVP,115,INVALID_PADDING_MODE
EVP,133,INVALID_PARAMETERS
+EVP,134,INVALID_PEER_KEY
EVP,116,INVALID_PSS_SALTLEN
EVP,131,INVALID_SIGNATURE
EVP,117,KEYS_NOT_SET
diff --git a/src/crypto/evp/evp.c b/src/crypto/evp/evp.c
index ed7cc85..0e90b6f 100644
--- a/src/crypto/evp/evp.c
+++ b/src/crypto/evp/evp.c
@@ -200,6 +200,8 @@
return &dsa_asn1_meth;
case EVP_PKEY_ED25519:
return &ed25519_asn1_meth;
+ case EVP_PKEY_X25519:
+ return &x25519_asn1_meth;
default:
return NULL;
}
@@ -330,7 +332,73 @@
return 1;
}
+EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
+ const uint8_t *in, size_t len) {
+ EVP_PKEY *ret = EVP_PKEY_new();
+ if (ret == NULL ||
+ !EVP_PKEY_set_type(ret, type)) {
+ goto err;
+ }
+ if (ret->ameth->set_priv_raw == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ goto err;
+ }
+
+ if (!ret->ameth->set_priv_raw(ret, in, len)) {
+ goto err;
+ }
+
+ return ret;
+
+err:
+ EVP_PKEY_free(ret);
+ return NULL;
+}
+
+EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
+ const uint8_t *in, size_t len) {
+ EVP_PKEY *ret = EVP_PKEY_new();
+ if (ret == NULL ||
+ !EVP_PKEY_set_type(ret, type)) {
+ goto err;
+ }
+
+ if (ret->ameth->set_pub_raw == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ goto err;
+ }
+
+ if (!ret->ameth->set_pub_raw(ret, in, len)) {
+ goto err;
+ }
+
+ return ret;
+
+err:
+ EVP_PKEY_free(ret);
+ return NULL;
+}
+
+int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ if (pkey->ameth->get_priv_raw == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+
+ return pkey->ameth->get_priv_raw(pkey, out, out_len);
+}
+
+int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ if (pkey->ameth->get_pub_raw == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+
+ return pkey->ameth->get_pub_raw(pkey, out, out_len);
+}
int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
if (a->type != b->type) {
@@ -361,3 +429,10 @@
void OpenSSL_add_all_digests(void) {}
void EVP_cleanup(void) {}
+
+int EVP_PKEY_base_id(const EVP_PKEY *pkey) {
+ // OpenSSL has two notions of key type because it supports multiple OIDs for
+ // the same algorithm: NID_rsa vs NID_rsaEncryption and five distinct spelling
+ // of DSA. We do not support these, so the base ID is simply the ID.
+ return EVP_PKEY_id(pkey);
+}
diff --git a/src/crypto/evp/evp_asn1.c b/src/crypto/evp/evp_asn1.c
index d56b93b..fc1dce3 100644
--- a/src/crypto/evp/evp_asn1.c
+++ b/src/crypto/evp/evp_asn1.c
@@ -73,6 +73,7 @@
&ec_asn1_meth,
&dsa_asn1_meth,
&ed25519_asn1_meth,
+ &x25519_asn1_meth,
};
static int parse_key_type(CBS *cbs, int *out_type) {
diff --git a/src/crypto/evp/evp_ctx.c b/src/crypto/evp/evp_ctx.c
index daa1954..9ca2c55 100644
--- a/src/crypto/evp/evp_ctx.c
+++ b/src/crypto/evp/evp_ctx.c
@@ -67,15 +67,14 @@
static const EVP_PKEY_METHOD *const evp_methods[] = {
- &rsa_pkey_meth,
- &ec_pkey_meth,
- &ed25519_pkey_meth,
+ &rsa_pkey_meth,
+ &ec_pkey_meth,
+ &ed25519_pkey_meth,
+ &x25519_pkey_meth,
};
static const EVP_PKEY_METHOD *evp_pkey_meth_find(int type) {
- unsigned i;
-
- for (i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
+ for (size_t i = 0; i < sizeof(evp_methods)/sizeof(EVP_PKEY_METHOD*); i++) {
if (evp_methods[i]->pkey_id == type) {
return evp_methods[i];
}
diff --git a/src/crypto/evp/evp_extra_test.cc b/src/crypto/evp/evp_extra_test.cc
index 1f6a61d..f520598 100644
--- a/src/crypto/evp/evp_extra_test.cc
+++ b/src/crypto/evp/evp_extra_test.cc
@@ -555,13 +555,10 @@
0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a,
};
- static const uint8_t kPrivateKey[64] = {
+ static const uint8_t kPrivateKeySeed[32] = {
0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a,
0xf4, 0x92, 0xec, 0x2c, 0xc4, 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32,
- 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60, 0xd7,
- 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3,
- 0xc9, 0x64, 0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23,
- 0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a,
+ 0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60,
};
static const uint8_t kPrivateKeyPKCS8[] = {
@@ -572,10 +569,38 @@
};
// Create a public key.
- bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_new_ed25519_public(kPublicKey));
+ bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_new_raw_public_key(
+ EVP_PKEY_ED25519, nullptr, kPublicKey, sizeof(kPublicKey)));
ASSERT_TRUE(pubkey);
EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(pubkey.get()));
+ // The public key must be extractable.
+ uint8_t buf[32];
+ size_t len;
+ ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), nullptr, &len));
+ EXPECT_EQ(len, 32u);
+ ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+ EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+ // Passing too large of a buffer is okay. The function will still only read
+ // 32 bytes.
+ len = 64;
+ ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+ EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+ // Passing too small of a buffer is noticed.
+ len = 31;
+ EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+ uint32_t err = ERR_get_error();
+ EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+ EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+ ERR_clear_error();
+
+ // There is no private key.
+ EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pubkey.get(), nullptr, &len));
+ err = ERR_get_error();
+ EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+ EXPECT_EQ(EVP_R_NOT_A_PRIVATE_KEY, ERR_GET_REASON(err));
+ ERR_clear_error();
+
// The public key must encode properly.
bssl::ScopedCBB cbb;
uint8_t *der;
@@ -589,16 +614,40 @@
// The public key must gracefully fail to encode as a private key.
ASSERT_TRUE(CBB_init(cbb.get(), 0));
EXPECT_FALSE(EVP_marshal_private_key(cbb.get(), pubkey.get()));
- uint32_t err = ERR_get_error();
+ err = ERR_get_error();
EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
EXPECT_EQ(EVP_R_NOT_A_PRIVATE_KEY, ERR_GET_REASON(err));
+ ERR_clear_error();
cbb.Reset();
// Create a private key.
- bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new_ed25519_private(kPrivateKey));
+ bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new_raw_private_key(
+ EVP_PKEY_ED25519, NULL, kPrivateKeySeed, sizeof(kPrivateKeySeed)));
ASSERT_TRUE(privkey);
EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(privkey.get()));
+ // The private key must be extractable.
+ ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), nullptr, &len));
+ EXPECT_EQ(len, 32u);
+ ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+ EXPECT_EQ(Bytes(buf, len), Bytes(kPrivateKeySeed));
+ // Passing too large of a buffer is okay. The function will still only read
+ // 32 bytes.
+ len = 64;
+ ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+ EXPECT_EQ(Bytes(buf, len), Bytes(kPrivateKeySeed));
+ // Passing too small of a buffer is noticed.
+ len = 31;
+ EXPECT_FALSE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+ err = ERR_get_error();
+ EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+ EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+ ERR_clear_error();
+ // The public key must be extractable.
+ len = 32;
+ ASSERT_TRUE(EVP_PKEY_get_raw_public_key(privkey.get(), buf, &len));
+ EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+
// The public key must encode from the private key.
ASSERT_TRUE(CBB_init(cbb.get(), 0));
ASSERT_TRUE(EVP_marshal_public_key(cbb.get(), privkey.get()));
@@ -617,7 +666,8 @@
EXPECT_EQ(1, EVP_PKEY_cmp(pubkey.get(), privkey.get()));
static const uint8_t kZeros[32] = {0};
- bssl::UniquePtr<EVP_PKEY> pubkey2(EVP_PKEY_new_ed25519_public(kZeros));
+ bssl::UniquePtr<EVP_PKEY> pubkey2(EVP_PKEY_new_raw_public_key(
+ EVP_PKEY_ED25519, nullptr, kZeros, sizeof(kZeros)));
ASSERT_TRUE(pubkey2);
EXPECT_EQ(0, EVP_PKEY_cmp(pubkey.get(), pubkey2.get()));
EXPECT_EQ(0, EVP_PKEY_cmp(privkey.get(), pubkey2.get()));
@@ -627,7 +677,6 @@
ASSERT_TRUE(
EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, privkey.get()));
EXPECT_FALSE(EVP_DigestSignUpdate(ctx.get(), nullptr, 0));
- size_t len;
EXPECT_FALSE(EVP_DigestSignFinal(ctx.get(), nullptr, &len));
ERR_clear_error();
@@ -637,6 +686,18 @@
EXPECT_FALSE(EVP_DigestVerifyUpdate(ctx.get(), nullptr, 0));
EXPECT_FALSE(EVP_DigestVerifyFinal(ctx.get(), nullptr, 0));
ERR_clear_error();
+
+ // The buffer length to |EVP_DigestSign| is an input/output parameter and
+ // should be checked before signing.
+ ctx.Reset();
+ ASSERT_TRUE(
+ EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, privkey.get()));
+ len = 31;
+ EXPECT_FALSE(EVP_DigestSign(ctx.get(), buf, &len, nullptr /* msg */, 0));
+ err = ERR_get_error();
+ EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+ EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+ ERR_clear_error();
}
static void ExpectECGroupOnly(const EVP_PKEY *pkey, int nid) {
@@ -695,3 +756,29 @@
raw = nullptr;
ExpectECGroupAndKey(pkey.get(), NID_X9_62_prime256v1);
}
+
+// Test that |EVP_PKEY_keygen| works for Ed25519.
+TEST(EVPExtraTest, Ed25519Keygen) {
+ bssl::UniquePtr<EVP_PKEY_CTX> pctx(
+ EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr));
+ ASSERT_TRUE(pctx);
+ ASSERT_TRUE(EVP_PKEY_keygen_init(pctx.get()));
+ EVP_PKEY *raw = nullptr;
+ ASSERT_TRUE(EVP_PKEY_keygen(pctx.get(), &raw));
+ bssl::UniquePtr<EVP_PKEY> pkey(raw);
+
+ // Round-trip a signature to sanity-check the key is good.
+ bssl::ScopedEVP_MD_CTX ctx;
+ ASSERT_TRUE(
+ EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
+ uint8_t sig[64];
+ size_t len = sizeof(sig);
+ ASSERT_TRUE(EVP_DigestSign(ctx.get(), sig, &len,
+ reinterpret_cast<const uint8_t *>("hello"), 5));
+
+ ctx.Reset();
+ ASSERT_TRUE(
+ EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
+ ASSERT_TRUE(EVP_DigestVerify(ctx.get(), sig, len,
+ reinterpret_cast<const uint8_t *>("hello"), 5));
+}
diff --git a/src/crypto/evp/evp_test.cc b/src/crypto/evp/evp_test.cc
index 4d74292..b4be636 100644
--- a/src/crypto/evp/evp_test.cc
+++ b/src/crypto/evp/evp_test.cc
@@ -119,6 +119,9 @@
if (name == "Ed25519") {
return EVP_PKEY_ED25519;
}
+ if (name == "X25519") {
+ return EVP_PKEY_X25519;
+ }
ADD_FAILURE() << "Unknown key type: " << name;
return EVP_PKEY_NONE;
}
@@ -179,7 +182,62 @@
!t->GetBytes(&output, "Output")) {
return false;
}
- EXPECT_EQ(Bytes(output), Bytes(der, der_len)) << "Re-encoding the key did not match.";
+ EXPECT_EQ(Bytes(output), Bytes(der, der_len))
+ << "Re-encoding the key did not match.";
+
+ if (t->HasAttribute("ExpectNoRawPrivate")) {
+ size_t len;
+ EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pkey.get(), nullptr, &len));
+ } else if (t->HasAttribute("ExpectRawPrivate")) {
+ std::vector<uint8_t> expected;
+ if (!t->GetBytes(&expected, "ExpectRawPrivate")) {
+ return false;
+ }
+
+ std::vector<uint8_t> raw;
+ size_t len;
+ if (!EVP_PKEY_get_raw_private_key(pkey.get(), nullptr, &len)) {
+ return false;
+ }
+ raw.resize(len);
+ if (!EVP_PKEY_get_raw_private_key(pkey.get(), raw.data(), &len)) {
+ return false;
+ }
+ raw.resize(len);
+ EXPECT_EQ(Bytes(raw), Bytes(expected));
+
+ // Short buffers should be rejected.
+ raw.resize(len - 1);
+ len = raw.size();
+ EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pkey.get(), raw.data(), &len));
+ }
+
+ if (t->HasAttribute("ExpectNoRawPublic")) {
+ size_t len;
+ EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len));
+ } else if (t->HasAttribute("ExpectRawPublic")) {
+ std::vector<uint8_t> expected;
+ if (!t->GetBytes(&expected, "ExpectRawPublic")) {
+ return false;
+ }
+
+ std::vector<uint8_t> raw;
+ size_t len;
+ if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len)) {
+ return false;
+ }
+ raw.resize(len);
+ if (!EVP_PKEY_get_raw_public_key(pkey.get(), raw.data(), &len)) {
+ return false;
+ }
+ raw.resize(len);
+ EXPECT_EQ(Bytes(raw), Bytes(expected));
+
+ // Short buffers should be rejected.
+ raw.resize(len - 1);
+ len = raw.size();
+ EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pkey.get(), raw.data(), &len));
+ }
// Save the key for future tests.
const std::string &key_name = t->GetParameter();
@@ -190,7 +248,7 @@
// SetupContext configures |ctx| based on attributes in |t|, with the exception
// of the signing digest which must be configured externally.
-static bool SetupContext(FileTest *t, EVP_PKEY_CTX *ctx) {
+static bool SetupContext(FileTest *t, KeyMap *key_map, EVP_PKEY_CTX *ctx) {
if (t->HasAttribute("RSAPadding")) {
int padding;
if (!GetRSAPadding(t, &padding, t->GetAttributeOrDie("RSAPadding")) ||
@@ -230,6 +288,74 @@
}
buf.release();
}
+ if (t->HasAttribute("DerivePeer")) {
+ std::string derive_peer = t->GetAttributeOrDie("DerivePeer");
+ if (key_map->count(derive_peer) == 0) {
+ ADD_FAILURE() << "Could not find key " << derive_peer;
+ return false;
+ }
+ EVP_PKEY *derive_peer_key = (*key_map)[derive_peer].get();
+ if (!EVP_PKEY_derive_set_peer(ctx, derive_peer_key)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool TestDerive(FileTest *t, KeyMap *key_map, EVP_PKEY *key) {
+ bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(key, nullptr));
+ if (!ctx ||
+ !EVP_PKEY_derive_init(ctx.get()) ||
+ !SetupContext(t, key_map, ctx.get())) {
+ return false;
+ }
+
+ bssl::UniquePtr<EVP_PKEY_CTX> copy(EVP_PKEY_CTX_dup(ctx.get()));
+ if (!copy) {
+ return false;
+ }
+
+ for (EVP_PKEY_CTX *pctx : {ctx.get(), copy.get()}) {
+ size_t len;
+ std::vector<uint8_t> actual, output;
+ if (!EVP_PKEY_derive(pctx, nullptr, &len)) {
+ return false;
+ }
+ actual.resize(len);
+ if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
+ return false;
+ }
+ actual.resize(len);
+
+ // Defer looking up the attribute so Error works properly.
+ if (!t->GetBytes(&output, "Output")) {
+ return false;
+ }
+ EXPECT_EQ(Bytes(output), Bytes(actual));
+
+ // Test when the buffer is too large.
+ actual.resize(len + 1);
+ len = actual.size();
+ if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
+ return false;
+ }
+ actual.resize(len);
+ EXPECT_EQ(Bytes(output), Bytes(actual));
+
+ // Test when the buffer is too small.
+ actual.resize(len - 1);
+ len = actual.size();
+ if (t->HasAttribute("SmallBufferTruncates")) {
+ if (!EVP_PKEY_derive(pctx, actual.data(), &len)) {
+ return false;
+ }
+ actual.resize(len);
+ EXPECT_EQ(Bytes(output.data(), len), Bytes(actual));
+ } else {
+ EXPECT_FALSE(EVP_PKEY_derive(pctx, actual.data(), &len));
+ ERR_clear_error();
+ }
+ }
return true;
}
@@ -243,6 +369,14 @@
return ImportKey(t, key_map, EVP_parse_public_key, EVP_marshal_public_key);
}
+ // Load the key.
+ const std::string &key_name = t->GetParameter();
+ if (key_map->count(key_name) == 0) {
+ ADD_FAILURE() << "Could not find key " << key_name;
+ return false;
+ }
+ EVP_PKEY *key = (*key_map)[key_name].get();
+
int (*key_op_init)(EVP_PKEY_CTX *ctx) = nullptr;
int (*key_op)(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len,
const uint8_t *in, size_t in_len) = nullptr;
@@ -266,19 +400,13 @@
} else if (t->GetType() == "Encrypt") {
key_op_init = EVP_PKEY_encrypt_init;
key_op = EVP_PKEY_encrypt;
+ } else if (t->GetType() == "Derive") {
+ return TestDerive(t, key_map, key);
} else {
ADD_FAILURE() << "Unknown test " << t->GetType();
return false;
}
- // Load the key.
- const std::string &key_name = t->GetParameter();
- if (key_map->count(key_name) == 0) {
- ADD_FAILURE() << "Could not find key " << key_name;
- return false;
- }
- EVP_PKEY *key = (*key_map)[key_name].get();
-
const EVP_MD *digest = nullptr;
if (t->HasAttribute("Digest")) {
digest = GetDigest(t, t->GetAttributeOrDie("Digest"));
@@ -300,7 +428,7 @@
bssl::ScopedEVP_MD_CTX ctx, copy;
EVP_PKEY_CTX *pctx;
if (!md_op_init(ctx.get(), &pctx, digest, nullptr, key) ||
- !SetupContext(t, pctx) ||
+ !SetupContext(t, key_map, pctx) ||
!EVP_MD_CTX_copy_ex(copy.get(), ctx.get())) {
return false;
}
@@ -347,7 +475,7 @@
!key_op_init(ctx.get()) ||
(digest != nullptr &&
!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest)) ||
- !SetupContext(t, ctx.get())) {
+ !SetupContext(t, key_map, ctx.get())) {
return false;
}
@@ -381,7 +509,7 @@
!EVP_PKEY_decrypt_init(decrypt_ctx.get()) ||
(digest != nullptr &&
!EVP_PKEY_CTX_set_signature_md(decrypt_ctx.get(), digest)) ||
- !SetupContext(t, decrypt_ctx.get()) ||
+ !SetupContext(t, key_map, decrypt_ctx.get()) ||
!EVP_PKEY_decrypt(decrypt_ctx.get(), nullptr, &plaintext_len,
actual.data(), actual.size())) {
return false;
@@ -401,7 +529,7 @@
!EVP_PKEY_verify_init(verify_ctx.get()) ||
(digest != nullptr &&
!EVP_PKEY_CTX_set_signature_md(verify_ctx.get(), digest)) ||
- !SetupContext(t, verify_ctx.get())) {
+ !SetupContext(t, key_map, verify_ctx.get())) {
return false;
}
if (t->HasAttribute("VerifyPSSSaltLength")) {
diff --git a/src/crypto/evp/evp_tests.txt b/src/crypto/evp/evp_tests.txt
index 9dbe1cb..2ac51d3 100644
--- a/src/crypto/evp/evp_tests.txt
+++ b/src/crypto/evp/evp_tests.txt
@@ -6,11 +6,15 @@
PrivateKey = RSA-2048
Type = RSA
Input = 308204bc020100300d06092a864886f70d0101010500048204a6308204a20201000282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f02030100010282010060297ac7991b167a06d6b24758b8cbe208beb9b2d9ec9738bd80f90a2e35005dd7ce292d9e29ba885bd316fef1f20913bc0ac90d6b0808b2414d82104441d8624a33ce0233c8f780a48b375aff02d76712228a702484db3f9ebecccfbbee1709dba182800d949e9e4216e0bff3558388f8bd90da373a1d82743ec3fbdd1427fd16825a657a316912e8695365117ca2f845c909405fcac55f895fc15d20386c26ee78c9e99075029a178a6c1e4cf0c200e8a9cfb27e9d156f86e6c2adc22b1a84a1cd5ca5b2790875d79407c84b352395cb81cc3fed5bb043b69ede0c07204550025cee8c5f440170b6120bb48e0f747bcd8f522110850df043c428dfd187053102818100f6f961b47cbc035d3aedebc7de850a956b65ecdb9cf60764063f15aa48553c58d972fe6675056e35ddfdc37bf3b9f2f622ee271337256849c9bef2176fe8f7c3f8bb91ba374dd53baf3dec814d2bdec10c1fdc88cdd16876f26b1edfa3f094197edf4d42ff1fb2971103b898ca859c427287086a842ab410bb69cf2d35af6be302818100d47e724a7ff41048b270c2524a4101878b73159bb73d3dbc187b220e635b3534f96e243a184d93f860b6bfbb6b71c1ed9a1e1f458583023c301e96a692c1a08b53d0ec9ca910100d80451e3b7dc6a01bac4aecef8df798846bc235a08cbba2cf4c06804cc11219e95608c714e3f1430d491fadbba32a5751a04f97745834c9a502818021f2452bb9b95dfd028c914bf799f1ca77e89a95d50d3c16d384f8455f8bd7af9eb3dfa3d591d9842def235f7630a8e48c088ff6642e101794535a933e1e976fa8509fc728b2da0c4a1a08d7fcf37abaae1ff3001aca1dc1bbb05d9dffbaa1a09f7fb1eef38237d9ebccc722b9338436dde7119112798c26809c1a8dec4320610281801f7510aa62c2d8de4a3c53282781f41e02d0e8b402ae78432e449c48110161a11403f02d01880a8dcc938152d79721a4711a607ac4471ebf964810f95be47a45e60499e29f4c9773c83773404f606637728c2d0351bb03c326c8bb73a721e7fa5440ea2172bba1465fcc30dcb0d9f89930e815aa1f7f9729a857e00e0338dd590281804d1f0d756fe77e01099a652f50a88b7b685dc5bf00981d5d2376fd0c6fe29cd5b638734479305a73ad3c1599d39eae3bae035fbd6fed07c28de705933879a06e48e6a603686ed8e2560a5f6af1f2c24faf4aa960e382186f15eedce9a2491ae730680dd4cf778b70faa86826ab3223477cc91377b19a6d5a2eaea219760beed5
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The public half of the same key encoded as a SubjectPublicKeyInfo.
PublicKey = RSA-2048-SPKI
Type = RSA
Input = 30820122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same key but with a negative RSA modulus.
PublicKey = RSA-2048-SPKI-Negative
@@ -31,21 +35,29 @@
PrivateKey = RSA-512
Type = RSA
Input = 30820154020100300d06092a864886f70d01010105000482013e3082013a020100024100dd20403d976a38c9d79152d87b5c8e9f05033eadd7b7de709bf5b0c4a5182a97d18483526b02362b992e154a9f37faa396ca2685cdab8fec09877ebe705f4dd70203010001024055bebcca655d7e39de8a6eaa9d636db682161907064039544755c53eeb99ec618c03a210dbc61471eaba10c5c365c9726d6b7a96f54d455f7d168d49367270e1022100f21a05d9fd6817301ce49ce10448f9bdd44f5ef5b7557cd7d83155db46382ae7022100e9d1f7157783db2feab1936954ddc4e83aa365695868144cda1be6813b61d791022100d6001eb0040920860ce41fafdf23ca6dfbdf74e6e9f98cf3164cf5c16f9e727d02206f6f73f4b52b10517be6f9bc5f87fa0a3bb817e2e711636b651f9af1c85d4f21022063eff2e57f5b4ca20342cfe793e25526624e3692f192461f9e1ce7f13f2d72c8
+ExpectNoRawPrivate
+ExpectNoRawPublic
# RSA 515 bit key.
PrivateKey = RSA-515
Type = RSA
Input = 30820157020100300d06092a864886f70d0101010500048201413082013d0201000241054fa166e205e658bbe8a2dc35311c0c2b75b7e4569fd9642c8bae809279271fc824f26baa1166ea46298ca63379ea76adbada2b61e5066820a35beaec1aca227f020301000102410266c972be0d30e53ac2acb1aa13b4bd0401cccf212452a66b4615f7e943831f67b4ca48560582d0ca886044aaaaf87945252a848c1947944186e6eb83969bf91102210309e631761842cc8a2ccfd372c20a9cba21de1a199c30ab440bc6b51079f4e825022101bf715c1db432627ca7c29a293b9210f2eff1e92d12f306ebaa5334f8ee03dcd30221018ac58a765f2b8f37d434081fe5ff92b81735ead2f263f4968ccf63d61fbe3d0d0221015b247a1159a2d5a25d0db049593c6405f77f3a278c521d066e290c2a2d8fb59d0221026224aa31fd95c14d24fd03b8a195bba4cc88df7c37f5370a5ab19f882f1404d6
+ExpectNoRawPrivate
+ExpectNoRawPublic
# EC P-256 key
PrivateKey = P-256
Type = EC
Input = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same key as above with the optional public key omitted.
PrivateKey = P-256-MissingPublic
Type = EC
Input = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same key as above with redundant parameters.
PrivateKey = P-256-ExtraParameters
@@ -53,6 +65,8 @@
Input = 308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a00a06082a8648ce3d030107a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
# The key re-encodes with the parameters removed.
Output = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same key, but with the redundant parameters in the ECPrivateKey mismatched.
PrivateKey = P-256-BadInnerParameters
@@ -63,6 +77,8 @@
PublicKey = P-256-SPKI
Type = EC
Input = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same as above, but with the curve explicitly spelled out.
PublicKey = P-256-SPKI
@@ -78,58 +94,84 @@
PrivateKey = DSA-1024
Type = DSA
Input = 308202650201003082023906072a8648ce3804013082022c02820101009e12fab3de12213501dd82aa10ca2d101d2d4ebfef4d2a3f8daa0fe0cedad8d6af85616aa2f3252c0a2b5a6db09e6f14900e0ddb8311876dd8f9669525f99ed65949e184d5064793271169a228680b95ec12f59a8e20b21f2b58eb2a2012d35bde2ee351822fe8f32d0a330565dcce5c672b7259c14b2433d0b5b2ca2b2db0ab626e8f13f47fe0345d904e7294bb038e9ce21a9e580b83356278706cfe768436c69de149ccff98b4aab8cb4f6385c9f102ce59346eaeef27e0ad222d53d6e89cc8cde5776dd00057b03f2d88ab3cedbafd7b585f0b7f7835e17a3728bbf25ea62572f245dc111f3ce39cb6ffacc31b0a2790e7bde90224ea9b09315362af3d2b022100f381dcf53ebf724f8b2e5ca82c010fb4b5eda9358d0fd88ed278589488b54fc3028201000c402a725dcc3a62e02bf4cf43cd17f4a493591220223669cf4193edab423ad08dfb552e308a6a57a5ffbc7cd0fb2087f81f8df0cb08ab2133287d2b6968714a94f633c940845a48a3e16708dde761cc6a8eab2d84db21b6ea5b07681493cc9c31fbc368b243f6ddf8c932a8b4038f44e7b15ca876344a147859f2b43b39458668ad5e0a1a9a669546dd2812e3b3617a0aef99d58e3bb4cc87fd94225e01d2dcc469a77268146c51918f18e8b4d70aa1f0c7623bcc52cf3731d38641b2d2830b7eecb2f09552ff137d046e494e7f33c3590002b16d1b97d936fda28f90c3ed3ca35338168ac16f77c3c57adc2e8f7c6c2256e41a5f65450590dbb5bcf06d66610423022100b0c768702743bc51242993a971a52889795444f7c6452203d0ce84fe6117d46e
+ExpectNoRawPrivate
+ExpectNoRawPublic
# A DSA public key.
PublicKey = DSA-1024-SPKI
Type = DSA
Input = 308201b73082012c06072a8648ce3804013082011f02818100b3429b8b128c9079f9b72e86857e98d265e5d91661ed8b5f4cc56e5eed1e571da30186983a9dd76297eab73ee13a1db841f8800d04a7cab478af6cde2ea4a2868531af169a24858c6268efa39ceb7ed0d4227eb5bbb01124a2a5a26038c7bcfb8cc827f68f5202345166e4718596799b65c9def82828ce44e62e38e41a0d24b1021500c5a56c81ddd87f47e676546c56d05706421624cf0281810094de40d27314fe929e47ff9b1ac65cfc73ef38c4d381c890be6217b15039ae18190e6b421af8c0bda35a5cfd050f58ae2644adce83e68c8e5ba11729df56bbb21e227a60b816cc033fa799a38fe1ba5b4aa1801b6f841ce3df99feb3b4fb96950c960af13fa2ce920aabc12dd24ad2044a35063ea0e25f67f560f4cfbdc5598303818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+ExpectNoRawPrivate
+ExpectNoRawPublic
# The same key as above, but without the parameters.
PublicKey = DSA-1024-SPKI-No-Params
Type = DSA
Input = 308192300906072a8648ce38040103818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+ExpectNoRawPrivate
+ExpectNoRawPublic
# Private keys from RFC 8032.
PrivateKey = Ed25519
Type = Ed25519
Input = 302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
+ExpectRawPrivate = 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
+ExpectRawPublic = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
PrivateKey = Ed25519-2
Type = Ed25519
Input = 302e020100300506032b6570042204204ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb
+ExpectRawPrivate = 4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb
+ExpectRawPublic = 3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
PrivateKey = Ed25519-3
Type = Ed25519
Input = 302e020100300506032b657004220420c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7
+ExpectRawPrivate = c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7
+ExpectRawPublic = fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
PrivateKey = Ed25519-4
Type = Ed25519
Input = 302e020100300506032b657004220420f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5
+ExpectRawPrivate = f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5
+ExpectRawPublic = 278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
PrivateKey = Ed25519-5
Type = Ed25519
Input = 302e020100300506032b657004220420833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42
+ExpectRawPrivate = 833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42
+ExpectRawPublic = ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
# Public keys from RFC 8032.
PublicKey = Ed25519-SPKI
Type = Ed25519
Input = 302a300506032b6570032100d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
+ExpectNoRawPrivate
+ExpectRawPublic = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
PublicKey = Ed25519-SPKI-2
Type = Ed25519
Input = 302a300506032b65700321003d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
+ExpectNoRawPrivate
+ExpectRawPublic = 3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
PublicKey = Ed25519-SPKI-3
Type = Ed25519
Input = 302a300506032b6570032100fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
+ExpectNoRawPrivate
+ExpectRawPublic = fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
PublicKey = Ed25519-SPKI-4
Type = Ed25519
Input = 302a300506032b6570032100278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
+ExpectNoRawPrivate
+ExpectRawPublic = 278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
PublicKey = Ed25519-SPKI-5
Type = Ed25519
Input = 302a300506032b6570032100ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
+ExpectNoRawPrivate
+ExpectRawPublic = ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
# The first key, private and public, with invalid NULL parameters.
PrivateKey = Ed25519-NULL
@@ -1565,3 +1607,42 @@
Input = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
Output = e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b
Error = OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE
+
+
+# Derive tests.
+
+PrivateKey = ECDH-P256-Private
+Type = EC
+Input = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104207d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534
+
+PublicKey = ECDH-P256-Peer
+Type = EC
+Input = 3059301306072a8648ce3d020106082a8648ce3d03010703420004700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac
+
+Derive = ECDH-P256-Private
+DerivePeer = ECDH-P256-Peer
+Output = 46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b
+SmallBufferTruncates
+
+PrivateKey = X25519-Private
+Type = X25519
+Input = 302e020100300506032b656e04220420a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4
+ExpectRawPrivate = a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4
+
+PublicKey = X25519-Peer
+Type = X25519
+Input = 302a300506032b656e032100e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
+ExpectRawPublic = e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
+
+PublicKey = X25519-SmallOrderPeer
+Type = X25519
+ExpectRawPublic = e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800
+Input = 302a300506032b656e032100e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800
+
+Derive = X25519-Private
+DerivePeer = X25519-Peer
+Output = c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552
+
+Derive = X25519-Private
+DerivePeer = X25519-SmallOrderPeer
+Error = INVALID_PEER_KEY
diff --git a/src/crypto/evp/internal.h b/src/crypto/evp/internal.h
index 43847ea..8b6a583 100644
--- a/src/crypto/evp/internal.h
+++ b/src/crypto/evp/internal.h
@@ -96,6 +96,11 @@
// |out|. It returns one on success and zero on error.
int (*priv_encode)(CBB *out, const EVP_PKEY *key);
+ int (*set_priv_raw)(EVP_PKEY *pkey, const uint8_t *in, size_t len);
+ int (*set_pub_raw)(EVP_PKEY *pkey, const uint8_t *in, size_t len);
+ int (*get_priv_raw)(const EVP_PKEY *pkey, uint8_t *out, size_t *out_len);
+ int (*get_pub_raw)(const EVP_PKEY *pkey, uint8_t *out, size_t *out_len);
+
// pkey_opaque returns 1 if the |pk| is opaque. Opaque keys are backed by
// custom implementations which do not expose key material and parameters.
int (*pkey_opaque)(const EVP_PKEY *pk);
@@ -239,14 +244,22 @@
char has_private;
} ED25519_KEY;
+typedef struct {
+ uint8_t pub[32];
+ uint8_t priv[32];
+ char has_private;
+} X25519_KEY;
+
extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ec_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meth;
extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD x25519_asn1_meth;
extern const EVP_PKEY_METHOD rsa_pkey_meth;
extern const EVP_PKEY_METHOD ec_pkey_meth;
extern const EVP_PKEY_METHOD ed25519_pkey_meth;
+extern const EVP_PKEY_METHOD x25519_pkey_meth;
#if defined(__cplusplus)
diff --git a/src/crypto/evp/p_dsa_asn1.c b/src/crypto/evp/p_dsa_asn1.c
index 34b2e70..d50e0fc 100644
--- a/src/crypto/evp/p_dsa_asn1.c
+++ b/src/crypto/evp/p_dsa_asn1.c
@@ -255,6 +255,11 @@
dsa_priv_decode,
dsa_priv_encode,
+ NULL /* set_priv_raw */,
+ NULL /* set_pub_raw */,
+ NULL /* get_priv_raw */,
+ NULL /* get_pub_raw */,
+
NULL /* pkey_opaque */,
int_dsa_size,
diff --git a/src/crypto/evp/p_ec_asn1.c b/src/crypto/evp/p_ec_asn1.c
index 0ad8f38..dedc5e0 100644
--- a/src/crypto/evp/p_ec_asn1.c
+++ b/src/crypto/evp/p_ec_asn1.c
@@ -237,6 +237,11 @@
eckey_priv_decode,
eckey_priv_encode,
+ NULL /* set_priv_raw */,
+ NULL /* set_pub_raw */,
+ NULL /* get_priv_raw */,
+ NULL /* get_pub_raw */,
+
eckey_opaque,
int_ec_size,
diff --git a/src/crypto/evp/p_ed25519.c b/src/crypto/evp/p_ed25519.c
index 062ea45..9149afb 100644
--- a/src/crypto/evp/p_ed25519.c
+++ b/src/crypto/evp/p_ed25519.c
@@ -16,6 +16,7 @@
#include <openssl/curve25519.h>
#include <openssl/err.h>
+#include <openssl/mem.h>
#include "internal.h"
@@ -23,6 +24,27 @@
// Ed25519 has no parameters to copy.
static int pkey_ed25519_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { return 1; }
+static int pkey_ed25519_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+ ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (!EVP_PKEY_set_type(pkey, EVP_PKEY_ED25519)) {
+ OPENSSL_free(key);
+ return 0;
+ }
+
+ uint8_t pubkey_unused[32];
+ ED25519_keypair(pubkey_unused, key->key.priv);
+ key->has_private = 1;
+
+ OPENSSL_free(pkey->pkey.ptr);
+ pkey->pkey.ptr = key;
+ return 1;
+}
+
static int pkey_ed25519_sign_message(EVP_PKEY_CTX *ctx, uint8_t *sig,
size_t *siglen, const uint8_t *tbs,
size_t tbslen) {
@@ -32,12 +54,22 @@
return 0;
}
- *siglen = 64;
if (sig == NULL) {
+ *siglen = 64;
return 1;
}
- return ED25519_sign(sig, tbs, tbslen, key->key.priv);
+ if (*siglen < 64) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!ED25519_sign(sig, tbs, tbslen, key->key.priv)) {
+ return 0;
+ }
+
+ *siglen = 64;
+ return 1;
}
static int pkey_ed25519_verify_message(EVP_PKEY_CTX *ctx, const uint8_t *sig,
@@ -58,7 +90,7 @@
NULL /* init */,
pkey_ed25519_copy,
NULL /* cleanup */,
- NULL /* keygen */,
+ pkey_ed25519_keygen,
NULL /* sign */,
pkey_ed25519_sign_message,
NULL /* verify */,
diff --git a/src/crypto/evp/p_ed25519_asn1.c b/src/crypto/evp/p_ed25519_asn1.c
index ba08c6d..1f996cf 100644
--- a/src/crypto/evp/p_ed25519_asn1.c
+++ b/src/crypto/evp/p_ed25519_asn1.c
@@ -28,45 +28,101 @@
pkey->pkey.ptr = NULL;
}
-static int set_pubkey(EVP_PKEY *pkey, const uint8_t pubkey[32]) {
+static int ed25519_set_priv_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+ if (len != 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
if (key == NULL) {
OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
return 0;
}
- key->has_private = 0;
- OPENSSL_memcpy(key->key.pub.value, pubkey, 32);
+
+ // The RFC 8032 encoding stores only the 32-byte seed, so we must recover the
+ // full representation which we use from it.
+ uint8_t pubkey_unused[32];
+ ED25519_keypair_from_seed(pubkey_unused, key->key.priv, in);
+ key->has_private = 1;
ed25519_free(pkey);
pkey->pkey.ptr = key;
return 1;
}
-static int set_privkey(EVP_PKEY *pkey, const uint8_t privkey[64]) {
+static int ed25519_set_pub_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+ if (len != 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
if (key == NULL) {
OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
return 0;
}
- key->has_private = 1;
- OPENSSL_memcpy(key->key.priv, privkey, 64);
+
+ OPENSSL_memcpy(key->key.pub.value, in, 32);
+ key->has_private = 0;
ed25519_free(pkey);
pkey->pkey.ptr = key;
return 1;
}
+static int ed25519_get_priv_raw(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ const ED25519_KEY *key = pkey->pkey.ptr;
+ if (!key->has_private) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+
+ if (out == NULL) {
+ *out_len = 32;
+ return 1;
+ }
+
+ if (*out_len < 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ // The raw private key format is the first 32 bytes of the private key.
+ OPENSSL_memcpy(out, key->key.priv, 32);
+ *out_len = 32;
+ return 1;
+}
+
+static int ed25519_get_pub_raw(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ const ED25519_KEY *key = pkey->pkey.ptr;
+ if (out == NULL) {
+ *out_len = 32;
+ return 1;
+ }
+
+ if (*out_len < 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ OPENSSL_memcpy(out, key->key.pub.value, 32);
+ *out_len = 32;
+ return 1;
+}
+
static int ed25519_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
// See RFC 8410, section 4.
// The parameters must be omitted. Public keys have length 32.
- if (CBS_len(params) != 0 ||
- CBS_len(key) != 32) {
+ if (CBS_len(params) != 0) {
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
return 0;
}
- return set_pubkey(out, CBS_data(key));
+ return ed25519_set_pub_raw(out, CBS_data(key), CBS_len(key));
}
static int ed25519_pub_encode(CBB *out, const EVP_PKEY *pkey) {
@@ -103,17 +159,12 @@
CBS inner;
if (CBS_len(params) != 0 ||
!CBS_get_asn1(key, &inner, CBS_ASN1_OCTETSTRING) ||
- CBS_len(key) != 0 ||
- CBS_len(&inner) != 32) {
+ CBS_len(key) != 0) {
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
return 0;
}
- // The PKCS#8 encoding stores only the 32-byte seed, so we must recover the
- // full representation which we use from it.
- uint8_t pubkey[32], privkey[64];
- ED25519_keypair_from_seed(pubkey, privkey, CBS_data(&inner));
- return set_privkey(out, privkey);
+ return ed25519_set_priv_raw(out, CBS_data(&inner), CBS_len(&inner));
}
static int ed25519_priv_encode(CBB *out, const EVP_PKEY *pkey) {
@@ -145,7 +196,7 @@
static int ed25519_size(const EVP_PKEY *pkey) { return 64; }
-static int ed25519_bits(const EVP_PKEY *pkey) { return 256; }
+static int ed25519_bits(const EVP_PKEY *pkey) { return 253; }
const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
EVP_PKEY_ED25519,
@@ -156,6 +207,10 @@
ed25519_pub_cmp,
ed25519_priv_decode,
ed25519_priv_encode,
+ ed25519_set_priv_raw,
+ ed25519_set_pub_raw,
+ ed25519_get_priv_raw,
+ ed25519_get_pub_raw,
NULL /* pkey_opaque */,
ed25519_size,
ed25519_bits,
@@ -164,27 +219,3 @@
NULL /* param_cmp */,
ed25519_free,
};
-
-EVP_PKEY *EVP_PKEY_new_ed25519_public(const uint8_t public_key[32]) {
- EVP_PKEY *ret = EVP_PKEY_new();
- if (ret == NULL ||
- !EVP_PKEY_set_type(ret, EVP_PKEY_ED25519) ||
- !set_pubkey(ret, public_key)) {
- EVP_PKEY_free(ret);
- return NULL;
- }
-
- return ret;
-}
-
-EVP_PKEY *EVP_PKEY_new_ed25519_private(const uint8_t private_key[64]) {
- EVP_PKEY *ret = EVP_PKEY_new();
- if (ret == NULL ||
- !EVP_PKEY_set_type(ret, EVP_PKEY_ED25519) ||
- !set_privkey(ret, private_key)) {
- EVP_PKEY_free(ret);
- return NULL;
- }
-
- return ret;
-}
diff --git a/src/crypto/evp/p_rsa_asn1.c b/src/crypto/evp/p_rsa_asn1.c
index 85f6fc8..c097103 100644
--- a/src/crypto/evp/p_rsa_asn1.c
+++ b/src/crypto/evp/p_rsa_asn1.c
@@ -178,6 +178,11 @@
rsa_priv_decode,
rsa_priv_encode,
+ NULL /* set_priv_raw */,
+ NULL /* set_pub_raw */,
+ NULL /* get_priv_raw */,
+ NULL /* get_pub_raw */,
+
rsa_opaque,
int_rsa_size,
diff --git a/src/crypto/evp/p_x25519.c b/src/crypto/evp/p_x25519.c
new file mode 100644
index 0000000..ed7df39
--- /dev/null
+++ b/src/crypto/evp/p_x25519.c
@@ -0,0 +1,110 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/evp.h>
+
+#include <openssl/curve25519.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+
+
+// X25519 has no parameters to copy.
+static int pkey_x25519_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { return 1; }
+
+static int pkey_x25519_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+ X25519_KEY *key = OPENSSL_malloc(sizeof(X25519_KEY));
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (!EVP_PKEY_set_type(pkey, EVP_PKEY_X25519)) {
+ OPENSSL_free(key);
+ return 0;
+ }
+
+ X25519_keypair(key->pub, key->priv);
+ key->has_private = 1;
+
+ OPENSSL_free(pkey->pkey.ptr);
+ pkey->pkey.ptr = key;
+ return 1;
+}
+
+static int pkey_x25519_derive(EVP_PKEY_CTX *ctx, uint8_t *out,
+ size_t *out_len) {
+ if (ctx->pkey == NULL || ctx->peerkey == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
+ return 0;
+ }
+
+ const X25519_KEY *our_key = ctx->pkey->pkey.ptr;
+ const X25519_KEY *peer_key = ctx->peerkey->pkey.ptr;
+ if (our_key == NULL || peer_key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_KEYS_NOT_SET);
+ return 0;
+ }
+
+ if (!our_key->has_private) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+
+ if (out != NULL) {
+ if (*out_len < 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!X25519(out, our_key->priv, peer_key->pub)) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+ return 0;
+ }
+ }
+
+ *out_len = 32;
+ return 1;
+}
+
+static int pkey_x25519_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) {
+ switch (type) {
+ case EVP_PKEY_CTRL_PEER_KEY:
+ // |EVP_PKEY_derive_set_peer| requires the key implement this command,
+ // even if it is a no-op.
+ return 1;
+
+ default:
+ OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+ return 0;
+ }
+}
+
+const EVP_PKEY_METHOD x25519_pkey_meth = {
+ EVP_PKEY_X25519,
+ NULL /* init */,
+ pkey_x25519_copy,
+ NULL /* cleanup */,
+ pkey_x25519_keygen,
+ NULL /* sign */,
+ NULL /* sign_message */,
+ NULL /* verify */,
+ NULL /* verify_message */,
+ NULL /* verify_recover */,
+ NULL /* encrypt */,
+ NULL /* decrypt */,
+ pkey_x25519_derive,
+ NULL /* paramgen */,
+ pkey_x25519_ctrl,
+};
diff --git a/src/crypto/evp/p_x25519_asn1.c b/src/crypto/evp/p_x25519_asn1.c
new file mode 100644
index 0000000..9025623
--- /dev/null
+++ b/src/crypto/evp/p_x25519_asn1.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/evp.h>
+
+#include <openssl/buf.h>
+#include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
+#include <openssl/err.h>
+#include <openssl/mem.h>
+
+#include "internal.h"
+#include "../internal.h"
+
+
+static void x25519_free(EVP_PKEY *pkey) {
+ OPENSSL_free(pkey->pkey.ptr);
+ pkey->pkey.ptr = NULL;
+}
+
+static int x25519_set_priv_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+ if (len != 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
+ X25519_KEY *key = OPENSSL_malloc(sizeof(X25519_KEY));
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ OPENSSL_memcpy(key->priv, in, 32);
+ X25519_public_from_private(key->pub, key->priv);
+ key->has_private = 1;
+
+ x25519_free(pkey);
+ pkey->pkey.ptr = key;
+ return 1;
+}
+
+static int x25519_set_pub_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+ if (len != 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
+ X25519_KEY *key = OPENSSL_malloc(sizeof(X25519_KEY));
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ OPENSSL_memcpy(key->pub, in, 32);
+ key->has_private = 0;
+
+ x25519_free(pkey);
+ pkey->pkey.ptr = key;
+ return 1;
+}
+
+static int x25519_get_priv_raw(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ const X25519_KEY *key = pkey->pkey.ptr;
+ if (!key->has_private) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+
+ if (out == NULL) {
+ *out_len = 32;
+ return 1;
+ }
+
+ if (*out_len < 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ OPENSSL_memcpy(out, key->priv, 32);
+ *out_len = 32;
+ return 1;
+}
+
+static int x25519_get_pub_raw(const EVP_PKEY *pkey, uint8_t *out,
+ size_t *out_len) {
+ const X25519_KEY *key = pkey->pkey.ptr;
+ if (out == NULL) {
+ *out_len = 32;
+ return 1;
+ }
+
+ if (*out_len < 32) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ OPENSSL_memcpy(out, key->pub, 32);
+ *out_len = 32;
+ return 1;
+}
+
+static int x25519_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
+ // See RFC 8410, section 4.
+
+ // The parameters must be omitted. Public keys have length 32.
+ if (CBS_len(params) != 0) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
+ return x25519_set_pub_raw(out, CBS_data(key), CBS_len(key));
+}
+
+static int x25519_pub_encode(CBB *out, const EVP_PKEY *pkey) {
+ const X25519_KEY *key = pkey->pkey.ptr;
+
+ // See RFC 8410, section 4.
+ CBB spki, algorithm, oid, key_bitstring;
+ if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&oid, x25519_asn1_meth.oid, x25519_asn1_meth.oid_len) ||
+ !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
+ !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
+ !CBB_add_bytes(&key_bitstring, key->pub, 32) ||
+ !CBB_flush(out)) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int x25519_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
+ const X25519_KEY *a_key = a->pkey.ptr;
+ const X25519_KEY *b_key = b->pkey.ptr;
+ return OPENSSL_memcmp(a_key->pub, b_key->pub, 32) == 0;
+}
+
+static int x25519_priv_decode(EVP_PKEY *out, CBS *params, CBS *key) {
+ // See RFC 8410, section 7.
+
+ // Parameters must be empty. The key is a 32-byte value wrapped in an extra
+ // OCTET STRING layer.
+ CBS inner;
+ if (CBS_len(params) != 0 ||
+ !CBS_get_asn1(key, &inner, CBS_ASN1_OCTETSTRING) ||
+ CBS_len(key) != 0) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+ return 0;
+ }
+
+ return x25519_set_priv_raw(out, CBS_data(&inner), CBS_len(&inner));
+}
+
+static int x25519_priv_encode(CBB *out, const EVP_PKEY *pkey) {
+ X25519_KEY *key = pkey->pkey.ptr;
+ if (!key->has_private) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+
+ // See RFC 8410, section 7.
+ CBB pkcs8, algorithm, oid, private_key, inner;
+ if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
+ !CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&oid, x25519_asn1_meth.oid, x25519_asn1_meth.oid_len) ||
+ !CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
+ !CBB_add_asn1(&private_key, &inner, CBS_ASN1_OCTETSTRING) ||
+ // The PKCS#8 encoding stores only the 32-byte seed which is the first 32
+ // bytes of the private key.
+ !CBB_add_bytes(&inner, key->priv, 32) ||
+ !CBB_flush(out)) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int x25519_size(const EVP_PKEY *pkey) { return 32; }
+
+static int x25519_bits(const EVP_PKEY *pkey) { return 253; }
+
+const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = {
+ EVP_PKEY_X25519,
+ {0x2b, 0x65, 0x6e},
+ 3,
+ x25519_pub_decode,
+ x25519_pub_encode,
+ x25519_pub_cmp,
+ x25519_priv_decode,
+ x25519_priv_encode,
+ x25519_set_priv_raw,
+ x25519_set_pub_raw,
+ x25519_get_priv_raw,
+ x25519_get_pub_raw,
+ NULL /* pkey_opaque */,
+ x25519_size,
+ x25519_bits,
+ NULL /* param_missing */,
+ NULL /* param_copy */,
+ NULL /* param_cmp */,
+ x25519_free,
+};
+
+int EVP_PKEY_set1_tls_encodedpoint(EVP_PKEY *pkey, const uint8_t *in,
+ size_t len) {
+ // TODO(davidben): In OpenSSL, this function also works for |EVP_PKEY_EC|
+ // keys. Add support if it ever comes up.
+ if (pkey->type != EVP_PKEY_X25519) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+ return 0;
+ }
+
+ return x25519_set_pub_raw(pkey, in, len);
+}
+
+size_t EVP_PKEY_get1_tls_encodedpoint(const EVP_PKEY *pkey, uint8_t **out_ptr) {
+ // TODO(davidben): In OpenSSL, this function also works for |EVP_PKEY_EC|
+ // keys. Add support if it ever comes up.
+ if (pkey->type != EVP_PKEY_X25519) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE);
+ return 0;
+ }
+
+ const X25519_KEY *key = pkey->pkey.ptr;
+ if (key == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET);
+ return 0;
+ }
+
+ *out_ptr = BUF_memdup(key->pub, 32);
+ return *out_ptr == NULL ? 0 : 32;
+}
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index d1e2cb9..e978820 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -129,6 +129,10 @@
perlasm(x86-mont.${ASM_EXT} bn/asm/x86-mont.pl)
if(FIPS_DELOCATE)
+ if(FIPS_SHARED)
+ error("Can't set both delocate and shared mode for FIPS build")
+ endif()
+
if(OPENSSL_NO_ASM)
# If OPENSSL_NO_ASM was defined then ASM will not have been enabled, but in
# FIPS mode we have to have it because the module build requires going via
@@ -189,12 +193,49 @@
OBJECT
+ fips_shared_support.c
is_fips.c
)
add_dependencies(fipsmodule global_target)
set_target_properties(fipsmodule PROPERTIES LINKER_LANGUAGE C)
+elseif(FIPS_SHARED)
+ if(NOT BUILD_SHARED_LIBS)
+ error("FIPS_SHARED set but not BUILD_SHARED_LIBS")
+ endif()
+
+ add_library(
+ fipsmodule
+
+ OBJECT
+
+ fips_shared_support.c
+ is_fips.c
+ )
+
+ add_dependencies(fipsmodule global_target)
+
+ add_library(
+ bcm_library
+
+ STATIC
+
+ bcm.c
+
+ ${BCM_ASM_SOURCES}
+ )
+
+ add_dependencies(bcm_library global_target)
+
+ add_custom_command(
+ OUTPUT bcm.o
+ COMMAND ld -r -T ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared.lds -o bcm.o --whole-archive $<TARGET_FILE:bcm_library>
+ DEPENDS bcm_library fips_shared.lds
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ )
+
+ add_custom_target(bcm_o_target DEPENDS bcm.o)
else()
add_library(
fipsmodule
@@ -202,6 +243,7 @@
OBJECT
bcm.c
+ fips_shared_support.c
is_fips.c
${BCM_ASM_SOURCES}
diff --git a/src/crypto/fipsmodule/FIPS.md b/src/crypto/fipsmodule/FIPS.md
index a60e2bf..69719b6 100644
--- a/src/crypto/fipsmodule/FIPS.md
+++ b/src/crypto/fipsmodule/FIPS.md
@@ -13,9 +13,9 @@
## Running CAVP tests
-CAVP results are calculated by `fipstools/cavp`, but that binary is almost always run by `fipstools/run_cavp.go`. The latter knows the set of tests to be processed and the flags needed to configure `cavp` for each one. It must be run from the top of a CAVP directory and needs the following options:
+CAVP results are calculated by `util/fipstools/cavp`, but that binary is almost always run by `util/fipstools/run_cavp.go`. The latter knows the set of tests to be processed and the flags needed to configure `cavp` for each one. It must be run from the top of a CAVP directory and needs the following options:
-1. `-oracle-bin`: points to the location of `fipstools/cavp`
+1. `-oracle-bin`: points to the location of `util/fipstools/cavp`
2. `-no-fax`: this is needed to suppress checking of the FAX files, which are only included in sample sets.
## Breaking power-on and continuous tests
@@ -62,9 +62,25 @@
BoringCrypto is linked (often statically) into a large number of binaries. It would be a significant cost if each of these binaries had to be post-processed in order to calculate the known-good HMAC value. We would much prefer if the value could be calculated, once, when BoringCrypto itself is compiled.
-In order for the value to be calculated before the final link, there can be no relocations in the hashed code and data. This document describes how we build C and assembly code in order to produce an object file containing all the code and data for the FIPS module without that code having any relocations.
+In order for the value to be calculated before the final link, there can be no relocations in the hashed code and data. This document describes how we build C and assembly code in order to produce a binary file containing all the code and data for the FIPS module without that code having any relocations.
-First, all the C source files for the module are compiled as a single unit by compiling a single source file that `#include`s them all (this is `bcm.c`). The `-fPIC` flag is used to cause the compiler to use IP-relative addressing in many (but not all) cases. Also the `-S` flag is used to instruct the compiler to produce a textual assembly file rather than a binary object file.
+There are two build configurations supported: static and shared. The shared build produces `libcrypto.so`, which includes the FIPS module and is significantly more straightforward and so is described first:
+
+### Shared build
+
+First, all the C source files for the module are compiled as a single unit by compiling a single source file that `#include`s them all (this is `bcm.c`). This, along with some assembly sources, comprise the FIPS module.
+
+The object files resulting from compiling (or assembling) those files is linked in partial-linking mode with a linker script that causes the linker to insert symbols marking the beginning and end of the text and rodata sections. The linker script also discards other types of data sections to ensure that no unhashed data is used by the module.
+
+One source of such data are `rel.ro` sections, which contain data that includes function pointers. Since these function pointers are absolute, they are written by the dynamic linker at run-time and so we must eliminate them. The pattern that causes them is when we have a static `EVP_MD` or `EVP_CIPHER` object thus, inside the module, this pattern is changed to instead reserve space in the BSS for the object, and to add a `CRYPTO_once_t` to protect its initialisation.
+
+Once the partially-linked result is linked again, with other parts of libcrypto, to produce `libcrypto.so`, the contents of the module are fixed, as required. The module code uses the linker-added symbols to find the its code and data at run-time and hashes them upon initialisation. The result is compared against a value stored inside `libcrypto.so`, but outside of the module. That value will, initially, be incorrect, but `inject-hash.go` can inject the correct value.
+
+### Static build
+
+The static build cannot depend on the shared-object link to resolve relocations and thus must take another path.
+
+As with the shared build, all the C sources are build in a single compilation unit. The `-fPIC` flag is used to cause the compiler to use IP-relative addressing in many (but not all) cases. Also the `-S` flag is used to instruct the compiler to produce a textual assembly file rather than a binary object file.
The textual assembly file is then processed by a script to merge in assembly implementations of some primitives and to eliminate the remaining sources of relocations.
@@ -80,9 +96,9 @@
##### Read-only data
-Normally read-only data is placed in a `.data` segment that doesn't get mapped into memory with execute permissions. However, the offset of the data segment from the text segment is another thing that isn't determined until the final link. In order to fix data offsets before the link, read-only data is simply placed in the module's `.text` segment. This might make building ROP chains easier for an attacker, but so it goes.
+Normally read-only data is placed in an `.rodata` segment that doesn't get mapped into memory with execute permissions. However, the offset of the data segment from the text segment is another thing that isn't determined until the final link. In order to fix data offsets before the link, read-only data is simply placed in the module's `.text` segment. This might make building ROP chains easier for an attacker, but so it goes.
-One special case is `rel.ro` data, which is data that contains function pointers. Since these function pointers are absolute, they are written by the dynamic linker at run-time and so we must eliminate them. The pattern that causes them is when we have a static `EVP_MD` or `EVP_CIPHER` object thus, inside the module, we'll change this pattern to instead to reserve space in the BSS for the object, and add a `CRYPTO_once_t` to protect its initialisation. The script will generate functions outside of the module that return pointers to these areas of memory—they effectively act like a special-purpose malloc calls that cannot fail.
+Data containing function pointers remains an issue. The source-code changes described above for the shared build apply here too, but no direct references to a BSS section are possible because the offset to that section is not known at compile time. Instead, the script generates functions outside of the module that return pointers to these areas of memory—they effectively act like special-purpose malloc calls that cannot fail.
##### Read-write data
@@ -92,7 +108,7 @@
##### Other transforms
-The script performs a number of other transformations which are worth noting but do not warrant their own sections:
+The script performs a number of other transformations which are worth noting but do not warrant their own discussions:
1. It duplicates each global symbol with a local symbol that has `_local_target` appended to the name. References to the global symbols are rewritten to use these duplicates instead. Otherwise, although the generated code uses IP-relative references, relocations are emitted for global symbols in case they are overridden by a different object file during the link.
1. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations.
@@ -112,15 +128,15 @@
(This is based on reading OpenSSL's [user guide](https://www.openssl.org/docs/fips/UserGuide-2.0.pdf) and inspecting the code of OpenSSL FIPS 2.0.12.)
-OpenSSL's solution to this problem is broadly similar but has a number of differences:
+OpenSSL's solution to this problem is very similar to our shared build, with just a few differences:
1. OpenSSL deals with run-time relocations by not hashing parts of the module's data.
-1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler.
-1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly.
+1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto's static build, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler.
+1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly for the static build, or uses a linker script for the shared build.
1. OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringCrypto calculates it prior to the final link and injects it into the object file.
1. OpenSSL references read-write data directly, since it can know the offsets to it. BoringCrypto indirects these loads and stores.
1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called. BoringCrypto does it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringCrypto will crash.
-1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation, the final integrity check is unbroken through the linking process.
+1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation (in the static case), the final integrity check is unbroken through the linking process.
Some of the similarities are worth noting:
diff --git a/src/crypto/fipsmodule/aes/aes_test.cc b/src/crypto/fipsmodule/aes/aes_test.cc
index 1f9a491..7fadb35 100644
--- a/src/crypto/fipsmodule/aes/aes_test.cc
+++ b/src/crypto/fipsmodule/aes/aes_test.cc
@@ -122,12 +122,40 @@
ciphertext.data(), ciphertext.size()));
}
+static void TestKeyWrapWithPadding(FileTest *t) {
+ std::vector<uint8_t> key, plaintext, ciphertext;
+ ASSERT_TRUE(t->GetBytes(&key, "Key"));
+ ASSERT_TRUE(t->GetBytes(&plaintext, "Plaintext"));
+ ASSERT_TRUE(t->GetBytes(&ciphertext, "Ciphertext"));
+
+ // Test encryption.
+ AES_KEY aes_key;
+ ASSERT_EQ(0, AES_set_encrypt_key(key.data(), 8 * key.size(), &aes_key));
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[plaintext.size() + 15]);
+ size_t len;
+ ASSERT_TRUE(AES_wrap_key_padded(&aes_key, buf.get(), &len,
+ plaintext.size() + 15, plaintext.data(),
+ plaintext.size()));
+ EXPECT_EQ(Bytes(ciphertext), Bytes(buf.get(), static_cast<size_t>(len)));
+
+ // Test decryption
+ ASSERT_EQ(0, AES_set_decrypt_key(key.data(), 8 * key.size(), &aes_key));
+ buf.reset(new uint8_t[ciphertext.size() - 8]);
+ ASSERT_TRUE(AES_unwrap_key_padded(&aes_key, buf.get(), &len,
+ ciphertext.size() - 8, ciphertext.data(),
+ ciphertext.size()));
+ ASSERT_EQ(len, plaintext.size());
+ EXPECT_EQ(Bytes(plaintext), Bytes(buf.get(), static_cast<size_t>(len)));
+}
+
TEST(AESTest, TestVectors) {
FileTestGTest("crypto/fipsmodule/aes/aes_tests.txt", [](FileTest *t) {
if (t->GetParameter() == "Raw") {
TestRaw(t);
} else if (t->GetParameter() == "KeyWrap") {
TestKeyWrap(t);
+ } else if (t->GetParameter() == "KeyWrapWithPadding") {
+ TestKeyWrapWithPadding(t);
} else {
ADD_FAILURE() << "Unknown mode " << t->GetParameter();
}
@@ -172,6 +200,48 @@
});
}
+TEST(AESTest, WycheproofKeyWrapWithPadding) {
+ FileTestGTest("third_party/wycheproof_testvectors/kwp_test.txt",
+ [](FileTest *t) {
+ std::string key_size;
+ ASSERT_TRUE(t->GetInstruction(&key_size, "keySize"));
+ std::vector<uint8_t> ct, key, msg;
+ ASSERT_TRUE(t->GetBytes(&ct, "ct"));
+ ASSERT_TRUE(t->GetBytes(&key, "key"));
+ ASSERT_TRUE(t->GetBytes(&msg, "msg"));
+ ASSERT_EQ(static_cast<unsigned>(atoi(key_size.c_str())), key.size() * 8);
+ WycheproofResult result;
+ ASSERT_TRUE(GetWycheproofResult(t, &result));
+
+ // Wycheproof contains test vectors with empty messages that it believes
+ // should pass. However, both RFC 5649 and SP 800-38F section 5.3.1 say that
+ // the minimum length is one. Therefore we consider test cases with an empty
+ // message to be invalid.
+ if (result != WycheproofResult::kInvalid && !msg.empty()) {
+ AES_KEY aes;
+ ASSERT_EQ(0, AES_set_decrypt_key(key.data(), 8 * key.size(), &aes));
+ std::vector<uint8_t> out(ct.size() - 8);
+ size_t len;
+ ASSERT_TRUE(AES_unwrap_key_padded(&aes, out.data(), &len, ct.size() - 8,
+ ct.data(), ct.size()));
+ EXPECT_EQ(Bytes(msg), Bytes(out.data(), len));
+
+ out.resize(msg.size() + 15);
+ ASSERT_EQ(0, AES_set_encrypt_key(key.data(), 8 * key.size(), &aes));
+ ASSERT_TRUE(AES_wrap_key_padded(&aes, out.data(), &len, msg.size() + 15,
+ msg.data(), msg.size()));
+ EXPECT_EQ(Bytes(ct), Bytes(out.data(), len));
+ } else {
+ AES_KEY aes;
+ ASSERT_EQ(0, AES_set_decrypt_key(key.data(), 8 * key.size(), &aes));
+ std::vector<uint8_t> out(ct.size());
+ size_t len;
+ ASSERT_FALSE(AES_unwrap_key_padded(&aes, out.data(), &len, ct.size(),
+ ct.data(), ct.size()));
+ }
+ });
+}
+
TEST(AESTest, WrapBadLengths) {
uint8_t key[128/8] = {0};
AES_KEY aes;
diff --git a/src/crypto/fipsmodule/aes/aes_tests.txt b/src/crypto/fipsmodule/aes/aes_tests.txt
index d4e4c61..efbe294 100644
--- a/src/crypto/fipsmodule/aes/aes_tests.txt
+++ b/src/crypto/fipsmodule/aes/aes_tests.txt
@@ -48,3 +48,16 @@
Key = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
Plaintext = 00112233445566778899aabbccddeeff000102030405060708090a0b0c0d0e0f
Ciphertext = 28c9f404c4b810f4cbccb35cfb87f8263f5786e2d80ed326cbc7f0e71a99f43bfb988b9b7a02dd21
+
+
+# Test vectors from https://tools.ietf.org/html/rfc5649#section-6
+
+Mode = KeyWrapWithPadding
+Key = 5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8
+Plaintext = c37b7e6492584340bed12207808941155068f738
+Ciphertext = 138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a
+
+Mode = KeyWrapWithPadding
+Key = 5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8
+Plaintext = 466f7250617369
+Ciphertext = afbeb0f07dfbf5419200f2ccb50bb24f
diff --git a/src/crypto/fipsmodule/aes/asm/aes-586.pl b/src/crypto/fipsmodule/aes/asm/aes-586.pl
index 25f1813..9b373de 100755
--- a/src/crypto/fipsmodule/aes/asm/aes-586.pl
+++ b/src/crypto/fipsmodule/aes/asm/aes-586.pl
@@ -2997,4 +2997,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aes-armv4.pl b/src/crypto/fipsmodule/aes/asm/aes-armv4.pl
index f4ae922..9eebb22 100644
--- a/src/crypto/fipsmodule/aes/asm/aes-armv4.pl
+++ b/src/crypto/fipsmodule/aes/asm/aes-armv4.pl
@@ -1247,4 +1247,4 @@
close SELF;
print $code;
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/aes/asm/aes-x86_64.pl b/src/crypto/fipsmodule/aes/asm/aes-x86_64.pl
index ea8b9a4..5b95785 100755
--- a/src/crypto/fipsmodule/aes/asm/aes-x86_64.pl
+++ b/src/crypto/fipsmodule/aes/asm/aes-x86_64.pl
@@ -2906,4 +2906,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesni-x86.pl b/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
index fcb5b98..d57127a 100644
--- a/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
@@ -2551,4 +2551,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl b/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
index b608425..15f6805 100644
--- a/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
@@ -5108,4 +5108,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl b/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
index 201da1a..62d4842 100644
--- a/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
@@ -1,5 +1,5 @@
#! /usr/bin/env perl
-# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2014-2018 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
@@ -40,6 +40,8 @@
# CBC en-/decrypt CTR XTS
# POWER8[le] 3.96/0.72 0.74 1.1
# POWER8[be] 3.75/0.65 0.66 1.0
+# POWER9[le] 4.02/0.86 0.84 1.05
+# POWER9[be] 3.99/0.78 0.79 0.97
$flavour = shift;
@@ -1827,7 +1829,7 @@
stvx_u $out1,$x10,$out
stvx_u $out2,$x20,$out
addi $out,$out,0x30
- b Lcbc_dec8x_done
+ b Lctr32_enc8x_done
.align 5
Lctr32_enc8x_two:
@@ -1839,7 +1841,7 @@
stvx_u $out0,$x00,$out
stvx_u $out1,$x10,$out
addi $out,$out,0x20
- b Lcbc_dec8x_done
+ b Lctr32_enc8x_done
.align 5
Lctr32_enc8x_one:
@@ -3802,4 +3804,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl b/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
index 13f86a0..187c221 100644
--- a/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
@@ -1018,4 +1018,4 @@
}
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl b/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
index d4db3b4..932b3b6 100644
--- a/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
+++ b/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
@@ -2431,4 +2431,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl b/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
index 5fa06d8..bae5e7e 100755
--- a/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
@@ -1359,4 +1359,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl b/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
index 81e7e84..2b40362 100644
--- a/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
@@ -920,4 +920,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl b/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
index 9429344..45463e8 100644
--- a/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
@@ -1550,4 +1550,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/key_wrap.c b/src/crypto/fipsmodule/aes/key_wrap.c
index a52c983..9a5b28d 100644
--- a/src/crypto/fipsmodule/aes/key_wrap.c
+++ b/src/crypto/fipsmodule/aes/key_wrap.c
@@ -48,6 +48,7 @@
#include <openssl/aes.h>
+#include <assert.h>
#include <limits.h>
#include <string.h>
@@ -100,18 +101,17 @@
return (int)in_len + 8;
}
-int AES_unwrap_key(const AES_KEY *key, const uint8_t *iv, uint8_t *out,
- const uint8_t *in, size_t in_len) {
+// aes_unwrap_key_inner performs steps one and two from
+// https://tools.ietf.org/html/rfc3394#section-2.2.2
+static int aes_unwrap_key_inner(const AES_KEY *key, uint8_t *out,
+ uint8_t out_iv[8], const uint8_t *in,
+ size_t in_len) {
// See RFC 3394, section 2.2.2. Additionally, note that section 2 requires the
// plaintext be at least two 8-byte blocks, so the ciphertext must be at least
// three blocks.
if (in_len > INT_MAX || in_len < 24 || in_len % 8 != 0) {
- return -1;
- }
-
- if (iv == NULL) {
- iv = kDefaultIV;
+ return 0;
}
uint8_t A[AES_BLOCK_SIZE];
@@ -133,9 +133,104 @@
}
}
- if (CRYPTO_memcmp(A, iv, 8) != 0) {
+ memcpy(out_iv, A, 8);
+ return 1;
+}
+
+int AES_unwrap_key(const AES_KEY *key, const uint8_t *iv, uint8_t *out,
+ const uint8_t *in, size_t in_len) {
+ uint8_t calculated_iv[8];
+ if (!aes_unwrap_key_inner(key, out, calculated_iv, in, in_len)) {
+ return -1;
+ }
+
+ if (iv == NULL) {
+ iv = kDefaultIV;
+ }
+ if (CRYPTO_memcmp(calculated_iv, iv, 8) != 0) {
return -1;
}
return (int)in_len - 8;
}
+
+// kPaddingConstant is used in Key Wrap with Padding. See
+// https://tools.ietf.org/html/rfc5649#section-3
+static const uint8_t kPaddingConstant[4] = {0xa6, 0x59, 0x59, 0xa6};
+
+int AES_wrap_key_padded(const AES_KEY *key, uint8_t *out, size_t *out_len,
+ size_t max_out, const uint8_t *in, size_t in_len) {
+ // See https://tools.ietf.org/html/rfc5649#section-4.1
+ const uint32_t in_len32_be = CRYPTO_bswap4(in_len);
+ const uint64_t in_len64 = in_len;
+ const size_t padded_len = (in_len + 7) & ~7;
+
+ *out_len = 0;
+ if (in_len == 0 || in_len64 > 0xffffffffu || in_len + 7 < in_len ||
+ padded_len + 8 < padded_len || max_out < padded_len + 8) {
+ return 0;
+ }
+
+ uint8_t block[AES_BLOCK_SIZE];
+ memcpy(block, kPaddingConstant, sizeof(kPaddingConstant));
+ memcpy(block + 4, &in_len32_be, sizeof(in_len32_be));
+
+ if (in_len <= 8) {
+ memset(block + 8, 0, 8);
+ memcpy(block + 8, in, in_len);
+ AES_encrypt(block, out, key);
+ *out_len = AES_BLOCK_SIZE;
+ return 1;
+ }
+
+ uint8_t *padded_in = OPENSSL_malloc(padded_len);
+ if (padded_in == NULL) {
+ return 0;
+ }
+ assert(padded_len >= 8);
+ memset(padded_in + padded_len - 8, 0, 8);
+ memcpy(padded_in, in, in_len);
+ const int ret = AES_wrap_key(key, block, out, padded_in, padded_len);
+ OPENSSL_free(padded_in);
+ if (ret < 0) {
+ return 0;
+ }
+ *out_len = ret;
+ return 1;
+}
+
+int AES_unwrap_key_padded(const AES_KEY *key, uint8_t *out, size_t *out_len,
+ size_t max_out, const uint8_t *in, size_t in_len) {
+ *out_len = 0;
+ if (in_len < AES_BLOCK_SIZE || max_out < in_len - 8) {
+ return 0;
+ }
+
+ uint8_t iv[8];
+ if (in_len == AES_BLOCK_SIZE) {
+ uint8_t block[AES_BLOCK_SIZE];
+ AES_decrypt(in, block, key);
+ memcpy(iv, block, sizeof(iv));
+ memcpy(out, block + 8, 8);
+ } else if (!aes_unwrap_key_inner(key, out, iv, in, in_len)) {
+ return 0;
+ }
+ assert(in_len % 8 == 0);
+
+ crypto_word_t ok = constant_time_eq_int(
+ CRYPTO_memcmp(iv, kPaddingConstant, sizeof(kPaddingConstant)), 0);
+
+ uint32_t claimed_len32;
+ memcpy(&claimed_len32, iv + 4, sizeof(claimed_len32));
+ const size_t claimed_len = CRYPTO_bswap4(claimed_len32);
+ ok &= ~constant_time_is_zero_w(claimed_len);
+ ok &= constant_time_eq_w((claimed_len - 1) >> 3, (in_len - 9) >> 3);
+
+ // Check that padding bytes are all zero.
+ for (size_t i = in_len - 15; i < in_len - 8; i++) {
+ ok &= constant_time_is_zero_w(constant_time_ge_8(i, claimed_len) & out[i]);
+ }
+
+ *out_len = constant_time_select_w(ok, claimed_len, 0);
+ return ok & 1;
+}
diff --git a/src/crypto/fipsmodule/bcm.c b/src/crypto/fipsmodule/bcm.c
index e15ecb8..7666222 100644
--- a/src/crypto/fipsmodule/bcm.c
+++ b/src/crypto/fipsmodule/bcm.c
@@ -76,7 +76,6 @@
#include "md4/md4.c"
#include "md5/md5.c"
#include "modes/cbc.c"
-#include "modes/ccm.c"
#include "modes/cfb.c"
#include "modes/ctr.c"
#include "modes/gcm.c"
@@ -100,11 +99,16 @@
#if defined(BORINGSSL_FIPS)
#if !defined(OPENSSL_ASAN)
-// These symbols are filled in by delocate.go. They point to the start and end
-// of the module, and the location of the integrity hash, respectively.
+// These symbols are filled in by delocate.go (in static builds) or a linker
+// script (in shared builds). They point to the start and end of the module, and
+// the location of the integrity hash, respectively.
extern const uint8_t BORINGSSL_bcm_text_start[];
extern const uint8_t BORINGSSL_bcm_text_end[];
extern const uint8_t BORINGSSL_bcm_text_hash[];
+#if defined(BORINGSSL_SHARED_LIBRARY)
+extern const uint8_t BORINGSSL_bcm_rodata_start[];
+extern const uint8_t BORINGSSL_bcm_rodata_end[];
+#endif
#endif
static void __attribute__((constructor))
@@ -116,17 +120,39 @@
// .text section, which triggers the global-buffer overflow detection.
const uint8_t *const start = BORINGSSL_bcm_text_start;
const uint8_t *const end = BORINGSSL_bcm_text_end;
+#if defined(BORINGSSL_SHARED_LIBRARY)
+ const uint8_t *const rodata_start = BORINGSSL_bcm_rodata_start;
+ const uint8_t *const rodata_end = BORINGSSL_bcm_rodata_end;
+#endif
static const uint8_t kHMACKey[64] = {0};
uint8_t result[SHA512_DIGEST_LENGTH];
unsigned result_len;
- if (!HMAC(EVP_sha512(), kHMACKey, sizeof(kHMACKey), start, end - start,
- result, &result_len) ||
+ HMAC_CTX hmac_ctx;
+ HMAC_CTX_init(&hmac_ctx);
+ if (!HMAC_Init_ex(&hmac_ctx, kHMACKey, sizeof(kHMACKey), EVP_sha512(),
+ NULL /* no ENGINE */)) {
+ fprintf(stderr, "HMAC_Init_ex failed.\n");
+ goto err;
+ }
+#if defined(BORINGSSL_SHARED_LIBRARY)
+ uint64_t length = end - start;
+ HMAC_Update(&hmac_ctx, (const uint8_t *) &length, sizeof(length));
+ HMAC_Update(&hmac_ctx, start, length);
+
+ length = rodata_end - rodata_start;
+ HMAC_Update(&hmac_ctx, (const uint8_t *) &length, sizeof(length));
+ HMAC_Update(&hmac_ctx, rodata_start, length);
+#else
+ HMAC_Update(&hmac_ctx, start, end - start);
+#endif
+ if (!HMAC_Final(&hmac_ctx, result, &result_len) ||
result_len != sizeof(result)) {
fprintf(stderr, "HMAC failed.\n");
goto err;
}
+ HMAC_CTX_cleanup(&hmac_ctx);
const uint8_t *expected = BORINGSSL_bcm_text_hash;
diff --git a/src/crypto/fipsmodule/bn/asm/armv4-mont.pl b/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
index 2ee389e..f3aa4be 100644
--- a/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
@@ -759,4 +759,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/armv8-mont.pl b/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
index aab9eaa..db2ba49 100644
--- a/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
@@ -1507,4 +1507,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/bn-586.pl b/src/crypto/fipsmodule/bn/asm/bn-586.pl
index 16818d5..05ef28c 100644
--- a/src/crypto/fipsmodule/bn/asm/bn-586.pl
+++ b/src/crypto/fipsmodule/bn/asm/bn-586.pl
@@ -31,7 +31,7 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
sub bn_mul_add_words
{
diff --git a/src/crypto/fipsmodule/bn/asm/co-586.pl b/src/crypto/fipsmodule/bn/asm/co-586.pl
index 5eeeef9..abe328a 100644
--- a/src/crypto/fipsmodule/bn/asm/co-586.pl
+++ b/src/crypto/fipsmodule/bn/asm/co-586.pl
@@ -22,7 +22,7 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
sub mul_add_c
{
diff --git a/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl b/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
index 51feb69..a0da239 100755
--- a/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
+++ b/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
@@ -1940,4 +1940,4 @@
___
}}}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86-mont.pl b/src/crypto/fipsmodule/bn/asm/x86-mont.pl
index 214f2b0..1f61ae5 100755
--- a/src/crypto/fipsmodule/bn/asm/x86-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86-mont.pl
@@ -628,4 +628,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl b/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
index 3d98e72..0a9e4d1 100755
--- a/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
@@ -1578,4 +1578,4 @@
}
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl b/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
index abcfe6a..b2ff114 100755
--- a/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
@@ -3930,4 +3930,4 @@
$code =~ s/\`([^\`]*)\`/eval($1)/gem;
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/delocate.h b/src/crypto/fipsmodule/delocate.h
index 59effde..d6564e4 100644
--- a/src/crypto/fipsmodule/delocate.h
+++ b/src/crypto/fipsmodule/delocate.h
@@ -20,7 +20,8 @@
#include "../internal.h"
-#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN) && !defined(OPENSSL_MSAN)
+#if !defined(BORINGSSL_SHARED_LIBRARY) && defined(BORINGSSL_FIPS) && \
+ !defined(OPENSSL_ASAN) && !defined(OPENSSL_MSAN)
#define DEFINE_BSS_GET(type, name) \
static type name __attribute__((used)); \
type *name##_bss_get(void) __attribute__((const));
diff --git a/src/crypto/fipsmodule/digest/digest.c b/src/crypto/fipsmodule/digest/digest.c
index 6705867..68e81c4 100644
--- a/src/crypto/fipsmodule/digest/digest.c
+++ b/src/crypto/fipsmodule/digest/digest.c
@@ -115,6 +115,11 @@
void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); }
+int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, uint8_t *out, size_t len) {
+ OPENSSL_PUT_ERROR(DIGEST, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+ return 0;
+}
+
int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
// |in->digest| may be NULL if this is a signing |EVP_MD_CTX| for, e.g.,
// Ed25519 which does not hash with |EVP_MD_CTX|.
diff --git a/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl b/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
index 5402885..994cb82 100755
--- a/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
+++ b/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
@@ -3112,17 +3112,24 @@
or $acc5, $acc4 # see if result is zero
or $acc0, $acc4
- or $acc1, $acc4
+ or $acc1, $acc4 # !is_equal(U1, U2)
- .byte 0x3e # predict taken
- jnz .Ladd_proceed$x # is_equal(U1,U2)?
movq %xmm2, $acc0
movq %xmm3, $acc1
- test $acc0, $acc0
- jnz .Ladd_proceed$x # (in1infty || in2infty)?
- test $acc1, $acc1
- jz .Ladd_double$x # is_equal(S1,S2)?
+ or $acc0, $acc4
+ .byte 0x3e # predict taken
+ jnz .Ladd_proceed$x # !is_equal(U1, U2) || in1infty || in2infty
+ # We now know A = B or A = -B and neither is infinity. Compare the
+ # y-coordinates via S1 and S2.
+ test $acc1, $acc1
+ jz .Ladd_double$x # is_equal(S1, S2)
+
+ # A = -B, so the result is infinity.
+ #
+ # TODO(davidben): Does .Ladd_proceed handle this case? It seems to, in
+ # which case we should eliminate this special-case and simplify the
+ # timing analysis.
movq %xmm0, $r_ptr # restore $r_ptr
pxor %xmm0, %xmm0
movdqu %xmm0, 0x00($r_ptr)
@@ -4146,4 +4153,4 @@
$code =~ s/\`([^\`]*)\`/eval $1/gem;
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl b/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
index 0bb6547..c05abba 100644
--- a/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
+++ b/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
@@ -400,4 +400,4 @@
___
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/ec/ec.c b/src/crypto/fipsmodule/ec/ec.c
index a0305a6..158d66c 100644
--- a/src/crypto/fipsmodule/ec/ec.c
+++ b/src/crypto/fipsmodule/ec/ec.c
@@ -892,8 +892,6 @@
}
int ret = 0;
- EC_SCALAR g_scalar_storage, p_scalar_storage;
- EC_SCALAR *g_scalar_arg = NULL, *p_scalar_arg = NULL;
BN_CTX *new_ctx = NULL;
if (ctx == NULL) {
new_ctx = BN_CTX_new();
@@ -903,35 +901,50 @@
ctx = new_ctx;
}
+ // If both |g_scalar| and |p_scalar| are non-NULL,
+ // |ec_point_mul_scalar_public| would share the doublings between the two
+ // products, which would be more efficient. However, we conservatively assume
+ // the caller needs a constant-time operation. (ECDSA verification does not
+ // use this function.)
+ //
+ // Previously, the low-level constant-time multiplication function aligned
+ // with this function's calling convention, but this was misleading. Curves
+ // which combined the two multiplications did not avoid the doubling case
+ // in the incomplete addition formula and were not constant-time.
+
if (g_scalar != NULL) {
- if (!arbitrary_bignum_to_scalar(group, &g_scalar_storage, g_scalar, ctx)) {
+ EC_SCALAR scalar;
+ if (!arbitrary_bignum_to_scalar(group, &scalar, g_scalar, ctx) ||
+ !ec_point_mul_scalar_base(group, &r->raw, &scalar)) {
goto err;
}
- g_scalar_arg = &g_scalar_storage;
}
if (p_scalar != NULL) {
- if (!arbitrary_bignum_to_scalar(group, &p_scalar_storage, p_scalar, ctx)) {
+ EC_SCALAR scalar;
+ EC_RAW_POINT tmp;
+ if (!arbitrary_bignum_to_scalar(group, &scalar, p_scalar, ctx) ||
+ !ec_point_mul_scalar(group, &tmp, &p->raw, &scalar)) {
goto err;
}
- p_scalar_arg = &p_scalar_storage;
+ if (g_scalar == NULL) {
+ OPENSSL_memcpy(&r->raw, &tmp, sizeof(EC_RAW_POINT));
+ } else {
+ group->meth->add(group, &r->raw, &r->raw, &tmp);
+ }
}
- ret = ec_point_mul_scalar(group, &r->raw, g_scalar_arg,
- p == NULL ? NULL : &p->raw, p_scalar_arg);
+ ret = 1;
err:
BN_CTX_free(new_ctx);
- OPENSSL_cleanse(&g_scalar_storage, sizeof(g_scalar_storage));
- OPENSSL_cleanse(&p_scalar_storage, sizeof(p_scalar_storage));
return ret;
}
int ec_point_mul_scalar_public(const EC_GROUP *group, EC_RAW_POINT *r,
const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
const EC_SCALAR *p_scalar) {
- if ((g_scalar == NULL && p_scalar == NULL) ||
- (p == NULL) != (p_scalar == NULL)) {
+ if (g_scalar == NULL || p_scalar == NULL || p == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
@@ -941,15 +954,24 @@
}
int ec_point_mul_scalar(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar) {
- if ((g_scalar == NULL && p_scalar == NULL) ||
- (p == NULL) != (p_scalar == NULL)) {
+ const EC_RAW_POINT *p, const EC_SCALAR *scalar) {
+ if (p == NULL || scalar == NULL) {
OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- group->meth->mul(group, r, g_scalar, p, p_scalar);
+ group->meth->mul(group, r, p, scalar);
+ return 1;
+}
+
+int ec_point_mul_scalar_base(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar) {
+ if (scalar == NULL) {
+ OPENSSL_PUT_ERROR(EC, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ group->meth->mul_base(group, r, scalar);
return 1;
}
diff --git a/src/crypto/fipsmodule/ec/ec_key.c b/src/crypto/fipsmodule/ec/ec_key.c
index 3ef17d9..3851c19 100644
--- a/src/crypto/fipsmodule/ec/ec_key.c
+++ b/src/crypto/fipsmodule/ec/ec_key.c
@@ -322,8 +322,8 @@
if (eckey->priv_key != NULL) {
point = EC_POINT_new(eckey->group);
if (point == NULL ||
- !ec_point_mul_scalar(eckey->group, &point->raw,
- &eckey->priv_key->scalar, NULL, NULL)) {
+ !ec_point_mul_scalar_base(eckey->group, &point->raw,
+ &eckey->priv_key->scalar)) {
OPENSSL_PUT_ERROR(EC, ERR_R_EC_LIB);
goto err;
}
@@ -440,8 +440,7 @@
// Generate the private key by testing candidates (FIPS 186-4 B.4.2).
!ec_random_nonzero_scalar(key->group, &priv_key->scalar,
kDefaultAdditionalData) ||
- !ec_point_mul_scalar(key->group, &pub_key->raw, &priv_key->scalar, NULL,
- NULL)) {
+ !ec_point_mul_scalar_base(key->group, &pub_key->raw, &priv_key->scalar)) {
EC_POINT_free(pub_key);
ec_wrapped_scalar_free(priv_key);
return 0;
diff --git a/src/crypto/fipsmodule/ec/ec_montgomery.c b/src/crypto/fipsmodule/ec/ec_montgomery.c
index caa1966..0cf1d91 100644
--- a/src/crypto/fipsmodule/ec/ec_montgomery.c
+++ b/src/crypto/fipsmodule/ec/ec_montgomery.c
@@ -282,7 +282,8 @@
BN_ULONG yneq = ec_felem_non_zero_mask(group, &r);
// This case will never occur in the constant-time |ec_GFp_mont_mul|.
- if (!xneq && !yneq && z1nz && z2nz) {
+ BN_ULONG is_nontrivial_double = ~xneq & ~yneq & z1nz & z2nz;
+ if (is_nontrivial_double) {
ec_GFp_mont_dbl(group, out, a);
return;
}
@@ -470,6 +471,7 @@
out->add = ec_GFp_mont_add;
out->dbl = ec_GFp_mont_dbl;
out->mul = ec_GFp_mont_mul;
+ out->mul_base = ec_GFp_mont_mul_base;
out->mul_public = ec_GFp_mont_mul_public;
out->felem_mul = ec_GFp_mont_felem_mul;
out->felem_sqr = ec_GFp_mont_felem_sqr;
diff --git a/src/crypto/fipsmodule/ec/ec_test.cc b/src/crypto/fipsmodule/ec/ec_test.cc
index 1219e2b..c0ad61f 100644
--- a/src/crypto/fipsmodule/ec/ec_test.cc
+++ b/src/crypto/fipsmodule/ec/ec_test.cc
@@ -764,7 +764,15 @@
ASSERT_TRUE(BN_set_word(bn32.get(), 32));
ASSERT_TRUE(EC_POINT_mul(group(), ret.get(), bn32.get(), p.get(), bn31.get(),
nullptr));
+ EXPECT_EQ(0, EC_POINT_cmp(group(), ret.get(), g, nullptr));
+ // Repeat the computation with |ec_point_mul_scalar_public|, which ties the
+ // additions together.
+ EC_SCALAR sc31, sc32;
+ ASSERT_TRUE(ec_bignum_to_scalar(group(), &sc31, bn31.get()));
+ ASSERT_TRUE(ec_bignum_to_scalar(group(), &sc32, bn32.get()));
+ ASSERT_TRUE(
+ ec_point_mul_scalar_public(group(), &ret->raw, &sc32, &p->raw, &sc31));
EXPECT_EQ(0, EC_POINT_cmp(group(), ret.get(), g, nullptr));
}
diff --git a/src/crypto/fipsmodule/ec/internal.h b/src/crypto/fipsmodule/ec/internal.h
index 05175a5..7934c3a 100644
--- a/src/crypto/fipsmodule/ec/internal.h
+++ b/src/crypto/fipsmodule/ec/internal.h
@@ -140,16 +140,15 @@
// dbl sets |r| to |a| + |a|.
void (*dbl)(const EC_GROUP *group, EC_RAW_POINT *r, const EC_RAW_POINT *a);
- // Computes |r = g_scalar*generator + p_scalar*p| if |g_scalar| and |p_scalar|
- // are both non-null. Computes |r = g_scalar*generator| if |p_scalar| is null.
- // Computes |r = p_scalar*p| if g_scalar is null. At least one of |g_scalar|
- // and |p_scalar| must be non-null, and |p| must be non-null if |p_scalar| is
- // non-null.
- void (*mul)(const EC_GROUP *group, EC_RAW_POINT *r, const EC_SCALAR *g_scalar,
- const EC_RAW_POINT *p, const EC_SCALAR *p_scalar);
- // mul_public performs the same computation as mul. It further assumes that
- // the inputs are public so there is no concern about leaking their values
- // through timing.
+ // mul sets |r| to |scalar|*|p|.
+ void (*mul)(const EC_GROUP *group, EC_RAW_POINT *r, const EC_RAW_POINT *p,
+ const EC_SCALAR *scalar);
+ // mul_base sets |r| to |scalar|*generator.
+ void (*mul_base)(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar);
+ // mul_public sets |r| to |g_scalar|*generator + |p_scalar|*|p|. It assumes
+ // that the inputs are public so there is no concern about leaking their
+ // values through timing.
void (*mul_public)(const EC_GROUP *group, EC_RAW_POINT *r,
const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
const EC_SCALAR *p_scalar);
@@ -325,13 +324,15 @@
int ec_scalar_inv_montgomery_vartime(const EC_GROUP *group, EC_SCALAR *r,
const EC_SCALAR *a);
-// ec_point_mul_scalar sets |r| to generator * |g_scalar| + |p| *
-// |p_scalar|. Unlike other functions which take |EC_SCALAR|, |g_scalar| and
-// |p_scalar| need not be fully reduced. They need only contain as many bits as
-// the order.
+// ec_point_mul_scalar sets |r| to |p| * |scalar|. Both inputs are considered
+// secret.
int ec_point_mul_scalar(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar);
+ const EC_RAW_POINT *p, const EC_SCALAR *scalar);
+
+// ec_point_mul_scalar_base sets |r| to generator * |scalar|. |scalar| is
+// treated as secret.
+int ec_point_mul_scalar_base(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar);
// ec_point_mul_scalar_public performs the same computation as
// ec_point_mul_scalar. It further assumes that the inputs are public so
@@ -370,8 +371,9 @@
int ec_field_element_to_scalar(const EC_GROUP *group, BIGNUM *r);
void ec_GFp_mont_mul(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar);
+ const EC_RAW_POINT *p, const EC_SCALAR *scalar);
+void ec_GFp_mont_mul_base(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar);
// ec_compute_wNAF writes the modified width-(w+1) Non-Adjacent Form (wNAF) of
// |scalar| to |out|. |out| must have room for |bits| + 1 elements, each of
diff --git a/src/crypto/fipsmodule/ec/p224-64.c b/src/crypto/fipsmodule/ec/p224-64.c
index 2749686..f8af39b 100644
--- a/src/crypto/fipsmodule/ec/p224-64.c
+++ b/src/crypto/fipsmodule/ec/p224-64.c
@@ -758,7 +758,9 @@
z1_is_zero = p224_felem_is_zero(z1);
z2_is_zero = p224_felem_is_zero(z2);
// In affine coordinates, (X_1, Y_1) == (X_2, Y_2)
- if (x_equal && y_equal && !z1_is_zero && !z2_is_zero) {
+ p224_limb is_nontrivial_double =
+ x_equal & y_equal & (1 - z1_is_zero) & (1 - z2_is_zero);
+ if (is_nontrivial_double) {
p224_point_double(x3, y3, z3, x1, y1, z1);
return;
}
@@ -871,95 +873,6 @@
return (in[i >> 3] >> (i & 7)) & 1;
}
-// Interleaved point multiplication using precomputed point multiples:
-// The small point multiples 0*P, 1*P, ..., 16*P are in p_pre_comp, the scalars
-// in p_scalar, if non-NULL. If g_scalar is non-NULL, we also add this multiple
-// of the generator, using certain (large) precomputed multiples in
-// g_p224_pre_comp. Output point (X, Y, Z) is stored in x_out, y_out, z_out
-static void p224_batch_mul(p224_felem x_out, p224_felem y_out, p224_felem z_out,
- const uint8_t *p_scalar, const uint8_t *g_scalar,
- const p224_felem p_pre_comp[17][3]) {
- p224_felem nq[3], tmp[4];
- uint64_t bits;
- uint8_t sign, digit;
-
- // set nq to the point at infinity
- OPENSSL_memset(nq, 0, 3 * sizeof(p224_felem));
-
- // Loop over both scalars msb-to-lsb, interleaving additions of multiples of
- // the generator (two in each of the last 28 rounds) and additions of p (every
- // 5th round).
- int skip = 1; // save two point operations in the first round
- size_t i = p_scalar != NULL ? 220 : 27;
- for (;;) {
- // double
- if (!skip) {
- p224_point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
- }
-
- // add multiples of the generator
- if (g_scalar != NULL && i <= 27) {
- // first, look 28 bits upwards
- bits = p224_get_bit(g_scalar, i + 196) << 3;
- bits |= p224_get_bit(g_scalar, i + 140) << 2;
- bits |= p224_get_bit(g_scalar, i + 84) << 1;
- bits |= p224_get_bit(g_scalar, i + 28);
- // select the point to add, in constant time
- p224_select_point(bits, 16, g_p224_pre_comp[1], tmp);
-
- if (!skip) {
- p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
- tmp[0], tmp[1], tmp[2]);
- } else {
- OPENSSL_memcpy(nq, tmp, 3 * sizeof(p224_felem));
- skip = 0;
- }
-
- // second, look at the current position
- bits = p224_get_bit(g_scalar, i + 168) << 3;
- bits |= p224_get_bit(g_scalar, i + 112) << 2;
- bits |= p224_get_bit(g_scalar, i + 56) << 1;
- bits |= p224_get_bit(g_scalar, i);
- // select the point to add, in constant time
- p224_select_point(bits, 16, g_p224_pre_comp[0], tmp);
- p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
- tmp[0], tmp[1], tmp[2]);
- }
-
- // do other additions every 5 doublings
- if (p_scalar != NULL && i % 5 == 0) {
- bits = p224_get_bit(p_scalar, i + 4) << 5;
- bits |= p224_get_bit(p_scalar, i + 3) << 4;
- bits |= p224_get_bit(p_scalar, i + 2) << 3;
- bits |= p224_get_bit(p_scalar, i + 1) << 2;
- bits |= p224_get_bit(p_scalar, i) << 1;
- bits |= p224_get_bit(p_scalar, i - 1);
- ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
-
- // select the point to add or subtract
- p224_select_point(digit, 17, p_pre_comp, tmp);
- p224_felem_neg(tmp[3], tmp[1]); // (X, -Y, Z) is the negative point
- p224_copy_conditional(tmp[1], tmp[3], sign);
-
- if (!skip) {
- p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 0 /* mixed */,
- tmp[0], tmp[1], tmp[2]);
- } else {
- OPENSSL_memcpy(nq, tmp, 3 * sizeof(p224_felem));
- skip = 0;
- }
- }
-
- if (i == 0) {
- break;
- }
- --i;
- }
- p224_felem_assign(x_out, nq[0]);
- p224_felem_assign(y_out, nq[1]);
- p224_felem_assign(z_out, nq[2]);
-}
-
// Takes the Jacobian coordinates (X, Y, Z) of a point and returns
// (X', Y') = (X/Z^2, Y/Z^3)
static int ec_GFp_nistp224_point_get_affine_coordinates(
@@ -1027,49 +940,197 @@
p224_felem_to_generic(&r->Z, z);
}
-static void ec_GFp_nistp224_points_mul(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar,
- const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar) {
+static void ec_GFp_nistp224_make_precomp(p224_felem out[17][3],
+ const EC_RAW_POINT *p) {
+ OPENSSL_memset(out[0], 0, sizeof(p224_felem) * 3);
+
+ p224_generic_to_felem(out[1][0], &p->X);
+ p224_generic_to_felem(out[1][1], &p->Y);
+ p224_generic_to_felem(out[1][2], &p->Z);
+
+ for (size_t j = 2; j <= 16; ++j) {
+ if (j & 1) {
+ p224_point_add(out[j][0], out[j][1], out[j][2], out[1][0], out[1][1],
+ out[1][2], 0, out[j - 1][0], out[j - 1][1], out[j - 1][2]);
+ } else {
+ p224_point_double(out[j][0], out[j][1], out[j][2], out[j / 2][0],
+ out[j / 2][1], out[j / 2][2]);
+ }
+ }
+}
+
+static void ec_GFp_nistp224_point_mul(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_RAW_POINT *p,
+ const EC_SCALAR *scalar) {
p224_felem p_pre_comp[17][3];
- p224_felem x_out, y_out, z_out;
+ ec_GFp_nistp224_make_precomp(p_pre_comp, p);
- if (p != NULL && p_scalar != NULL) {
- // We treat NULL scalars as 0, and NULL points as points at infinity, i.e.,
- // they contribute nothing to the linear combination.
- OPENSSL_memset(&p_pre_comp, 0, sizeof(p_pre_comp));
- // precompute multiples
- p224_generic_to_felem(x_out, &p->X);
- p224_generic_to_felem(y_out, &p->Y);
- p224_generic_to_felem(z_out, &p->Z);
+ // Set nq to the point at infinity.
+ p224_felem nq[3], tmp[4];
+ OPENSSL_memset(nq, 0, 3 * sizeof(p224_felem));
- p224_felem_assign(p_pre_comp[1][0], x_out);
- p224_felem_assign(p_pre_comp[1][1], y_out);
- p224_felem_assign(p_pre_comp[1][2], z_out);
+ int skip = 1; // Save two point operations in the first round.
+ for (size_t i = 220; i < 221; i--) {
+ if (!skip) {
+ p224_point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+ }
- for (size_t j = 2; j <= 16; ++j) {
- if (j & 1) {
- p224_point_add(p_pre_comp[j][0], p_pre_comp[j][1], p_pre_comp[j][2],
- p_pre_comp[1][0], p_pre_comp[1][1], p_pre_comp[1][2], 0,
- p_pre_comp[j - 1][0], p_pre_comp[j - 1][1],
- p_pre_comp[j - 1][2]);
+ // Add every 5 doublings.
+ if (i % 5 == 0) {
+ uint64_t bits = p224_get_bit(scalar->bytes, i + 4) << 5;
+ bits |= p224_get_bit(scalar->bytes, i + 3) << 4;
+ bits |= p224_get_bit(scalar->bytes, i + 2) << 3;
+ bits |= p224_get_bit(scalar->bytes, i + 1) << 2;
+ bits |= p224_get_bit(scalar->bytes, i) << 1;
+ bits |= p224_get_bit(scalar->bytes, i - 1);
+ uint8_t sign, digit;
+ ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+ // Select the point to add or subtract.
+ p224_select_point(digit, 17, (const p224_felem(*)[3])p_pre_comp, tmp);
+ p224_felem_neg(tmp[3], tmp[1]); // (X, -Y, Z) is the negative point
+ p224_copy_conditional(tmp[1], tmp[3], sign);
+
+ if (!skip) {
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 0 /* mixed */,
+ tmp[0], tmp[1], tmp[2]);
} else {
- p224_point_double(p_pre_comp[j][0], p_pre_comp[j][1], p_pre_comp[j][2],
- p_pre_comp[j / 2][0], p_pre_comp[j / 2][1],
- p_pre_comp[j / 2][2]);
+ OPENSSL_memcpy(nq, tmp, 3 * sizeof(p224_felem));
+ skip = 0;
}
}
}
- p224_batch_mul(x_out, y_out, z_out,
- (p != NULL && p_scalar != NULL) ? p_scalar->bytes : NULL,
- g_scalar != NULL ? g_scalar->bytes : NULL,
- (const p224_felem(*)[3])p_pre_comp);
+ // Reduce the output to its unique minimal representation.
+ p224_felem_to_generic(&r->X, nq[0]);
+ p224_felem_to_generic(&r->Y, nq[1]);
+ p224_felem_to_generic(&r->Z, nq[2]);
+}
- // reduce the output to its unique minimal representation
- p224_felem_to_generic(&r->X, x_out);
- p224_felem_to_generic(&r->Y, y_out);
- p224_felem_to_generic(&r->Z, z_out);
+static void ec_GFp_nistp224_point_mul_base(const EC_GROUP *group,
+ EC_RAW_POINT *r,
+ const EC_SCALAR *scalar) {
+ // Set nq to the point at infinity.
+ p224_felem nq[3], tmp[3];
+ OPENSSL_memset(nq, 0, 3 * sizeof(p224_felem));
+
+ int skip = 1; // Save two point operations in the first round.
+ for (size_t i = 27; i < 28; i--) {
+ // double
+ if (!skip) {
+ p224_point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+ }
+
+ // First, look 28 bits upwards.
+ uint64_t bits = p224_get_bit(scalar->bytes, i + 196) << 3;
+ bits |= p224_get_bit(scalar->bytes, i + 140) << 2;
+ bits |= p224_get_bit(scalar->bytes, i + 84) << 1;
+ bits |= p224_get_bit(scalar->bytes, i + 28);
+ // Select the point to add, in constant time.
+ p224_select_point(bits, 16, g_p224_pre_comp[1], tmp);
+
+ if (!skip) {
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
+ tmp[0], tmp[1], tmp[2]);
+ } else {
+ OPENSSL_memcpy(nq, tmp, 3 * sizeof(p224_felem));
+ skip = 0;
+ }
+
+ // Second, look at the current position/
+ bits = p224_get_bit(scalar->bytes, i + 168) << 3;
+ bits |= p224_get_bit(scalar->bytes, i + 112) << 2;
+ bits |= p224_get_bit(scalar->bytes, i + 56) << 1;
+ bits |= p224_get_bit(scalar->bytes, i);
+ // Select the point to add, in constant time.
+ p224_select_point(bits, 16, g_p224_pre_comp[0], tmp);
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
+ tmp[0], tmp[1], tmp[2]);
+ }
+
+ // Reduce the output to its unique minimal representation.
+ p224_felem_to_generic(&r->X, nq[0]);
+ p224_felem_to_generic(&r->Y, nq[1]);
+ p224_felem_to_generic(&r->Z, nq[2]);
+}
+
+static void ec_GFp_nistp224_point_mul_public(const EC_GROUP *group,
+ EC_RAW_POINT *r,
+ const EC_SCALAR *g_scalar,
+ const EC_RAW_POINT *p,
+ const EC_SCALAR *p_scalar) {
+ // TODO(davidben): If P-224 ECDSA verify performance ever matters, using
+ // |ec_compute_wNAF| for |p_scalar| would likely be an easy improvement.
+ p224_felem p_pre_comp[17][3];
+ ec_GFp_nistp224_make_precomp(p_pre_comp, p);
+
+ // Set nq to the point at infinity.
+ p224_felem nq[3], tmp[3];
+ OPENSSL_memset(nq, 0, 3 * sizeof(p224_felem));
+
+ // Loop over both scalars msb-to-lsb, interleaving additions of multiples of
+ // the generator (two in each of the last 28 rounds) and additions of p (every
+ // 5th round).
+ int skip = 1; // Save two point operations in the first round.
+ for (size_t i = 220; i < 221; i--) {
+ if (!skip) {
+ p224_point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+ }
+
+ // Add multiples of the generator.
+ if (i <= 27) {
+ // First, look 28 bits upwards.
+ uint64_t bits = p224_get_bit(g_scalar->bytes, i + 196) << 3;
+ bits |= p224_get_bit(g_scalar->bytes, i + 140) << 2;
+ bits |= p224_get_bit(g_scalar->bytes, i + 84) << 1;
+ bits |= p224_get_bit(g_scalar->bytes, i + 28);
+
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
+ g_p224_pre_comp[1][bits][0], g_p224_pre_comp[1][bits][1],
+ g_p224_pre_comp[1][bits][2]);
+ assert(!skip);
+
+ // Second, look at the current position.
+ bits = p224_get_bit(g_scalar->bytes, i + 168) << 3;
+ bits |= p224_get_bit(g_scalar->bytes, i + 112) << 2;
+ bits |= p224_get_bit(g_scalar->bytes, i + 56) << 1;
+ bits |= p224_get_bit(g_scalar->bytes, i);
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
+ g_p224_pre_comp[0][bits][0], g_p224_pre_comp[0][bits][1],
+ g_p224_pre_comp[0][bits][2]);
+ }
+
+ // Incorporate |p_scalar| every 5 doublings.
+ if (i % 5 == 0) {
+ uint64_t bits = p224_get_bit(p_scalar->bytes, i + 4) << 5;
+ bits |= p224_get_bit(p_scalar->bytes, i + 3) << 4;
+ bits |= p224_get_bit(p_scalar->bytes, i + 2) << 3;
+ bits |= p224_get_bit(p_scalar->bytes, i + 1) << 2;
+ bits |= p224_get_bit(p_scalar->bytes, i) << 1;
+ bits |= p224_get_bit(p_scalar->bytes, i - 1);
+ uint8_t sign, digit;
+ ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+ // Select the point to add or subtract.
+ OPENSSL_memcpy(tmp, p_pre_comp[digit], 3 * sizeof(p224_felem));
+ if (sign) {
+ p224_felem_neg(tmp[1], tmp[1]); // (X, -Y, Z) is the negative point
+ }
+
+ if (!skip) {
+ p224_point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 0 /* mixed */,
+ tmp[0], tmp[1], tmp[2]);
+ } else {
+ OPENSSL_memcpy(nq, tmp, 3 * sizeof(p224_felem));
+ skip = 0;
+ }
+ }
+ }
+
+ // Reduce the output to its unique minimal representation.
+ p224_felem_to_generic(&r->X, nq[0]);
+ p224_felem_to_generic(&r->Y, nq[1]);
+ p224_felem_to_generic(&r->Z, nq[2]);
}
static void ec_GFp_nistp224_felem_mul(const EC_GROUP *group, EC_FELEM *r,
@@ -1111,8 +1172,9 @@
ec_GFp_nistp224_point_get_affine_coordinates;
out->add = ec_GFp_nistp224_add;
out->dbl = ec_GFp_nistp224_dbl;
- out->mul = ec_GFp_nistp224_points_mul;
- out->mul_public = ec_GFp_nistp224_points_mul;
+ out->mul = ec_GFp_nistp224_point_mul;
+ out->mul_base = ec_GFp_nistp224_point_mul_base;
+ out->mul_public = ec_GFp_nistp224_point_mul_public;
out->felem_mul = ec_GFp_nistp224_felem_mul;
out->felem_sqr = ec_GFp_nistp224_felem_sqr;
out->bignum_to_felem = ec_GFp_nistp224_bignum_to_felem;
diff --git a/src/crypto/fipsmodule/ec/p256-x86_64.c b/src/crypto/fipsmodule/ec/p256-x86_64.c
index dd8108d..b4af544 100644
--- a/src/crypto/fipsmodule/ec/p256-x86_64.c
+++ b/src/crypto/fipsmodule/ec/p256-x86_64.c
@@ -48,7 +48,8 @@
// Precomputed tables for the default generator
#include "p256-x86_64-table.h"
-// Recode window to a signed digit, see util-64.c for details
+// Recode window to a signed digit, see |ec_GFp_nistp_recode_scalar_bits| in
+// util.c for details
static unsigned booth_recode_w5(unsigned in) {
unsigned s, d;
@@ -316,74 +317,57 @@
return booth_recode_w7(wvalue);
}
-static void mul_p_add_and_store(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar,
- const EC_RAW_POINT *p_,
- const EC_SCALAR *p_scalar,
- p256_point_union_t *t, p256_point_union_t *p) {
- const int p_is_infinity = g_scalar == NULL;
- if (p_scalar != NULL) {
- P256_POINT *out = &t->p;
- if (p_is_infinity) {
- out = &p->p;
- }
+static void ecp_nistz256_point_mul(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_RAW_POINT *p,
+ const EC_SCALAR *scalar) {
+ alignas(32) P256_POINT out;
+ ecp_nistz256_windowed_mul(group, &out, p, scalar);
- ecp_nistz256_windowed_mul(group, out, p_, p_scalar);
- if (!p_is_infinity) {
- ecp_nistz256_point_add(&p->p, &p->p, out);
- }
+ assert(group->field.width == P256_LIMBS);
+ OPENSSL_memcpy(r->X.words, out.X, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Y.words, out.Y, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Z.words, out.Z, P256_LIMBS * sizeof(BN_ULONG));
+}
+
+static void ecp_nistz256_point_mul_base(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar) {
+ alignas(32) p256_point_union_t t, p;
+
+ uint8_t p_str[33];
+ OPENSSL_memcpy(p_str, scalar->bytes, 32);
+ p_str[32] = 0;
+
+ // First window
+ unsigned index = 0;
+ unsigned wvalue = calc_first_wvalue(&index, p_str);
+
+ ecp_nistz256_select_w7(&p.a, ecp_nistz256_precomputed[0], wvalue >> 1);
+ ecp_nistz256_neg(p.p.Z, p.p.Y);
+ copy_conditional(p.p.Y, p.p.Z, wvalue & 1);
+
+ // Convert |p| from affine to Jacobian coordinates. We set Z to zero if |p|
+ // is infinity and |ONE| otherwise. |p| was computed from the table, so it
+ // is infinity iff |wvalue >> 1| is zero.
+ OPENSSL_memset(p.p.Z, 0, sizeof(p.p.Z));
+ copy_conditional(p.p.Z, ONE, is_not_zero(wvalue >> 1));
+
+ for (int i = 1; i < 37; i++) {
+ wvalue = calc_wvalue(&index, p_str);
+
+ ecp_nistz256_select_w7(&t.a, ecp_nistz256_precomputed[i], wvalue >> 1);
+
+ ecp_nistz256_neg(t.p.Z, t.a.Y);
+ copy_conditional(t.a.Y, t.p.Z, wvalue & 1);
+
+ // Note |ecp_nistz256_point_add_affine| does not work if |p.p| and |t.a|
+ // are the same non-infinity point.
+ ecp_nistz256_point_add_affine(&p.p, &p.p, &t.a);
}
assert(group->field.width == P256_LIMBS);
- OPENSSL_memcpy(r->X.words, p->p.X, P256_LIMBS * sizeof(BN_ULONG));
- OPENSSL_memcpy(r->Y.words, p->p.Y, P256_LIMBS * sizeof(BN_ULONG));
- OPENSSL_memcpy(r->Z.words, p->p.Z, P256_LIMBS * sizeof(BN_ULONG));
-}
-
-static void ecp_nistz256_points_mul(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar,
- const EC_RAW_POINT *p_,
- const EC_SCALAR *p_scalar) {
- assert((p_ != NULL) == (p_scalar != NULL));
-
- alignas(32) p256_point_union_t t, p;
-
- if (g_scalar != NULL) {
- uint8_t p_str[33];
- OPENSSL_memcpy(p_str, g_scalar->bytes, 32);
- p_str[32] = 0;
-
- // First window
- unsigned index = 0;
- unsigned wvalue = calc_first_wvalue(&index, p_str);
-
- ecp_nistz256_select_w7(&p.a, ecp_nistz256_precomputed[0], wvalue >> 1);
-
- ecp_nistz256_neg(p.p.Z, p.p.Y);
- copy_conditional(p.p.Y, p.p.Z, wvalue & 1);
-
- // Convert |p| from affine to Jacobian coordinates. We set Z to zero if |p|
- // is infinity and |ONE| otherwise. |p| was computed from the table, so it
- // is infinity iff |wvalue >> 1| is zero.
- OPENSSL_memset(p.p.Z, 0, sizeof(p.p.Z));
- copy_conditional(p.p.Z, ONE, is_not_zero(wvalue >> 1));
-
- for (int i = 1; i < 37; i++) {
- wvalue = calc_wvalue(&index, p_str);
-
- ecp_nistz256_select_w7(&t.a, ecp_nistz256_precomputed[i], wvalue >> 1);
-
- ecp_nistz256_neg(t.p.Z, t.a.Y);
- copy_conditional(t.a.Y, t.p.Z, wvalue & 1);
-
- // Note |ecp_nistz256_point_add_affine| does not work if |p.p| and |t.a|
- // are the same non-infinity point, so it is important that we compute the
- // |g_scalar| term before the |p_scalar| term.
- ecp_nistz256_point_add_affine(&p.p, &p.p, &t.a);
- }
- }
-
- mul_p_add_and_store(group, r, g_scalar, p_, p_scalar, &t, &p);
+ OPENSSL_memcpy(r->X.words, p.p.X, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Y.words, p.p.Y, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Z.words, p.p.Z, P256_LIMBS * sizeof(BN_ULONG));
}
static void ecp_nistz256_points_mul_public(const EC_GROUP *group,
@@ -438,7 +422,13 @@
ecp_nistz256_point_add_affine(&p.p, &p.p, &t.a);
}
- mul_p_add_and_store(group, r, g_scalar, p_, p_scalar, &t, &p);
+ ecp_nistz256_windowed_mul(group, &t.p, p_, p_scalar);
+ ecp_nistz256_point_add(&p.p, &p.p, &t.p);
+
+ assert(group->field.width == P256_LIMBS);
+ OPENSSL_memcpy(r->X.words, p.p.X, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Y.words, p.p.Y, P256_LIMBS * sizeof(BN_ULONG));
+ OPENSSL_memcpy(r->Z.words, p.p.Z, P256_LIMBS * sizeof(BN_ULONG));
}
static int ecp_nistz256_get_affine(const EC_GROUP *group,
@@ -645,7 +635,8 @@
out->point_get_affine_coordinates = ecp_nistz256_get_affine;
out->add = ecp_nistz256_add;
out->dbl = ecp_nistz256_dbl;
- out->mul = ecp_nistz256_points_mul;
+ out->mul = ecp_nistz256_point_mul;
+ out->mul_base = ecp_nistz256_point_mul_base;
out->mul_public = ecp_nistz256_points_mul_public;
out->felem_mul = ec_GFp_mont_felem_mul;
out->felem_sqr = ec_GFp_mont_felem_sqr;
diff --git a/src/crypto/fipsmodule/ec/simple_mul.c b/src/crypto/fipsmodule/ec/simple_mul.c
index e05f491..4ed6c48 100644
--- a/src/crypto/fipsmodule/ec/simple_mul.c
+++ b/src/crypto/fipsmodule/ec/simple_mul.c
@@ -21,9 +21,8 @@
#include "../../internal.h"
-static void ec_GFp_mont_mul_single(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_RAW_POINT *p,
- const EC_SCALAR *scalar) {
+void ec_GFp_mont_mul(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_RAW_POINT *p, const EC_SCALAR *scalar) {
// This is a generic implementation for uncommon curves that not do not
// warrant a tuned one. It uses unsigned digits so that the doubling case in
// |ec_GFp_mont_add| is always unreachable, erring on safety and simplicity.
@@ -79,21 +78,7 @@
}
}
-void ec_GFp_mont_mul(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar, const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar) {
- assert(g_scalar != NULL || p_scalar != NULL);
- if (p_scalar == NULL) {
- ec_GFp_mont_mul_single(group, r, &group->generator->raw, g_scalar);
- } else if (g_scalar == NULL) {
- ec_GFp_mont_mul_single(group, r, p, p_scalar);
- } else {
- // Support constant-time two-point multiplication for compatibility. This
- // does not actually come up in keygen, ECDH, or ECDSA, so we implement it
- // the naive way.
- ec_GFp_mont_mul_single(group, r, &group->generator->raw, g_scalar);
- EC_RAW_POINT tmp;
- ec_GFp_mont_mul_single(group, &tmp, p, p_scalar);
- ec_GFp_mont_add(group, r, r, &tmp);
- }
+void ec_GFp_mont_mul_base(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_SCALAR *scalar) {
+ ec_GFp_mont_mul(group, r, &group->generator->raw, scalar);
}
diff --git a/src/crypto/fipsmodule/ec/util.c b/src/crypto/fipsmodule/ec/util.c
index 7303a15..4f39f18 100644
--- a/src/crypto/fipsmodule/ec/util.c
+++ b/src/crypto/fipsmodule/ec/util.c
@@ -18,6 +18,7 @@
#include "internal.h"
+
// This function looks at 5+1 scalar bits (5 current, 1 adjacent less
// significant bit), and recodes them into a signed digit for use in fast point
// multiplication: the use of signed rather than unsigned digits means that
@@ -43,13 +44,13 @@
// of a nonnegative integer (b_k in {0, 1}), rewrite it in digits 0, 1, -1
// by using bit-wise subtraction as follows:
//
-// b_k b_(k-1) ... b_2 b_1 b_0
-// - b_k ... b_3 b_2 b_1 b_0
-// -------------------------------------
-// s_k b_(k-1) ... s_3 s_2 s_1 s_0
+// b_k b_(k-1) ... b_2 b_1 b_0
+// - b_k ... b_3 b_2 b_1 b_0
+// -----------------------------------------
+// s_(k+1) s_k ... s_3 s_2 s_1 s_0
//
// A left-shift followed by subtraction of the original value yields a new
-// representation of the same value, using signed bits s_i = b_(i+1) - b_i.
+// representation of the same value, using signed bits s_i = b_(i-1) - b_i.
// This representation from Booth's paper has since appeared in the
// literature under a variety of different names including "reversed binary
// form", "alternating greedy expansion", "mutual opposite form", and
@@ -73,7 +74,7 @@
// (1961), pp. 67-91), in a radix-2^5 setting. That is, we always combine five
// signed bits into a signed digit:
//
-// s_(4j + 4) s_(4j + 3) s_(4j + 2) s_(4j + 1) s_(4j)
+// s_(5j + 4) s_(5j + 3) s_(5j + 2) s_(5j + 1) s_(5j)
//
// The sign-alternating property implies that the resulting digit values are
// integers from -16 to 16.
@@ -81,14 +82,164 @@
// Of course, we don't actually need to compute the signed digits s_i as an
// intermediate step (that's just a nice way to see how this scheme relates
// to the wNAF): a direct computation obtains the recoded digit from the
-// six bits b_(4j + 4) ... b_(4j - 1).
+// six bits b_(5j + 4) ... b_(5j - 1).
//
-// This function takes those five bits as an integer (0 .. 63), writing the
+// This function takes those six bits as an integer (0 .. 63), writing the
// recoded digit to *sign (0 for positive, 1 for negative) and *digit (absolute
-// value, in the range 0 .. 8). Note that this integer essentially provides the
-// input bits "shifted to the left" by one position: for example, the input to
-// compute the least significant recoded digit, given that there's no bit b_-1,
-// has to be b_4 b_3 b_2 b_1 b_0 0.
+// value, in the range 0 .. 16). Note that this integer essentially provides
+// the input bits "shifted to the left" by one position: for example, the input
+// to compute the least significant recoded digit, given that there's no bit
+// b_-1, has to be b_4 b_3 b_2 b_1 b_0 0.
+//
+// DOUBLING CASE:
+//
+// Point addition formulas for short Weierstrass curves are often incomplete.
+// Edge cases such as P + P or P + ∞ must be handled separately. This
+// complicates constant-time requirements. P + ∞ cannot be avoided (any window
+// may be zero) and is handled with constant-time selects. P + P (where P is not
+// ∞) usually is not. Instead, windowing strategies are chosen to avoid this
+// case. Whether this happens depends on the group order.
+//
+// Let w be the window width (in this function, w = 5). The non-trivial doubling
+// case in single-point scalar multiplication may occur if and only if the
+// 2^(w-1) bit of the group order is zero.
+//
+// Note the above only holds if the scalar is fully reduced and the group order
+// is a prime that is much larger than 2^w. It also only holds when windows
+// are applied from most significant to least significant, doubling between each
+// window. It does not apply to more complex table strategies such as
+// |EC_GFp_nistz256_method|.
+//
+// PROOF:
+//
+// Let n be the group order. Let l be the number of bits needed to represent n.
+// Assume there exists some 0 <= k < n such that signed w-bit windowed
+// multiplication hits the doubling case.
+//
+// Windowed multiplication consists of iterating over groups of s_i (defined
+// above based on k's binary representation) from most to least significant. At
+// iteration i (for i = ..., 3w, 2w, w, 0, starting from the most significant
+// window), we:
+//
+// 1. Double the accumulator A, w times. Let A_i be the value of A at this
+// point.
+//
+// 2. Set A to T_i + A_i, where T_i is a precomputed multiple of P
+// corresponding to the window s_(i+w-1) ... s_i.
+//
+// Let j be the index such that A_j = T_j ≠ ∞. Looking at A_i and T_i as
+// multiples of P, define a_i and t_i to be scalar coefficients of A_i and T_i.
+// Thus a_j = t_j ≠ 0 (mod n). Note a_i and t_i may not be reduced mod n. t_i is
+// the value of the w signed bits s_(i+w-1) ... s_i. a_i is computed as a_i =
+// 2^w * (a_(i+w) + t_(i+w)).
+//
+// t_i is bounded by -2^(w-1) <= t_i <= 2^(w-1). Additionally, we may write it
+// in terms of unsigned bits b_i. t_i consists of signed bits s_(i+w-1) ... s_i.
+// This is computed as:
+//
+// b_(i+w-2) b_(i+w-3) ... b_i b_(i-1)
+// - b_(i+w-1) b_(i+w-2) ... b_(i+1) b_i
+// --------------------------------------------
+// t_i = s_(i+w-1) s_(i+w-2) ... s_(i+1) s_i
+//
+// Observe that b_(i+w-2) through b_i occur in both terms. Let x be the integer
+// represented by that bit string, i.e. 2^(w-2)*b_(i+w-2) + ... + b_i.
+//
+// t_i = (2*x + b_(i-1)) - (2^(w-1)*b_(i+w-1) + x)
+// = x - 2^(w-1)*b_(i+w-1) + b_(i-1)
+//
+// Or, using C notation for bit operations:
+//
+// t_i = (k>>i) & ((1<<(w-1)) - 1) - (k>>i) & (1<<(w-1)) + (k>>(i-1)) & 1
+//
+// Note b_(i-1) is added in left-shifted by one (or doubled) from its place.
+// This is compensated by t_(i-w)'s subtraction term. Thus, a_i may be computed
+// by adding b_l b_(l-1) ... b_(i+1) b_i and an extra copy of b_(i-1). In C
+// notation, this is:
+//
+// a_i = (k>>(i+w)) << w + ((k>>(i+w-1)) & 1) << w
+//
+// Observe that, while t_i may be positive or negative, a_i is bounded by
+// 0 <= a_i < n + 2^w. Additionally, a_i can only be zero if b_(i+w-1) and up
+// are all zero. (Note this implies a non-trivial P + (-P) is unreachable for
+// all groups. That would imply the subsequent a_i is zero, which means all
+// terms thus far were zero.)
+//
+// Returning to our doubling position, we have a_j = t_j (mod n). We now
+// determine the value of a_j - t_j, which must be divisible by n. Our bounds on
+// a_j and t_j imply a_j - t_j is 0 or n. If it is 0, a_j = t_j. However, 2^w
+// divides a_j and -2^(w-1) <= t_j <= 2^(w-1), so this can only happen if
+// a_j = t_j = 0, which is a trivial doubling. Therefore, a_j - t_j = n.
+//
+// Now we determine j. Suppose j > 0. w divides j, so j >= w. Then,
+//
+// n = a_j - t_j = (k>>(j+w)) << w + ((k>>(j+w-1)) & 1) << w - t_j
+// <= k/2^j + 2^w - t_j
+// < n/2^w + 2^w + 2^(w-1)
+//
+// n is much larger than 2^w, so this is impossible. Thus, j = 0: only the final
+// addition may hit the doubling case.
+//
+// Finally, we consider bit patterns for n and k. Divide k into k_H + k_M + k_L
+// such that k_H is the contribution from b_(l-1) .. b_w, k_M is the
+// contribution from b_(w-1), and k_L is the contribution from b_(w-2) ... b_0.
+// That is:
+//
+// - 2^w divides k_H
+// - k_M is 0 or 2^(w-1)
+// - 0 <= k_L < 2^(w-1)
+//
+// Divide n into n_H + n_M + n_L similarly. We thus have:
+//
+// t_0 = (k>>0) & ((1<<(w-1)) - 1) - (k>>0) & (1<<(w-1)) + (k>>(0-1)) & 1
+// = k & ((1<<(w-1)) - 1) - k & (1<<(w-1))
+// = k_L - k_M
+//
+// a_0 = (k>>(0+w)) << w + ((k>>(0+w-1)) & 1) << w
+// = (k>>w) << w + ((k>>(w-1)) & 1) << w
+// = k_H + 2*k_M
+//
+// n = a_0 - t_0
+// n_H + n_M + n_L = (k_H + 2*k_M) - (k_L - k_M)
+// = k_H + 3*k_M - k_L
+//
+// k_H - k_L < k and k < n, so k_H - k_L ≠ n. Therefore k_M is not 0 and must be
+// 2^(w-1). Now we consider k_H and n_H. We know k_H <= n_H. Suppose k_H = n_H.
+// Then,
+//
+// n_M + n_L = 3*(2^(w-1)) - k_L
+// > 3*(2^(w-1)) - 2^(w-1)
+// = 2^w
+//
+// Contradiction (n_M + n_L is the bottom w bits of n). Thus k_H < n_H. Suppose
+// k_H < n_H - 2*2^w. Then,
+//
+// n_H + n_M + n_L = k_H + 3*(2^(w-1)) - k_L
+// < n_H - 2*2^w + 3*(2^(w-1)) - k_L
+// n_M + n_L < -2^(w-1) - k_L
+//
+// Contradiction. Thus, k_H = n_H - 2^w. (Note 2^w divides n_H and k_H.) Thus,
+//
+// n_H + n_M + n_L = k_H + 3*(2^(w-1)) - k_L
+// = n_H - 2^w + 3*(2^(w-1)) - k_L
+// n_M + n_L = 2^(w-1) - k_L
+// <= 2^(w-1)
+//
+// Equality would mean 2^(w-1) divides n, which is impossible if n is prime.
+// Thus n_M + n_L < 2^(w-1), so n_M is zero, proving our condition.
+//
+// This proof constructs k, so, to show the converse, let k_H = n_H - 2^w,
+// k_M = 2^(w-1), k_L = 2^(w-1) - n_L. This will result in a non-trivial point
+// doubling in the final addition and is the only such scalar.
+//
+// COMMON CURVES:
+//
+// The group orders for common curves end in the following bit patterns:
+//
+// P-521: ...00001001; w = 4 is okay
+// P-384: ...01110011; w = 2, 5, 6, 7 are okay
+// P-256: ...01010001; w = 5, 7 are okay
+// P-224: ...00111101; w = 3, 4, 5, 6 are okay
void ec_GFp_nistp_recode_scalar_bits(uint8_t *sign, uint8_t *digit,
uint8_t in) {
uint8_t s, d;
diff --git a/src/crypto/fipsmodule/ecdh/ecdh.c b/src/crypto/fipsmodule/ecdh/ecdh.c
index b9dc237..a7b2f08 100644
--- a/src/crypto/fipsmodule/ecdh/ecdh.c
+++ b/src/crypto/fipsmodule/ecdh/ecdh.c
@@ -93,7 +93,7 @@
EC_RAW_POINT shared_point;
uint8_t buf[EC_MAX_BYTES];
size_t buflen;
- if (!ec_point_mul_scalar(group, &shared_point, NULL, &pub_key->raw, priv) ||
+ if (!ec_point_mul_scalar(group, &shared_point, &pub_key->raw, priv) ||
!ec_point_get_affine_coordinate_bytes(group, buf, NULL, &buflen,
sizeof(buf), &shared_point)) {
OPENSSL_PUT_ERROR(ECDH, ECDH_R_POINT_ARITHMETIC_FAILURE);
diff --git a/src/crypto/fipsmodule/ecdsa/ecdsa.c b/src/crypto/fipsmodule/ecdsa/ecdsa.c
index 010ee02..38771d5 100644
--- a/src/crypto/fipsmodule/ecdsa/ecdsa.c
+++ b/src/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -232,7 +232,7 @@
ec_scalar_from_montgomery(group, out_kinv_mont, out_kinv_mont);
// Compute r, the x-coordinate of generator * k.
- if (!ec_point_mul_scalar(group, &tmp_point, &k, NULL, NULL) ||
+ if (!ec_point_mul_scalar_base(group, &tmp_point, &k) ||
!ec_get_x_coordinate_as_scalar(group, out_r, &tmp_point)) {
goto err;
}
diff --git a/src/crypto/fipsmodule/fips_shared.lds b/src/crypto/fipsmodule/fips_shared.lds
new file mode 100644
index 0000000..6d44abc
--- /dev/null
+++ b/src/crypto/fipsmodule/fips_shared.lds
@@ -0,0 +1,19 @@
+SECTIONS
+{
+ .text : {
+ BORINGSSL_bcm_text_start = .;
+ *(.text)
+ BORINGSSL_bcm_text_end = .;
+ }
+ .rodata : {
+ BORINGSSL_bcm_rodata_start = .;
+ *(.rodata)
+ BORINGSSL_bcm_rodata_end = .;
+ }
+
+ /DISCARD/ : {
+ *(.rela.dyn)
+ *(.data)
+ *(.rel.ro)
+ }
+}
diff --git a/src/crypto/fipsmodule/fips_shared_support.c b/src/crypto/fipsmodule/fips_shared_support.c
new file mode 100644
index 0000000..2a66a1f
--- /dev/null
+++ b/src/crypto/fipsmodule/fips_shared_support.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdint.h>
+
+
+#if defined(BORINGSSL_FIPS) && defined(BORINGSSL_SHARED_LIBRARY)
+// BORINGSSL_bcm_text_hash is is default hash value for the FIPS integrity check
+// that must be replaced with the real value during the build process. This
+// value need only be distinct, i.e. so that we can safely search-and-replace it
+// in an object file.
+const uint8_t BORINGSSL_bcm_text_hash[64];
+const uint8_t BORINGSSL_bcm_text_hash[64] = {
+ 0xae, 0x2c, 0xea, 0x2a, 0xbd, 0xa6, 0xf3, 0xec, 0x97, 0x7f, 0x9b,
+ 0xf6, 0x94, 0x9a, 0xfc, 0x83, 0x68, 0x27, 0xcb, 0xa0, 0xa0, 0x9f,
+ 0x6b, 0x6f, 0xde, 0x52, 0xcd, 0xe2, 0xcd, 0xff, 0x31, 0x80, 0xa2,
+ 0xd4, 0xc3, 0x66, 0x0f, 0xc2, 0x6a, 0x7b, 0xf4, 0xbe, 0x39, 0xa2,
+ 0xd7, 0x25, 0xdb, 0x21, 0x98, 0xe9, 0xd5, 0x53, 0xbf, 0x5c, 0x32,
+ 0x06, 0x83, 0x34, 0x0c, 0x65, 0x89, 0x52, 0xbd, 0x1f,
+};
+#endif // FIPS && SHARED_LIBRARY
diff --git a/src/crypto/fipsmodule/md5/asm/md5-586.pl b/src/crypto/fipsmodule/md5/asm/md5-586.pl
index ded9442..20c226b 100644
--- a/src/crypto/fipsmodule/md5/asm/md5-586.pl
+++ b/src/crypto/fipsmodule/md5/asm/md5-586.pl
@@ -36,7 +36,7 @@
&md5_block("md5_block_asm_data_order");
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
sub Np
{
diff --git a/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl b/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
index 6eb33c0..509bcde 100644
--- a/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
+++ b/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
@@ -380,4 +380,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl b/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
index b9edb79..d3e3763 100644
--- a/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
@@ -1143,4 +1143,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl b/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
index 778b543..54c80f7 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
@@ -553,4 +553,4 @@
print $_,"\n";
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl b/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
index 972be41..f57017d 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
@@ -284,4 +284,4 @@
print $_,"\n";
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
index 0d9ce15..45e1ee1 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
@@ -285,4 +285,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
index 1dd2519..e0e5010 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
@@ -410,4 +410,4 @@
}
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-x86.pl b/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
index 02edf03..a2ab859 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
@@ -1150,7 +1150,7 @@
&asciz("GHASH for x86, CRYPTOGAMS by <appro\@openssl.org>");
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
# A question was risen about choice of vanilla MMX. Or rather why wasn't
# SSE2 chosen instead? In addition to the fact that MMX runs on legacy
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl b/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
index b267698..5c4122f 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
@@ -1803,4 +1803,4 @@
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl b/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
index c46cdb5..7a1259b 100644
--- a/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
@@ -667,4 +667,4 @@
print $_,"\n";
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl b/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
index 1435db5..99124a2 100644
--- a/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
@@ -425,4 +425,4 @@
}
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/ccm.c b/src/crypto/fipsmodule/modes/ccm.c
deleted file mode 100644
index 5a153f4..0000000
--- a/src/crypto/fipsmodule/modes/ccm.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/* ====================================================================
- * Copyright (c) 2011 The OpenSSL Project. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- * software must display the following acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- * endorse or promote products derived from this software without
- * prior written permission. For written permission, please contact
- * openssl-core@openssl.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- * nor may "OpenSSL" appear in their names without prior written
- * permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- * acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include <openssl/cpu.h>
-#include <openssl/mem.h>
-
-#include "../../internal.h"
-#include "internal.h"
-
-
-struct ccm128_state {
- union {
- uint64_t u[2];
- uint8_t c[16];
- } nonce, cmac;
-};
-
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
- block128_f block, ctr128_f ctr, unsigned M, unsigned L) {
- if (M < 4 || M > 16 || (M & 1) != 0 || L < 2 || L > 8) {
- return 0;
- }
- ctx->block = block;
- ctx->ctr = ctr;
- ctx->M = M;
- ctx->L = L;
- return 1;
-}
-
-size_t CRYPTO_ccm128_max_input(const CCM128_CONTEXT *ctx) {
- return ctx->L >= sizeof(size_t) ? (size_t)-1
- : (((size_t)1) << (ctx->L * 8)) - 1;
-}
-
-static int ccm128_init_state(const CCM128_CONTEXT *ctx,
- struct ccm128_state *state, const AES_KEY *key,
- const uint8_t *nonce, size_t nonce_len,
- const uint8_t *aad, size_t aad_len,
- size_t plaintext_len) {
- const block128_f block = ctx->block;
- const unsigned M = ctx->M;
- const unsigned L = ctx->L;
-
- // |L| determines the expected |nonce_len| and the limit for |plaintext_len|.
- if (plaintext_len > CRYPTO_ccm128_max_input(ctx) ||
- nonce_len != 15 - L) {
- return 0;
- }
-
- // Assemble the first block for computing the MAC.
- OPENSSL_memset(state, 0, sizeof(*state));
- state->nonce.c[0] = (uint8_t)((L - 1) | ((M - 2) / 2) << 3);
- if (aad_len != 0) {
- state->nonce.c[0] |= 0x40; // Set AAD Flag
- }
- OPENSSL_memcpy(&state->nonce.c[1], nonce, nonce_len);
- for (unsigned i = 0; i < L; i++) {
- state->nonce.c[15 - i] = (uint8_t)(plaintext_len >> (8 * i));
- }
-
- (*block)(state->nonce.c, state->cmac.c, key);
- size_t blocks = 1;
-
- if (aad_len != 0) {
- unsigned i;
- // Cast to u64 to avoid the compiler complaining about invalid shifts.
- uint64_t aad_len_u64 = aad_len;
- if (aad_len_u64 < 0x10000 - 0x100) {
- state->cmac.c[0] ^= (uint8_t)(aad_len_u64 >> 8);
- state->cmac.c[1] ^= (uint8_t)aad_len_u64;
- i = 2;
- } else if (aad_len_u64 <= 0xffffffff) {
- state->cmac.c[0] ^= 0xff;
- state->cmac.c[1] ^= 0xfe;
- state->cmac.c[2] ^= (uint8_t)(aad_len_u64 >> 24);
- state->cmac.c[3] ^= (uint8_t)(aad_len_u64 >> 16);
- state->cmac.c[4] ^= (uint8_t)(aad_len_u64 >> 8);
- state->cmac.c[5] ^= (uint8_t)aad_len_u64;
- i = 6;
- } else {
- state->cmac.c[0] ^= 0xff;
- state->cmac.c[1] ^= 0xff;
- state->cmac.c[2] ^= (uint8_t)(aad_len_u64 >> 56);
- state->cmac.c[3] ^= (uint8_t)(aad_len_u64 >> 48);
- state->cmac.c[4] ^= (uint8_t)(aad_len_u64 >> 40);
- state->cmac.c[5] ^= (uint8_t)(aad_len_u64 >> 32);
- state->cmac.c[6] ^= (uint8_t)(aad_len_u64 >> 24);
- state->cmac.c[7] ^= (uint8_t)(aad_len_u64 >> 16);
- state->cmac.c[8] ^= (uint8_t)(aad_len_u64 >> 8);
- state->cmac.c[9] ^= (uint8_t)aad_len_u64;
- i = 10;
- }
-
- do {
- for (; i < 16 && aad_len != 0; i++) {
- state->cmac.c[i] ^= *aad;
- aad++;
- aad_len--;
- }
- (*block)(state->cmac.c, state->cmac.c, key);
- blocks++;
- i = 0;
- } while (aad_len != 0);
- }
-
- // Per RFC 3610, section 2.6, the total number of block cipher operations done
- // must not exceed 2^61. There are two block cipher operations remaining per
- // message block, plus one block at the end to encrypt the MAC.
- size_t remaining_blocks = 2 * ((plaintext_len + 15) / 16) + 1;
- if (plaintext_len + 15 < plaintext_len ||
- remaining_blocks + blocks < blocks ||
- (uint64_t) remaining_blocks + blocks > UINT64_C(1) << 61) {
- return 0;
- }
-
- // Assemble the first block for encrypting and decrypting. The bottom |L|
- // bytes are replaced with a counter and all bit the encoding of |L| is
- // cleared in the first byte.
- state->nonce.c[0] &= 7;
- return 1;
-}
-
-static int ccm128_encrypt(const CCM128_CONTEXT *ctx, struct ccm128_state *state,
- const AES_KEY *key, uint8_t *out, const uint8_t *in,
- size_t len) {
- // The counter for encryption begins at one.
- for (unsigned i = 0; i < ctx->L; i++) {
- state->nonce.c[15 - i] = 0;
- }
- state->nonce.c[15] = 1;
-
- uint8_t partial_buf[16];
- unsigned num = 0;
- if (ctx->ctr != NULL) {
- CRYPTO_ctr128_encrypt_ctr32(in, out, len, key, state->nonce.c, partial_buf,
- &num, ctx->ctr);
- } else {
- CRYPTO_ctr128_encrypt(in, out, len, key, state->nonce.c, partial_buf, &num,
- ctx->block);
- }
- return 1;
-}
-
-static int ccm128_compute_mac(const CCM128_CONTEXT *ctx,
- struct ccm128_state *state, const AES_KEY *key,
- uint8_t *out_tag, size_t tag_len,
- const uint8_t *in, size_t len) {
- block128_f block = ctx->block;
- if (tag_len != ctx->M) {
- return 0;
- }
-
- // Incorporate |in| into the MAC.
- union {
- uint64_t u[2];
- uint8_t c[16];
- } tmp;
- while (len >= 16) {
- OPENSSL_memcpy(tmp.c, in, 16);
- state->cmac.u[0] ^= tmp.u[0];
- state->cmac.u[1] ^= tmp.u[1];
- (*block)(state->cmac.c, state->cmac.c, key);
- in += 16;
- len -= 16;
- }
- if (len > 0) {
- for (size_t i = 0; i < len; i++) {
- state->cmac.c[i] ^= in[i];
- }
- (*block)(state->cmac.c, state->cmac.c, key);
- }
-
- // Encrypt the MAC with counter zero.
- for (unsigned i = 0; i < ctx->L; i++) {
- state->nonce.c[15 - i] = 0;
- }
- (*block)(state->nonce.c, tmp.c, key);
- state->cmac.u[0] ^= tmp.u[0];
- state->cmac.u[1] ^= tmp.u[1];
-
- OPENSSL_memcpy(out_tag, state->cmac.c, tag_len);
- return 1;
-}
-
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
- uint8_t *out, uint8_t *out_tag, size_t tag_len,
- const uint8_t *nonce, size_t nonce_len,
- const uint8_t *in, size_t len, const uint8_t *aad,
- size_t aad_len) {
- struct ccm128_state state;
- return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
- len) &&
- ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, in, len) &&
- ccm128_encrypt(ctx, &state, key, out, in, len);
-}
-
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
- uint8_t *out, uint8_t *out_tag, size_t tag_len,
- const uint8_t *nonce, size_t nonce_len,
- const uint8_t *in, size_t len, const uint8_t *aad,
- size_t aad_len) {
- struct ccm128_state state;
- return ccm128_init_state(ctx, &state, key, nonce, nonce_len, aad, aad_len,
- len) &&
- ccm128_encrypt(ctx, &state, key, out, in, len) &&
- ccm128_compute_mac(ctx, &state, key, out_tag, tag_len, out, len);
-}
diff --git a/src/crypto/fipsmodule/modes/internal.h b/src/crypto/fipsmodule/modes/internal.h
index dec1e56..0971a90 100644
--- a/src/crypto/fipsmodule/modes/internal.h
+++ b/src/crypto/fipsmodule/modes/internal.h
@@ -345,42 +345,6 @@
#endif // GHASH_ASM
-// CCM.
-
-typedef struct ccm128_context {
- block128_f block;
- ctr128_f ctr;
- unsigned M, L;
-} CCM128_CONTEXT;
-
-// CRYPTO_ccm128_init initialises |ctx| to use |block| (typically AES) with the
-// specified |M| and |L| parameters. It returns one on success and zero if |M|
-// or |L| is invalid.
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
- block128_f block, ctr128_f ctr, unsigned M, unsigned L);
-
-// CRYPTO_ccm128_max_input returns the maximum input length accepted by |ctx|.
-size_t CRYPTO_ccm128_max_input(const CCM128_CONTEXT *ctx);
-
-// CRYPTO_ccm128_encrypt encrypts |len| bytes from |in| to |out| writing the tag
-// to |out_tag|. |key| must be the same key that was passed to
-// |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
- uint8_t *out, uint8_t *out_tag, size_t tag_len,
- const uint8_t *nonce, size_t nonce_len,
- const uint8_t *in, size_t len, const uint8_t *aad,
- size_t aad_len);
-
-// CRYPTO_ccm128_decrypt decrypts |len| bytes from |in| to |out|, writing the
-// expected tag to |out_tag|. |key| must be the same key that was passed to
-// |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
- uint8_t *out, uint8_t *out_tag, size_t tag_len,
- const uint8_t *nonce, size_t nonce_len,
- const uint8_t *in, size_t len, const uint8_t *aad,
- size_t aad_len);
-
-
// CBC.
// cbc128_f is the type of a function that performs CBC-mode encryption.
diff --git a/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl b/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
index 76b5f9b..eb2a592 100644
--- a/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
+++ b/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
@@ -84,4 +84,4 @@
.size CRYPTO_rdrand_multiple8_buf,.-CRYPTO_rdrand_multiple8_buf
___
-close STDOUT; # flush
+close STDOUT or die "error closing STDOUT"; # flush
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index 903ba9a..ab2abe9 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -554,7 +554,7 @@
if (!ret) {
OPENSSL_PUT_ERROR(RSA, RSA_R_PADDING_CHECK_FAILED);
} else {
- CONSTTIME_DECLASSIFY(out, out_len);
+ CONSTTIME_DECLASSIFY(out, *out_len);
}
err:
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-586.pl b/src/crypto/fipsmodule/sha/asm/sha1-586.pl
index 87fd361..bf42961 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-586.pl
@@ -1483,4 +1483,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
index 27187dd..ca82514 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
@@ -740,4 +740,4 @@
print $_,$/;
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
index 7c8880f..c147462 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
@@ -353,4 +353,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl b/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
index fd4ff2a..df7cbc3 100755
--- a/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
@@ -2122,4 +2122,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha256-586.pl b/src/crypto/fipsmodule/sha/asm/sha256-586.pl
index 129a9f4..240a604 100644
--- a/src/crypto/fipsmodule/sha/asm/sha256-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha256-586.pl
@@ -1287,4 +1287,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl b/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
index e5ecdfd..15d78de 100644
--- a/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
@@ -735,4 +735,4 @@
print $_,"\n";
}
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-586.pl b/src/crypto/fipsmodule/sha/asm/sha512-586.pl
index 25a5f25..01acf67 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-586.pl
@@ -922,4 +922,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
index cc247a4..c8c715e 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
@@ -670,4 +670,4 @@
close SELF;
print $code;
-close STDOUT; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
index 3f69071..64306be 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
@@ -457,4 +457,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl b/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
index 4927850..8c5a5f3 100755
--- a/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
@@ -278,7 +278,6 @@
jnz _shaext_shortcut
___
# XOP codepath removed.
-___
$code.=<<___ if ($avx>1);
and \$`1<<8|1<<5|1<<3`,%r11d # check for BMI2+AVX2+BMI1
cmp \$`1<<8|1<<5|1<<3`,%r11d
@@ -2083,4 +2082,4 @@
print $_,"\n";
}
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index b80f065..76a317a 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -240,6 +240,36 @@
#define CONSTTIME_TRUE_8 ((uint8_t)0xff)
#define CONSTTIME_FALSE_8 ((uint8_t)0)
+// value_barrier_w returns |a|, but prevents GCC and Clang from reasoning about
+// the returned value. This is used to mitigate compilers undoing constant-time
+// code, until we can express our requirements directly in the language.
+//
+// Note the compiler is aware that |value_barrier_w| has no side effects and
+// always has the same output for a given input. This allows it to eliminate
+// dead code, move computations across loops, and vectorize.
+static inline crypto_word_t value_barrier_w(crypto_word_t a) {
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+ __asm__("" : "+r"(a) : /* no inputs */);
+#endif
+ return a;
+}
+
+// value_barrier_u32 behaves like |value_barrier_w| but takes a |uint32_t|.
+static inline uint32_t value_barrier_u32(uint32_t a) {
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+ __asm__("" : "+r"(a) : /* no inputs */);
+#endif
+ return a;
+}
+
+// value_barrier_u64 behaves like |value_barrier_w| but takes a |uint64_t|.
+static inline uint64_t value_barrier_u64(uint64_t a) {
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+ __asm__("" : "+r"(a) : /* no inputs */);
+#endif
+ return a;
+}
+
// constant_time_msb_w returns the given value with the MSB copied to all the
// other bits.
static inline crypto_word_t constant_time_msb_w(crypto_word_t a) {
@@ -352,7 +382,13 @@
static inline crypto_word_t constant_time_select_w(crypto_word_t mask,
crypto_word_t a,
crypto_word_t b) {
- return (mask & a) | (~mask & b);
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ //
+ // Adding barriers to both |mask| and |~mask| breaks the relationship between
+ // the two, which makes the compiler stick with bitmasks.
+ return (value_barrier_w(mask) & a) | (value_barrier_w(~mask) & b);
}
// constant_time_select_8 acts like |constant_time_select| but operates on
diff --git a/src/crypto/mem.c b/src/crypto/mem.c
index 14e0bdf..0ca0e84 100644
--- a/src/crypto/mem.c
+++ b/src/crypto/mem.c
@@ -79,7 +79,10 @@
static void __asan_unpoison_memory_region(const void *addr, size_t size) {}
#endif
-#if defined(__GNUC__) || defined(__clang__)
+// Windows doesn't really support weak symbols as of May 2019, and Clang on
+// Windows will emit strong symbols instead. See
+// https://bugs.llvm.org/show_bug.cgi?id=37598
+#if defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))
// sdallocx is a sized |free| function. By passing the size (which we happen to
// always know in BoringSSL), the malloc implementation can save work. We cannot
// depend on |sdallocx| being available so we declare a wrapper that falls back
diff --git a/src/crypto/obj/obj_dat.h b/src/crypto/obj/obj_dat.h
index 0313a08..1a9bf15 100644
--- a/src/crypto/obj/obj_dat.h
+++ b/src/crypto/obj/obj_dat.h
@@ -57,7 +57,7 @@
/* This file is generated by crypto/obj/objects.go. */
-#define NUM_NID 960
+#define NUM_NID 961
static const uint8_t kObjectData[] = {
/* NID_rsadsi */
@@ -8756,6 +8756,7 @@
{"KxANY", "kx-any", NID_kx_any, 0, NULL, 0},
{"AuthANY", "auth-any", NID_auth_any, 0, NULL, 0},
{"CECPQ2", "CECPQ2", NID_CECPQ2, 0, NULL, 0},
+ {"CECPQ2b", "CECPQ2b", NID_CECPQ2b, 0, NULL, 0},
};
static const unsigned kNIDsInShortNameOrder[] = {
@@ -8818,6 +8819,7 @@
109 /* CAST5-ECB */,
111 /* CAST5-OFB */,
959 /* CECPQ2 */,
+ 960 /* CECPQ2b */,
894 /* CMAC */,
13 /* CN */,
141 /* CRLReason */,
@@ -9723,6 +9725,7 @@
179 /* CA Issuers */,
785 /* CA Repository */,
959 /* CECPQ2 */,
+ 960 /* CECPQ2b */,
131 /* Code Signing */,
783 /* Diffie-Hellman based MAC */,
382 /* Directory */,
diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num
index 5fa839d..f2d4e8c 100644
--- a/src/crypto/obj/obj_mac.num
+++ b/src/crypto/obj/obj_mac.num
@@ -948,3 +948,4 @@
kx_any 957
auth_any 958
CECPQ2 959
+CECPQ2b 960
diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt
index 6dbb7ad..6e7ecf0 100644
--- a/src/crypto/obj/objects.txt
+++ b/src/crypto/obj/objects.txt
@@ -1337,6 +1337,9 @@
# NID for CECPQ2 (no corresponding OID).
: CECPQ2
+# NID for CECPQ2 (no corresponding OID).
+ : CECPQ2b
+
# See RFC 8410.
1 3 101 112 : ED25519
diff --git a/src/crypto/perlasm/arm-xlate.pl b/src/crypto/perlasm/arm-xlate.pl
index eb8ea59..655be61 100755
--- a/src/crypto/perlasm/arm-xlate.pl
+++ b/src/crypto/perlasm/arm-xlate.pl
@@ -168,6 +168,15 @@
$line =~ s|^\s+||; # ... and skip white spaces in beginning...
$line =~ s|\s+$||; # ... and at the end
+ if ($flavour =~ /64/) {
+ my $copy = $line;
+ # Also remove line comments.
+ $copy =~ s|//.*||;
+ if ($copy =~ /\b[wx]18\b/) {
+ die "r18 is reserved by the platform and may not be used.";
+ }
+ }
+
{
$line =~ s|[\b\.]L(\w{2,})|L$1|g; # common denominator for Locallabel
$line =~ s|\bL(\w{2,})|\.L$1|g if ($dotinlocallabels);
diff --git a/src/crypto/pkcs7/pkcs7.c b/src/crypto/pkcs7/pkcs7.c
index c04bffd..1d0b139 100644
--- a/src/crypto/pkcs7/pkcs7.c
+++ b/src/crypto/pkcs7/pkcs7.c
@@ -134,7 +134,7 @@
int pkcs7_bundle(CBB *out, int (*cb)(CBB *out, const void *arg),
const void *arg) {
CBB outer_seq, oid, wrapped_seq, seq, version_bytes, digest_algos_set,
- content_info;
+ content_info, signer_infos;
// See https://tools.ietf.org/html/rfc2315#section-7
if (!CBB_add_asn1(out, &outer_seq, CBS_ASN1_SEQUENCE) ||
@@ -150,7 +150,8 @@
!CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
!CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
- !cb(&seq, arg)) {
+ !cb(&seq, arg) ||
+ !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET)) {
return 0;
}
diff --git a/src/crypto/pkcs7/pkcs7_test.cc b/src/crypto/pkcs7/pkcs7_test.cc
index 1ac9af2..948b44f 100644
--- a/src/crypto/pkcs7/pkcs7_test.cc
+++ b/src/crypto/pkcs7/pkcs7_test.cc
@@ -469,7 +469,7 @@
"fNQMQoI9So4Vdy88Kow6BBBV3Lu6sZHue+cjxXETrmshNdNk8ABUMQA=\n"
"-----END PKCS7-----\n";
-static void TestCertRepase(const uint8_t *der_bytes, size_t der_len) {
+static void TestCertReparse(const uint8_t *der_bytes, size_t der_len) {
bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
ASSERT_TRUE(certs);
bssl::UniquePtr<STACK_OF(X509)> certs2(sk_X509_new_null());
@@ -638,11 +638,11 @@
}
TEST(PKCS7Test, CertReparseNSS) {
- TestCertRepase(kPKCS7NSS, sizeof(kPKCS7NSS));
+ TestCertReparse(kPKCS7NSS, sizeof(kPKCS7NSS));
}
TEST(PKCS7Test, CertReparseWindows) {
- TestCertRepase(kPKCS7Windows, sizeof(kPKCS7Windows));
+ TestCertReparse(kPKCS7Windows, sizeof(kPKCS7Windows));
}
TEST(PKCS7Test, CrlReparse) {
diff --git a/src/crypto/pkcs8/p5_pbev2.c b/src/crypto/pkcs8/p5_pbev2.c
index 7497b00..e58cf44 100644
--- a/src/crypto/pkcs8/p5_pbev2.c
+++ b/src/crypto/pkcs8/p5_pbev2.c
@@ -81,6 +81,10 @@
static const uint8_t kHMACWithSHA1[] = {0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x02, 0x07};
+// 1.2.840.113549.2.9
+static const uint8_t kHMACWithSHA256[] = {0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x02, 0x09};
+
static const struct {
uint8_t oid[9];
uint8_t oid_len;
@@ -140,18 +144,18 @@
}
static int pkcs5_pbe2_cipher_init(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
- unsigned iterations, const char *pass,
- size_t pass_len, const uint8_t *salt,
- size_t salt_len, const uint8_t *iv,
- size_t iv_len, int enc) {
+ const EVP_MD *pbkdf2_md, unsigned iterations,
+ const char *pass, size_t pass_len,
+ const uint8_t *salt, size_t salt_len,
+ const uint8_t *iv, size_t iv_len, int enc) {
if (iv_len != EVP_CIPHER_iv_length(cipher)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
return 0;
}
uint8_t key[EVP_MAX_KEY_LENGTH];
- int ret = PKCS5_PBKDF2_HMAC_SHA1(pass, pass_len, salt, salt_len, iterations,
- EVP_CIPHER_key_length(cipher), key) &&
+ int ret = PKCS5_PBKDF2_HMAC(pass, pass_len, salt, salt_len, iterations,
+ pbkdf2_md, EVP_CIPHER_key_length(cipher), key) &&
EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, key, iv, enc);
OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
return ret;
@@ -201,9 +205,9 @@
return 0;
}
- return pkcs5_pbe2_cipher_init(ctx, cipher, iterations, pass, pass_len, salt,
- salt_len, iv, EVP_CIPHER_iv_length(cipher),
- 1 /* encrypt */);
+ return pkcs5_pbe2_cipher_init(ctx, cipher, EVP_sha1(), iterations, pass,
+ pass_len, salt, salt_len, iv,
+ EVP_CIPHER_iv_length(cipher), 1 /* encrypt */);
}
int PKCS5_pbe2_decrypt_init(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
@@ -264,6 +268,7 @@
}
}
+ const EVP_MD *md = EVP_sha1();
if (CBS_len(&pbkdf2_params) != 0) {
CBS alg_id, prf;
if (!CBS_get_asn1(&pbkdf2_params, &alg_id, CBS_ASN1_SEQUENCE) ||
@@ -273,14 +278,18 @@
return 0;
}
- // We only support hmacWithSHA1. It is the DEFAULT, so DER requires it be
- // omitted, but we match OpenSSL in tolerating it being present.
- if (!CBS_mem_equal(&prf, kHMACWithSHA1, sizeof(kHMACWithSHA1))) {
+ if (CBS_mem_equal(&prf, kHMACWithSHA1, sizeof(kHMACWithSHA1))) {
+ // hmacWithSHA1 is the DEFAULT, so DER requires it be omitted, but we
+ // match OpenSSL in tolerating it being present.
+ md = EVP_sha1();
+ } else if (CBS_mem_equal(&prf, kHMACWithSHA256, sizeof(kHMACWithSHA256))) {
+ md = EVP_sha256();
+ } else {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
return 0;
}
- // hmacWithSHA1 has a NULL parameter.
+ // All supported PRFs use a NULL parameter.
CBS null;
if (!CBS_get_asn1(&alg_id, &null, CBS_ASN1_NULL) ||
CBS_len(&null) != 0 ||
@@ -301,7 +310,7 @@
return 0;
}
- return pkcs5_pbe2_cipher_init(ctx, cipher, (unsigned)iterations, pass,
+ return pkcs5_pbe2_cipher_init(ctx, cipher, md, (unsigned)iterations, pass,
pass_len, CBS_data(&salt), CBS_len(&salt),
CBS_data(&iv), CBS_len(&iv), 0 /* decrypt */);
}
diff --git a/src/crypto/pkcs8/pkcs12_test.cc b/src/crypto/pkcs8/pkcs12_test.cc
index e6c0e56..d345006 100644
--- a/src/crypto/pkcs8/pkcs12_test.cc
+++ b/src/crypto/pkcs8/pkcs12_test.cc
@@ -683,9 +683,12 @@
0xfe, 0x3a, 0x66, 0x47, 0x40, 0x49, 0x02, 0x02, 0x07, 0xd0,
};
-// kPBES2 is a PKCS#12 file using PBES2 created with:
+// kPBES2WithSHA1 is a PKCS#12 file using PBES2 and HMAC-SHA-1 created with:
// openssl pkcs12 -export -inkey key.pem -in cert.pem -keypbe AES-128-CBC -certpbe AES-128-CBC
-static const uint8_t kPBES2[] = {
+//
+// This was generated with an older OpenSSL, which used hmacWithSHA1 as the PRF.
+// (There is currently no way to specify the PRF in the pkcs12 command.)
+static const uint8_t kPBES2WithSHA1[] = {
0x30, 0x82, 0x0a, 0x03, 0x02, 0x01, 0x03, 0x30, 0x82, 0x09, 0xc9, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
0x09, 0xba, 0x04, 0x82, 0x09, 0xb6, 0x30, 0x82, 0x09, 0xb2, 0x30, 0x82,
@@ -901,6 +904,318 @@
0xc3, 0x8c, 0xc6, 0x69, 0xdc, 0xc6, 0x7f, 0x54, 0xef, 0x04, 0x08, 0xf8,
0x9c, 0x8b, 0x12, 0x27, 0xe8, 0xec, 0x65, 0x02, 0x02, 0x08, 0x00};
+// kPBES2WithSHA256 is a PKCS#12 file using PBES2 and HMAC-SHA-256. It was
+// generated in the same way as |kPBES2WithSHA1|, but using OpenSSL 1.1.1b,
+// which uses hmacWithSHA256 as the PRF.
+static const uint8_t kPBES2WithSHA256[] = {
+ 0x30, 0x82, 0x0a, 0x7f, 0x02, 0x01, 0x03, 0x30, 0x82, 0x0a, 0x45, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
+ 0x0a, 0x36, 0x04, 0x82, 0x0a, 0x32, 0x30, 0x82, 0x0a, 0x2e, 0x30, 0x82,
+ 0x04, 0xa2, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
+ 0x06, 0xa0, 0x82, 0x04, 0x93, 0x30, 0x82, 0x04, 0x8f, 0x02, 0x01, 0x00,
+ 0x30, 0x82, 0x04, 0x88, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x07, 0x01, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86,
+ 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0xb2,
+ 0x5e, 0x0d, 0x6d, 0xda, 0xaa, 0x2f, 0xbe, 0x02, 0x02, 0x08, 0x00, 0x30,
+ 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05,
+ 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
+ 0x01, 0x02, 0x04, 0x10, 0x3c, 0x04, 0x78, 0x37, 0xb3, 0xb2, 0x24, 0xd3,
+ 0xb5, 0x46, 0x20, 0xb7, 0xd2, 0xdd, 0x5d, 0x2e, 0x80, 0x82, 0x04, 0x20,
+ 0x3a, 0x01, 0xe4, 0xf4, 0x57, 0xd3, 0xed, 0x14, 0xd0, 0x42, 0x3f, 0xd3,
+ 0x61, 0xee, 0x84, 0xcd, 0x2b, 0x08, 0x60, 0x30, 0xbd, 0x72, 0xa7, 0xd5,
+ 0xa4, 0xf2, 0x13, 0xe9, 0xf0, 0x44, 0x66, 0x26, 0x34, 0xe7, 0x2c, 0x5d,
+ 0xc9, 0xb0, 0x4b, 0xab, 0x47, 0x16, 0xab, 0xe6, 0x06, 0xa6, 0x3b, 0x79,
+ 0x41, 0x0c, 0x79, 0xd5, 0x9b, 0x02, 0x67, 0xd8, 0x7f, 0xc8, 0x36, 0x37,
+ 0x27, 0xb4, 0x44, 0xa2, 0x5e, 0x0d, 0x38, 0xb8, 0x41, 0x8e, 0x3a, 0xf1,
+ 0xe9, 0xab, 0xe0, 0x19, 0xd0, 0xe1, 0xc7, 0x92, 0xd4, 0x5b, 0x35, 0xf3,
+ 0x79, 0x48, 0x3b, 0xfc, 0x25, 0xfc, 0xc6, 0x9f, 0xed, 0x35, 0x28, 0x5b,
+ 0xfa, 0xee, 0x50, 0x42, 0xa3, 0xc3, 0x96, 0xee, 0xe0, 0x87, 0x33, 0x5e,
+ 0xa7, 0xc7, 0x0a, 0xfe, 0xda, 0xe5, 0xd5, 0x29, 0x6a, 0x57, 0x08, 0x7f,
+ 0x56, 0x37, 0x2a, 0x1a, 0xa0, 0x6d, 0xe9, 0x84, 0xac, 0xed, 0x0e, 0xd8,
+ 0xc0, 0xd8, 0xc6, 0x77, 0xb1, 0xdd, 0x1b, 0xa1, 0xed, 0xa7, 0x79, 0x13,
+ 0x2e, 0x5b, 0x9b, 0x80, 0x44, 0x9e, 0xff, 0x0a, 0x6e, 0x99, 0x33, 0xcf,
+ 0xf1, 0x47, 0x24, 0xaa, 0x48, 0xe7, 0x2c, 0xb3, 0xe6, 0xdc, 0xd4, 0x1e,
+ 0xe4, 0xb8, 0x5e, 0x72, 0xaf, 0x3f, 0xd3, 0x25, 0x4a, 0xac, 0x7b, 0x35,
+ 0xb1, 0x82, 0xa5, 0xd9, 0xf8, 0x01, 0x12, 0x92, 0x49, 0x4c, 0x17, 0x07,
+ 0xb2, 0xb1, 0x3e, 0xcb, 0xfd, 0xd1, 0x17, 0xb5, 0x65, 0x3d, 0x0c, 0x2b,
+ 0x2b, 0xc0, 0x37, 0x9c, 0xe7, 0x04, 0x9b, 0x71, 0x5a, 0x10, 0xc0, 0xba,
+ 0x3b, 0x31, 0xde, 0x0d, 0x66, 0x6c, 0x0d, 0x4c, 0x99, 0x22, 0x76, 0x2a,
+ 0x75, 0x7f, 0x84, 0xd1, 0x07, 0x1f, 0x57, 0xf0, 0x0b, 0x71, 0x41, 0xea,
+ 0x38, 0xe2, 0xe7, 0xbe, 0x11, 0x3c, 0x92, 0x8c, 0x7b, 0x0e, 0xb4, 0x7e,
+ 0x76, 0xc4, 0x80, 0x41, 0xae, 0x4c, 0xe2, 0x38, 0x36, 0xcb, 0x82, 0x39,
+ 0x38, 0x3a, 0x55, 0xb4, 0xe2, 0x35, 0x94, 0xc3, 0xae, 0x3d, 0xd1, 0x03,
+ 0xf3, 0xdb, 0x00, 0xd9, 0xfa, 0x96, 0x62, 0x25, 0x97, 0x51, 0xc5, 0xcf,
+ 0x84, 0xe8, 0xf7, 0x8b, 0x2f, 0x31, 0xeb, 0xa7, 0x0a, 0x22, 0x6f, 0xad,
+ 0xf5, 0x28, 0x25, 0xaa, 0x99, 0x0e, 0xb1, 0x83, 0x9f, 0x70, 0x79, 0xaf,
+ 0x10, 0x7c, 0x2c, 0x55, 0xfe, 0x24, 0x7d, 0xea, 0x85, 0x48, 0x8e, 0x7a,
+ 0xf7, 0x47, 0xd8, 0x0c, 0x64, 0x97, 0xe0, 0x8f, 0x62, 0x5e, 0xd0, 0x4f,
+ 0x21, 0xa4, 0x46, 0x8e, 0x28, 0xb0, 0xb1, 0x90, 0xec, 0x01, 0x7d, 0xc4,
+ 0xc8, 0x6f, 0xf2, 0xe2, 0xb7, 0xc4, 0x35, 0x6c, 0xa9, 0xf6, 0xaf, 0xc2,
+ 0xb6, 0xa9, 0x02, 0x6d, 0xb2, 0x8b, 0x43, 0x6b, 0x41, 0x80, 0x9d, 0x5e,
+ 0x51, 0xa7, 0x31, 0x00, 0x1b, 0xb5, 0x24, 0xed, 0x40, 0x99, 0x33, 0xde,
+ 0x87, 0xd1, 0x4b, 0x76, 0x78, 0x57, 0x4c, 0x33, 0x79, 0x89, 0xd3, 0xfa,
+ 0x70, 0x0f, 0x2f, 0x31, 0x42, 0x8c, 0xce, 0xe9, 0xc0, 0x58, 0xe1, 0x30,
+ 0x30, 0xf1, 0xe9, 0xab, 0xc8, 0x60, 0x7c, 0xe0, 0x6a, 0x99, 0xe7, 0xd3,
+ 0x21, 0x1a, 0xcc, 0x98, 0x60, 0x44, 0xaa, 0xff, 0xee, 0xec, 0x34, 0x20,
+ 0x19, 0xba, 0x03, 0x3b, 0x67, 0x6f, 0xee, 0xd5, 0xb3, 0xa7, 0x21, 0x57,
+ 0xd6, 0x49, 0xaf, 0x91, 0x8f, 0xec, 0x70, 0xd0, 0x59, 0x1a, 0x79, 0xe2,
+ 0xd2, 0x94, 0x82, 0x53, 0xfb, 0xea, 0xd6, 0x83, 0x49, 0x4a, 0x6f, 0xd6,
+ 0xed, 0x15, 0xc3, 0x71, 0x08, 0x3a, 0xbf, 0xde, 0xa8, 0x2d, 0x54, 0xaf,
+ 0x4a, 0x40, 0xbc, 0xe5, 0x53, 0xae, 0x4b, 0x3d, 0x70, 0xfe, 0x1c, 0x03,
+ 0x1e, 0xb2, 0x9d, 0x1c, 0x35, 0xbd, 0x9a, 0xf8, 0xc5, 0xd1, 0xa5, 0x4a,
+ 0x63, 0x18, 0x02, 0xd4, 0xff, 0xdd, 0xcd, 0xb3, 0x6c, 0x38, 0xd1, 0x9a,
+ 0xad, 0x16, 0x71, 0xf1, 0xc6, 0x1d, 0x8f, 0x6c, 0x30, 0xfa, 0x2e, 0x13,
+ 0x9d, 0x0b, 0x4e, 0xe6, 0xd3, 0x37, 0x80, 0x58, 0x26, 0x0d, 0x04, 0x97,
+ 0xe6, 0x8d, 0xcc, 0x63, 0x3c, 0x39, 0x38, 0x2f, 0x7a, 0x73, 0x01, 0x0f,
+ 0x22, 0x69, 0x47, 0x54, 0x9e, 0x42, 0xc8, 0x59, 0xb5, 0x35, 0x43, 0xb4,
+ 0x37, 0x45, 0x59, 0x85, 0xf2, 0x47, 0xc3, 0xfb, 0x23, 0x13, 0x18, 0xef,
+ 0xd8, 0x11, 0x70, 0x74, 0xce, 0x97, 0xcf, 0xbf, 0xd5, 0x2d, 0x99, 0x00,
+ 0x86, 0x56, 0x9b, 0xdf, 0x05, 0x67, 0xf4, 0x49, 0x1e, 0xb5, 0x12, 0x23,
+ 0x46, 0x04, 0x83, 0xf3, 0xc1, 0x59, 0xc7, 0x7b, 0xc3, 0x22, 0x0c, 0x2c,
+ 0x1b, 0x7d, 0x18, 0xb6, 0xd2, 0xfa, 0x28, 0x36, 0x8b, 0x51, 0x6d, 0x58,
+ 0xf4, 0xd6, 0xdf, 0x38, 0x94, 0xcf, 0x6c, 0x50, 0x4f, 0x0a, 0xf3, 0xc3,
+ 0x91, 0x39, 0xa5, 0xc9, 0xbc, 0xa8, 0xeb, 0x24, 0x1a, 0xdd, 0x58, 0x9e,
+ 0xdc, 0xb2, 0xee, 0xe1, 0xa5, 0x16, 0x68, 0xc2, 0x63, 0x8c, 0xc9, 0xa7,
+ 0xbe, 0x1e, 0x30, 0x84, 0xa6, 0x28, 0xeb, 0x50, 0xd9, 0xdd, 0x15, 0xea,
+ 0x64, 0x34, 0xf0, 0x7a, 0x56, 0x6a, 0xdd, 0xb2, 0x70, 0x2e, 0xea, 0x72,
+ 0x66, 0x39, 0x54, 0xaa, 0x36, 0xfa, 0x68, 0xaa, 0x06, 0x5d, 0x48, 0xca,
+ 0xad, 0x4e, 0xfe, 0x4b, 0x40, 0xdf, 0x43, 0x46, 0xd6, 0xdf, 0x3f, 0xa1,
+ 0x9e, 0x4c, 0xdc, 0xfe, 0x4c, 0x01, 0x09, 0x7f, 0xd8, 0x00, 0x84, 0x94,
+ 0x29, 0x17, 0x67, 0x00, 0xd3, 0x46, 0xd2, 0xba, 0xb9, 0x62, 0x66, 0x50,
+ 0xcd, 0x7c, 0x7a, 0x70, 0x46, 0x4a, 0x32, 0x62, 0xc2, 0x6e, 0xe7, 0x5e,
+ 0x04, 0x24, 0xc5, 0xfd, 0x9d, 0xf4, 0x9b, 0xc8, 0xe9, 0xeb, 0x73, 0xf9,
+ 0xaa, 0xa4, 0xcc, 0x63, 0xa3, 0xdc, 0x63, 0xe0, 0x30, 0xec, 0x70, 0x40,
+ 0x9e, 0x7c, 0x63, 0x79, 0xae, 0xba, 0xfd, 0x95, 0x4c, 0x46, 0xf1, 0xc4,
+ 0xae, 0xb9, 0x03, 0xe8, 0xd4, 0xe4, 0x90, 0x29, 0x3a, 0xbb, 0xdb, 0xd8,
+ 0x8f, 0x40, 0xc3, 0x39, 0x9a, 0x4c, 0x70, 0x54, 0x9f, 0xc9, 0x0a, 0x04,
+ 0x23, 0x98, 0x6b, 0x9c, 0xc2, 0xe0, 0xad, 0xae, 0x30, 0xef, 0xff, 0x44,
+ 0x5b, 0x73, 0x2e, 0x8f, 0xd7, 0x2b, 0x12, 0xf0, 0x31, 0x08, 0xfb, 0xb9,
+ 0x55, 0xf0, 0xc3, 0x62, 0xbb, 0x5f, 0x6d, 0xa7, 0x1d, 0x61, 0xc2, 0x26,
+ 0xce, 0xab, 0xb6, 0x88, 0x25, 0xce, 0x8b, 0x02, 0xb6, 0xc5, 0xa2, 0xcc,
+ 0xd4, 0xa3, 0x74, 0x5b, 0x76, 0xf7, 0xb4, 0xd9, 0x9c, 0x93, 0x86, 0x7e,
+ 0xac, 0x82, 0xe0, 0x0d, 0x83, 0xe1, 0xc9, 0x7f, 0x2a, 0x86, 0xbb, 0xaa,
+ 0xfe, 0xdc, 0x17, 0x9c, 0x28, 0x77, 0xe1, 0x58, 0x18, 0x15, 0x09, 0xe3,
+ 0xda, 0xdb, 0x8d, 0xee, 0x55, 0xf6, 0xda, 0xad, 0xe5, 0x52, 0x84, 0xb4,
+ 0xf0, 0x24, 0xce, 0xa1, 0x54, 0x4b, 0x9f, 0xea, 0x5d, 0x4d, 0x7f, 0x53,
+ 0x0b, 0x79, 0x1d, 0x87, 0xcb, 0x0b, 0xa8, 0xef, 0x03, 0xfa, 0x58, 0x57,
+ 0xf6, 0x02, 0x70, 0xdb, 0x7a, 0x64, 0x89, 0x1f, 0xc7, 0xca, 0x87, 0x02,
+ 0x27, 0x33, 0xc5, 0x5b, 0x2a, 0x50, 0xc5, 0xb5, 0x7b, 0x2d, 0x3d, 0xa9,
+ 0xbc, 0x21, 0x7b, 0xf2, 0xbe, 0x9c, 0x56, 0x35, 0x83, 0xba, 0xce, 0x34,
+ 0x8d, 0xec, 0x7b, 0xaa, 0xe4, 0xcb, 0xd1, 0x4f, 0x4a, 0x31, 0x00, 0xd1,
+ 0xb8, 0x30, 0x38, 0xaf, 0xe8, 0xe3, 0xd7, 0xc2, 0x8c, 0xe3, 0xb4, 0x23,
+ 0xb3, 0x27, 0x07, 0xc6, 0x88, 0xec, 0x58, 0xe9, 0x59, 0xfb, 0xa9, 0x11,
+ 0xa2, 0xc8, 0x77, 0x22, 0x6a, 0x5b, 0x86, 0xde, 0xdc, 0xed, 0x76, 0x6e,
+ 0x73, 0x79, 0x5c, 0xb4, 0xcf, 0x19, 0x76, 0x5c, 0x6b, 0x1c, 0x4b, 0x03,
+ 0xcb, 0x35, 0x08, 0x94, 0x37, 0x01, 0x98, 0x52, 0xd8, 0x31, 0x42, 0x3d,
+ 0x7f, 0xa1, 0x11, 0x06, 0x07, 0x88, 0xb8, 0x31, 0x35, 0xb2, 0x49, 0x28,
+ 0xc6, 0x2c, 0x44, 0x43, 0xb6, 0xbc, 0x58, 0x76, 0x6c, 0x4f, 0xc8, 0xb6,
+ 0x30, 0x82, 0x05, 0x84, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x07, 0x01, 0xa0, 0x82, 0x05, 0x75, 0x04, 0x82, 0x05, 0x71, 0x30,
+ 0x82, 0x05, 0x6d, 0x30, 0x82, 0x05, 0x69, 0x06, 0x0b, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, 0x05, 0x31,
+ 0x30, 0x82, 0x05, 0x2d, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08,
+ 0x79, 0x31, 0xf9, 0xe2, 0x42, 0x33, 0xf1, 0xaa, 0x02, 0x02, 0x08, 0x00,
+ 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09,
+ 0x05, 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x01, 0x02, 0x04, 0x10, 0xc9, 0xda, 0x5f, 0x96, 0xc8, 0x2c, 0x85,
+ 0x5d, 0xa0, 0x30, 0x82, 0x16, 0x6b, 0xf4, 0xfd, 0x91, 0x04, 0x82, 0x04,
+ 0xd0, 0xc3, 0x89, 0x6a, 0x56, 0x6a, 0x84, 0x58, 0x76, 0xd7, 0x23, 0xd5,
+ 0xa8, 0xc1, 0x2f, 0x43, 0x38, 0x99, 0xf8, 0x64, 0x97, 0xe7, 0xe8, 0xd2,
+ 0xcf, 0x36, 0x9b, 0x7e, 0x04, 0xe5, 0x87, 0x80, 0xff, 0xd5, 0x58, 0x50,
+ 0x5a, 0xb3, 0xc0, 0x15, 0xc9, 0xd5, 0x61, 0xd6, 0x3b, 0x7f, 0x2f, 0x3b,
+ 0x98, 0x50, 0x55, 0x09, 0xcf, 0xc3, 0xdd, 0xbd, 0x8b, 0xcd, 0xdf, 0x20,
+ 0x90, 0xe1, 0xd2, 0xcd, 0x22, 0x9f, 0xa7, 0x3e, 0x10, 0xd3, 0xb7, 0x26,
+ 0x54, 0x65, 0xfb, 0x18, 0x12, 0x58, 0x81, 0xd8, 0xe6, 0x97, 0xdf, 0x32,
+ 0xd1, 0x04, 0x4a, 0xdb, 0x05, 0xb4, 0x13, 0xa9, 0x86, 0x62, 0x20, 0x94,
+ 0xdc, 0xaf, 0x98, 0x53, 0x16, 0xc7, 0xb2, 0x9c, 0x44, 0x30, 0xc5, 0xaa,
+ 0x14, 0x7a, 0x2d, 0x93, 0x20, 0xff, 0x6d, 0x8d, 0x47, 0x69, 0x6f, 0x39,
+ 0xd4, 0x15, 0x81, 0x6b, 0x85, 0x36, 0xf9, 0x59, 0xa5, 0x8e, 0x5c, 0x40,
+ 0x62, 0xf8, 0xfe, 0xf7, 0xe6, 0x75, 0xf7, 0x37, 0xfe, 0x5d, 0x53, 0xa6,
+ 0x66, 0xe5, 0x0e, 0x4a, 0x23, 0xa9, 0x80, 0x4b, 0x04, 0x11, 0x0e, 0x50,
+ 0xef, 0x9e, 0x88, 0xed, 0x39, 0xd1, 0x5f, 0xfa, 0x90, 0x22, 0xa3, 0x70,
+ 0x0c, 0x8b, 0x20, 0x9c, 0x80, 0x2c, 0x90, 0x2e, 0x2c, 0xe0, 0xe6, 0x26,
+ 0x84, 0xd8, 0x6a, 0xe4, 0x20, 0x1e, 0xbc, 0x96, 0xba, 0x07, 0x9d, 0x1d,
+ 0x3d, 0x6c, 0xd1, 0x04, 0xc8, 0xd1, 0x79, 0x2c, 0x96, 0x0f, 0xe8, 0xa5,
+ 0x6b, 0x03, 0x06, 0x51, 0xfd, 0x7b, 0x44, 0xab, 0x66, 0x4a, 0x41, 0x04,
+ 0x02, 0x64, 0x5a, 0x40, 0x7d, 0x6b, 0x1a, 0xbc, 0x6e, 0xee, 0x68, 0x70,
+ 0x3c, 0x10, 0x32, 0x73, 0x76, 0x28, 0x48, 0xd9, 0xa4, 0xe1, 0x21, 0xf6,
+ 0xe4, 0x03, 0x94, 0x10, 0xef, 0x82, 0xe0, 0x76, 0x7c, 0x99, 0x30, 0x26,
+ 0x9a, 0x95, 0xa2, 0xc5, 0xb9, 0xa7, 0xae, 0x9f, 0x85, 0xcb, 0xf1, 0x82,
+ 0xcd, 0x3d, 0x06, 0xec, 0xaf, 0x72, 0xc1, 0x33, 0x09, 0xf9, 0x51, 0x94,
+ 0x42, 0xf0, 0x69, 0xb9, 0xc6, 0x04, 0xe6, 0x7a, 0xfb, 0x1c, 0xee, 0xac,
+ 0x95, 0x9b, 0x88, 0x67, 0x19, 0xa8, 0x79, 0x67, 0xc7, 0x1b, 0xcc, 0x72,
+ 0xe9, 0x18, 0xd2, 0x96, 0xcf, 0x3d, 0xf8, 0x98, 0x20, 0x53, 0xc9, 0x37,
+ 0x0f, 0x92, 0xb1, 0xbc, 0xaf, 0xc6, 0xec, 0x4f, 0x25, 0xda, 0x95, 0x14,
+ 0xed, 0xb8, 0x3e, 0xaf, 0xd1, 0x52, 0x4c, 0x28, 0x3b, 0x84, 0x8c, 0x49,
+ 0x34, 0x63, 0x2b, 0xd4, 0xf4, 0x78, 0xb1, 0x8f, 0xb0, 0x35, 0x7b, 0xd5,
+ 0x44, 0xc3, 0x98, 0x9e, 0x85, 0x86, 0xae, 0xee, 0x05, 0xdd, 0xa1, 0x6f,
+ 0x53, 0xe4, 0xdc, 0x6f, 0xf5, 0x7c, 0x7e, 0xd8, 0x7a, 0x9b, 0x18, 0x43,
+ 0x3f, 0x7b, 0x2a, 0xf3, 0xb5, 0x39, 0x5a, 0x1c, 0x72, 0x3b, 0xdd, 0x01,
+ 0x79, 0x97, 0xff, 0xdb, 0x58, 0xe5, 0x4d, 0x61, 0xde, 0xcf, 0x2f, 0x13,
+ 0x7b, 0xaf, 0x6b, 0xa4, 0xf2, 0x59, 0x0a, 0x13, 0x56, 0x1c, 0x05, 0x00,
+ 0x0f, 0x18, 0x66, 0x33, 0x72, 0xbd, 0x62, 0x8d, 0x11, 0xf7, 0x20, 0x52,
+ 0x29, 0x42, 0x83, 0x33, 0xc1, 0x0f, 0x07, 0x80, 0xd4, 0x58, 0xe2, 0x22,
+ 0x94, 0xad, 0xec, 0xbf, 0x01, 0xb6, 0x71, 0x7d, 0x92, 0xb1, 0x75, 0x14,
+ 0xf2, 0xfb, 0x77, 0x39, 0x0d, 0x82, 0xb5, 0x51, 0xba, 0x1f, 0x65, 0x57,
+ 0xaa, 0x68, 0x6a, 0x17, 0x41, 0x13, 0x38, 0xc0, 0xe5, 0xeb, 0xcc, 0x8c,
+ 0xdd, 0xb7, 0x00, 0x4e, 0x01, 0x06, 0x25, 0xab, 0x87, 0x1c, 0x30, 0x69,
+ 0xc4, 0x15, 0x0e, 0xf8, 0xf0, 0x72, 0xb6, 0x1d, 0x92, 0x7e, 0xe2, 0xe6,
+ 0x77, 0xed, 0xb8, 0x3f, 0xcf, 0x57, 0x8d, 0x90, 0xe4, 0xa3, 0x79, 0x49,
+ 0x9a, 0xe0, 0x1f, 0x4a, 0xde, 0xe9, 0x44, 0x8d, 0xd5, 0x23, 0x3b, 0x07,
+ 0x63, 0x92, 0x9f, 0xde, 0xba, 0x7e, 0x67, 0xb0, 0x82, 0x41, 0x2a, 0xcd,
+ 0xe1, 0xbb, 0x40, 0xf1, 0x8a, 0x66, 0x70, 0x74, 0xf1, 0x99, 0x7d, 0xb0,
+ 0x0b, 0x6a, 0xa2, 0x5e, 0x7e, 0xc0, 0x8c, 0xb2, 0x71, 0xda, 0xcf, 0xbc,
+ 0xfb, 0x9c, 0x03, 0x0e, 0x33, 0x5e, 0x13, 0xb2, 0x34, 0x38, 0xc1, 0x83,
+ 0x95, 0xdf, 0x46, 0xfc, 0xe0, 0xe0, 0xaf, 0x93, 0xe0, 0x70, 0xd5, 0x15,
+ 0x8c, 0x2f, 0xae, 0x4b, 0xa6, 0xeb, 0x13, 0x8f, 0xaf, 0x1b, 0xf5, 0x71,
+ 0xc4, 0x62, 0x71, 0x08, 0x97, 0x10, 0x52, 0xfe, 0xbd, 0x60, 0xd7, 0x9f,
+ 0xdf, 0x3d, 0xc5, 0xdd, 0xcd, 0xe7, 0x8e, 0x85, 0x60, 0xdf, 0x61, 0x79,
+ 0x5b, 0x90, 0xd9, 0xaa, 0x56, 0x30, 0x6d, 0x0f, 0xfb, 0x27, 0x84, 0xdd,
+ 0x3d, 0x04, 0x6a, 0xe0, 0x70, 0x7e, 0xbb, 0x59, 0xf4, 0xeb, 0xe8, 0xc0,
+ 0x62, 0xaa, 0xf6, 0xed, 0xca, 0xae, 0xb2, 0x2b, 0x0f, 0xc1, 0x56, 0x45,
+ 0xe7, 0x24, 0x6b, 0xaf, 0xeb, 0x15, 0x26, 0xb2, 0xcd, 0xae, 0x1f, 0xe7,
+ 0x11, 0xc0, 0x1c, 0x19, 0x4a, 0xc7, 0x51, 0x2a, 0x29, 0xdf, 0x14, 0x82,
+ 0x43, 0xfe, 0x52, 0x39, 0xba, 0xe6, 0x6c, 0xa5, 0x76, 0x8b, 0xb1, 0x21,
+ 0x9c, 0x20, 0xb0, 0x10, 0x0c, 0x44, 0xf2, 0xd4, 0x6e, 0x41, 0x1b, 0x8f,
+ 0x90, 0x23, 0xe3, 0x87, 0xfc, 0xf1, 0x46, 0xc6, 0x5b, 0xae, 0xd0, 0x2a,
+ 0x2b, 0x78, 0xf5, 0x2b, 0xb9, 0x9f, 0x46, 0x4b, 0x30, 0xf8, 0x49, 0x57,
+ 0x7e, 0xb4, 0xff, 0xca, 0xad, 0x4d, 0xf3, 0xc1, 0x7b, 0x42, 0xe0, 0xa4,
+ 0x37, 0x2f, 0xe2, 0xb2, 0x60, 0xe8, 0xaf, 0xd7, 0x39, 0x23, 0x4c, 0x67,
+ 0x44, 0xe5, 0x6d, 0xb3, 0x25, 0x11, 0x9f, 0x2b, 0xea, 0x23, 0xfb, 0x1e,
+ 0xce, 0xbf, 0xa4, 0x2f, 0x88, 0xec, 0x18, 0x40, 0x16, 0x43, 0x9f, 0x71,
+ 0x9c, 0x8d, 0xbd, 0x5d, 0x55, 0x3b, 0x92, 0x4e, 0x23, 0x3c, 0x87, 0xed,
+ 0x5f, 0x2e, 0x8f, 0xde, 0x83, 0xad, 0x30, 0x42, 0x7e, 0x1a, 0x5e, 0xf5,
+ 0xc5, 0x75, 0xbb, 0x99, 0x6e, 0xf1, 0x87, 0xe0, 0xf3, 0x51, 0x1e, 0x7d,
+ 0xe8, 0xfc, 0xc6, 0x88, 0xf2, 0x39, 0x6d, 0xae, 0x73, 0x9f, 0xad, 0x9b,
+ 0x7b, 0x67, 0x99, 0xdb, 0x90, 0x0e, 0xa0, 0xfc, 0xaf, 0xcc, 0xdb, 0x8b,
+ 0xaa, 0xc2, 0x54, 0xd5, 0x2d, 0xb3, 0x5f, 0xa3, 0x0a, 0x3e, 0xd6, 0x8d,
+ 0x40, 0x4d, 0x3b, 0xe5, 0x2d, 0x31, 0xd8, 0xb2, 0x12, 0x07, 0xca, 0x36,
+ 0x56, 0xd9, 0x2f, 0x55, 0x82, 0xdc, 0x8e, 0x92, 0xa9, 0x6c, 0x91, 0x9e,
+ 0x22, 0xe4, 0xc6, 0x27, 0x8b, 0x1a, 0xa2, 0x78, 0x56, 0x2c, 0x5a, 0x19,
+ 0xdf, 0x40, 0xf9, 0xfb, 0x44, 0x21, 0x5b, 0xdf, 0x2f, 0x99, 0x84, 0x49,
+ 0xcf, 0x1a, 0x15, 0xa5, 0x59, 0x3a, 0x66, 0x09, 0x4d, 0xc1, 0xf2, 0xb1,
+ 0x24, 0x33, 0xbd, 0x86, 0x41, 0xdc, 0x33, 0x9b, 0x03, 0xc0, 0xa8, 0xf8,
+ 0x94, 0x78, 0x2e, 0x16, 0x97, 0xef, 0x23, 0xee, 0xa4, 0xac, 0x3a, 0x90,
+ 0xb6, 0xd9, 0xc0, 0xda, 0x5e, 0x26, 0x34, 0x26, 0xce, 0xc9, 0xf8, 0x45,
+ 0x37, 0x83, 0x7c, 0xbd, 0x9c, 0x60, 0x40, 0x61, 0x28, 0xcd, 0x9c, 0xb4,
+ 0xe4, 0xe6, 0x5c, 0x4f, 0xd1, 0x79, 0x42, 0x13, 0xa9, 0x6f, 0x26, 0x23,
+ 0xc2, 0x6c, 0x8e, 0x8d, 0x7e, 0x3f, 0xee, 0x2b, 0x4d, 0xd2, 0x5b, 0x80,
+ 0xdc, 0x74, 0xda, 0x1f, 0xbc, 0x26, 0x54, 0xc5, 0xfe, 0xee, 0xa9, 0x4f,
+ 0xce, 0x46, 0xaf, 0x90, 0xb0, 0x12, 0x9a, 0x18, 0x0e, 0x06, 0x05, 0xc7,
+ 0x98, 0xef, 0xcc, 0x6d, 0xa3, 0x46, 0x91, 0xa5, 0x0e, 0xe7, 0x35, 0x1a,
+ 0x7f, 0x9d, 0xae, 0xa0, 0xb4, 0x0a, 0x32, 0x3b, 0xe4, 0xcd, 0x4b, 0x3e,
+ 0x89, 0x73, 0xc9, 0x97, 0x38, 0xe5, 0x86, 0x4f, 0x24, 0xed, 0x4a, 0x43,
+ 0x04, 0x02, 0xc1, 0x29, 0x8d, 0x85, 0xa2, 0xdd, 0xb2, 0x61, 0x3c, 0xce,
+ 0x8b, 0x47, 0x2e, 0xed, 0x4b, 0x24, 0x94, 0xb7, 0xbf, 0x9d, 0x55, 0x42,
+ 0x95, 0xc2, 0x27, 0xe5, 0x09, 0xd4, 0x20, 0x03, 0x20, 0x21, 0x3a, 0xd8,
+ 0xd2, 0xa2, 0xb3, 0x47, 0x93, 0x4f, 0x5a, 0x39, 0xca, 0xd8, 0x74, 0xa9,
+ 0x19, 0xa6, 0x9a, 0x23, 0xb1, 0x21, 0xa3, 0xb3, 0x14, 0xcc, 0xe2, 0x12,
+ 0x91, 0x30, 0xdb, 0x50, 0xf8, 0x44, 0x74, 0xd6, 0x70, 0xdd, 0x7d, 0x26,
+ 0x7f, 0xbf, 0x32, 0x93, 0x1f, 0x3d, 0x40, 0xbf, 0x2e, 0xec, 0x28, 0xf5,
+ 0xb1, 0xaf, 0x11, 0xc7, 0x4e, 0x64, 0x13, 0x3c, 0xbf, 0x2e, 0x19, 0x81,
+ 0xfe, 0x35, 0xba, 0xec, 0x6e, 0xb6, 0xa9, 0xfe, 0xc6, 0x85, 0x33, 0x41,
+ 0x58, 0xab, 0x06, 0xae, 0x2b, 0x96, 0x62, 0x1f, 0x2c, 0x6c, 0xad, 0xec,
+ 0x1a, 0x59, 0x55, 0x5a, 0x6f, 0xe0, 0xeb, 0x71, 0x8d, 0xb5, 0x0c, 0x81,
+ 0x2a, 0x39, 0xbd, 0x67, 0x39, 0x48, 0xfb, 0x91, 0x64, 0xad, 0x01, 0x4c,
+ 0x4a, 0x0f, 0x30, 0x29, 0xa0, 0xcf, 0x30, 0x96, 0x43, 0xe9, 0xfc, 0x22,
+ 0x4b, 0xf3, 0x4f, 0xab, 0xec, 0xbc, 0x5a, 0xfb, 0x7f, 0x20, 0xd9, 0xd5,
+ 0xc7, 0xce, 0x93, 0xa3, 0x2e, 0x82, 0xd1, 0xa0, 0xc6, 0x16, 0xd5, 0x64,
+ 0x2d, 0x3f, 0x69, 0x15, 0xfd, 0xf3, 0x28, 0x3d, 0x4e, 0x61, 0x01, 0x2c,
+ 0xd4, 0x2b, 0x40, 0x51, 0x6e, 0x95, 0x00, 0xa4, 0x34, 0x31, 0x25, 0x30,
+ 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15,
+ 0x31, 0x16, 0x04, 0x14, 0x47, 0xf4, 0x18, 0xa5, 0x4b, 0x85, 0xb7, 0x02,
+ 0xc1, 0x97, 0xff, 0x57, 0xb6, 0x6f, 0x21, 0x45, 0x34, 0x3d, 0x92, 0x22,
+ 0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02,
+ 0x1a, 0x05, 0x00, 0x04, 0x14, 0x17, 0x45, 0x0c, 0xdf, 0x53, 0x76, 0x9b,
+ 0xce, 0x3b, 0x12, 0xdd, 0x47, 0x05, 0x6d, 0x16, 0x90, 0x9d, 0x29, 0x9b,
+ 0xe1, 0x04, 0x08, 0xa1, 0xf2, 0x82, 0x1c, 0xd1, 0xd1, 0x7b, 0x5c, 0x02,
+ 0x02, 0x08, 0x00,
+};
+
+// kNoEncryption is a PKCS#12 file with neither the key or certificate is
+// encrypted. It was generated with:
+//
+// openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -keypbe NONE -certpbe NONE -password pass:foo
+static const uint8_t kNoEncryption[] = {
+ 0x30, 0x82, 0x03, 0x6e, 0x02, 0x01, 0x03, 0x30, 0x82, 0x03, 0x34, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
+ 0x03, 0x25, 0x04, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x82,
+ 0x02, 0x3e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
+ 0x01, 0xa0, 0x82, 0x02, 0x2f, 0x04, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x02,
+ 0x27, 0x30, 0x82, 0x02, 0x23, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x03, 0xa0, 0x82, 0x01, 0xeb, 0x30, 0x82,
+ 0x01, 0xe7, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
+ 0x16, 0x01, 0xa0, 0x82, 0x01, 0xd7, 0x04, 0x82, 0x01, 0xd3, 0x30, 0x82,
+ 0x01, 0xcf, 0x30, 0x82, 0x01, 0x76, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
+ 0x09, 0x00, 0xd9, 0x4c, 0x04, 0xda, 0x49, 0x7d, 0xbf, 0xeb, 0x30, 0x09,
+ 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55,
+ 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53,
+ 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30,
+ 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65,
+ 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73,
+ 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x34, 0x30, 0x34, 0x32, 0x33, 0x32, 0x33, 0x32, 0x31, 0x35, 0x37,
+ 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x32, 0x33, 0x32, 0x33, 0x32,
+ 0x31, 0x35, 0x37, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53,
+ 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+ 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20,
+ 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
+ 0x4c, 0x74, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
+ 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65,
+ 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b,
+ 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc,
+ 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4,
+ 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
+ 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1, 0xa3, 0x50,
+ 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78,
+ 0x07, 0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xab, 0x84,
+ 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78, 0x07, 0x55, 0x57,
+ 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+ 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x09, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x48, 0x00, 0x30, 0x45,
+ 0x02, 0x21, 0x00, 0xf2, 0xa0, 0x35, 0x5e, 0x51, 0x3a, 0x36, 0xc3, 0x82,
+ 0x79, 0x9b, 0xee, 0x27, 0x50, 0x85, 0x8e, 0x70, 0x06, 0x74, 0x95, 0x57,
+ 0xd2, 0x29, 0x74, 0x00, 0xf4, 0xbe, 0x15, 0x87, 0x5d, 0xc4, 0x07, 0x02,
+ 0x20, 0x7c, 0x1e, 0x79, 0x14, 0x6a, 0x21, 0x83, 0xf0, 0x7a, 0x74, 0x68,
+ 0x79, 0x5f, 0x14, 0x99, 0x9a, 0x68, 0xb4, 0xf1, 0xcb, 0x9e, 0x15, 0x5e,
+ 0xe6, 0x1f, 0x32, 0x52, 0x61, 0x5e, 0x75, 0xc9, 0x14, 0x31, 0x25, 0x30,
+ 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15,
+ 0x31, 0x16, 0x04, 0x14, 0x3f, 0x31, 0x38, 0xec, 0xb9, 0xf1, 0x45, 0xe1,
+ 0x3e, 0x90, 0x71, 0x0d, 0xc1, 0x28, 0xba, 0x4e, 0x6f, 0xa0, 0x9c, 0xed,
+ 0x30, 0x81, 0xd8, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x07, 0x01, 0xa0, 0x81, 0xca, 0x04, 0x81, 0xc7, 0x30, 0x81, 0xc4, 0x30,
+ 0x81, 0xc1, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c,
+ 0x0a, 0x01, 0x01, 0xa0, 0x81, 0x8a, 0x30, 0x81, 0x87, 0x02, 0x01, 0x00,
+ 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
+ 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30,
+ 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x07, 0x0f, 0x08, 0x72, 0x7a, 0xd4,
+ 0xa0, 0x4a, 0x9c, 0xdd, 0x59, 0xc9, 0x4d, 0x89, 0x68, 0x77, 0x08, 0xb5,
+ 0x6f, 0xc9, 0x5d, 0x30, 0x77, 0x0e, 0xe8, 0xd1, 0xc9, 0xce, 0x0a, 0x8b,
+ 0xb4, 0x6a, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2,
+ 0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5,
+ 0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e,
+ 0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9,
+ 0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a,
+ 0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
+ 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x3f, 0x31, 0x38, 0xec, 0xb9,
+ 0xf1, 0x45, 0xe1, 0x3e, 0x90, 0x71, 0x0d, 0xc1, 0x28, 0xba, 0x4e, 0x6f,
+ 0xa0, 0x9c, 0xed, 0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
+ 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0xd0, 0xb4, 0x17, 0x1a,
+ 0xdb, 0xa3, 0x27, 0xd8, 0x9e, 0xd3, 0xf2, 0xb3, 0x3e, 0x96, 0x07, 0x3a,
+ 0xf2, 0x6a, 0xc2, 0x1c, 0x04, 0x08, 0xb5, 0xa8, 0xb9, 0xdb, 0x2f, 0xf1,
+ 0xa4, 0xcd, 0x02, 0x02, 0x08, 0x00,
+};
+
static const char kPassword[] = "foo";
// Generated with
@@ -1230,7 +1545,12 @@
}
TEST(PKCS12Test, TestPBES2) {
- TestImpl("PBES2", kPBES2, kPassword, nullptr);
+ TestImpl("kPBES2WithSHA1", kPBES2WithSHA1, kPassword, nullptr);
+ TestImpl("kPBES2WithSHA256", kPBES2WithSHA256, kPassword, nullptr);
+}
+
+TEST(PKCS12Test, TestNoEncryption) {
+ TestImpl("kNoEncryption", kNoEncryption, kPassword, nullptr);
}
TEST(PKCS12Test, TestEmptyPassword) {
@@ -1506,3 +1826,152 @@
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
NID_pbe_WithSHA1And3_Key_TripleDES_CBC, 100, 100);
}
+
+static bssl::UniquePtr<EVP_PKEY> MakeTestKey() {
+ bssl::UniquePtr<EC_KEY> ec_key(
+ EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
+ if (!ec_key ||
+ !EC_KEY_generate_key(ec_key.get())) {
+ return nullptr;
+ }
+ bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+ if (!evp_pkey ||
+ !EVP_PKEY_assign_EC_KEY(evp_pkey.get(), ec_key.release())) {
+ return nullptr;
+ }
+ return evp_pkey;
+}
+
+static bssl::UniquePtr<X509> MakeTestCert(EVP_PKEY *key) {
+ bssl::UniquePtr<X509> x509(X509_new());
+ if (!x509) {
+ return nullptr;
+ }
+ X509_NAME* subject = X509_get_subject_name(x509.get());
+ if (!X509_gmtime_adj(X509_get_notBefore(x509.get()), 0) ||
+ !X509_gmtime_adj(X509_get_notAfter(x509.get()), 60 * 60 * 24) ||
+ !X509_NAME_add_entry_by_txt(subject, "CN", MBSTRING_ASC,
+ reinterpret_cast<const uint8_t *>("Test"), -1,
+ -1, 0) ||
+ !X509_set_issuer_name(x509.get(), subject) ||
+ !X509_set_pubkey(x509.get(), key) ||
+ !X509_sign(x509.get(), key, EVP_sha256())) {
+ return nullptr;
+ }
+ return x509;
+}
+
+static bool PKCS12CreateVector(std::vector<uint8_t> *out, EVP_PKEY *pkey,
+ const std::vector<X509 *> &certs) {
+ bssl::UniquePtr<STACK_OF(X509)> chain(sk_X509_new_null());
+ if (!chain) {
+ return false;
+ }
+
+ for (X509 *cert : certs) {
+ if (!bssl::PushToStack(chain.get(), bssl::UpRef(cert))) {
+ return false;
+ }
+ }
+
+ bssl::UniquePtr<PKCS12> p12(PKCS12_create(kPassword, nullptr /* name */, pkey,
+ nullptr /* cert */, chain.get(), 0,
+ 0, 0, 0, 0));
+ if (!p12) {
+ return false;
+ }
+
+ int len = i2d_PKCS12(p12.get(), nullptr);
+ if (len < 0) {
+ return false;
+ }
+ out->resize(static_cast<size_t>(len));
+ uint8_t *ptr = out->data();
+ return i2d_PKCS12(p12.get(), &ptr) == len;
+}
+
+static void ExpectPKCS12Parse(bssl::Span<const uint8_t> in,
+ EVP_PKEY *expect_key, X509 *expect_cert,
+ const std::vector<X509 *> &expect_ca_certs) {
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(in.data(), in.size()));
+ ASSERT_TRUE(bio);
+
+ bssl::UniquePtr<PKCS12> p12(d2i_PKCS12_bio(bio.get(), nullptr));
+ ASSERT_TRUE(p12);
+
+ EVP_PKEY *key = nullptr;
+ X509 *cert = nullptr;
+ STACK_OF(X509) *ca_certs = nullptr;
+ ASSERT_TRUE(PKCS12_parse(p12.get(), kPassword, &key, &cert, &ca_certs));
+
+ bssl::UniquePtr<EVP_PKEY> delete_key(key);
+ bssl::UniquePtr<X509> delete_cert(cert);
+ bssl::UniquePtr<STACK_OF(X509)> delete_ca_certs(ca_certs);
+
+ if (expect_key == nullptr) {
+ EXPECT_FALSE(key);
+ } else {
+ ASSERT_TRUE(key);
+ EXPECT_EQ(1, EVP_PKEY_cmp(key, expect_key));
+ }
+
+ if (expect_cert == nullptr) {
+ EXPECT_FALSE(cert);
+ } else {
+ ASSERT_TRUE(cert);
+ EXPECT_EQ(0, X509_cmp(cert, expect_cert));
+ }
+
+ ASSERT_EQ(expect_ca_certs.size(), sk_X509_num(ca_certs));
+ for (size_t i = 0; i < expect_ca_certs.size(); i++) {
+ EXPECT_EQ(0, X509_cmp(expect_ca_certs[i], sk_X509_value(ca_certs, i)));
+ }
+}
+
+// Test that |PKCS12_parse| returns values in the expected order.
+TEST(PKCS12Test, Order) {
+ bssl::UniquePtr<EVP_PKEY> key1 = MakeTestKey();
+ ASSERT_TRUE(key1);
+ bssl::UniquePtr<X509> cert1 = MakeTestCert(key1.get());
+ ASSERT_TRUE(cert1);
+ bssl::UniquePtr<X509> cert1b = MakeTestCert(key1.get());
+ ASSERT_TRUE(cert1b);
+ bssl::UniquePtr<EVP_PKEY> key2 = MakeTestKey();
+ ASSERT_TRUE(key2);
+ bssl::UniquePtr<X509> cert2 = MakeTestCert(key2.get());
+ ASSERT_TRUE(cert2);
+ bssl::UniquePtr<EVP_PKEY> key3 = MakeTestKey();
+ ASSERT_TRUE(key3);
+ bssl::UniquePtr<X509> cert3 = MakeTestCert(key3.get());
+ ASSERT_TRUE(cert3);
+
+ // PKCS12_parse uses the key to select the main certificate.
+ std::vector<uint8_t> p12;
+ ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
+ {cert1.get(), cert2.get(), cert3.get()}));
+ ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
+
+ ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
+ {cert3.get(), cert1.get(), cert2.get()}));
+ ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert3.get(), cert2.get()});
+
+ ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(),
+ {cert2.get(), cert3.get(), cert1.get()}));
+ ExpectPKCS12Parse(p12, key1.get(), cert1.get(), {cert2.get(), cert3.get()});
+
+ // In case of duplicates, the last one is selected. (It is unlikely anything
+ // depends on which is selected, but we match OpenSSL.)
+ ASSERT_TRUE(
+ PKCS12CreateVector(&p12, key1.get(), {cert1.get(), cert1b.get()}));
+ ExpectPKCS12Parse(p12, key1.get(), cert1b.get(), {cert1.get()});
+
+ // If there is no key, all certificates are returned as "CA" certificates.
+ ASSERT_TRUE(PKCS12CreateVector(&p12, nullptr,
+ {cert1.get(), cert2.get(), cert3.get()}));
+ ExpectPKCS12Parse(p12, nullptr, nullptr,
+ {cert1.get(), cert2.get(), cert3.get()});
+
+ // The same happens if there is a key, but it does not match any certificate.
+ ASSERT_TRUE(PKCS12CreateVector(&p12, key1.get(), {cert2.get(), cert3.get()}));
+ ExpectPKCS12Parse(p12, key1.get(), nullptr, {cert2.get(), cert3.get()});
+}
diff --git a/src/crypto/pkcs8/pkcs8.c b/src/crypto/pkcs8/pkcs8.c
index ee25ee2..a19b4a3 100644
--- a/src/crypto/pkcs8/pkcs8.c
+++ b/src/crypto/pkcs8/pkcs8.c
@@ -486,6 +486,10 @@
goto err;
}
+ // TODO(davidben): OpenSSL has since extended |pbe_nid| to control either the
+ // PBES1 scheme or the PBES2 PRF. E.g. passing |NID_hmacWithSHA256| will
+ // select PBES2 with HMAC-SHA256 as the PRF. Implement this if anything uses
+ // it. See 5693a30813a031d3921a016a870420e7eb93ec90 in OpenSSL.
int alg_ok;
if (pbe_nid == -1) {
alg_ok = PKCS5_pbe2_encrypt_init(&epki, &ctx, cipher, (unsigned)iterations,
diff --git a/src/crypto/pkcs8/pkcs8_test.cc b/src/crypto/pkcs8/pkcs8_test.cc
index df275fb..beb532f 100644
--- a/src/crypto/pkcs8/pkcs8_test.cc
+++ b/src/crypto/pkcs8/pkcs8_test.cc
@@ -22,14 +22,15 @@
#include "../internal.h"
-// kDER is a PKCS#8 encrypted private key. It was generated with:
+// kEncryptedPBES2WithDESAndSHA1 is a PKCS#8 encrypted private key using PBES2
+// with DES-EDE3-CBC and HMAC-SHA-1. It was generated with:
//
// openssl genrsa 512 > test.key
-// openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 des3 -outform der
+// openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 des3 -v2prf hmacWithSHA1 -outform der
// hexdump -Cv test.key.encrypted
//
// The password is "testing".
-static const uint8_t kDER[] = {
+static const uint8_t kEncryptedPBES2WithDESAndSHA1[] = {
0x30, 0x82, 0x01, 0x9e, 0x30, 0x40, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05,
0x0d, 0x30, 0x33, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c,
0x30, 0x0e, 0x04, 0x08, 0x06, 0xa5, 0x4b, 0x0c, 0x0c, 0x50, 0x8c, 0x19, 0x02, 0x02, 0x08, 0x00,
@@ -59,6 +60,55 @@
0xd6, 0x2d,
};
+// kEncryptedPBES2WithAESAndSHA256 is a PKCS#8 encrypted private key using PBES2
+// with AES-128-CBC and HMAC-SHA-256. It was generated with:
+//
+// openssl genrsa 512 > test.key
+// openssl pkcs8 -topk8 -in test.key -out test.key.encrypted -v2 aes-128-cbc -v2prf hmacWithSHA256 -outform der
+// hexdump -Cv test.key.encrypted
+//
+// The password is "testing".
+static const uint8_t kEncryptedPBES2WithAESAndSHA256[] = {
+ 0x30, 0x82, 0x01, 0xbd, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a,
+ 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08,
+ 0xd0, 0x39, 0xb7, 0x6d, 0xd0, 0xff, 0x85, 0xa8, 0x02, 0x02, 0x08, 0x00,
+ 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09,
+ 0x05, 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x01, 0x02, 0x04, 0x10, 0x95, 0xc6, 0x49, 0xc1, 0x61, 0x70, 0x74,
+ 0x36, 0xfe, 0xc0, 0x98, 0x19, 0x26, 0x32, 0x9e, 0x1a, 0x04, 0x82, 0x01,
+ 0x60, 0x87, 0xe3, 0x4d, 0xff, 0x33, 0x2b, 0x25, 0x07, 0x5f, 0x99, 0x24,
+ 0x70, 0x40, 0x29, 0xb4, 0xaa, 0xce, 0xdd, 0x93, 0x60, 0x5c, 0x65, 0xec,
+ 0xba, 0xee, 0xf8, 0x5e, 0xc7, 0x8f, 0x3e, 0x49, 0x00, 0xdf, 0xa7, 0xb2,
+ 0xbb, 0xa7, 0xf7, 0x74, 0x56, 0xe0, 0xeb, 0x33, 0xe2, 0x30, 0x60, 0x3b,
+ 0xd3, 0x34, 0x7b, 0xb0, 0x76, 0xc0, 0xed, 0xa2, 0x7d, 0x51, 0x99, 0x4d,
+ 0x52, 0x1c, 0x9e, 0xd7, 0x28, 0x58, 0x32, 0xcf, 0x55, 0xf0, 0x19, 0x87,
+ 0x5e, 0x66, 0x69, 0x00, 0x75, 0xd7, 0x68, 0x98, 0x64, 0x6a, 0x1e, 0x8c,
+ 0xef, 0x7b, 0xbc, 0x9e, 0xed, 0xaf, 0x67, 0xcf, 0xc4, 0x6c, 0x40, 0x8a,
+ 0x23, 0x73, 0xe6, 0x79, 0x5f, 0x7a, 0x15, 0x53, 0x2c, 0x70, 0x3b, 0x69,
+ 0xba, 0x3d, 0xfb, 0x6e, 0xb9, 0x27, 0x78, 0x9b, 0xc3, 0x4b, 0xb8, 0xe3,
+ 0xe7, 0x31, 0x82, 0x43, 0xee, 0x81, 0x5b, 0x01, 0x3b, 0x7e, 0x23, 0xb1,
+ 0xcc, 0x20, 0xc9, 0xe0, 0xca, 0x9d, 0xaa, 0x9c, 0xe9, 0x47, 0x96, 0x0d,
+ 0xc4, 0x1e, 0x1b, 0x1f, 0xb1, 0xee, 0x2b, 0x99, 0xaf, 0x2a, 0xb4, 0x9f,
+ 0x29, 0xb5, 0x9b, 0x83, 0x5e, 0xe5, 0x7e, 0xf7, 0xf7, 0x58, 0x31, 0x54,
+ 0xf6, 0x29, 0x6b, 0xfc, 0x85, 0x1d, 0x8a, 0x5f, 0x22, 0x03, 0xed, 0xce,
+ 0x06, 0x5b, 0x93, 0x57, 0x23, 0x4e, 0x0e, 0x5b, 0xf8, 0x29, 0x29, 0x7c,
+ 0x2d, 0x4a, 0xc6, 0x9a, 0x97, 0x64, 0x4c, 0x15, 0x72, 0xee, 0xaa, 0xd9,
+ 0xed, 0xfd, 0x8f, 0xa2, 0x36, 0xf5, 0xf8, 0xe7, 0xf7, 0xfe, 0x67, 0x4b,
+ 0xbe, 0x85, 0xe3, 0xda, 0x3a, 0xcc, 0x5f, 0x17, 0xbe, 0xa6, 0x1a, 0x38,
+ 0xed, 0x85, 0x4a, 0xd6, 0x05, 0x4b, 0x5f, 0x54, 0x0e, 0xfd, 0xdc, 0x32,
+ 0x9a, 0x0d, 0xca, 0xec, 0x02, 0x3f, 0x78, 0x23, 0x6e, 0x6a, 0xfa, 0x2c,
+ 0x04, 0x86, 0xc8, 0x74, 0xa6, 0xf7, 0x2f, 0x1a, 0xf1, 0x1a, 0x51, 0x09,
+ 0x2b, 0x04, 0xdc, 0x53, 0xaf, 0x39, 0x85, 0x35, 0x81, 0xc3, 0x11, 0xf0,
+ 0x7d, 0xb7, 0xb3, 0x77, 0x0e, 0x8e, 0x22, 0xda, 0x1f, 0xb0, 0x3d, 0x59,
+ 0xca, 0xa9, 0x70, 0xc3, 0x33, 0x66, 0xcb, 0x81, 0xfc, 0x85, 0x2a, 0xf0,
+ 0xbc, 0xd0, 0x30, 0x85, 0x1b, 0xe6, 0x9b, 0x76, 0x88, 0xca, 0xf1, 0xf5,
+ 0xc8, 0x19, 0x8a, 0x62, 0x16, 0x7b, 0x01, 0xc1, 0x1d, 0x80, 0xc0, 0xf7,
+ 0xc2, 0x0b, 0xe3, 0xbf, 0x83, 0x9a, 0x3c, 0xc0, 0x0f, 0x5e, 0xea, 0xf7,
+ 0xb2, 0x4c, 0xcd, 0x5e, 0xf0, 0xad, 0xa5, 0x98, 0x25, 0x62, 0x69, 0xba,
+ 0xd7, 0xab, 0x78, 0x59, 0xd0,
+};
+
// kNullPassword is a PKCS#8 encrypted private key using the null password.
static const uint8_t kNullPassword[] = {
0x30, 0x81, 0xb0, 0x30, 0x1b, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
@@ -218,7 +268,10 @@
}
TEST(PKCS8Test, DecryptString) {
- TestDecrypt(kDER, sizeof(kDER), "testing");
+ TestDecrypt(kEncryptedPBES2WithDESAndSHA1,
+ sizeof(kEncryptedPBES2WithDESAndSHA1), "testing");
+ TestDecrypt(kEncryptedPBES2WithAESAndSHA256,
+ sizeof(kEncryptedPBES2WithAESAndSHA256), "testing");
}
TEST(PKCS8Test, DecryptNull) {
diff --git a/src/crypto/pkcs8/pkcs8_x509.c b/src/crypto/pkcs8/pkcs8_x509.c
index 2c7841e..4458b56 100644
--- a/src/crypto/pkcs8/pkcs8_x509.c
+++ b/src/crypto/pkcs8/pkcs8_x509.c
@@ -293,6 +293,10 @@
return ret;
}
+// 1.2.840.113549.1.12.10.1.1
+static const uint8_t kKeyBag[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x0c, 0x0a, 0x01, 0x01};
+
// 1.2.840.113549.1.12.10.1.2
static const uint8_t kPKCS8ShroudedKeyBag[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02};
@@ -392,16 +396,20 @@
return 0;
}
- if (CBS_mem_equal(&bag_id, kPKCS8ShroudedKeyBag,
- sizeof(kPKCS8ShroudedKeyBag))) {
- // See RFC 7292, section 4.2.2.
+ const int is_key_bag = CBS_mem_equal(&bag_id, kKeyBag, sizeof(kKeyBag));
+ const int is_shrouded_key_bag = CBS_mem_equal(&bag_id, kPKCS8ShroudedKeyBag,
+ sizeof(kPKCS8ShroudedKeyBag));
+ if (is_key_bag || is_shrouded_key_bag) {
+ // See RFC 7292, section 4.2.1 and 4.2.2.
if (*ctx->out_key) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_MULTIPLE_PRIVATE_KEYS_IN_PKCS12);
return 0;
}
- EVP_PKEY *pkey = PKCS8_parse_encrypted_private_key(
- &wrapped_value, ctx->password, ctx->password_len);
+ EVP_PKEY *pkey =
+ is_key_bag ? EVP_parse_private_key(&wrapped_value)
+ : PKCS8_parse_encrypted_private_key(
+ &wrapped_value, ctx->password, ctx->password_len);
if (pkey == NULL) {
return 0;
}
@@ -902,9 +910,25 @@
return 0;
}
+ // OpenSSL selects the last certificate which matches the private key as
+ // |out_cert|.
+ //
+ // TODO(davidben): OpenSSL additionally reverses the order of the
+ // certificates, which was likely originally a bug, but may be a feature by
+ // now. See https://crbug.com/boringssl/250 and
+ // https://github.com/openssl/openssl/issues/6698.
*out_cert = NULL;
- if (sk_X509_num(ca_certs) > 0) {
- *out_cert = sk_X509_shift(ca_certs);
+ size_t num_certs = sk_X509_num(ca_certs);
+ if (*out_pkey != NULL && num_certs > 0) {
+ for (size_t i = num_certs - 1; i < num_certs; i--) {
+ X509 *cert = sk_X509_value(ca_certs, i);
+ if (X509_check_private_key(cert, *out_pkey)) {
+ *out_cert = cert;
+ sk_X509_delete(ca_certs, i);
+ break;
+ }
+ ERR_clear_error();
+ }
}
if (out_ca_certs) {
diff --git a/src/crypto/siphash/siphash.c b/src/crypto/siphash/siphash.c
new file mode 100644
index 0000000..7e4e9c5
--- /dev/null
+++ b/src/crypto/siphash/siphash.c
@@ -0,0 +1,80 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/siphash.h>
+
+
+static void siphash_round(uint64_t v[4]) {
+ v[0] += v[1];
+ v[2] += v[3];
+ v[1] = (v[1] << 13) | (v[1] >> (64 - 13));
+ v[3] = (v[3] << 16) | (v[3] >> (64 - 16));
+ v[1] ^= v[0];
+ v[3] ^= v[2];
+ v[0] = (v[0] << 32) | (v[0] >> 32);
+ v[2] += v[1];
+ v[0] += v[3];
+ v[1] = (v[1] << 17) | (v[1] >> (64 - 17));
+ v[3] = (v[3] << 21) | (v[3] >> (64 - 21));
+ v[1] ^= v[2];
+ v[3] ^= v[0];
+ v[2] = (v[2] << 32) | (v[2] >> 32);
+}
+
+uint64_t SIPHASH_24(const uint64_t key[2], const uint8_t *input,
+ size_t input_len) {
+ const size_t orig_input_len = input_len;
+
+ uint64_t v[4];
+ v[0] = key[0] ^ UINT64_C(0x736f6d6570736575);
+ v[1] = key[1] ^ UINT64_C(0x646f72616e646f6d);
+ v[2] = key[0] ^ UINT64_C(0x6c7967656e657261);
+ v[3] = key[1] ^ UINT64_C(0x7465646279746573);
+
+ while (input_len >= sizeof(uint64_t)) {
+ uint64_t m;
+ memcpy(&m, input, sizeof(m));
+ v[3] ^= m;
+ siphash_round(v);
+ siphash_round(v);
+ v[0] ^= m;
+
+ input += sizeof(uint64_t);
+ input_len -= sizeof(uint64_t);
+ }
+
+ union {
+ uint8_t bytes[8];
+ uint64_t word;
+ } last_block;
+ last_block.word = 0;
+ memcpy(last_block.bytes, input, input_len);
+ last_block.bytes[7] = orig_input_len & 0xff;
+
+ v[3] ^= last_block.word;
+ siphash_round(v);
+ siphash_round(v);
+ v[0] ^= last_block.word;
+
+ v[2] ^= 0xff;
+ siphash_round(v);
+ siphash_round(v);
+ siphash_round(v);
+ siphash_round(v);
+
+ return v[0] ^ v[1] ^ v[2] ^ v[3];
+}
diff --git a/src/crypto/siphash/siphash_test.cc b/src/crypto/siphash/siphash_test.cc
new file mode 100644
index 0000000..6d8f9e7
--- /dev/null
+++ b/src/crypto/siphash/siphash_test.cc
@@ -0,0 +1,59 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdint.h>
+
+#include <gtest/gtest.h>
+
+#include <openssl/siphash.h>
+
+#include "../test/file_test.h"
+#include "../test/test_util.h"
+
+TEST(SipHash, Basic) {
+ // This is the example from appendix A of the SipHash paper.
+ union {
+ uint8_t bytes[16];
+ uint64_t words[2];
+ } key;
+
+ for (unsigned i = 0; i < 16; i++) {
+ key.bytes[i] = i;
+ }
+
+ uint8_t input[15];
+ for (unsigned i = 0; i < sizeof(input); i++) {
+ input[i] = i;
+ }
+
+ EXPECT_EQ(UINT64_C(0xa129ca6149be45e5),
+ SIPHASH_24(key.words, input, sizeof(input)));
+}
+
+TEST(SipHash, Vectors) {
+ FileTestGTest("crypto/siphash/siphash_tests.txt", [](FileTest *t) {
+ std::vector<uint8_t> key, msg, hash;
+ ASSERT_TRUE(t->GetBytes(&key, "KEY"));
+ ASSERT_TRUE(t->GetBytes(&msg, "IN"));
+ ASSERT_TRUE(t->GetBytes(&hash, "HASH"));
+ ASSERT_EQ(16u, key.size());
+ ASSERT_EQ(8u, hash.size());
+
+ uint64_t key_words[2];
+ memcpy(key_words, key.data(), key.size());
+ uint64_t result = SIPHASH_24(key_words, msg.data(), msg.size());
+ EXPECT_EQ(Bytes(reinterpret_cast<uint8_t *>(&result), sizeof(result)),
+ Bytes(hash));
+ });
+}
diff --git a/src/crypto/siphash/siphash_tests.txt b/src/crypto/siphash/siphash_tests.txt
new file mode 100644
index 0000000..ce45c30
--- /dev/null
+++ b/src/crypto/siphash/siphash_tests.txt
@@ -0,0 +1,1081 @@
+# Random test vectors generated from another implementation of SipHash-2-4.
+
+KEY: 5a174c22c487d0c5c1161e570d10d145
+IN:
+HASH: 9f06a76d8ae7ff05
+
+KEY: d56745469ae42734c2ad87e7c13ea101
+IN: 3c
+HASH: 9698cd8e2620362f
+
+KEY: cc7c78ae24ca106c5d771742b530cbce
+IN: 6cfb
+HASH: a94c8f359a6034d0
+
+KEY: c8de6d120462739b29b25913f75f8be4
+IN: 8bff10
+HASH: 5f12329867deb530
+
+KEY: c435d95c06085dc26060e488f0691013
+IN: f575791b
+HASH: 38824fa193583e1c
+
+KEY: 5d5853675bbe37b0034e019c8703bbea
+IN: 20be54d583
+HASH: 5586f5ed0a52d560
+
+KEY: 77fdc2467cb5cfadba4dcb02f08d5de0
+IN: 58ff9d2af71e
+HASH: acc2bf8eb5308b5b
+
+KEY: 3f1abc8a74470e697aade7bb409d8c36
+IN: 2a0d12d99b8c10
+HASH: 2349992cbb601e73
+
+KEY: ad977640b6902db5fb05c35bde85e221
+IN: 336ddbba25fc15fd
+HASH: c7a610c0743b7f8c
+
+KEY: 9eeb5f0ce1545597c93fcd236e022df9
+IN: 123569d0a53d5399cf
+HASH: 7f25736f962974ce
+
+KEY: 56d9ecda6345127adbf79e4ec0871116
+IN: b472f565c1370a620c66
+HASH: 3f3acf72be80cb20
+
+KEY: 4b5a67fad0b4c6dd41656e79e47e4ccc
+IN: 317e7ef64c39cf111212cd
+HASH: 4c8ee337c7103c0b
+
+KEY: e0e04322fb57cf43fb6809a33d392565
+IN: a783aca573cb07e82829eb47
+HASH: 3d5ca090c68d3a94
+
+KEY: 2895c38788e064541584f222900a45fb
+IN: 6381894b7947f3cdb6eca1e8c5
+HASH: 8ee7682323bbf06b
+
+KEY: c3a0b4ea71d4e824459acac0aa2caa9d
+IN: 6a9257065f991f36ba175ec177b8
+HASH: 078b76ac3dc840e2
+
+KEY: 56e7c5dc7ba9c37095117d3b78b6be0c
+IN: d9a8402bc9303b5a67c3aa0f1975ac
+HASH: f5fc32c55915e9ff
+
+KEY: ffcd0ca31dbb4c0a64bce83d4a876e2f
+IN: 4c50fca688dcc34a62bf77554af22fca
+HASH: 9e266efdc2b55efa
+
+KEY: a358acb475fe3a545aba172e6a2e606b
+IN: e2e6007adac976736a222ad2a607d4d15d
+HASH: c4bb7312a7d39486
+
+KEY: cc611d043067a99cb5572bb0121beac8
+IN: 7428cfc5fd76f0ffc59a216b142576d9ee5d
+HASH: 67dc4aa8a52069df
+
+KEY: 19c44128e61c992e625a1187036566b5
+IN: f6b8f9422f6eda5c70999737573142bd0d503f
+HASH: 35d6199bb8b26627
+
+KEY: cc690d64cbbd6766af28d5e8809804e2
+IN: 80b9e15766ecf0fd988658701328a266220528f9
+HASH: 8da47f7d2c8a24b9
+
+KEY: d097c32707d111358a8f6470c4bbd3bb
+IN: 4a174edf086a5406f36aa20f1dc5f854df264ec159
+HASH: 8dab02709978b647
+
+KEY: 79a047071edd76cc634f9510dd0113c7
+IN: 247ba72455b9a97dabab6905b0a1227635f79f3e530e
+HASH: 1364c8043fb9baac
+
+KEY: b3092acdd6023215701aa9c99ae4e218
+IN: ce36b755300273c22805eebdfe7dadb59fff0bf8167fba
+HASH: c8e50f39c0605ecb
+
+KEY: db6c7ce746a0431f8269407f8c35a4cd
+IN: 40dbedc0f4c351a6ffc058a2ff63794419b1c2ecb4759f6d
+HASH: 9f46bd8f53a03217
+
+KEY: 110301071f6676fc2ae41a5cea83b10f
+IN: 7947c7e55c5fe0fd584ade5a30f8af69cfbcc4825ca4e45fd8
+HASH: 7305e77b0e3dd8bb
+
+KEY: 976c2ee47783766ea8235001f65d256b
+IN: 912be0ef8e0e7becfa176ed4006ee8f2e77107f022e42351cc20
+HASH: 2f05ddca521adcf1
+
+KEY: 58b1de6e33ffea228e144b4f25e5762e
+IN: 02e8b89312479f1b0a6da0f7f35e6ccb50ade322e70ba2f5b0a354
+HASH: dd0547e80e8505e1
+
+KEY: bc57f28c8f461d9714b48c087164a695
+IN: 990ceb64628bbafa3c0c4bdabf465a19d075fb66918dd240ebef176f
+HASH: 7ce492853fc22eb8
+
+KEY: 16c2ffc4e03d5f17064f22fe0512eebc
+IN: 42e78f029eaaa1474268d534d1542e6019af7c311b70a3b80d89fc8394
+HASH: 5830bdb3f6cdbf80
+
+KEY: 546cec4ec1ccdd1dec16ffd684fba160
+IN: 36fa97e2d20f91a56389c84d3c2670c25afd2d551de8930c735f30dc0f8e
+HASH: cc29ae6a40051fa9
+
+KEY: dc04255c1214077c60ed57c00719c4ae
+IN: f6eddb62225c75e47636587d992abb31fb1429cf0e4bdf7ca7610833c6c145
+HASH: 8959c817d89057b1
+
+KEY: 66fa7b74c5d112044e6e26a8f056672e
+IN: 9c303c5097358b4671f2b282d2286ccb9925e22717f3e1bb16a2a7f088c89efb
+HASH: ef44eb07b3dabf20
+
+KEY: e68c0b8b0fc194402a074069fa79bcc9
+IN: 68740dd32b54ab57313b7aba7212eeaeedcd00fc34b5a295c3742361acb450c331
+HASH: 31c38d85506f95e2
+
+KEY: 5a9bda075c2c3756bb03637b833a2c7c
+IN: 0a20ae05077a2775accf3e93197c2cbe58ec0c8cb2d666a6d1c5ab8f6a88921b33a6
+HASH: 83ac7417ef2f78cc
+
+KEY: bddb529beb2c89ee5f3e6b91c786f7be
+IN: a963b7e8a6ab9ebce913d39af035540d1172488c29b85217edf02143c23183da52a3ba
+HASH: 87bfacf3844aa239
+
+KEY: 65bf6deca182d55e94ebbf8fe0df1e01
+IN: 7c95a702ffec11b19ddafcb53feb2571214b06219feae9b8a058509fde4b54e1f16cb89b
+HASH: 170b6b574e480859
+
+KEY: 7ef0aa07183687c13f800f268a00ea86
+IN: 8c2b513a5facec6f5a34f49eedcdcda7bccf99fd9de0526ceff7c81ee11797d451d62cb689
+HASH: 5d0f521bc4b1c1e1
+
+KEY: 490cc83d84a0102cd83c11f8123733d2
+IN: 1f7312100fe65f082abecc08f276ff81164b21ad7ea83d7729dfd1ef622b5c39c884b3f82e49
+HASH: 2b72bb41af9f002a
+
+KEY: 81e1981c81e5c3010ca4b048eea1cc72
+IN: 012273854b89fb3c66e4427d460e8493927d71f44ba7ed7dc2b3cd3178faeb6f0471bc31436a85
+HASH: 6d67485ebe1e15ad
+
+KEY: b47f522fa34143dbccffc3b1f4093b09
+IN: ba235ce1ad78f2566f78270021fe9f31417c3f65f1533c43ef71d6d281d17722ae4bc8eb87636c2c
+HASH: 390e3fe50119cac3
+
+KEY: 9a3fedb06d1e550dbbb4c4c6c42c7b58
+IN: 95f641d66e88414ee49060d1b7bbb2d62f326eaf6cc3c77a359ea2dbcb0526f737b4a1797e7026d813
+HASH: a58b6f3f1df212fb
+
+KEY: 9111abb078f38c77eed9ff96e25ec5ea
+IN: e1476ccebc8fd7a5f5d1b944bd488bafa08caa713795f87e0364227b473b1cd5d83d0c72ce4ebab3e187
+HASH: 30527d54ef667563
+
+KEY: 42808b700fca9f85fddfb5d590807f0a
+IN: 5f81bd275320d97416e5e50d5d185d5542a157778b2d05521f27805b925e4f187d06829a2efd407ba11691
+HASH: e58617ceafeab62f
+
+KEY: b609075989500f06abed2bfc45c83d13
+IN: dfedfc41d8a98c617adab43f94b8d55a4fb1c02bd9c4939f8517e7207d3b8227d2bb8af086dc37e2ac24f437
+HASH: 0037588ae129b9e4
+
+KEY: c9afb75085e1759dc2bb6790ae29ff1d
+IN: 01a06ae550215331ad34fc87c2e9597d2a369753009c5a5fa2044481c6126bbbeab1a9f3d49f6198565fc6db9b
+HASH: 236763204e7ce6be
+
+KEY: ca4a9f84ef63e8b62514d34f2d74b4b6
+IN: 5f670e9e199c590700b7a7fd6b777f325ceb90f05b611ab77a970dda67db8636ba82bfc8770f742a22e66d39a59c
+HASH: bb3bd93cf471803a
+
+KEY: bbb3452a0a57a75460a77f238bdde048
+IN: 6279c2ee80f83cfc3074f24e5026a3fdbbc29e95f46a4e2862af2dda8e0c49b896f8186e0a3aa527dc4fad02a7c21b
+HASH: 823931f402c9d3ae
+
+KEY: c1f2268a501653c08ce64a34b1eda186
+IN: 2b55a854ed9125c148e97ed0fd128ab7a48bd0abf150aa86f60292feb3cb02da159698a01adc48bea1bd38a8f0339496
+HASH: 050afb47067c73d4
+
+KEY: b4a12074ce0969ae5de1cbd25e4f6f5d
+IN: a1543098dc8e85c7319269c590513eafae8bc5bec292b7718ea49018e3a0ce80843ce1aa644732eb083cfdb418582a4a7f
+HASH: 7c0fb55d3f5e8363
+
+KEY: 598233eb95280098c909e3dd3f93dcc7
+IN: af6e57e39dbfe09b91e57c212977b9b5ef48d30bf8bc8764e3796b5aa82680fb590d7ec73f4ef2357c34aaf2e12b45dd1f47
+HASH: 78ab42112974448c
+
+KEY: a91a39cfbfd3a01ba163294d96d99477
+IN: 092c683817c7484996b32e6c1fb46f3754b829b1b46120bfe1bb9123d139d46f5565c8dbc11a60273bf6b8e1bc38bc9a6f1995
+HASH: 7fa74c95642f9644
+
+KEY: 0b564ed2eee9e3649ccf53fe50d8b1c6
+IN: 05f42bd7cc89c80fb6eb8d09d4f4968fc47aabc0db6ebdbdf70a416815622d6e92de69cc675671db24d021be0b6c7545aab0becd
+HASH: 708bebc79de07dc6
+
+KEY: bae7c48ecb04834a577ad26c87022cd3
+IN: 9f5af8c66983938b16fe3a2de4d59faef425ca769d3a31c62330aafa1aba57e53bfc3d61357b618f1ae01bba3efecc65a70edcc8b8
+HASH: e9d43d98f790f1e9
+
+KEY: ac86394cf9e8668900fc3e731e5573d2
+IN: b832f6c610c6f3bc5ae50b656709f26777a9cfa6266faa80d788376d7a4b0af3c84e2f14a4b538bcde23e45f0554e7a333a95224b474
+HASH: 689de641675e6b3c
+
+KEY: dd60e6ec34522428fe517767fd94a5cf
+IN: 3ed67357449c3b24b6d8a381d92a443d333532aafd1c90411a29b80ffb6566cb13762c5c8c8ef87741023ffafe8ca473f77934acbd25c6
+HASH: 30ff83beb6a4eb30
+
+KEY: 9f5c48c10551808854f2c5965f2b8402
+IN: 9b160f17162a71a9aebbca3a32939cf09ea4aaaeb98c75aa5fe0c15a8a94cdbfb7716ea37b7f0aaa9e058d93ef5800e9ec863a5df85d51ff
+HASH: 987b4b16546765bc
+
+KEY: df955ec1e84432fec581bc446b10ba8a
+IN: 44ae821f9ddb1e1bfbec2259fad2042558fb216dee7c56af074d24b94a61074f37a011a46fc7542907af3a5c03ad64b34f1940219e3c129b8f
+HASH: ed3e957005bf498a
+
+KEY: 5a901ece66329a9488bd17ad3f350ec9
+IN: 3e404145993bf0c296c97729f9f2e6eb3bf22010fe642312c8136c0da176ed4c314eaee878047cfe0705a835a8a22e7d2b29c9328370032d4824
+HASH: b5e479243d036ead
+
+KEY: 6835a439c331ce630ef771f866f045a5
+IN: 5f2d95f898406fce05b36d1cfe21c57541bcbbb9293c3dd56e6fa8519e1ee76b40c2db8097ed008e84dd47b8aaae3c2b33037f9f7af38f3f41c9c4
+HASH: 7ce56ef8daac6676
+
+KEY: 84b224c92018a348dba300d4e17cd139
+IN: 9c520b111bb008086c5815f450a6b7b6daec0925c4b0c8cf99f9f9ddb6198000a379fcb62527d7c361ccbda2597deecdd055850abc6a17251c08577b
+HASH: 3e5ab339d1f90e72
+
+KEY: b3af10ef15d3e728b36171cd7e0bfc54
+IN: b0c30990fa7d8451403c84c7cbd650847dab3e087fdf2985eda79c48deda583bc9c4957e24b0502ab6004a85bbaaba74efe9bdf2377043d008ae14e169
+HASH: 0821d1f9e241ca1d
+
+KEY: 3f0dfe713054af061ab05dec911b8895
+IN: 0d3ee8fcd134e9814641fdff20b22ddf17ffcf3f23af7327e203cf1971329f92e99622d1b8329f9a8f9244c5efbd4ce3e07f1b9779f1d84927e8fb16c030
+HASH: 0a5fe889f9475d8e
+
+KEY: 62663e655ae3a122b869b11182f16a11
+IN: 82696187d910792ac92d50900677a1a0238d91cee3cd72ad949b50c53a0613add3bf0ab02c78e87f96847d5bda2bb31e4e19d92c933b1637aa00be18eec696
+HASH: a2c05353cb689240
+
+KEY: 1d3f1977fc1aaa27f459cb4de22a736e
+IN: 935e1cd9a08b1d0b57dbbc640915c6de3eec62f481cc64a27cda6a08db9e7dfa58d13dedad1ce2abfa967f059185b41f2b72114a4ba51a9a5d279f067ed9fba6
+HASH: f64a0fe86b7dffa1
+
+KEY: 96bf9034f02772a5150b0f2dfdd49c88
+IN: 2315e242205287d3e9dc5cbe317ddf3f286ad02fc4385c82bfdbd43ff6d5f425347e229faf0521acf9bbf3eb6f3abc2029c7af2506972444425e1b92aa1d6601ea
+HASH: 2ceb3eded2754829
+
+KEY: 5e2d0b77b3478b1d041b9b6784bd4e0c
+IN: f10b956532deea1838bebcb192cf256817525cb95242e5295830db8cee586e5cf3fdd0d9a5277d5a50a8dc6e4878d2cc6a549eb52bdc5beeb89ce870e65a87702eee
+HASH: 8fc78a2ca1ba1f7f
+
+KEY: b2c34273ff91123facb2e3f4ac03952d
+IN: a43e581d35caf54ece5b668df2f4a77e29bee3e1fe26add027b07e814991bc538da16f1649886e42be0a5be8b221ad155eb7489e81330ee91b194904086f91e9e71a78
+HASH: 36ea4accc3181075
+
+KEY: 828238257380186239aad56fda379060
+IN: 46f7fce30d03f04bee6559ace020e6ce72379001c20e5fb30bc7a500ce91262e0d7af70d8be30c61fee623e67a5e46db55d1dec64bd4be6af45bfff65050800194ba175c
+HASH: 7a1a36cfdd778b12
+
+KEY: aa751839cf5a43613a3b686bb22f9e02
+IN: e0848a835142871c489c772d01ca0115f226d39f94efde92178a38f87fbb371d4791f13954feb4f493bc0707c4dca732e24642cc6effa26da527ee7472c1c34c4b0b4834a2
+HASH: e144d10851a7e0da
+
+KEY: e3d027fcfc629cc735fdb70912799363
+IN: 08a51183fb0396a1633bf7a2c6ce4abd1e44d9153d7bc2a269f478269181df5ff29346366cea689a8301efca949693e1836d27d9cff181099e878b2bd53da75866f4abe0b64f
+HASH: 4a935a091f380fbe
+
+KEY: c9852a9cdd185cb16fd88ef793bdf598
+IN: 49cb5ab1bbb45afb32878b059685a40016f3add53623e23859c9384641c537e13aff631d814deed607bb6abc375c855f98744e455e937ee1c9e478c4878854166c30d0b686328b
+HASH: f2df3a49621a40ad
+
+KEY: a713bd5752e392d99947a0a0fef98da9
+IN: 15064ba012aa36887257f3f84261ac66134c36dc02d4d8688e2fb10ea36974c4fd3963144f8ff01e3132d2c69e5b57cdba82f98453238a51653facc718467ca781e73044aa368879
+HASH: f187bacabe914603
+
+KEY: 5292ed7c7317c2aa71831d03b905d0c2
+IN: e3eea270140e52ee204c78b9a4d86c0be9c74127acb4f3957b1fd380417dbd57f91fe69509126e531a62144cf7ec147b36aee7931b883a028f93993bb8068552e1ac9736775038c8cf
+HASH: a9a8c0bc50104e8d
+
+KEY: c3117a19ed788199add874b10f62190c
+IN: 112a91dad631502ec6d9a569d14cc009779bcf2ffa489ea4df85c275ec37148b25a133dcb6d4c3dbea8ed4375e7cf26885b991f96ad984880bff76a62568fc115d3762dccb4522f1a27c
+HASH: 4fc2a87b168477a8
+
+KEY: 45862eabc24e2b62acfd8833595b329c
+IN: 921a7d233720992b5d767b61f86b4d6857caa99d39e18656de8c5571f2a7295af7e0d703467e4c0b83f6f7079c63b55657776d3020424443756388ea217c2407284fc8657d3935a9b34573
+HASH: c1cc6da484446857
+
+KEY: d185262e194717d27be06c1297b8f27b
+IN: 7804f030a6d67847f53cc93052953516f66c915affba735df79df7da6d70bd511e4edb33fd712b58bfdff47d98b8acfaff064bd4e1f64828d61a82a5e72ad97573f4631fcec0ebfcbd16e67e
+HASH: f9d37ca5a7750584
+
+KEY: 53ea30bf7b1aeba84ae8e0b434a8657e
+IN: 65ffe0a087ce955a6170462711dd53fc057aeee9fb5e7a8cd866527fdce70aa4b8acc65ee4a366cdae649cb5342120cd7cbb9d536b1697cc45326a44494aedefb4f7d96fa5d0ddd104bdc6a84c
+HASH: 965ba9830abddee5
+
+KEY: 79bad8d7ff51922ef1d540b7d8d7663d
+IN: ed536d02d21049bfe6c1428ebb6a8faa481321a4977a685409b0fb2ca39d72c92d3db0ac406d56e2f15d6b6b62c73246807215bae613d283b8a35678df263fdbb6d3172c16909deb8e97d78694f3
+HASH: 4841b9dd5502f605
+
+KEY: 104104e76e645e83c7b40c6674906d55
+IN: a7546cb4e6b1b1c112aa28563028e910915c6b44b668bb57bd6623b941af4c3e22c7cf6eff2f6c474657fc5ee293db60a84944bafed2acabdbe6a6e7f0804ebed61786c2cf29cdfb0c62e8d41df81b
+HASH: d21db84542deb383
+
+KEY: 8d3c174a295a9d859e009f73e113403e
+IN: 16ed05c2b7b0c54df274bb67804c5d7671b915d899e15b2e166c3f1e4d9e990ab5be59c5e9fd70e1967a8021797ed8b2e40182f860dab2d0208dcc7c9fdb7bdcaabbc81f5b9b8751c558c5418dd654b2
+HASH: 0c4d09f8827c0c37
+
+KEY: 55bc19da3d5e60a3308379d5900165d9
+IN: d18ece72cd2f5fafe52fd55def2e6af0370e420d7aff7968f9cb5efc44ebb161a6bdd22132aad0aa9685e9168ebb9578209e51865625a4cc86c8f7898ad629b64e400a7d4687a5c3c000ad724a281440f2
+HASH: 7c47369a6d8387ff
+
+KEY: 58a3165308ef3c5e2a7c05070085455e
+IN: d9382554d5b633afa16065e6dbc0d472ee26629e6217a293b63af73cbd4a5dc3cf61ef7ebd5b6900a34bf4ade833c0dab9afe9d2d97369bf2814d03349d6e917adb5e0e1388772fad1a4baf64d7836fe3ffc
+HASH: 2ba3e3082bdd312b
+
+KEY: b636290e491b0ac22476c91c958ab313
+IN: 3d27f5c20db62e3abe1068c018fe8e09e4823b8c1209b74a50dbc19302e190350dba1c5ad616007c72be04bc21d7ef0a82804f266ba95fb3047a6a05de3f0d7ca3cc7c4633a02dda4f367051685535d8e1a155
+HASH: d14da7a2dc3b015c
+
+KEY: 667094c06354337178e646e23e453403
+IN: 215f856c43336295b5e4d625d8cf5b37c4a5b07a39eff2b42427d5df683982ae78ec85ba085740a28446c7928bce24be1b66a087898634a3f6260a3926c9a8f953e235e469c4de23fd32dde181168525b716a2cb
+HASH: 2f7cb69c5e4f942d
+
+KEY: 52eebf5170c603985982122bd8eeb1d6
+IN: da1ac8ccb4c66979633092524e42ca05df667ecc3921849a24c8fbf6aee70a01504ee2a80a000ffbc7b7629843f15270982a59ff9f3f081963a109ae8eee0a1d59baac5207f44071e51c64535b6920bc07bbc9048b
+HASH: d3a5fe4ac656bef1
+
+KEY: 1c1515cae3eba8cc9ed94c10f8a1c211
+IN: 4348d387039f7fe59a1d94a9e6d894241d62eb3ee91c19ebb09064792abb126710a619073fd293b59fecae7d90655d651e542be56a01ae5d419f14b1cee06cd90ee68e0106cd48130dd6e0d73db8fedbad3b00ada87e
+HASH: 57a4daa6abfb5a2d
+
+KEY: 8a8680122ba96ad89992741a8802f79e
+IN: 6ae10dfa31e5d06aeed3bf640591b9810245ccc175a8ff9f36893ad6e10c6dd9347d001f37c41123e4ea16b86b08aa3498c75bf5b8702228fb654595fb930cea2bb9ec97ffe1fba10fdbb7e8b16a2495df30abdfe1fc4f
+HASH: fc353ccf54b8c294
+
+KEY: 40d3e4941056b2585e1ed3bbd3196b24
+IN: e1764c110b024e2c406a18d6a6cb1b2d8dc05f8cbf635d9c6b59f8c54f7cfa1c9022f719d28979dfd75f6a221687690a046404fb1204cb27ab6502f6f24ac6d9c272852acfccb2d51948ddb427950c95ae4045699ec002c1
+HASH: 47108a5f3f664b98
+
+KEY: aa377b72cf2c282946fb87e6f3f16fbe
+IN: 4bf7833eb1e828df6fc190ecb568e3e175307408c4f851532afe731986d05c2e0a6d21c40b0228af69a0cf4f30293ba119462f3f95200386c70a4c49ad7251bc797be0b504efd50c98c3099b1119772deb764ba799bed28d35
+HASH: 6afdff80d074b585
+
+KEY: 304b7a8d2825ab0b4c78a82b202b1942
+IN: 43094418f2f65b85acacf5b3190711332fd3fa7c1e471a49a7d5134ab7f22def6fa973cb135a0add46fa482e7e29976abff74c3bc33af797b46540c85eecca45b848b55d3e412970c8937fb97d2b61af68ff3f2876bf8cb72dc3
+HASH: 8f89c9b9a3b9bf62
+
+KEY: 1b54ae53fbea3567a9678350ae934303
+IN: 8d02b02800d9605227fa73cc920962339645c72b560ba8c266b0b9e94eb8be0c9748718ba9cc48f1afd12e8b458d745596b763c7e65cb8a0fcc3d937c56e04a6873ceeefd3a77f3c545eb957a9a7bfbefabd47dca867ea92203c9a
+HASH: 86f09b1edb5f7934
+
+KEY: ada9526398c0ffe64b200ec1823a0d16
+IN: f6fcdf72c13acae3886dbf6a842806fc9ad021d0329e595748a8cf82f375fd357e1cc37a7d995d869373cad1dd8ecb8dbf0e5333767470378fd7e5b0d1fefedc18fcf045cfac7d883e67a8a32efbccc1a87a8e089f34186f7ea4ac41
+HASH: 64e098ae04f9e06b
+
+KEY: 30f08a4322a8c71e1e1a6353b371fd4d
+IN: 80df14c6bb51846621cd95b57c02e34afe6a96eeba8bc29002b2514f26c3ce53dc81330ebdfe8bce32d4e789f5bc354b03b4d10a64a10248dd1626726ff607529386f7becf9d716664bed65656629cef7fc7482e9046af09aa7ed60072
+HASH: 746b10b00aafaf2c
+
+KEY: 9bfa27a589f425dd70390c6ca1e1760b
+IN: 79fc7caea4cf78001a8d601de3438584517972e81d55c8e00b5c8ffd1e1aa5896058a56e636a4e66842d31f5287e01587601a9f79488db6f28f0a5644b34de163831cf462493ec579ee0c7631adda09f5e135cd70e6a4504e52823c1cbc5
+HASH: 1899998766305ccb
+
+KEY: fab33b56c2b97f9899438d8c4448a721
+IN: fb7e625072c6dedcf31d0fbdaaac81585465f6227d1a37d60befeb9662823d2bfb70f0dca67af4c1c60f72a524fef0c243a758f8b2883f17f2b113277fd71d28378c027cc9aa8c79d5dadcd65c2ff275f29a428437f424ab2171c33f819df4
+HASH: 208be5482e6ebba2
+
+KEY: 6ede0e899a0dacffa8c8ae3cf7552310
+IN: 9865e6577b8811c937447194caf30ca9db318f3949a0a095a148fa0fe0fe7a0ef4efd7c04c7f0ac13206841ab8b30b6b1a55b1555da37a40d5abf1543d59f1331309c1ce5f2adb39259c152628dfbe10c0c5e81ab172c025f9b84abc2d996834
+HASH: e46838d2cbfe6c33
+
+KEY: 2f0b8714a8f0454f62db8dc4fa506a6b
+IN: 6c45eb8e88bf9e63db9d1558e381f4a6f831727d866daaff9d402ba1c3ec6911aa4c62dff1bfac2fb00a50be2c2d945b4c1cd10d1ab0d96d201a5e38d80a5ac2cbee6b2945091e91aa40ccff6f37656392758bbb7da5ce2ef3c933b1bf83f82201
+HASH: 433657c53e5fa081
+
+KEY: 85cf9b7e4bd9af87f8fff9d854fb894a
+IN: 0e808570f9bfa7fde488e3fedfd61905b232a2e6c512ff4659b6b03722d4d1a8ab0b757de0eb114c52620054364d51d6087417f2ecc73bfc78caa8dc5c3063722b8c7387f5cf1eea7369baf108092b1736f34f3c85a1a64caaf188753cc348ac0934
+HASH: 25272780593c358a
+
+KEY: 903764f0b9bcab23a0d1e7b0d8248711
+IN: e63b264edf7a3ab19e8f38ae22fb9e00ed7c80d25e3a9d76def3c3cc7975ed5a19aeb552a559da1cc6d6f4d7ad5ade44bd7bb57ad1910a469fc7082267ce9644e9d4e24f1ce5afe6cb3886172485df77b3e47dce36e549c62129788648c3738f836caf
+HASH: e995824b960f3012
+
+KEY: 902dea339b29a5a4c54c71ac3486b509
+IN: e33910d372dedb9a154cd8b1ffd4d98c58c4b8c93b5d7ff59a87a40730547ffb145a820aea56bca4f7c043c40aa2f5e0099b9a91683d2d2927febca53c727ecfd8dca77830012c0d33135a2d1a5665c013c4126acec9073cee84f429c2a1d57abff643aa
+HASH: 7a6d533f1d13f598
+
+KEY: d760372d5a2224bdd096ca9c317b315d
+IN: 868257422e5898c7de261682a088bf97a19bddba87650eaef967e4085d5145787dd8a2236859c67b1d96e64a81934210b63ee44adeb5cedae03cf3598496f7b77caa7f2a2f123778e3720229ee7eeef87837a0d5de7ced3baf40114d663bc0e576ce6cec70
+HASH: 05eeed619f335b0b
+
+KEY: d443db5067af5681f8b61a489400d3b3
+IN: 2f69ba36de9d7f2489ad092263f06e04b87179dbbdc3725a8478abf6930f19ebfd1a23823d41f851618f6d533ca56273e602918c27926cd77e7380de5af322bfa2704ec669ef402971bf606da799b85c8d51ea8e2f8b8df3e9e4ecd9cb03f9c1bc387fb69a02
+HASH: 3b54dfef0537f65a
+
+KEY: ff85c309dd953710c4c450015a92db7d
+IN: 3b4cc08fb998a83c0bcfda112957646e708a5e7f457c33a4e31fcfeba9fb5bc37be521bf351eb5ac1bf11144c3fd4837cf9d9728b02b75c51fef0342140e3a0d199fe0a67710a3a7131c6d3c33e0ca00259cab1b7cd1d6de7ef81e97dee7ecaba9e2073c08096f
+HASH: 0880d53fcd40525d
+
+KEY: 50703fdb1e055a0f72353a13de243741
+IN: e94047f4276abc898fa1e268b53ba5effecad744a315d93f1218a47725ac6512bbf0e418681cb15e6a98c3a82bb3fff9f64583ba39268a70a45d91f432baabf3f38335b1e9f52a4141bfaa1c55570ab0ecdfccd5d7cee5bbf692277849891625deb64a8850a477bc
+HASH: 758c1f2f92b68e49
+
+KEY: 2b022fdf4bb13146ba014e1f2abd73d3
+IN: af723b77807c45b4aaea43e9d79a9149fcd1fdc59849b1bffa88abf27c6a779fd8172f325909ae43e49c2a44d3991350a377d58643e5f6ca9d8743c7842d58cb8706814d783e2c7855b8d63e55164cbc1fd7a13bb963d6c5bf4be737a0159c1347a72b88c0c5115a4f
+HASH: 2275a762396e6942
+
+KEY: d6c230457b3494e373c113c4fbfc8fef
+IN: 7820e1b5f75780ae7e6360c6708ec8bb5f82f948e656deb214a29d7887162c67aa1dc547b9b1b878f870cb44b22c61f804c6a9bfefd38588253360532a558c4176c98b63872df3741a718d8487e9bf17d5da3ca9145cb76a9ace837fc9b1f3065b8a777e10fa95dcfbb3
+HASH: 53b8f6e69f5a89d7
+
+KEY: 17f7716d7d49fbabdf1583287dda7802
+IN: 96ab47e1979e30b418b98f8b0f86ee5f5a773b3ab1d062274a4335f2f8cec6a0586aeaae7dc4d0da82e52c3b4b670b0ccd724b0100af58ddc74899995bf000fc626490b19b8bbf4f6352879a0530e97f9bd2418104a1a27e7252df3a6b996b27f54503ef8a718a8123d580
+HASH: 8aedf4fc510a8fe7
+
+KEY: b12168e2e46319863bc595aa421cb4ba
+IN: 0ce0a170319192d56484fc847a8285abe28b3ba3f8aef58702df5bf50e4d0ad2320946d8256dbf2338c8c0a6a1da48496adf9941a4d38d74e0c8aa52dbb5c757e28fe3756421964a3e4eeb9fdecf2529d70216b00515d5869087b611ed0ca1607a650ed6a3707ec6bdfa4d5b
+HASH: 4d85e666f836edcd
+
+KEY: b05bd753656eae367d8f37b4907e4fa7
+IN: fa2a52f77554506d25e8847bfe613139565cedbce07110a7a5af53d024204af9e0bb08c8266616dfc1e21bd5c651de626e1303b08c0c90ab709613caf2713848ff1ba3310852a575d07e12c7691cb081157147a413e80d53a55248240eb5dc566b8dd67d616c5d4dd8199d5d69
+HASH: 885116def6316c20
+
+KEY: 0bb368a25169217504ce1bbce5820394
+IN: 34b4df9ec90072e91548fb40a35128349986b7e50e425d894664a270f919456abc031284b29b6bebd734e53d5241919a8dccb05c38c633fcbad53979d92eb7cfe111584155a4c352f06c29242178ed4d904ed739a04d4ebedd27b7043d79afa9b740df4ad58c5640da67eb121997
+HASH: fd965ec9c1ef4449
+
+KEY: fdaec5633f8d54859574792bcda87468
+IN: ed96b9cd567f55d1d9e087bd63abccd07baaacca8a6d375d2576f00b1e09b5d920200a74cb12e8721e550c80b8c61ac7a8e733e1a14ad0311c30afc83f5aa7a8cd9fca47aafa84babbef5f93a86eacb5d50f696b7a60f0ac48f9b57dcab2168ce26f6390ce3c091a947397db050ce5
+HASH: e92615340f232b12
+
+KEY: d529ce056cb054580b538c81752d63b5
+IN: 75677757aa8eec8e44411d238aac77fbf1158c1c9c3174b78eab4a70034c325e52cd1a275fc0a24c432f82a10b5920a922981dac43571500263c0bbfb95d08bf20acb526bc6a180e6519499cd571414f7bf69d704b476d62083d53049674c46fb616b028bd269e1b822daae40ae4ec31
+HASH: 9d6ee722d9af0ac1
+
+KEY: 28b413218335bcc2f9031dc3554b0585
+IN: e4ac05d0860d28f45b7dde35028dfa67c25c8364545bf27a016228c4f1b5cbdacc418806d229d1fd2d30570fdb1304e38c7443c02d2e55ddd20718dd09d0b3a667df71c2040f079c9798a7f5ca7b09186df6c44edf4740433ec17873180038f7c5e0d4220d3755e264b0543ad5a5c1d537
+HASH: 7d6d8be4026abd19
+
+KEY: ab31c8820d2d02f63dc3ac5acac3828b
+IN: dae9931233787d476b472cb8e7ca4f86c2f8b9cdc38f49a50857f99e4e64b89d451e1bc8e5afacae36579d51b8bf67a4be36a65f9b464a150c1fc012c115bec08e45bea214b80ad39e1a705cb9b0b759901133d6619812ded8abf7848a67758f483aacd11297315dc190746a44e9ed56708b
+HASH: c9cd54ada0139758
+
+KEY: 145d3281341d26c75cbffbaab06d783a
+IN: 9859eb836c49cb41beb29eb493988b459f5d0b22a4dac9e64c34f93a71a700612cd568f76b84cf71966edfebaf6b27ef52524208e4905b83f8d52e879618180101c1ea0e326a44bb4d59539f779a0157af835c5bd7009882f16bb2aa2c87d49b6db49e996390a3ace578794c4d0f1802dc0a82
+HASH: 219b0dfe6c21ff2a
+
+KEY: db21409aca53c06e34752eaf651ae7ec
+IN: 962ec5dbe61976d91eca580b50d3b21c478bb8139da94be0a90e68f89b26b8b28d3484cb938791cf729a269c54af470e528a72ac9122ea10c08e836f8f32f1c1bbcf10716ef243d2abbe31bbf52dfd6c6884b81d6c48f18f087842b055432e5f67de978a2e7bdcd645dec43feb33f2be7fc8bb5d
+HASH: b31faaa12c80d2b3
+
+KEY: 0a92a6dce6418f53cd3823b9a40c5183
+IN: d1f4d53bd0409a3127ef19482d47964197bc061bc779b32ca5ca6f546c18180dfabd32b5a8519f868ac3ab67cd72c3bfea3ad96f5e40fc09a64046ff818928ef87ed043d0a0994c1adb8b0bdf446188b98b20dc65ada689654c5fcf8fe26d66baba8c02d8b39ee2be9217c09a62a4fce8236eda6f7
+HASH: 3e9be4b24cd3f47f
+
+KEY: 868dfe7c81ff02bded7c42a242d4c1bd
+IN: dea44a7466a4910b89fe6abcd824e65603a17ac436890fecbf4f6bc6809be8b13c0a6d593840d48ac59de059787dab1c1ca09a6834114cbf7ff2686a71ec12921aff5e13e9cd80c77ba7adbd361e72429c2b46eccbe27b25068cf457f865bd0149da14383317bcf8fbf949e36eabc7f8dde08c5483a4
+HASH: a0b3afbcb9412ad0
+
+KEY: bc662a2870b8baf74a1153e5d24b4832
+IN: 503401e821133e2ec69942d28503993e0337ac43502466f788fde4821e5bb2be6b0aa80a6886eb10dceec3225762a1357992b740d5ada6f6acb26761c57532599c66c9b55812ced61bc0293781f600fa1b2d211a7556702f41203b7824654207894feb9e2f744e03f5d79682ba4570756050e42eecb3e0
+HASH: 285affa883d1597e
+
+KEY: a51217e323aaa13dc0debaa2d26a7141
+IN: 52c40d6b8dfd5fc99158481dd889643452c533643534298382d2f3d159a7c0f019cb614d8773299451ff87520d9680e5f283e9e9a2fe9a8a7b5e4bda6b94e578b97948eb9bd868ac3c33ba79df325c141eb83ad6be7c1b5c9001d59f88a4bb2208ac62dace5922b6df16f09092b48d432bcdd79b4eb484cc
+HASH: 5aae64a7fcaf3072
+
+KEY: 89c8b8541be48278ee4cafed6616a581
+IN: 86a7c752d913e379429724ff0358994fb8ccc6605573437ed5742fd1b2934c6943259c2eefc5000f5292901a154e856df3c8b7fdf370bc72ffae5957f104fb3b07ed448def575680bc637e3804ce9cca9ce1d85fb79b33e8e4c45f1f974d0613d40c63734b9c927fd0e6e16bb4d288204bb759b199269fa7cf
+HASH: 339b0843b05f2652
+
+KEY: e8b664ce32d35f060e5a015caa287b7f
+IN: c9d496f2e1c2141154bfeb0c4a4347fa7fa002ab2573c634b2c2a376fd270c7d45d2fd6078e4d997aed34bd4dbfb308e2c1a14d07f58e1363c3fc4bf32adf4af4a6d1bcb79e4c5c3db2ed2c68fd2229b6b5fc831d7a2cce989d2ef8dce13f9e076aeabb5a30c7ae72f4e98874f885cdb52518e71a020e0bfb7c9
+HASH: 254a78254aa531f7
+
+KEY: 1ce06b6d7f0c15734448e53a4fa52fb3
+IN: fda649ace37b9fe4f4d572d8b1b74e28ba13db46f6b0f42ba9ac940d379d6d50623fc1ef7545763651818347ce38a3d86828148b14ef090dea626c5c3d1379341704f719ffbb234e423b0024ee635ccd2ee50c501d8689f84c929d689745a05bba54f99a23fb248366ab05e31bfbc18be87110d419ac43834476f2
+HASH: e5d2f7f9315945d9
+
+KEY: 84c2f71ddfa145332dabe73460c7e03d
+IN: a8a162a8b39975a998568ede08dac2c98a7da7021711e159356c13cd39f5080fbfe7f9f2ffd75174ed6bcd82fe0cb158a83d10bd64639f0c6a10335635c4aab34bc0986b898b9b4dbc013fa456a34374aee1a0d437f1de697f5fd2b87c9ad39017434d66761c0f26e81e9c99a9dd57f390f2488ebbfe7d1666cfda65
+HASH: 7c416c3bb889ff8b
+
+KEY: 63ab163cdeda549ba268896b9a677d89
+IN: 64be9cb0aadb6544d4401ba45a3d2b7d766d7d67e09bb2f141e743fa72b8dd14d7427fa2ec37ca6dc5c32861962c7744b1afeea8f28f878420cf67268281ab9ad6dcf0aacdbb52a6983cb91ebfafd294aefce90c6f3628376d7afaf4fd5818602328e215cf1aa3c9510ce53f42dc04288accde985e90224260e318d589
+HASH: 21cb18940175042b
+
+KEY: 2f8057e7a18f356d187ac98e43127515
+IN: f5c76f7b6d6cf30342c1f4673ac063091c9c02862238549255bfc2530f743c69cf4943a9d4035fa1912826673b3c9892a489e0afc011abc45bb5a251f2c80ec97962f366376841939e9997637ee1a2dc0655e9e8a1d262b046cc4c0eccb981ffb46d494d5537cdb202b840601380212f67e728cf9c3867cbc53c0a825f61
+HASH: 843747a123971b2c
+
+KEY: 2e764495a9e9d8ce7dce2a634a374423
+IN: 7c7b09631d3e04123eed9efc8ffa34f3182e40ef700f2f0989627d667719a7004a4f0be4fd987400ee12216dd7ce4b57b889b8d5747e5c0c00dfd4eb9011bcd848070ad0d6dee431c50caf613125f9165ad05c69b4859f7ef55869892385cf2fe40dd3c4051a056980805e816e86e28493f7bfd16218e523b2c72ae478d0b8
+HASH: 65fb43b58c02946d
+
+KEY: 1f2aabaf8e9f8a8a428a564020d44b46
+IN: a2af9818cad478d6384f541cd1ed9dfb764f6953c641a6ce40ba2adcdca047eba021f5593b48d18bcbd6581f776614dfd3c6765fd22fc6e76baf6464d377b1b6607dc3329475c999f8f6683b22ab7f0ba1c8d0867656a0cb3840cc80c543acc598a5df3f14dd3d7de2554c837fbcbdeae84529170e5927c5a737c11b92d36470
+HASH: 256afb4a3c5c11bc
+
+KEY: 6613048242a353b7a908c3c1d137a289
+IN: cb24d4c11dbc1adc4f4b65a04b1e9ad37b5257d314c091b2ef870556cfdebb4ac2e4c1348ab5ba2047ff9d62e9b78d6b8a29858dd105549d943d2fbe4c96c78c07b864756a9445a0235cdec30c191e86912d83ee0bd1fa094431fe7cca2ae73e2b611b11f67883812914524f5872011d52514b340c6ff107ae2bd65a1792cd9875
+HASH: 6bc58dc448c8bfec
+
+KEY: a8496ceffa962e684541790ea6a43b6b
+IN: 96c08d385c2eb59b806b9ce99b82f28abbe3ee3dcd481073ce17301029454c7ddc303f84a019c99ba45b339e4f7a25852508c364fd6d6fd507d9e1d77a7e9f71f4df72ecf9d197a31d40b379c576a0dce84c7121c368cf8da052782059ad80f47b0b8cdc6e6c986aba78b6fe8171d830dd4540aa95e8b122961d9191182ff57453a7
+HASH: 56771e9dec77ba8a
+
+KEY: c97c5bcf612aad1b71528b6af8f5a160
+IN: 8761e3d8f78ed3f66ddf9733dc8db84e7c1a0c268e91b6c071d4570272d9bf6d40c76752a745b304ae09506a8b751b76663dc85e48fc52df8df8aa978d082ef1a527d55d3cabd2007eda751dd2589bc4d86b69ce8ec9d0e790ea486168a0fb621861c9c9d4aa76fd67217a9c1fc9eb329160208616d5f491c46fb5159a68bf376df2e1
+HASH: 3e3d191e902ffbfe
+
+KEY: 0300b9d764049d7976f2c54ac2b0ddf8
+IN: a95265b3049332c4de454f08c0ad3c2eddc62b01b6aa111a2900ebb4ab126a6739157d84895e96214cb5353653f880bcd04a6410277991baf42fe34aaf131c3c84a83b6c29899180b1f67b76d7b327a1816c4236ee9d6306fb986bedd21570ebf9efa8e676cb723f91ee14857c186c80725f35d9ad0da73b1e07a1b935c29fe08e6af0cc
+HASH: bf8fdf47f81bbb66
+
+KEY: f93caa15e37c8785f1f4d06a7a318f6f
+IN: 06c1ffceef712559ccf22ff74eb33df2dc03230886b3b76f54c8dad4a6129e5472e4e9491d2ca5fdaa9be2c5fcafe80f957b99d3e98537e67bb6742901fd442e06fa4cfc906f46dca20182f07fa6f92ac75ef53562468288eba37d6e449026c26f08cf49333e7056f9eb29694bb68b4cd2375e409c8b470a77ea9e207c10578cbb1069da88
+HASH: d8ef7ea4f58a0dc7
+
+KEY: 127bc3d3828482fd62dc36ec9a558edd
+IN: 98578efde5a93aeb470503b3400c28c513530c7cef901d7c335b30edeaab40b6cfe429d6ddbf4b32701bb5924c82fd8c683d77bb41ff056e3f325c107493b00b4cf5288d2b364027713aa4f0bf67b77356ca36b5a77f1ddce1ef04b8b7569ea816647849e6eda1d51073a076a9c7eb5b21a3f392b35d1930d0a90de0a28e5e14d4b90f054e16
+HASH: 2fd51c80624ffa26
+
+KEY: 938dbdca5b04ce705fcfa14f2829430b
+IN: 77b7e8762402e4feb7bbb5c68b44003ebefad025c844f62feebdd82b0f221e1efb92bf831e6d0bac80c97234a4f59593bf209f2169a410935c52215928c56d6ebb6cd2ea78b3e497feca2f90f9053607626fd8572f08158a2596247330ee57f58f57eaced51682848f7a979cf95c65b84e9b8d0daf8aa0987c51eaf5b887339c70f00099e65eab
+HASH: faecf6c8febda350
+
+KEY: 8aa112a940a8855fbaa384623b949a22
+IN: 77be78dab861a36e7f7511d0b10866b9b04da12c4d4ffb6126081e8ec26ddbf41bb72f883a1136bc0adec7d6b848472a0037316515a70ce899e3636cfcbb548799371a52dc68551c4636f9aaf358f294aa1ead08ef78c21afa2f5b93fbee8d92ca2829ef1fa1396cd6cab03ed18d85a6250fabfc7cd538afaf2a014ffef82a63646a0e2025db9c89
+HASH: fadfb08f99d573ed
+
+KEY: 3219a0421a7369f055c9d5f7766eb1c2
+IN: db47f97f5d4d49aee4e5145172818736158676bc1bf7bc075bf76aa97af680ef81d2e2a1fab5fa83bf4cdf88bd7aa7610483150ef7046414772157921d0d01a495f35c1f262f4679990a1dfb4839c3085cf716fe040216febbb974ae49650fc7e9b7c30e354bde856888beb4bcaf8d4b14aa4bce08ebdd742f3074aaa6e742c4864d48c94e5e6574e0
+HASH: 0034c746323cf711
+
+KEY: 7197a414a48b2ce83a992351965bd6c5
+IN: 1be7cbb6274e2b1e6b588c6d70fd1ef929bd9ab0649003a69fd7cadd2b9b9917cbc89a069ec9944455e944b4c1bb520bdb97740d9174e6f9eed4fca24a7253ebbf8cfb15e56a352eff8fda4b0823a6b52ea28f214af0caf568c750cb43c2f39219a57b930cf8f5151543194b0f8e68c78ad7a50b08b642333b6f3b674c4ae4879c8d3ed5c9ba7dc16455
+HASH: 89daef38d2d0b92b
+
+KEY: 57e780c523aee814bbf98f6359a23caa
+IN: 44908bdfa0d5fd1f5ba1bf253b20c16c3e2c5834da8e8fc7eeadd21835efcf45ab6d42447bee2530db303bd6821f1b1f1010afb192fe818574e292ac430ed5b6d712b9bccbd235ccef4dc618dc7192f4b80de5ce5088ec3aed940b80c84efd4c6f31ab9053da0f84e39dc9718401c0231f9e13ecfe4f4b731c8b24c6a8f9a9bfda9ececc2eec200ff742d0
+HASH: 5ae9f9dc3920c206
+
+KEY: b0d51d3ade35641688f258adea74e9d1
+IN: a522a571bdb8904928f1c4e79aa00e791cab00e426d94d0bfac5a9af48f340531fc0c256aa799240befbce757f314ff9a38ad1e994431e11b771687c06e0a345085a5bc3f95a76af6600f217cb66ff85f60b39c120a86ba012e86d43811347ff37b61908e65542a102747daca5d3558118d3ce2ca2dad1c38131df27e42fccbcfee78eed2f87f2bb250159e5
+HASH: 4bc688bb42425151
+
+KEY: 49980eb295bb6997555a0504ba4754f5
+IN: 31e9b2ac04a1cbaf9f7c03cf2d70f90f683bbc0eb2ddcddedea917d14414f7e90fa09be64ac18492e2d90d6977675976cd5abf0e0556284a5edd773d6e4af29b326cd2be4e16c7293ab0af09fccb9d16701e6db5a81d42ea0575531c51cb5ea076b7c957a94401da78d43076515343db0816fa20c38ec5d35413924c70fa0157b280b02dbf982614984427d950
+HASH: 1d70036369da4147
+
+KEY: 67a590f4b8c902865a25e380a81cd630
+IN: 35423f98b55467adf90e372f516483fc3ef1d72a001e190c7a29545c9c993267be0757fca45ba1495f65218702a5f617ec8237710aaf030dc9264afa9c7da8ccc16f2c6775ba1c65a1a91f4eaa4ff035508fcd4cae9faf3223e97f3d84ff0897025a25c687d58b2876395647d7489d1fd714688f64ff47f97720f4ec5b0becedecb3da35949b40d9bfedc35dba7a
+HASH: 92b8f8135b61c24e
+
+KEY: 5542c4636e7c3067df8a15c873bbce77
+IN: be24fea7363b5f0a72a850c79d709e3a990b416082c4c33fbb0b09c97649fa1c56d15abaf14cc3c2c34d2a9cfa30e67988b697b7d3362cb07c460555ebf1f5e1c03eb905c8d0262880aebaca524bb7edaf79bd3aaf888be6d11e511d7bf6db5b89b0a2d2c1e42baece57345055b6de9eb67c2befe8ced5b4e2184d46342f3f2ff24bb38899ab974d71d2548f50d21d
+HASH: 5236a88232eb5ab1
+
+KEY: 2147fb23cfb2f82fb93cfe405cc89f22
+IN: a65cf5b4dc2eb1973009838bb3815749f4f3644a43057ddfd61edd942c4192ede85787193bf707d6bad890ace02c94b4ee2348a159e61172305eccf50e7cf8559cff42fbaf7a100c234df85422d6d9c0b8699f25b58e991a1be64d537566c602fbe54980398c238a2e87425eb7c74adc6d2f76162412b358978ced512bf26e7507d671c393729a9ac3a07c7436dc9cba
+HASH: 2da3b7beed2821f4
+
+KEY: d429971af1d1f3ddc043dd56bea43ecc
+IN: 3a28e01bb245cb6435025bef500ddf08593d7b3c7a8be01651be6363dce1e7a3a59f49c0f9403b072664700e74f4599b92f21d57886d97013a24d1c6a49db7de84176be8eda33a373f347ea8664aab786b337ac1d88148bdb4dc456a92b2a36621dabf7107e39e4b499e931bdcd21a1949f0e9eedb716fa94752d2a565592523960700187aed35e490f45da2d7ba382258
+HASH: 838d68d92d6af47e
+
+KEY: fed60ab1233c909413a7f8de9a641c8e
+IN: d46c4556b5031479e01d40cdeae3adcd14fc6ea6e1515ce7c976ad6e62a97df03b786ff0e7a66604de5d935256e550853609aa6498af380d2ac609723f949f01662240c3925eeffd16793c1b589e25cbe6ba00bf437e4ac6c56d96db33b17bf54530480588adcb14d8b8eebf1e6a5dc796907239bca7fa2f43e1c51ceffbde22a61eed45aca9ec48f7ae11ea798c16b903ce
+HASH: 4cbc3c663ef52c50
+
+KEY: 2d2231ab61372d4b93421dddb351467a
+IN: cf9d2b12f0d61a9ed081b308fbac0ed639285ce379d6c37aa4acb97a675a13bd2a0acab0ca4f06e557aa670f7cf2208cd5e37981f4c09dd37ed4677cf8d2206f0b09aa6185d152263b8636ab1f439c39d33ff2e6ff433e3d19231f07853dc2b762f527be9eb3640af960743f2500731b7f4593bc4c6a322434dccbe808aab7f0936a1fde0c10d8aa0c856d782ffeacc3e2e768
+HASH: 832d6dbe4d2ec17e
+
+KEY: bb1310e68d324c65d8d1269d60acd39d
+IN: 8a8091da5940bf6ba16eef2ea3cc5d4728128e0684fd453f6527d330abc2ca8374004f1f10a54bfa4be8b39d4f77b6c89d481e0fee2604d402c1593dfb930e7da4d314c70d4312dc2051cd05f16680b5a5748f81a1a448fe4e51b9184e6515f3dc61a2ed6d3866d8aef1df5610c41a3515420c6d5ffa9a89f9b64a8a73f0275ac63d5c09e60e3446ee888869e42a8ba3b849dfb6
+HASH: 342bc4c512c02b30
+
+KEY: 20d58e0da885103776f5147b19bb948c
+IN: 02b2b75f81589f8948471cf477f22765729a7b75368e3955662cc3938e05f0c5a33ef9e99877a5bdf2ad758165d5ba9b3e6646eb0e53a07aae5abaf73654747a9163038ab35c1303e28320d26ef8aa56df8f3d83c10863c5364acbb8d4a11b8a881524876e7eedfe087a76d19ccb4af14ead8390327b5b878f93758a665fd330d7923c9e2d56a20fc8896bfe5a3533df613ea95717
+HASH: 2fe22c1ba3676005
+
+KEY: 2ec216c155f5e259a96fdcdb37f40028
+IN: fe8d46bdcfdb671a2eaedffea8c9a454a5556c41b0cc25d688ef101117707c14f9d1c1410c15e895bbd90170d3a2b910af074b40d8eb426c588a2121dc2d952a200c364a31dd919d7792645ac3e1534ca5cbceac7a95eeca04b5cffbac9b9f3b58e6a60d9d1f1bc7a961975b8f52532fa7ec0a9c7250cc383f6b772be31bfe86accb6d2cc9a32ae42b80dfd723a401ea668774d41086
+HASH: c0c1c1dd9b39ca84
+
+KEY: c43d26f4654e21ef41fa72e94bd6d887
+IN: 8425382252dacdba8ec398cc247232f3fae06c2efe5b19401c4671f2664bb4a97e5987e31ae32ee0004ec4c8d0abf10c35bff9e6052d490f1578054ceee87f9eb7d14e0511cc65bff05bff59893781b8a81d58ad072d8e84fb8295210e0ab19cb4b2acaf63e6fbe79b74bcdf569e35aa14c66897d98a9b548e65ddbf5e50f3360666bf3099b8bff1d7ffb9a14bf6ecd12227c504fd0b68
+HASH: 2333bacf5cfe1865
+
+KEY: 9b564f82baa0817b7d29c809f724f776
+IN: 1d3b1a79ae01ca377affeb064b11bcc87499c9b1d1ed9b953c80fd54ee703117705e673ed099d4578cb46f717bca43847c6e98c3784dd69a5298b4e93739d5e8f2c216cf0added49d4a0d9ffa38bfa9558a71b5c43edf6868b295cc020ca0bcaccabe0ac3f4d9d1c6af44e26d8fda776665d2fe26ef1f3b45fe25b4fa2bb6b14632673694f76bc83faa20636f11be7f20fa349006f5d6e4c
+HASH: 911924aacd551b6b
+
+KEY: a52a368bf2f969a1a37d793ed929111c
+IN: af15d675299a6b3a5088ba60d36494a5445d8d3eecf2225290cfbef8e86b1fb333db105d24f10eeb9b132a2bb9f5aa754d593a1bf96995a3e5ad900d9b6b2e202ed87777238ec6a717ff2323c9df32845bbe0e89a1f71e22a1b603929782e3f70e09de5e18d47f07ff481f8ef04b0e6e2c4fe6f3d76f24123083b25e15fca632d83c66fb474d649815d75139dee141c96942b4b9940d10e3fb
+HASH: a3f506fe42dbdeea
+
+KEY: 6a7f1c676caba982cdd659f531b20c07
+IN: d3a37b3f01de3a0497cf16531f4887c2e62afe87b80859329c24f76ebd3cbb1f4ddd1d9960b19308dde72ea1c6bb5b5884a9acd93c5041986fd7ba8e8717c66f675141db28098523e1af51d6b31ea372c8a7eecd9a9bd7597ec7c270ff5dc04851dade76a6439aa7b5a1a4257413f6996ce2f4093cc70aa26b0418749808f11460f02ca76076e558664115fd627eeff45241e11842f406a56f8f
+HASH: 8ea572557b7fc5ee
+
+KEY: 045745caf81790221ff9f8acb4bba8f0
+IN: 260d7c94a27aa378db8d6a2a9da9d477d4880631d5b1741810f8b56db31aa1ad16d7c6f956f4460a2b1d99405c1fd51fb6749dd87cbd0df18ac4b6daeb46572e26a5bcd5e5db1b8e8b59ccc78883ca734320b0835275fd9a8bfa35f7ecca13025b603369e449e2c1f35a506f72b24b021aff6ffed29abd07478b5af22223a889017ec4db768f1ac65d6bd0618bb5b5af4721ce797626e12e0b600a
+HASH: 832ec0dcece0b555
+
+KEY: 72bf12388602a326c7cc1c9939495847
+IN: d3fb27b0d7a06d49c5fe54f085bedd948bca58314854dae941bdfeca2ff34d17d8df38faea4945795e7527bca957585e0f461e6f08ed8575693490f047edc22d04e4a3c77a9447179b2885dfc22f602da9fa0f17ed17f83ee422bb6dd72a7eba32a85017a275dec1174dee0fb88264a65761a812d74ad9375c415d843a9ac00b7c55d8afa324ac9f29cde5c3cfb73174d95671e7742917bcf1a40318
+HASH: b4f1eb30f213d07a
+
+KEY: b01c6a8a5c49380e00bc20c6c506ad00
+IN: 127d4caa813734f023cb302861fece4f776a46a1732fb0c9131c311ff977935451cb5efa232c4bdb7cf7723f4b9aed17231444131cf97c97dd0ca77f4f7ca88663d17ed60d0937fc0361383c3d7ee9fcac18811d36b7e49b2ea6e6c36bab906a11a25ef73b4a4417f716945dad3b6f18e25ae62511d5d2ec760d3deb77b7b6a9b1b5cbc855841c883c598dbb407a546b999fc31c26ce6e94f8a9a16c47
+HASH: 0931c8aab2db6545
+
+KEY: c495ae680fcbc9cd7759e3e14ff143a3
+IN: 0c1ce9df5a85811cbc326c0746fd6ff0d1f501746fe0c2c2f18312c8c8625df7b57e0333972fd0988bfb5dc827a72321391f6c4cae82007b0402dfe4d47e42f8677d2f134044a4e12375e185d02008e4fe81c6558afec5de8844e7c4eb64922b2ff968cc3f58d0a90666e4f840185ec72f0b52514cad7ac2f8087551f05a9753b9a52d8c8bac82d598e145313eafcd44570a170ccad306b0c146296b8ceb
+HASH: 2a93ef0059f5c80c
+
+KEY: 926bf63e73343bee34936da2401499fd
+IN: 8194c53c998266d9d0c158bba46f4849b399e72b786131cec217d2da503bd8d35a5625cfe92752069d5e4f738669bf1c26029398397609bd2d5a85ee6bbd3a244630bb5ee6986680296db1aa64d1d7b9d35629d239bf31bdc3a4a3984b4e0904b034c746c8e4d6d2a49cc6597aa0357df845a00a37c924deb62b77f3a15389c3188de0bda3623168607b3ea827d62b53453df4d54ea3f2a4b6f017a4959901
+HASH: 98be298ca4415fd8
+
+KEY: de5c7a655882d27c37ca279616de0b33
+IN: e93570ef6bf14f46669bbb6fbcacf70462b7a71aeb777966a5722b3dc1432362f84b952627a0f32d6fa6be2e1dab80eacdf5dfadf55db54c2e8c93a38991fd7d728f3427b6de778e12ac63e1a914f68e7841b01c322ce2f16edd00c74b828eeeda4a2cf7367e668501fac1f3308e3d2b51555c62622275c48f0aca93d8cc8756ed9bf936ca17b6821a98a8ef21934a7944bae99362d0fca7f0c2f83e0afcd554
+HASH: 562d46195b277315
+
+KEY: d666f70660c76d1aa3cbf1a0956fbf4a
+IN: db25c93ba0b4101123ba569bbd02b72271f9cd53e1ce0a278196e2577373ef4d8a6101620e80059178c246549a0ba044de308892f57f10364833fbe4217d3075b8c1cae540118fcf1ba8fefcb8ea62644461b9e59ed12aff0e097614e111226e42615ea3bcf5f80b1f1eeb5335d156beb0dab7877a646199c55a36dc53bc4b44276b5e698df21075d4b49ddd50d0deb12d953b5f90432d5afaad25833baf978947
+HASH: c34b39849d9c589d
+
+KEY: 7a5fa32e27e1f28d306844fb734fc755
+IN: 10ecc8fdbf72e80f52dc49e927a9303c890c0b31173a96ba10e3afcaebf25167a251fc824b8699d665dddc8230b960ed6431ef13778d4e5f940db1418d794e012c6b9b351d046948faed0198e2c1603d32f33cfb43f67599a850bc7579fea83cda310a73e448178c80892b66e13fa136d0c99bd2f190dee75a8e1cb4c82ee7515993519bdd4ff3050c5325fa0d4c80534f93a432e7dc3ce04c32eb60f9ec3eccc85d
+HASH: cc4f9f646046b152
+
+KEY: e0689b5340183fb889927912068fa4fc
+IN: 0824240ab7fbff4fcfffcf200d9bffd34d7c48261be53c7a31db69ba7234808fe3db7bb6eedb30519336cd08c8bb2971a1be8e60924146eb399c31166c0ffe3033df9c52ecdb4d03c73e3cffead68159f41078557716a5aec441193ffdb700512ca60e84b7e366438d55456dbbcbe7d3ee3c579a3e76e64890976f92883e050b54dbcf3d3baf064a4e8282c3ebad12fac36220c8a132994572121bc0581cba69a727ea
+HASH: 0811c1fd2047ada5
+
+KEY: 8da46e363894fd77718a5ffe68c6d857
+IN: 3b956f2042d2b790ca3119286099ef78778248eedb2d12f70a3e26381d738a8369a4828b8e361e5b4deef28e7acebcd4bfa231548b0d94cb910ebf819c605ef37d93de7465672daffb2cd923a2e812b20a34569aee20ed20cea5f5f5c32b9cc9f09fafd58c18a05e9822ebfe21d461bc29b2017a199ff0dddf06880e385fac6505f91117db236edcb086b381f61c33d5af6d2ffb9cc5382a7cc77a7579a6d8456fbcf7eb
+HASH: 23263a4fca4dc1cb
+
+KEY: 3a0ab8c2c4de1229d5bfd41d75978052
+IN: 7d3ce1f1eb5fe650d815124a0f3a2d07220ac9bd487367c6e836b669709c9908ac0de2caa7b2e793ae32eece0e413f2f7dfec4ca7edfe7af975bc76fa84a4c9ff21da673de4d1f3c0c0b594ec0b88ad80d7f85e6aaff2b716e236a551e26cb20f3272ef4a60d46827ced1e5c1a4fefcd315c34d7611d985f2f6127ab0c69499e17de7a99fd9788c025a9991c6a1d79377ecfca18f72853192dd407a2344a51893cfcbc42b7
+HASH: 8221bf0da6c9d0d3
+
+KEY: 38efa39401d4d49225b67bd14f103ebd
+IN: 4c16db14037807b7ab4d46c057234d05be8970ce094b5be27a2346394e77e777b66e623633dfce3dfd941f8effb955e41fcbe15c8c934a684f2fdedc270516950dcfb16a99208335fdc69d913e189b04e4c60f71cc714f79a9523840f9498bf419862108c2285688db934605921d2c3b04a8db17cee417b8ea8d4cfc2bd67380a415bfdc2cac0c071845d7e6e4b967275fb4e9ea3156ab23db809cc986a0cdce5b09c12d827b
+HASH: 4f1e74abfdb697da
+
+KEY: fcfea01b83e67edf2ac9deabf7b30cdd
+IN: 105e560add4f7bf41757314d320a0741cf5659e45426fd73af9327b7408ec22fefa5aea59f43fbe0ec4a2fd0029d9779a19beaa10198084aa6c1c392a3d25518e9f77f83c7c780a129d87f06358dc90601f96fd25aacefe3f08612a98455e5ef9def288aa840a53d062d9e32d6729404fbcc48acf54784a715c13181804ee8ee55b68e0d5a43daf90fbbc36b22e786d895d6ca5b0a1241bef424052fa29a060b861c344e68c234
+HASH: 457fb4e835a8fd94
+
+KEY: d8d385fb07052df7511d0ac37d760a64
+IN: 88458bbc3d96c209e981e3b3e7845b529f373b80fecf8d4f3a5e6db3ee2fba920067733939598454548183bb425523bbf4cf9abb3942ab09a754f760b381b7105d2a31aa76210715f3e6bc6c79f62e754dfefcb0db512ca02397c2f00a169706a5e602ed64a62e307ef506e249b26afa2bf49fb4af6cd8b821a03469b0ddc4e054c6d5368bd2d69545dcb7b1df7fabda3edaa8c8c2ba9cfa9d126bca2defffc67b23c910003e05bb
+HASH: 2b23830e90785de3
+
+KEY: 278c8962711f0aded8a253e4b8ee6c0a
+IN: d10cbfbbd89f11c12183799340af2ea59c6f83465e864736dbabfe83d53fad727d0b432172cf68fea351f69261d63cde5753f8ce15afcb728e60f2d6f85e02e8089a5f6e230595f0664dc1472bcce53abdd0565744b6c58f3c874365a76efeb043804df302dcba2d7550834a443279889673f1f1b305a27da243ddacbb3e6fb60451121a7ff17301d66ee4081b09bcd50d94eaa1a087905085fb84ee6df10224f953f2356e729e53bf
+HASH: 2e6f8d56bc4ffa70
+
+KEY: 12a1dc868d6221dc01ce0b91a9e3292b
+IN: 49ef1e9f72c7099589233022f6f88ddfd33eb9b078987d92564b014312613be083e108eddfac4a202904056e6eccce225cf259c35bd2e07b23e68240b37503c5c93e1bc497685cc82986fed06c8ed474cd4fcc59630ac1c5dab3bd5096264468910f741622ee6a42c75b5bf899e64a0bead1e68d44f55b633bff935bea0ed7f9c39d46c1296ea9c690f341b30a482bc04c87520a64b6cf2c55110e9d018288bb162612ef0a55f8bce78c
+HASH: c15f435c7372dfda
+
+KEY: 04cf89ae0df7e320e9adaa73df59a141
+IN: ae5d6ce0da399f5b9fd2ba3ef2ebfb8f039e41cf723eac3e52fc599b07e952fcc9dbbba12ffd22c6ac6c7135a80ac0812f6b09457806cc5c02fa6250cfd65dbf7987ef56c180ec5c2f6312f98e9a72369156329abd461845e9b02a05575f07a08fa3f462dd0cb618f752a4f0977eca76da4f46cb22987c2e6854c9828ad7cd03863b0e3020ec6737fbb48c354a0bb8214df8d1172f42744d5d3878a27acb63dc6a004a1eec25074149424e
+HASH: 511dc81ec62424dc
+
+KEY: 16ecb48798f97a2c4a8ebe5fe9b1b375
+IN: a4a59d310cdf2c1b4acacfb9e63d2e1f1244c4cb48c665d72c79cd70453680f98f2753b1c5a5a3323b1d33566ef0ebdb80d0df3e5120c6152bbf4abd3572a80d98e583a55cd78634b555886234776e32847f8d39e39819d2dbc9b7c4e301cebd02b12559192dad2e06a5cd0a79823e46ef80f8e645e1b4f9b3803cdb6ba8f8e09c4aa9672931bff14c558a78f1309a006d2f64cba68537e4aee3002d5a9ce6a8a23503174d98e508773826c4
+HASH: 080bbaaddc2f3aca
+
+KEY: a9b3eb1c9fed71274a5cc15e1175e68e
+IN: f1bec245f19516cfad98d7262d1a3b478fc82d78df05739cf65f5e6d9ba7c986f3efc3b04c947ed2f0fbac09d2518dbce61526e9e1701e04379245d178819b1da0376d2d08f38c666434a07acb7918696eb8bd81606c8114d348add06f385c1665a783aa0b33d443e1d7277fda197165da4a905ad9e1b5468ca03fd818f0de7414a5a36e96ab244f61eae53ae9c11755d9bf751cf2e78c902377270354b23dd6241fc56128d058374b2090707c
+HASH: 4ccdc25aec4ba25b
+
+KEY: cadece55ade80e5af3978ba47a67dcc5
+IN: 1fe9e5710d3c0c773d6b8859177e867bf08170e6682939f9817a55ad53519627a59d5a9acb9fe2111db912cb0697f6452b33d478702f6bf03bc92465d7f1ef1c3a7517f791c9b80afa76cdeb5b35dc7c5a281ee75a10e9b27a350bfa7a48fef3033fa1bbe3f2601a3b9da372df9233a0ea05415d2a5289345a1770aa71f6afeac2b848d164f63e4ab6437669615e46d27102d8528d70fc76d61c2e9cdd2468311e4cf32eff087aa79252c43deffd
+HASH: f1e25541eebd5ab8
+
+KEY: 84a27a58c3b295d938ab319eaea838e9
+IN: 2f160c9df2cfb78e48e40097671d58f5d7091b789754da01fdd86e3dfeae138151a618197e7c171f29173e529a0750dbbae1331ed480d116190453b1ea8fd1fe100a6333865adba4b2bc1a54f1fc74e1d0453e7de1254802db15bc0c33faf00aeefb328f3b28bbe80b21b71c9982a9074563e3a4c23082128f3852301833cb68946b51ab5d27b8a7c92e4e4d79938e1a92253efc411e0744a4fd1d4e8cb8d171a154753a1a10bedcd310f0115bfa42
+HASH: 9c32983911d68018
+
+KEY: 66babb962005f1759419a54c3858f983
+IN: e280ef4695c8ba99cb645c0625dbe09222f3c010aa29569b54deff561ef13e59c3e9a8f5f76e93146c1ba6b99ac541520dea5adb84a23395c281a6b31c18fa9019245f024054ab9f6c0496228186648cf72ba6b01249028092b2ae655c80abce501c96b2274d70648ce2eae53ee08d60f5cf719ff96ad3379654c8575978f14b381c2c8da88a2fad32372bbc469909cd390bb3dff628822aaee61d17ff04f16e819e10b2fcf5bee6029d6b1da03f6d66
+HASH: cc54beb20f2d80b2
+
+KEY: dc36bfedadf2e697f77984e0e9b17588
+IN: 97743e1750aed90f037cb783a884de3826b789ca544cfcd8c5b76e35f895d75f37f9c15c4b99ae656cc8abe6019379f76002359726e3b144e24bff09665e6c24cec3811247a2ba6dad10d0fa2f7ae0a63863ef20e773ede65f5483460945696d4594af5090499e44503e74cba836970f2bba2407b6da628da8153334a320f272eb995095a4bc71db29f5c597fcc7341c37784333370f88d16c37e8c6e4c06e641fe103b1d103c633600d784a9dde0a6283
+HASH: fd468e0f40f32894
+
+KEY: d24f95a5b91e2350f3cefd0554bddd74
+IN: cd119df440a59ef6b00fc48821a499c61b8075a3b22fcc688c7e1f692e7e3f4bf32d388d332260a1d8d19fd08b75ace47f8d723cfaa4ba09338be841a45d9a9e6710a72c3df42d651062062c99561f64edb9b7eeddd4f16fc27548333d08ef7e53e70278ce10f96c007786a3daec7a5102acdcbb9e5e60f48ce5a3707e4b302315a191852a70d60af7ecf808a48ebb736b0d50579f846fb2cc836ee0326d37c9e1a23ab4c084e682349c13fcd9c86a2632ba
+HASH: 8285187584947455
+
+KEY: 1704ef13ef28111c9fbf1d8a90ed3df0
+IN: 0a7847cffeb15d085af98301ecdff96a65ed9bd8b4f9bab78843352b449fbcd770a4058558d4035bdc0e2792a373c663824c435c8e8b18654560c3777a4b90b39309c7aea843ed450eca8c31a5dc0186745ab445a033254c3d7e5e6aa15a185626a5c3815e5571cb1af2d9aef4d3d52aa6aa845ce8d0880d1cd4bc90cb921bb52d1718c092daf861fa19cbf9c0fa830b0d7224764d01259fb23886926b50a7ae1c5800440b2dda17faa360011611716d91f926
+HASH: a9a18028cc49d5b3
+
+KEY: 79ca5921362a58d045b88e0a295ff5f3
+IN: 238512ada01cc415f77d12dd755b1977356ce47adda5fd1a21585ff3e58da64dfc0bbe908bf12cf65dde9997d75df49f73058c13d5aa8fb43608517e2ceb9ffc723260a7fae31e642c987cc131db9d423f762296608d1acdd10a47fe8042f26952c26b92e7008e3d356c8888ad8de121396145cea0b5bc4ad38eaf2a9d27202d639319ba092b236d4f837d8b2220f97f2d274578748583ff6d9441b52fed194c781f05dec21d226459ba2cca9e5822eacd9a0dbd
+HASH: ec16a5874532d913
+
+KEY: 4638aa9c30b7f2c3cb2ec0a91cf25051
+IN: 38d6e168f88add060593c951113f8de24a52f6308fd547653f50f51e366547aa97c1b3520bf3d109fd5ab90c27154fd94ad2516bba6b248b0ee289781bf76b88c00b28898affc3ac9fa59dd386b9dd97b9504ccf7a1b2f233c4cfe091eaad78930711bd3c7be41bcbae01eda41f1eeb34b1b60d00536890fa3b1b26defee5ce964d9b6a30fc7645e5f0bdd4fa6b37ad00ac09d5b1fb494f62245c1f3a7b829132dcbd4290d98d77cc3493dc15a15703994c86ec6da
+HASH: 99f7786350012739
+
+KEY: b534ec6c31e22214349aebd7bbe1752b
+IN: 0f4ce510497c8751967724da17867ec2fa94213d7a8856fd47f043dbc98d88b29e6250981e9aee4622b18a7ec345b30db72ebda47cb0d51b602ad130e880cb4fe9added6c65bb813342b434bd5a14ce77c942c311ddf2035fe57f02ec143ea99ad88c72120697add3c4ec612084f57c004fce2cd3c21e2be8e0445d5aa808a2448f580b8a72dd05d7ecea3d0fb7a632c7a74acb86d759f16523d4e539ff6122d10b830011b0b64c5a8cafb88f8bebe73f5c7218fb753
+HASH: 2b5795dc26423d82
+
+KEY: 9f113029b93fc164cdfdd12ea7e0ffda
+IN: 50e2a2d204f8647e339608ea3577a1217846c9a4d8275c73afbbb867c1e8dfbdeb6e2e429b6fe6b99fda5e790dbce8b943bad7f94cf3d725073f55c64093ac597ffe4b05cee782096794ca3679dd261e935d83b626f56104f5bd642da423ee3228e9a15fc59034faffc516161c9dcbb55c4e881d6ff51c6b9aec9f2c836c730c152c42346001648fbec676564b204ef7e6c61dd8e20e8d7da0ca21625964f98396839f1065b0a3c11f8234fdf133147a13b26fd3379eb9
+HASH: 9ec5a9511a23ab04
+
+KEY: 77d7b10b166f5a87af290aafa2f89685
+IN: b77006843d57fc1202e0e9e8e058fb4cb77ba199d2e08adc3165463d36a2976f2e0a8a20ae95d28d0b97febcadf861af46cc88efee3d8011b7d4c63e2679bf4a72539f9dbfe9ce2db404345d6d2bd767e1c5a59919efef9037a5b6285e766ad9ac92e15e3e68f831c98ed160646f39c040539141ea91e90e7e8853652088bbbf849a7edd9bddbf5889275d02fc7cf7cdd35b6607fd4597dbfe6b615a4c8097c345d086730ff6f27435527f7b9c5b78928e390d5828e956e2
+HASH: cb7dba8ab55e0d82
+
+KEY: 1171800ebea1b1ff4d261351b05b3264
+IN: c0d61e58cce4facfb83cc1cb920f20f2e294ff8acdc5138bb0f61e9143dd753f0b6d1f7e58f2f25585fd353220061ab0ec0a01b9d2f85ac62f7fa9bdf0ad132c96543b3dd99aa06bc379c5f2563d158dd4822b4d4ec6de886b45337d29d5db1f46838d7f69c43ed425afbcec67bb6d61f76248d83b8979556403bd213b7b5af1be5d98f7c221e91a96e07bffbdbebe905dd2b23c7728e6a8229ced70da22d7f2175458c57d00d9eee6d6faacc6a8d376bcad29839b1b598b51
+HASH: 6a8a679785440307
+
+KEY: 4b6cccc67b66f8f146e2f94820531037
+IN: 4e3676a68fb5a4b0ce3ed5c82710a6e8f536cd810c619567352a65aaa7feb7981adb9072e8851908cbbe4f9cec829bba2b1ff542b6172fda4c8204be0e9f7933708739879f48a15806d9c1e03cde5892d453631e9d36f9d1be055f205f242f261b7499cc4fdf2fc2cf77a477631112643640d4bc394cae8e530741a296f1dc814a5e3882731117d213499e27bf4bac4e358b9740f14f93b3aa7e18e8bcddc78c13f0a4ed6135cb2fd70fc8affa434df3d026417f7fd8d0e3a44b
+HASH: 27984128c2833ea8
+
+KEY: af3b7b93b04ccb242d6e5f712ea36eee
+IN: f432ba2d3f6223aaf5c91832408d2e4963ea34a7950dece6b06255ded67e3a9857c36cd8d1cb745ee6d24ea498e1a6ed93b54120719c1ce5b801b77da662774ecc450946f12d5e6408f6584a7c665f85caf59a081c7994c7c34f63b1050443a59232c9f4a498d0939b181cfe51251f80927060d1442db2b8c07dcdf4d688094d5b12eee1cb54216acc8b5f75a5901496d23bd9d1115025982ee04d8285a404637d3438001043bcf8f87fab445bda616dcdbaaa642a18c0bf7c6135
+HASH: 80b732324609fca0
+
+KEY: 50141e86f8f9671e95fac62f082e3924
+IN: e14ae4e8bc9e02f5ae1124ec5c7db0f5f626e7cc67e1f5b8eed1a51468d332dd0a5704b184166e7fbbf40608dd3733845f5d0445b413ae56aaf51b0049b363f54e2b44f075da74c86e90fdf8b8dd42914fd46e56113850a5b038165a2e0dfc531791ce21ef15d800503163c4d314dfa5ba7da19d80f7b63cf903f40d030ffd7833d98d39a206d44d2d3f901df4cb7872285644e83b84b300a18a8af2e7714b8562a984b8f091c161cf63ff2a27bcd1c0fa74ab2c9b4659c6d865e83d
+HASH: 11cefb921f4e2a54
+
+KEY: 3ab1d3d853fea4c8c40da2d64ab8c34e
+IN: 3e14ebadcad09ff9550fa9a14a0ff24bef1b9acfd979ae767ce7c7f316ed56a0d77ef6d205ba315b8a8a8e931f365753c673842f75d4449d8ddaf010fa6b1e2cd7b7b7c49cc85231a827c1549836dcae35e8ea7ed914760858d52e9ec31ef4435485ba98a98e065ae13648547813bcff9052fc5ec8048bc17b06dd05047235ec8ef9eaf2570efec25933877f44f06f3d2f4fd8adf9103babb7cf528abad21431ff18bdf0aff912e2b4e6850f819f349da617626a204b6ccb28cdbc254b
+HASH: 851108e3cffeaa47
+
+KEY: c39b333d7d4a61a37122e1a399020b0a
+IN: 428352711ca82f0e4f10a53adbb7cba3eea77065db8f7c4375c7533330682761b095faf2744b17f057a60855b6a432ff18f46be9d9a6ff928b4a1dac4103cf9ff6d8a24f84c3709be5a6014155fdb66148d4c1286adf18ab325f4486210b941e48eb29cceae0301b786b72cbe7abcb4da0caa151d010a64dbd6d1b36a1dab555552dffb0acae07e4b9ec5a3a3e0909db4049293feae4351ace9ae8f49370910e20c84f6ba6b6e440439cebc0736516cb8efa4ec594eb06fe335e72c08f8b
+HASH: 4ad30b7af37ee5d1
+
+KEY: df1f17488985e7fe097d7ee58e087a73
+IN: 2ceb8b8694a92ab60a31c6b8f1ca7622ec9ec5b3c7548540feb0fa63846ef5dae7a1d0566cae8bb6936c658dd004a2ae17c50e392c37c8daef2d10494ddc7f576224e173a42b549eef4a53afe9c0de7c36d3b3f4bd34daf6d143a3abe08729aa6f9c7e373865ef8eff4a0d11c4d632e14801c6581c7bc87f0db3d87e82b9dd7bcc99b1ff883771770b12c755cdc5fd2b9697c1a49e17bd3beac0f1d094ec995fca8766fb41f0780cc0c6837b620bbf1c2be83a40c599072a0fcf660d39e603
+HASH: 7d1899d71fc9ed14
+
+KEY: ca74e3d171ff6157d16dd34deee34055
+IN: 2799d78643a39f5580dcd35258bcb28cb70e11ed8196a463bc6077ee88545b68ec43ccd0fc6b6e8e4d0788e8836554c6f071199ddaac02e81496017703800e7f30c79d420e2ecdc02ee1af6491be002d19793f50bc1070cb2c4a26c63b47c9fad2c3b5e9bf0918ac7f020dd3d88994b3750644eb15189d5c610c1068cb57174d645bdb1a8cb848940d0cfe9aa142c148890e4781753ab5ce30b5bf8037f9c78a4bd8023beefdbb00df2899a561fef1d80d33d8ebbbb87006becf7f0120f37495
+HASH: 1c2be2a14e15d1f6
+
+KEY: 31135badd1dc6ca387d00ce70579ec42
+IN: ba28ea9b2002a941a88611510582291a68b09b61301cb4abd0b70647c7118431aa435710679f462556daf91b587cff691344b8a8e5ff9314ec33b9bf240cc03995d968a52d35ce359a34d2f5a49c4ba61658aa65c3cde58ab36f46131f0513e21a9e199e00a8626741a641b53fc910e6667bb99ec2656866a90558ce2056bef7524b8c90c531f17003717257230ec9d3e56a60161db370b3958b2bd91723f1abde20cd69ff24819ddc50df63995cfbe1fb7560d24916c2e328568895b1ee7ba0b2
+HASH: 3c0f1863d7a2d4e9
+
+KEY: 039bf03ade84eca468720c36b402d4cc
+IN: 2bd881325c05856a1734e711ea05d6e541616bdf20bdd15efb684d8e59b57ab8c0821e877283210ee3eb0cf6730ca441850a6320a40eff262df7bcc00978ff53a6027ba13788c0ee38c31343d0f9aa2cda03766c6e77541fb1716345eb5c65d13f0140feea53a98e30a2c3c8c7d1c47d99423b4f4bd688f7d97cd9595dcb656abfb1ae6bd5069143d096274c4cb06be3994b7533f5dc3b942349034bab8364c252b16e2b31bb4815b6a0f0a9487865d093c22ff0b68e23d957edd4a5364f4c8ffc98
+HASH: 2fb62b186e731663
+
+KEY: f856d5b9728dcea44e15d80ddb3b6b33
+IN: 4874462c3e4302b6ad627c8ade84d1117f33599c56831b7a133811417e6993742637d8453551931456413c6382f7563b77c93bbd9b0d19704cbeb5ea5f1dd6ee7ce41b3f4511f8c9e4879072168ea0eb9a010032d1c219af6b9539124d2df8f3ab76d12a436326c210ef6929c138ec4e019e4362c0e3ad2008ad90a22f8f38afcbf2c056c22cebf975ebb1fc1de28769466cdb08ad033a98a9988dc8fe887b7ab879614fa3fa2c32967983b268299252aa380dc18c14e88991fdd69040e868bcc05cd4
+HASH: a8a18ad22ad98ef9
+
+KEY: 75c2435bcb43d763cdd27e211afc3f0b
+IN: 53383c8bad7c0d2b9f50b9c241c90b4d35db5a93e91f3b00374b83c34c88f3914cb2ae9af3d7f828022e6c096d22527614830010e495f609b860f68f7e42bb95fa76862a7309b4bff31037500c73220ff9649bb4e2b0a80cb57cd88d85f88fbe79f34350e8a28c4f9d99badf1ac1d869f8c1063497c7ea7e51a5fe96341bf7d448d865ef3a199fc68308c22d4c3b41ef57bfa185a50c35c1f3c79bfb77395eb9cdef055b26a0bad6bbd0c038300f127e74dbdd92c986818320f4812d9c85ac2b06ce9508
+HASH: c85bdf13c1da265f
+
+KEY: aaa6cf947414e4c120c6efdba9f474dc
+IN: 5f2eea1b42f0984a7088d6fa9b9c87702b5469a55372fed5689d3063ae338a8ec902301fde5e25f3ec0b533427b8443a66f0daa3688104cf1b5691e1336bc1fe487f62dc2e78ac879f5e488be2f6e1dfc2d9144f6bb688ee76f545dde08b91a6470ef4c22a391d7a665978d38cb46235bab1d8b8b0e7944280398cdc0cc643a58fc1c6c646ab1766c1cc616ff4ec2b6f2ed0662a9382434295f122c877736c3ba96632c4763c89ded4541a95a8066c5b6e30ed9c025b07887b5996439c3e13e357a49ca0a0
+HASH: 0fc0dd702f54bf34
+
+KEY: 299646082ee293d1126e514a30d75da8
+IN: 91422941ba7d149928ddb44df20cf55d71e2056ed59a569be53bdfe40349f066cffa24c1d55157af584af63558e86413e6dc3617d090a8328badef05590a6d2336ff73007342d339295981cb5c4b6cedb0808f5e48da37b6af2baf00f9e491fe2e1ec078a7046a15621b921d1a18e1671db82e56ad141725ea37bb7e736c8ce98ca1df77d1635deef933ac635e1bf178ef4102b6bc2b8d8f93d42f1df1bd7d434f7b451b33a694749bcb5e23d62a254cbed73a2273a3c161a7734cc8466a025a42ea712237b6
+HASH: ea1b7e5156f09163
+
+KEY: ee08dbf30fc222795a5ad75bb6c3714e
+IN: 082ba0d74dd5dfb47a49ca851322acab428a9fd84148e66fd5786639d791444356bc79ad9d8b9ee1a2a02d3fca92256ea04b7666708723e2075ffaf3dab7ff1ac397393153e420e8b623634e37e051fbaf5c0d48eba065345cb1d7d62304a88671043a1f16368c311ca501143f109e850a0f2943a17494be97d839bedd35285c4b7fc38b59280595db53acde23d78725a376b17cbcb06325e4b6ab5b0f8d0da4f190c5a288f4ccbb02b59b1d20b40fd8226051cd29ce948bbba97b526819dd93b85943ae6d42bb
+HASH: d45c3f2164c5034e
+
+KEY: e5fa0a4bb5313d1b481c5a018fe78952
+IN: 89effb15fa40020a554fea99be68572c03f02137fd9f2a41b8fadbdf6dea0e58b0378ce72c7e9ea4c2222b748aaeea397dffed1337a80e23beaeff1dbd9a16603ebfd53499116987660c8973752d1c9826370185be332bb1ac482baca01cc566db732da2a806cd6b7d2343879d137704414ff4dda4d82abd82cc9c5a6e5c4b6bfffa0995ffe8b0331294490fd05c675fce48bcf1480d4fd0f27bbe5be94b8b9dc1405a6ad757af9f4eb4b1d9a2e702bede28e7c7ed4781d244c2604c1e8ce8ad5316726a3c351bc6
+HASH: cab01455abaff8c4
+
+KEY: 1cc039133a9fccf3965fe20292a9a6b0
+IN: 058962e5b3328cc3905371467abe8123facddb62a59ab5f321f6b3cc8c43f58168ad8596a5b8ae47a18f327a89b0916e7b5df23dea5f7917773f4bc2560ae7b65ec57a4de0f03708e8ae7e209e5136f196e6a731f39650ca5fcd35355c5e77a8c3c5b31ad7ad1364e6ec11a86d26a9aabf0a4d1d1cf2f3c6ddb399fc4959f2662c0dc56657c55e451ee0e7baf581f2ea73ff843d42e75975e06937ab4239c0d6ee67b4ff101cf6350e0f8c73ab63a590769ed282d11b976a92eccc462a58050514ec2c42f11f546861
+HASH: 57b4c25b6cd917ed
+
+KEY: cb47076e391ccc17998bbe98bb92e790
+IN: 02a2f35de13c06d3f20322cc427e8dffef4e99656acd8c9a6ab28f3eb9f65d41eb54b2b5c7ff8f77ba42a88c1dbe02195684c3149c1875dfb3611910245c734479936a94aae8e337956687a062d82654ac1f688171da51331bef1a180975e4f7b4711d587033e6b7705fb44fcd4d746e071c34a2c88fae2af09f1f7335def555f809ba8c85abe1af21ac4e67bc30d9dd81cc8a453063286fc090c6a08c2269fcc48531853337dc9707811eca54ed466f8d7996c46b599b6a1e82b1a39d3185f98c7333c87fd4af5121b9
+HASH: 3b2294558b1e0353
+
+KEY: 3fa82dc1ed52e4711cbec3305346dfdd
+IN: d79baa661ae8ad2832adff360b38a1779b580f6357eda6b37911c4197854b5aa7e63149356e451c57480e1e149e2627a627e60c3e49b60eb1a1e5c690dae76c0d6a7112e3d19f0e8041254d07ffe4c21d8d931e889b0ff6710c5a7462027cc7e67266a332d596dfe6b854fc00888956e58f7babbbd3f8fa25c2024376f0132c78faf6c9559b18f32cc14e97b925080070e3ebfdab0f81a7b05d9d597329770f778657facbf1732f12fdb080e220960b7835717ef9fa58f1a7b535db36db9f2b944db2a6b63e4492c73b335
+HASH: e867bebdc0e185f7
+
+KEY: 4997e7ff91d638de29f22687c97f9835
+IN: 568283eab6f40f158e5c6742da6338d402f4d05cea50d6e18da7e3b0556edf1ca5121a58f44d9c86066b917b57cbe7f17f497772be54022bc493de67201e60268a23d3f0e8024db58eb3b6c4805c326374218374c5927d60069c61e37777bf76c59f166079cfecd65c4479e98859c514780c07d7259d2b25575f56ecc936976534537908de6e4d2965ef16ca8af35ee21b937ee17a8a125fab11a0f59c450f4b8c3b5d1c5035ea73c5d71ce2ffaa1689870170b4414bfb17faed8baab239a504027a7770782c8e6c25e02162
+HASH: 6f9ce421d15fac38
+
+KEY: 02c1994ab75a1dd3b6c46791272f8265
+IN: d5a7e586866fd0bd586e76e15fcee45323cb40ab0a7c92030c07d9ceaf315c8e8e10c20620ef20b365f693c5b71011e05afcca7e74f2dd92385a6fa9bafd6ae6e97a3a4195aa50e75a223573f5cfab0ece3183f5ddd9580607323bb16fc819b3cf76e0d4147830cfb7c9711577707bed8a224c806ce062748a20c8f386609da289827be50904e960809e0ce76ff4416a3d9cf82b4b5e3c96ffb11ffff6d846481f2035029d3c1add9be3232121d3794ecb1bea6058a299f099118c20be4ad5baa4e67784f92feaaadd54262383
+HASH: be78e88292bdb465
+
+KEY: ac06026fb727ae3a1211d369197706d7
+IN: ee203a0e9ebf6898bdd645057f8afb27ded7d7e9c1660f3fd27f4489cdf3286a79b9112f1ded1a878db6336144b37a591ea074d6ac358da8f08b980f4739facce60e0685ccc3d939255044996e060b082e9055c66a81724a3e2e00aa0d4779bdd5417f23a8cb1e8364c0404225700df4a06342db47cbe185629a8b4e14f14c24a8c6784f9168eaf4f0ef312ebb18d6472d63e748d0bc2b64f8d31035bab3aeb9736a22e2b3b42deb5d97a3c405bea74049086ab4621658adf42293820bc735bd4a748677f711588c5cb7986c6c20
+HASH: de3a48d266533d5e
+
+KEY: 93db71499f0fe1bb7035d7a4eac9cfe7
+IN: 182709207410de3757d35250ce4483017e6f1e8bed619dd367b9e2761efdc1b538006730fc67f57919bb47f9022ecbd88db269aea43693ac22206c4223179671155b7726e5c87d7ba5abdf38b1d1380bac5a9a52be718a37899d2d35ca1da2276a42d411a7c54a2ffb1ad98f7c247b3fc48b30c54a342b03ed474d7ccf18e3c72c8ed3b00cd5680ea8a0b8bef4df50f12bb6df3352b8c7b10d0e6a4a2a9317c8f25b9a1340356041208d7bd503bb6a66de10cdf8775b56b198260bf964720811933dce95d39205ff8675c89a321545
+HASH: dbbfc5d600229e64
+
+KEY: e8d3571f69d87217642056d3c53b5f1a
+IN: 3a00d589366a4d245980592068b36e1c4b9125cb93c8db5f4269167eb99dab924ba3b95cd0a7688bae6d3a52e12c463e904a4525467fc46c31e32a1681c0b0a08c1738caf7beb944c676406fe3eef522a873230117b940642c651a947e0edefda4a9230762a12cfbb4895293c4290b8be3d04ca14307ae885f348a25b8f61addb5b8cbc942f92abb872769d4c9e8e8538224271bed35d767bb77e1723fb09208412e604e1fa23b6cc43c137551eb9a963e0b8c7b622f454a436023523ab27aecf545768ab50fb34c6ea4b88ad704db9b
+HASH: e5d08edf17eab577
+
+KEY: 749fceed424ea2c74ec4418aa5ad47c1
+IN: e8b594f46e60f77f4596e2c8a90ea2f042791fe19c3ddb9a69740f36cf25469605a0f16065f0cf223df2eaaccce329c25c91108df87ed1f5134c53037d980b3eaebf94801389802c32e0866e2f499c832358c3992786bde05779f8eb8c9ff1c81ff31e626f9c91e615410d5a8625f4f37536059df6363d7087d2f47ba9c599528a7115ce419fc291b006e3dcad7c61840f5900cb4687d8f9206700b251c5a6420fa693afe05070537fa87925d10d5fee47d246d5ede432f43299e823ff838b8a6485cddfb5bfff149823c32688a039ee87
+HASH: 6bb82b5bc56b8451
+
+KEY: 1a5ea3537ee7a7a2ae0a22001daca94e
+IN: 76b8bc81bc9783f6f8a7e85151baeb8a62f8ea9b6a7a6aaa51bad995af4e0369f8f61b6e6475da34fb00fc1940f32f2dc0b1ab0d0a2a6f04ae9480f368e455e6437d2fbca09a5458095bb89f6cf36a44b5ad3ae88b34191ac42469a3c28e9d8d29ea60397d01ddf11ba80dca571b7095bdd08737df05c982b3a115bd3a87a7edd65d988ddd09217f3f31386676d6ca6592ae707e51f16849444bbdf3c2686763e4a7ad2f44fa5959d4ef1579f5ad937dba6c39e4cac0f278466350bde9ce81a9296c2e422f79add65343d8a67d9732d72e10
+HASH: 51a33c222edcb979
+
+KEY: abefde6166ad815d7364b65e911585c8
+IN: 0a7c0e36d9fab03b4a53a627d25262379c17ce645d6dee8df4f333d512e37bfbcdd6ed77209fff0897242fe43f57677b246c4b19786f9ea9e00191ec25268b4a1ee715183358570d6b45a93dbe2defe8f20bf0142c2a1090652c14e2bef6bcd5067dab3dfa701199f6d5a14b363492b0a0ffb6516c7eb17953701cb69b78b290be408f23b9ae79ea9b72d6795d33b85119ca5c6fae9f1a28311e80a3cc652f941b0a94172bab5b2e5f95e23576234ba3dadb320f69752f8bd6187934c10238817c11724a7040f6dd02c2b69bd48eb1dcfffc6b
+HASH: 09e933b789e7ecc3
+
+KEY: 2563d2eb21e7a757ce3133b6e85086ea
+IN: 568c4781aba9925979d203afcb6c995087821486609d3fe07296f541c497ac43cb00e44610ad841f9e344d1ecfb86c2b27059120a68e1249e00f8cdb0d04612a8d4ee9a0c59a03fdb595e567108d6daa3afcae8c2190e6c2c29ea917361b9d68e496016aa2266523fc582f080f16fbc8c876ba574fe1c766291e378e3d0b79705d32a54ca421fb45b9e27fba4e2502f82c422dc0862c00cff5f309eb45c635f1ab89345ed4b817a0cbc32de3e19f3dd5d69c209873fd001f091c04080669a8baf5f6944e00c6a0455dd8eea0b73c388fff64b43c
+HASH: 19ac849b2fdf3f69
+
+KEY: 4129341a51fddf4cac0364142b916150
+IN: bf02d01892c4a2f1d3f7d09945bb8b8dd2b0a363feb64565ad4ac4000bc54d3d564e73e429e9c5ae94147b85fbf532d253af6fa867c155467591d39a35e0b68de5775f53f875ebb9e363edba916c1092909c15c745f34acd6d6ac09e0d8820703aa9ac7cdf141bf40a87c9d1563c5d8d17ac6cf7cc3de6fbb4cca5948989cf292a582a836b6d293677b51e015cade0491134924331bf94e3926c81e84817eff7877d5432be95fcdfb7eeb17f40ea151ea360cfcdbd20e69b43827936ebf5ce8f9065091584d79dcb5562a9da7bc3378731e3619be4
+HASH: 388b701724a52e01
+
+KEY: dbb81916db9909090e64fc0e23cae7e6
+IN: fcf78a3867f7a9c6d585359e18e67bb535c5925c8b2c6cdf9d08d1043d0785a55c36dbdd6c8b8d33362e84e8156a1ad4dfc63f18f003a0719a51e83928e79b703b0324c26897c6cb65b43c25d396288d23576032c7570f54121d55bc58bf051ae1c447d94860b796c259e24ccb3f2fddb8069537415a173486e09144837be1ac318f043de66ac72cb663cc5be42f6bf0e243170441acb7beff6c3070b54cac5712d4d95b330ce153eb4978112daa11629e214aaf9ce4d65cac7e5af9508aeead765e0b305eec25d7cacce52956cfb3258f246a1019c7
+HASH: 54e52ffe0cc75930
+
+KEY: 1c4d246d801bea4ede8caa08359a3052
+IN: 06c1b4fd79efc017ca2e7951d2c5b0351e66203b38b331a1470f820e899f5bb740425c8beb3aaef9e5a3aedaff0561b9cd18f723730efa44b55a4f23aa3c9deed14107517e2849b014978716d9ba9fe2b04782139ddaf7e711695443c2bbb18c4242af5f92cf8f2ddae4007f6c7f7c072c5ae1bfc010e3eb843392d139660cdf31b3367e24fdd5da452211fba2398a52c3dbb63c8cf7367eb7f5fa5401129f5ad707d7d0bdc8d6a551152050885ea17f58f199e7db9fcaa1784ba0e7124931cca9bd60fa45df20beaac79529a7bc16fbb0da3886ab67ee
+HASH: 13b535675c3671c7
+
+KEY: 89593db764c35959b384a1070ee77a82
+IN: 036dd2e35625b4537eb938d749a732471a6846496f98c8d92649894ea39df2d6d480ac36a3480b3b9725a5db9610a48696e843fd35f2816b7544d38eb7113f0d67405867b7e96a5159e7c2815c3e5f2ef9e964ec288a71d0a85676877db10e137e6eaa9261be28972f45c41bd9aba896fd8aed4fddc416bd90335bbe5254df22a6d84f1f7043a9a57f603c472007e58fa5287266ec2ea4173064bd0355f2cbf705a37000e71e790670b6a6f21e8676ffc6449980b94763fa6c7f267caa6c674f9ea7c2a7f72b9abc74ac83d199d77ef9d9d129348f0f6408
+HASH: 9edbfd9a1509e0af
+
+KEY: c26b64248cd814a5cbe5582ce0877f12
+IN: b2d4d37c49a0a9ba827d65f47463e436415e6e0303a703e9596c503ecc04e071fad32f8ab39b1a86354cc8b0327333150ca21a8b7981f58fa94336fb314cf76541f0a9a1fb88b5126c088c1c21193ed7b601900dca8138861968bffcd5925795ca31dce36b850e45a86085cf9218f2130355a86da21d662f37936f03805d8f7a68a6ba8e1c13160919dcf66d176725ea3a83a7284db9f579d792f855b21cb2569690d7c290802dedad31895a9fb196b17c681dda835149629c0b6f920d39369383756ce4cf3a1e0abef2c071a0894f95284aca453dbe7efd3f
+HASH: c8f99a471202a47e
+
+KEY: d843a02999da4224a30a0adb34a80268
+IN: a3a3d15d413fa2f3310998ea550afd2dbcabba608513091dc4e843421960a29698731aabfea889af810df61ce468e826197b5fa275ab23f6908057d8eb2acc0f8c81efc9685231a198a2090d30775fcbeed564cde84f84e4c7e524f16fdc6a987814369e6aafd206e7428186339d959f084410e41c3bced089e948dd2a0158b5931c77d95ddfc7197ada7d3b212a566b7dd8483214a7de9294fbaf78fd00466650d1a244dc0c507101f05ce6d0950af15c51d21c9ae04a93cbab01307e3324d4b834252c279c28f507006646abf701bc9cc403ee9380dc8e9e2c
+HASH: a0f82e3e201341f3
+
+KEY: 5359880d90b305690c64bb649086fc2a
+IN: 16eecf2748419995e64f241eed4d538db76e06f2e4f8310a982712020c02765274b86026c6478e887a7f557e56af5e56d43ba416603d776c9342244425a05230d5ebcc501f4ae8fe5bb86f8e992ab4aa082db872395e320826e67c606bd933d803afdb22ce3bc4da0d19c5a539278f0f51ddd3ccff495e957c3ad0e08ea8a70f3083f40b9dde9545cbc47e73d5cb89ae8f8351ea0ac46e9aa7fc4712adf5722a7b26886b6b5b594fb05725456c6d948e58302cea70ed3f2ae704215216966d087a366723b7c600977cbc974c027ad2d9409c94fd9988d819735675
+HASH: d6b14df3cf4c7f49
+
+KEY: 0f0ec13184dbca8f609961b6b9fce767
+IN: 204174a8e023de7e271ac5db5fc7ad543293b4f17e1e648aa605617cec55abb9e53796d747fb13dedde4416321442fbeb94e0afb5db024dc3555b7d7d62cef09b3fbb3f7f3ba3ea5369984818f62cd37924c2089b03df6b73373e6b45db6202f7ea04931187fc8b6bc395dddf1a9156e91073bb024ee7b8150646b69995ebb47c8f513e0840ff2a4da3cdfbe5ead7debf930be5fd08ad6a8c839677a92bb6d2c7ade3bb15a07ed1b935113dadde7471fb366aac6b3d57c425edf0117914f76c51b8b590e16640179f881f784b909afcf79744a869368b7318d47ae92
+HASH: 2eaf85ca296e71f6
+
+KEY: 26f952322129ad96deb1742c4cf02db6
+IN: 6f104f1a7dce93e9387b9e8c5fbc819d17b8912d86e74ae98b499ef9ea195e4a7bef3ea9920b7f250c69b77eac0f9c9b6496338b978f6d9edffe36194a450920435d608696e38dccfc6a95aeb48d184d7bc98c5e94a0b60edd1125dbcfc34846384598324eb20b037f3394215ec67b8f24bc7cec72207ed36ea3499611a67a1673e62ff6b269689626817f7a2283e5756134189d9aa74ebdd2527a28cc9d5fe41b7c43bf25dc691cff051588adcac559923847c45aeb4e2b3d193490ea4bb69baef9d4e9df8ed11ce2ad1e59bb0788d1c4ffc4f1578218373a6d28ef73
+HASH: f1cfda7a4f7e71ea
+
+KEY: 7b0ffcf9df3ac2b8921489d0663a6a41
+IN: a11ba4969f1e542b4fbf785b1d626a02a98244a6dc3cbb4b443dbdc700781abd74f27b61af16f2bbbf7439454ba11b317e91348195b75bc72bd84b6855552befb8bfe2c02917a3a8392d689e136835edb24a32b973abc1f2ce2a12e09d3cbb42bf06a73d91df928894768111261264470b58d7053ec41a0dce1b752a8bf4e3f586a41fef78e75dc34e0842db1466a7809b41492b6f65ae1e204f44861fb0e9ca8d315b0731a476eab1a6824280862314c45653da7a9f5d4b23496aa7d0d78162b66d8eb0350e1f163210c99f0e1b63401eddc576a8dcc6a99b204f21c161
+HASH: 0c6d32d8384d66ad
+
+KEY: 127e0b96527d4f78bbbb561ee2f94216
+IN: 883b47f04789efa9dcb466454c40233e4f4d1ddaef746069110a98f023314bd24f5a83c384ca001fe4e32430f7fd16d60aa674bced1f782c9d3f2b9be7ae27eba152316619fdfd9344127f3e95e65ce0494ca9a9aaf86a5c23be50f2cd33fd36431b44e1b86706adb892930be8a5999295cdcb4ac4a7a298f83eb60f239f7aebb2c2bba0cd0cba3a78431209d16517d9c856cdabdd08c0623548a54046e482332e7bc980de6cabff201b0a3795b29f178965009fdf297dbf6826e4cfb8e8db44fe7c825f2e22836bad8f5cc6feb414d983d37a4e8ea9afabaa636181b0f2a9
+HASH: 5df3435199d3acf9
+
+KEY: e245a39e5f7900351fb68b658dedd8ab
+IN: ae84ea795dd308604a72c3dad429ed381664c81f5ac779548e3a1e5b06e93a704a53feac37e62720800bd12fbebaec2e4bb9aed6f5455f7a9625428a98658a244d05e24365f38e984b08f1d8bf7956d92c8dd384eb359d8804a4117e3dd3eb9e77dea725578af70e99fc04005792b2e4446f5800330a82eb7ba6c2f73f8538a5deb50c07f63ace94f924de49aef68ae81dbd8031f6763d68648a4579048944a154b2c4800e02d3cb283678fb28d8b2a0cbbf7f67735263f82b139e0c91d333a9c2efe3535900b7ffa3916dc737a79f6a7cdbd93c928c0c1f3c6a71bb6b1c0faf
+HASH: 009604731001be85
+
+KEY: ff848cf4962c85efa5873d63e0b0676a
+IN: c9783e9b655458a54665ce596f2f507ee53711e724b2fcdbb6a00555e782b2874f723eb9b42c9f05f81df2e400394c18f9520266deb0b39b3d7080d2609d498eb14c2d549fd5571abea40967901ce78a24510d9725812a099609bd23e8f6e1f9c8065a36af3a47aa6b0e31f4a51a13dff8a0b015c556c198915698ccca62093570e9a00fffbb9062411689aef2e23c7ca2a00637ea413dec66cfaa53f6ed0eaa1f7cfd047eb62291418f0992b4020b92574cc1bb16c8e1e0007e84f93f348d596123c0f762dc88eaee3e5acff070c3ad765194c65df5e1a09a29f17528886433bf
+HASH: eaa52c9d937058bc
+
+KEY: 0edd6290e53e244919303e4caf5b6a85
+IN: 005cd6087242d71b5c2824016534c2844f8aba3aaadc2754b5248d2ecb19b55894ebed831f2882485af9676128d7918b8360ac5011fde984a712d4a2966844fef7cd106c64f55ab7420f85b61b6be4a29abc8588f8a88980a61fbadb8501abd4b8713ad9064cad8089755eedf9ed3c60a920b0755a1da89104c95396372468ea9f58450e82f8af1129de70647b72fd3f5f8c0dd38546e140ff2c60b364bbc344bed76ffc7611b69ac958b75b626b3b1ba3334ade07a1b651b4c16877d9e3fd03872d9ee39d3553c929687b31636ddb09eca10dd4b780bb7454aad77b08cf261aee58
+HASH: aeb231e3d8dc7929
+
+KEY: eff94cde18e3fb3ae08782d7417bb658
+IN: 4cf8ef9b0f992acf9cc2616eb3a37a4947a8ba33332fe3e52c3d574c60f257f22173c03f09d7c5dee536c9125516b834877342d33ff351dd036726cbb62cb9aa711a46023c88f912f3b9edca4d0062337c54b811e235afc1f53c3b2ad75af8ff680e17fac8edd9af5ac89ed27d6a2a14d03e2cbb5e3b9c600d3825ba2b1a7363fb840aa29135a07f784dd28edcc7f2999d00c7b192f1dfce15b253e3cdccb36910f0074518b46d03b22c02645687eb19ed0f35c23f21b9c8ef504d1f14cb2ed6e8020f56bc18a0b301dffe80b8f10b8dfc5ce2ce6891bc730981f2decc8462b7238d4a
+HASH: a252d05a57be030a
+
+KEY: a98c1a0a3d066da10ea4dd2531b4ead2
+IN: 98ab505e3aa1551a94a0cf971d3263ab8b65136acdf76ff50363bec0c44ce7e0cc7cdfa157baf16798297cb5e6b8da1bdeeed67f17f8ac5f25399aead8323cb40f78f3e8aa57af8fe42a0a5c54bc40263750a20171b485f9528c6908c081bc06ffc370293c286c105fb01b4dc3a748e3ab0bfde3deeae6462577a56170d735aaf91bbc47df478f7422a1512663b5cfa60574c005d4f42a944c5a9034553ba253d4a9ec91a4c13afbb6fc2a33820466b3566e48242cda460651cf05d2bbdfa6f24700971dc8423e697aa50bd74f6b8517e36be876cdfff785f28fc4057f754426cb63f06c
+HASH: d2c20d0e5079c609
+
+KEY: fc4f980ee86aec735579dc60fe34ce08
+IN: b04be0eb00f65857e72182b6c72e441bb0fce911b147a18e920e32fe4f68b34c401ee1553cd03bcd9ec5e04030e61c9e652d1ca028bbe3e58b1b673748b8be01c135a210888f2b1729dcf430d3762634e88282ed383a2c72df11234f3d6f1c664bece2789016020695f64b325129fdb41aad72411546219708967523ce612c23fcdbb5bd96d75dfda8331033c4dbc6b4a6eeeefe3981913363420ba4e1cbecabeccb1caaf737ccaa3fc23b7bed2d19e9eff329b5e5d7c022951eced50fdb4581b69dedfbb73dfd7ef0f1a2e20fac4cd945a62459bc09499557651215584a2206541a874d08
+HASH: e9bb0be3d8c71cdc
+
+KEY: ffba67ce7af257ece6904cef96d30c3a
+IN: 59db9d944785bfbfab97d99707be5a2bd6b73fd5b566d030e6d50066d41eb3999cf4403f41a5ce5ff43e222c72b5f55d9d97e4e7841ed4630ad11eafac49237978bbeda43d16a713443f21747ad304af680d188fae4204073323c41b9a2ed6ea43649f9155f4da34ac26dbbac19823cfec2916f51093cb60e422949c105b051e1344ec1cadfbec60b16da6ec4e1704702f62bd312b61e572463c9971f8f4e57a835e2e13c938281d5ac256ecf525745ea6a9a31b4e3057bbcdfde132745976e6f37d84616917091bec6c5596705e87f931e1ffacc81fdb16ef5d0cf2395f01d8132a2a726853
+HASH: 9b41935860e05a2a
+
+KEY: e7b8fb02585eac064789f1bcf136ec3a
+IN: ab7721278f866963614de30544ccab492095c464773362883aafad7c6b314091edbbf3b031ed77c0bae0280bc0be2caa9930102dd42b89812617234fed1103978441b72c554c8152af11cf0cf610cd930baea47c50ad28dc46650fb55f5d82257d6fd070f7055f488915d5d39e2d0ee5fa9433ef942172d044f2caf9a2dfda8d4db42fd8099fd9427992db51570078943ba5033e2d36f8f5cf6f8d5044144c23bfed90f8f75bda025931e92f75ad0d2cf017835bf36fb77deb888f921867a1e3a3e39ad6d0166dae4985871b0001bec49fa62d744d20a87735c737b1860d5fbe068e7e1ff31787
+HASH: 65913fb8e4f1ef6b
+
+KEY: 7d039acba5bf658f2a8de71fe1f2808c
+IN: da6da5c841965e182434279138ad525350dfe97aabdd2a6bd0e9b3b558ee366f6b4ebf993e8b8883b2dec949ab2dcbb5afc987e86e6f57ed1c7f87ce4f672b7c599c7801dce3fb3b554f2baf70269916e82444c93ed3bdf32b4737574794b4def37a289707bbb4bd0b2d9d9c0350ce6ca9e8c1ecc60eff91e2dcdc6acd20595089e9971eaa4a486bf47c31944c98fecdc320ded6719276c29f1b785235eb90c1d8548dfcbfa1eff50f0eaf67b60f301793334feb02ea4b7ba276b870eb309f67a0c1df94a4695faaff50a34802c0e2697df2777e4b56355e02fb20b0055f477a4c8b67ea92cdc072
+HASH: b199822f1811ec96
+
+KEY: c54508b0162a1e8b0f79394dd51d3cbb
+IN: c72b4d57526c9a62c225aaf4b200cfd58ffb6bb43f081582bc2b7d18c722f83db54718509f22e3f561039d6ea67087be8da0ded3edfde792e4aa4e6b28e3f5b92634e3fa0655c9fcb8fdb6463a7b94952c8ec26d1b36363022ae29d3befbc74675c65ad84105ee22aecb291ce239bfd99e6472662b6bd211667079050e4795a52a66a690e0552c4649df2a872eca47a9d9df5eac593d3db636c0eb9a382d9e53a23a2233fad1bff227abb35ef0081e2d998b7369a3c82b1906cd6f089404624ff98bec2d3086dc784399bc31e91a8c6e1d1b9d5362d552204fd4220fe950efc4347e92f97a07bc5169
+HASH: e974a29e63624bfd
+
+KEY: c7e90e3bdde2cea53430ffa4d24b864a
+IN: 024c6b2a7e1c73e343484ee28f186e4df5209c1a1ddb6382b32f6af94a7b92f12d1d4afe1a046cc47fabff118b8cf4d3455b8a9bc35851a12e0d8f64b7b2aac84f9d31264955c0098afe2b41a461c5c05758cdf32b4a2bd7d9aed617c443afcc027e44639504706dc2dea1437772dde830885d6b9f8027ece4a9b9fc94e636b6ad2d54345bafa989a2210215dca60ce1ffeb7d999001bd1ac947b46e88bc024025241e1275a68f04046fb2c67850fe73b3d968c56e6f3e10846a33f088cf5576c8819d983c5c1d84e9e0eaeea4801d4800df0d575f4bce9a4b638cc46f546c7b18e5c6454f676fbd3425
+HASH: 173022c25c47bbcc
+
+KEY: 6e2d1460bcf8bc398481a0f14e807ce3
+IN: c3928a0547e7e1157e794f0c01be408412011794688fb8e0bc40508834e92f65930e91c3be3bf16c4f1980aa9261ba332e1c858397f3bdf0ae7b568730d6e294e351656f0a3e266ff119ef6dde49f6b4e79428bf00d7fd3a984f7b0e58272614e5d44d8f1babeff6e6152654ac264bd5de4d39f33656604f32576b7f98a412b822292a838e6a028cb6ebbe76c393f413c5ab635d9ffa568e371169d38e9fae4161055d87e2337fdbfea5f63553122d95d74acf9059a23bf4c5cb21189facf61e307adfcab2ffe9a00acbd7c8c447a301006863481caded3b0ec5b596ae1a394be98dc12454f6238fded0c9
+HASH: e91f147c8967bfff
+
+KEY: 92154b863e95c4b049170adf959758f4
+IN: a24952cdc02ef6ab70e588ac34dc10ce71b79441e11c601e8814631da2c8e0c53b757d9d8642139643bdc9a266d957d82174b160d56913abb72d14c9c3ab9af9232dbcee2b28949906df9c58d31aee3920faf7fb15f1b5047d88cd78d3d4f337a85dd7aafa745cfcd367ade881ee88b9af1835dbc8ba2d2c9ec0744937eb287f9d81959adcc513d180dc0b10503a8fceac08c51e0b479d42279d743887f51c7d66f4a57953f5123940818ceee6311b88428e626f1ffd411fb956683e02fde1e1bff712ef61cc0181fca7a29630de7954360037ba7d9d3ee2129ae6572321897734b1082ca8d4478052a5a1df
+HASH: 6e651cdf9607d1b2
+
+KEY: 5e85e11e3aa3b90a184e2024fb78a2c2
+IN: c3ec1bc51d40d05304ddf0bf8bc7b65537cc0038971fb18786188c7f621cabb636d27ceefe189483d6ab0e5ec88965ed58d32abf344478d67c2c194dc8748ed5cfecc3c47d7f197119c92724480e5fad420a5c80526e5178f25298ca1676f01e45cb72544d4f8715f3e495312fef1a6b7ac3dbc5703e580e159355c608ce6e6f5ea516d5c0b62325be4533fc595793c37ee212dd921b1a98247cc761fe3c0d9b8e9b031d295de0a910120f6e6c88ad01ae2c6c14d3a24b01da0c7ce0ad98e94420827b4f05a52227330d5612f8af1283b20cc89f3a1dd66079b4d5a42463c94b472a96b2f5380c8ad73aa2c897
+HASH: 79cafc4c1f36d921
+
+KEY: 3e9d8c681b3db54b76e7e788e3f9fc77
+IN: 8f76eb5689f2651a493e9b3844915e79356ace323dcda0971d1b73749d1fcc1de35fb610790a47d2c694c8772aa66aa1f960235a8fa1ffda2be3a218a0e95158f0833afc096301e2a471f949a35a011b7fad96e4df097507a8b4f26534971ab1c7003fcd49ae2b453f85b83b493877412f6e7296ca1b41684a05432f9bd127c158bba9e1961c027c0e718ccd130176da0d846fec138aab4be2f2a353304e20e1e733802b0a78bdc120d5e67dd75f1a592441907d5ad4301cea73da09c85e0098648fd020812dc0b7123b57bdd8f43eb330cb7974e3fa304261b6cad45fd8c18f771e46150a37a97fd459d9a36425
+HASH: f6a64307c9821557
+
+KEY: 6890a33ba93b36f9735b14da070629bb
+IN: 8b999ad629181b9d8fd808f7573a015b621243d48c894ac3083c3f01ebd793a9d52477c604046ef3465160a53e1842a4a0ad59608d72649a88be0fc798ed6498fac2927857dd35612ca3c3c457a6b051938d30a88cdc6f4df013e038681610b6c418cde5865c59c585f0f85234011fb1cd97b4d845b33f42cca4363aa49d4a72b481984018000d92d15c9f1d37d474dadcfa7de5c6cd21a68ea3a8c494d0a61c1415dc2e831f227f0778088e70b57cb8e2ee7d4111f9f2dd5130ef964894de7dcbc4bfe4cdbde5cb5ae97006bc7f12410d1610cd5576f4e4179f9bed7f7c8cdbb4690d2ae351b88ed047ae38822512
+HASH: 761ba884dec4ac03
+
+KEY: e78c745365b61651ebaffe8c42d46f6f
+IN: fac2039fb07eea055fe1788f9002ebd278decb65fcb642977109ab11d967544118c20c28c0f221cf97588e709b0c1972770a1d9dd90e2c8327b827369c7df92ae171ff5ae67c3a473f4149d886fa86d306039743b3709ac4bc03bb3d4aa6ccda74b5eed3b1e6206e26f113458a442a2582d43776ce4792d71882fd03497eb1dc4368df563a0e81cd2ec4fa0b07c7db846af0dfbac90f36b366cc5c6afcd347df9267c50d3d7b69bc5b2ec686feee2adbc2aade8cf2b7dedf70dcff62535cd37c9ca232797f515cf8f2e18bc1d061ed3133bab326988fcd00903eb3382c5188257823ce6b19b5030532c744ec29fc4c6f
+HASH: 302751549cc2415e
+
+KEY: 0ce7aa35b6a163f25444f387a2358e93
+IN: 4f3c83824e76591565f4d76e2144fbf598ea40e7d0f35ab8dac089548179c564732a8e9b047bc710ceeb514c29c8f0bbdf29b03203058aadea827e805b5b06b15d4fa1be0d2911793cfefa5b77631704081c8e0473060819d6c0a8cff55899ba8715761ccc01b6771b69ccf41e565d14f1857a17b57da20e5437a7f854d7950d659476ee55368c375a36e178097d3c36d3453df382963301e0759c61b470d14aa5744e6cc92bc1968517ead59081885baa92117737a724a2a5c64b33cc42f46dd7df800148b104d82e347a9872a8b42f7f8a24c017960569d163a21d1d99306c19b08d1c7447b56f0b1326a5983e21b51a
+HASH: 2583827c53bdf070
+
+KEY: 0e1a6d15be1f656b7a21a344f2e0d314
+IN: 7514736145f300344d473bbe03f1190bd65bfb8756ca358e2b9004320b1b7888b37e699c0b31d30793e1041250165ce39d1fc4d88a4ffd31b8b067436f659b762b48008d1508e699dd652c73da4557dae02d2199ec8cf489e2622622cffc36462fc663995530d5184be2b2f2bd107da854ceec4bafa2c6855c4b642b36cda7dd71e155757730b3fafadf0c974cff7dd965127349d7fa287091480e774e442e885cbaf336fc5d6713371c8bce55757efde52321dad605b1dd0b8760123d756487b2c061ec33e339c55796ed9121e9d5090041065a3a4adaeeb8da3af3f11cebe81a216f2332b9f9e1c1d9cb8a288c54c6ae01
+HASH: 023f403d40ddc4d3
+
+KEY: a79f2d6168912675a055766b8570075e
+IN: 8b872f2d9cf5fe9f7b9844e817fd39766e2a86bf6a463e15198e2316bf54fe6d9dac25f642d0fd828aa0ff836c863082165cd44b95c40139e86165281ca264333b470fd3db7887db448056ecdee005423ac2f1444683456064b37007de2143dcc5b75637f4043f1841ad8324b0fbe1fb8af2d69776986b057f97d9acbc44e1b11ee764b03c3f5d6f1ccda44cef0cac2e90ca4c5c938120f0533ee069b9a6fd698acdf7abca384948538e9028b461ea9e8f63369b070602d31459b69dd7e70473b5306a99609c9575cc438e4b95bff7011cdd8a14bfccc2ac32167e2e2c87e2571e4680ea71889197c3e14b61bf46ddecedb6a2
+HASH: 2da234639b4f5b51
+
+KEY: c0ed7efd7c3a530b33c0a8c9f75ef3b3
+IN: f3348518f9cc5cb4789aa17cc376e9f0f0a813e0759ec41c73b92aac9bb61dc0dc257b4980895e52483453dcee6780db1c81c3ed797c9a034c6fc2ef9b9e87746d2526c0494d3f68a89b92745cbd7a1f6cd7cba08c7572d20cd325934997f568b7be8f487f2d9fb1f377ae12db20c4175ba7a7217d6763a40ee6fd1ee8593b49f22b16e737a20617fa3d369971f26c669e4a71940c8be44eda90c2326f834669485643ae820eedf05385451ab4d5a37f175996e5f19bebbc45d3052f5fbca7fdc413762c0cabeca5660131bde115c4b236c0e4ba137f9fc0c62dadc7cd40a4b1e81f41508ba1ff86dd0c5437207b41c1869a1ffb
+HASH: 525c8c3503737a42
+
+KEY: c9322e7a0fa3d3cb63448c0cf91a710e
+IN: 46c7eaab931b65b4f7734440a7da94bbb6b7bbed3c875edbf086d5338e9fb89c9586eaf834a9ec1522de16bfee02ecf525bf9ba94ba3723af970b958e32212ddbe9ef1a28005093cb725f95fffe8f3add2d468d0224345d75acc4e1e3db00c0f4a219904c39931a7d6fcef5aeed12d67c6a2c05a41934a04c68c9a5ec4bb2b7328a1650c1841fc14fbacee0b570e3c6d8f6383c6b37ae35b1d7118f45d43d55a3b453d3698acd84e9404b54e2fa8b063ee75d1fdc13f984a79a9b555d190f108a0118d89f95285f92d1dac17e04dbd51aebbc40683acaba35552a569cef829b5a3a6c41832c63aa136a9b707e3b037c80376175c77
+HASH: ee54e3909a0b371c
+
+KEY: 49a26a0a06a3d9aef4fc6ea1a9805c17
+IN: bfaa93598cd0e67d40090305ffe8f20d52b1ae7f87e2ba8ef15cffeb8098089805161159807562fd9d47c75a2d1e00994707f98170090391ed72163f8311e1e578ceee03601ed10ea6257668330810b79564d30d87ce3e5398af7930ae24d6d9988dc18b1494e83b60500c728e090a466116e6685af67267d83f202f67ac31e8669f6dc27afbcf4aef3c015f63bd0106cc2ff1cbb98e9799642ff744b49e6dc34aed31df345d06de561470afe82e233739be2abe648a3b02cc5f28372b5b8dcef13162699c08ff46b2bc236f63a32f7a5b3600fb09a7a77f9e3ffdf9b0e1a358199fe3ef5a790cbb6ea022aa7f174436a686d57183d3
+HASH: f803371f2f4d3277
+
+KEY: 303c484fb49204e725e2d2951bbd9896
+IN: a63ba5a033912d1dd5c673c2ccd53c5e6ab5f2bc39bc7ff4f5c35c0b5d2c4e0bddce4229fe836afcba360bec541aa5253f9d2c54e884ed07eab9fb454fe5243dd16239b8c50b5801600c6534d2dca1eb61da95426837abcce212454ebf79d1fbe456d6ee12eedc4f1a48a7e4af5d9308adc4f5cf4c54386b0faab7891bc00e177aba304455eec8b6ce64445e1bd7f71f586f6cfc200eb56a579e8649b8ead10786588becb7754d76de920e1fb57882649e1443e77cac77a227e183c7756557e3b36e1028f4971fe47e4dc18ffa1d3d24edfd6bc4bdd6e6e918029b4070980c6f3f7b2aa96a7fff3cade78e8030ef576491d7114c6c28ad
+HASH: 7e9bce366d20bea8
+
+KEY: c37aaabc6d0db4b4236d30ddf60ca347
+IN: 5a0567f73ac4f0a913051bf056ef2ea3722f1736a812e78524884da840dc3b5b253f88a63d1feda1b4b09ca8e6d63fcf7b547f6d181dc1b6db7efc46bb5e2972aa56ec3a08385e9b9863692db11c615b3f2f72d294cf57943197a5248d88efdb2a4c00cabc5eab1a615f61bed515ab0d6ed652fdb41eaee68566081e89052ba7f4cc1b168676609dd1c54821d82461ce36e660bff3e9f526259773ee811e1292b6fe1d7cb2a155c3a638bfa7b1b7040ae1ab8bf31ae2afcea6a3d502e89da2fdadf74bd6a9705ba6f2a6ca8784d8b6a25f3ed874371842244198e596d0dcda9823dd918eb3677b23ee6cae08a0f1c923ca9580833b70dd02
+HASH: 03b55be4e5f448ad
+
+KEY: e3c6637edb2431c3d7e19d789b9b795f
+IN: da494da4807bbf8a852809b22467a8d386c55e9233125cb73c1915fd8b0eb94f683085472825041ccba2ea14cf600f4f03555d73c8b239c9bfae0d2cc1e7641b43e93524a58fe6730cfaf7e62311250f4934077e3883205c7f53b5719be2c2382659accbc9e7a79839c1156c4314110889f7123f3876fe8aa2fa38fc5a0c39fe0b1e8e5652111f0b420c3bbaf082f50454abecc6f8ad2bc37f655965a972cfd8ec726d0169f916847a5065321d817d67c243677346b96c6bdbc9ba8e7eda16b2ba56cfc0dc8336b31eb2bf8b14033fab8dcacb612e556743b28a9c24ddbe58b27ca303bd504ba2be547c3b6893b9eac8e21c5c75e85fa0530e
+HASH: 135f12ed177a486f
+
+KEY: c2723318c7fcee8351ffbfc223d5654a
+IN: 4bc1f8e3e5a6369473829b634a171988c009dcc4b2206036ebf419f6a722710cddec29901c62e892f2ec519caa6be7771f09065ceb21332e30a8ed662ba62af0b2d6ddcd33095dd280f0ba22122e7f3bbfe8809a6eccd983176749a7d9f086df6fd895494d3bd225cd0552884c90cd6915f57185af57dde6b41b7449a89918592c6b9b9944ae2c78d3a65d74fa9174d395fe5e33452997286d8e52853197a01e159172c663e2fb88919a03c710c9967c2de00d55622b854d42d72eb99bdc4b3324b1e022b6c383b26621fe81071ffb5e94193ee9847b76b98325b42548cd1728b77dba4384284881b69934759421f7a259a8e54dc89f7938687a
+HASH: ee2d28c3ea0b7ce5
+
+KEY: 390fc266fd6339d769eeda067acc5cc5
+IN: 53fcef219449622f20e5978afd27c4568e130d0c1db23b4d94c110069353aa40932d5f0b1fb6edf5f54ffb217516a094f0f11fef692ac66420f7c192c1fee13892971cecdaf923824c55679a379185cb7b74af34a920e64a1fcffaabf699c1ee057fd7f64e73ae45597d4dced74f59926f9b7a989a963ecc12f0e31143fd89f57270b272cbb8a19f106e83a134f1e7f63a6714ba36b1fad3cb7094d42d36bf42f0e66696972ea0bc7167bf718e4ed57ce0735de11407e7f06a171f7c7ee5a2982cfe34db94aac9227ad759bf66c45e791b4cbc9dc0eb44596520008b435ab78778e057960aecc669645d51326a4533760eed682dcf21d38b0a7f85
+HASH: b5fcdecae4d9f22f
+
+KEY: 4b7f98e24a91752f71cb2a1cf4133b7a
+IN: cc21aebd00b960f302d250f64241d428c2774bd70275248b39619a3c428819c7061de63b44c2d9b865ce0f2348de73ce7d1875fdf34bb3b74690bf03dae40b4f8fdd348196fb5e265518c67d643984b3046f5dba1aa2a3308b03009e7fa67263bcdcb4c1ba6721bdd0475a9f3a202492f24e8c40216ab4a47efc4fb156cb0d3e8491128e50214da90ade66065f75bd3087957b547d5421a9f4502da385482b4b75003a05e6f35dc2ccff7835782c79a1456417c169e76f7de8894ffe07315b7d3787c9209e73a3747e64dc0e2672f3d4f1912aeb1e8fa861a4de41852a8456f619c335568aed6917c8c93c91e2933dd8d8c51229b6bea28f2445dd85
+HASH: b18e5bc73472b149
+
+KEY: 3a0caae8e1afa4bc915f83260c4b69a4
+IN: 541af16c8314adbdec55ed3eb06820b178883ce26aae5dd1ffac1544b615426e5af0d585d7e2f922a228d9812fccef3e0c9f0a78b98d5de8c683bfb011c547af744eebac3d2879cd1ba2d01f39ec8efd116309cba48a7c280a0656a53a68d9a90947124829852c076d4ab85ad2f1a73121f0db1f8de6520c53b3a010a1e9febb37dc27906e4358e00b61e3f7be83d070731c1c56c59f1aa93185ae1c6e944db721b3e389f0d77f2546d8aa32b54ea8f9f6d6d2f2c3bea3b7e4044623745c06f1c99d4ba71b7e052112f30823b210e09cedc62ed1a8a38716f263ed0f5cfca0bf760c5005b6bf5479c7585fafeb34fd9742a909414bee7fcd0b7256f961
+HASH: 33e708ecdb29ab35
+
+KEY: 70e8b305ad3d34346b7f21a84e99a9b0
+IN: d8f7d6548477826bbd0a2a5aa8993869057a032e7631aa4870e3a2dbc80e3c571e516519780f0ca5b63969171b5bf2f48ff15e922de8e438fe56152d214e828f869f309d711bf00f41b52a9181984afe09c526fb7bf9af7a02c52f77a419f07b13a2c06af5a1589d69b56440a7bad5290ecb3ebc16314e016603c5f96f2480054caea48adcd8eb7d3c79bcf7117073eaac802243c878a202ec5a1c20360c3fc97c688d35eba6c894a183377a9b836fd442da42fbb0fa8fe68e824a00c509eabcfc68e61faa731554f9b9d2cd1bf85c3c59e8eb64a6040f345685ea5c8202690ed62eb9392f932af9107e54ddbd59f150d252a5c3467ba44e1544d889e7b0
+HASH: 0af2dc6739b90eb2
+
+KEY: a96ec37c773d908b5cc732eabb877b24
+IN: 6baf8f4a20f45bf086c0003c0c6e3837cbdbee6f861d27417547d06e21feb53703fa0f896dde73cb97671e4af7b27fb175d802bf3d941e13172ef158aa6f19e27a504e46c1f1b1dac741bc3ce66b73f46941821561be24448e37a64af54c9beb16da91ee8705d13b2882b9c80fdbe249e1d3b6e5c671ee524d76f543dac473bf83cddcd95c07e82d70ed4f0d86615e6d3776e6db7f740ff7db55a949193734b2b9caa48dfafd8bb40bd8cf2eac7bb6462e92f2a0fc101c7b1fef491f6a16c1c555df9a57a1f7a8e70eeceee14bc6e81882a8fecc48ff9b171b9e8915882f281925282a479b8239bfb48d8cdcc56c22294210a484702c5a8e4d4fdee2f6223e
+HASH: d10db578e9b44fc3
+
+KEY: d5f074017db58f6e68d210b58e091d7b
+IN: 319a1100ad4e268dcc7f3ce71fac847336fbedb9046c1b1b433c373385326fcde6824b3b1ff34f86a0a27b9216afe3145945eb5f7aa37875fac08cc62abf01e2591f8d8109f102214457193412c58a393c4819812ec9fc2079df042a3f57d172d76a8f2709b942e79b3ef839f2f51dab93d225e6eaef30b2a67324481d60a9c5a215ada44eca05470337379dd01eb0dea2cd14bcfd0991d0bb11a6b0fce97b4e51492fa665ee7e29cbba50a5ce0d50c663b16ec8260edce17d548c6d8566cc76ea7270e406f9c888849d15197b37d30c64eac4eb90f3101ef65b3c1493370f8758e7d954b41a543e520394f2da5229efd6f06ca947bfff71b11425a67229120f
+HASH: 1b460b968867e42d
+
+KEY: 338962444ff46d8d1c4ef2d14849202c
+IN: bdfc2290fb71a50aa63595c3f11ffc426539678601b31bae30b1771435e3199668980adb627c738ea28ba458c0179e48a802d81afe0b2d09b5c3efb06808a22d73173049781c0822d780e360e442619dd82cfca21291eb0b793eaef44202b7a5e6c30505ce4e026bbf724ce76a05e827c526ea99bef552460abff8058142e11e1c223aa0cc31bfa2e5f9184d9a0fb9533facadfa08b5da0a7b65ea1758675bbeb1ce2faa11ce20ade70ad5fcf88fd253c75e5a2d0ec79f3468e5056309b4d46e1e96ec811a14cf6e64fdb91a044979a86b571a9d701b6663d8f24ad77c4a514576fa942f45970993dc703dbc68a06eb688b9f7e87fbae04019d54320a26a5b7b84
+HASH: 2529cebc7a6002d2
+
+KEY: 94f80db0bb92d9f844c8fcc43ce0d94b
+IN: 3d09d5fadd1e79e648ed155dde5646d2b9b90c9103d4d7b4d53d5ff184033fb17db63516b70d4407aaf3be325d66d7196d08e310851c7a9f6ecb6d3a73b7ebc9bd604db7bf93879e17c2e1d7654247295ea02b97a60a3b85261119d021ed1f040f27e456f011cb46dad312b65c1927765cad2c56227d6aed2a5d7754a57de58c11f185aee88e38ae5c8fdbfe6242eb3dfc7981592556cdaf21351648abc64667998c4003cfb8376417ff98b506c0fa51dd2a152d00f34f6ab58c883362b08c986041724d360f68b94d8d071e327ff536023c98313f09df4ecdf11e16ecfaf3970ad682086ffc7e43d571a96b7103e9eb80acdb6fb8f78107a8042b2c8893b1d2f19f
+HASH: c4746b2d19941e56
+
+KEY: 995882b99f51c4a67f5b55f90e2a9aa2
+IN: f640e51da483aadfa88812d5851ea3a0d9e605b144013ffb7f74df26cae9317fd90233e2893575d5e470df2d80a2e9f4e8f6f5e5f885498bdad37d1489ab823dab96832d48c7617b255d40716d54dc45e9bb3d88f908d573172486d88c698496faf792696ac1f446419f6459866cd4ccfac5359ad9fc8a7bf85f755d08fa5e44f4b0def68513a39ddb97698180592baa03e37220eb955c636baef003040d753d5b2c307edd1b7a24ba1df13b9bcdd72cfbea7b17aeea59c604e509f511855e3784328479de46457d1ce706cd2f9e937d71192ff215733c62a205a6f08b901e2521e1935ee33d524c3d10c73c0a593acced88f73bf6624b15de138076569945cf2b6b11
+HASH: b24f340cc0fb1d2d
+
+KEY: 8da14614383bf855cdc281b5225515ce
+IN: e17c973f19eb796bfbc57ddb9601806998a5e97f18a3eca6a1d20c3cdcd109815cd8ee13981858324ab89e8b30214e3251e6f308648552034545f7d24ca74aa6de5bc1513c20d869a310ad1a44e462666d2817c0cc225a28efa78b1435c80adca1e07683e1792152267649aeb7ee19efade02dbc631b1fc58ac59671ea5982234da90dad42430fbd85fe6527991ccea11bb805815fdd0c8a920dc9e818a5fcf8fbf680a06999c42c33c7cc712dac8badf82059d142c6c3f80a8d09c881182cf336cce2e0e088d0af39fcdf6dc29c1ef496968455e2fa149a65cc57df06fe6830895e2daf352e922bff8cc3fc757161f3c61b2ec9e7d787e1ad976595d27f0f5fc5a04da5
+HASH: 36bd2cce6ed21122
+
+KEY: 70e11b8cda2ab4a0abcb736625145d90
+IN: 7f4fa8fc41f72c8fdcce996949b74560ea9fc7a3c57e502628487144472bee4300671cc1617fa7c76850df408b92020e7f5d377f4c8f410288ff60aca90516d0f1d13969c9e5af7e348ea7f8f9a36b8c69d0645432103f5b93ab560f3113e2d86b35fd6e704116ae5e7bfdf1c29b6d5a4570e2b1082a3e6023f14e666a76af86e4661e140eb836f262dbd5d388c166215ac4f39dd9fccbe3a3a8c58a2236304e8fc0fa1fc535e860e888a0d640277426e2e2a05d855c5ffd0876ee0306cc0c394d064fc45e974d1c19094a1e83d59057bf269f581637f343a9fac376ed41f4d193aa5e3c30141d31391c02390afd81bd32b6865ddd8974a44724217eaadd7ef316e0b4c2a5
+HASH: f6624d4d13833699
+
+KEY: e974c2247093c75ef6ed70632f16924e
+IN: 71f2e0508d7d36b373e8ee0fabf49dfd69638c5a6c79be9c89bbd0043b3bc66abb3618c67299c0afc4e24af8440185ce8d7d8b1d45732f123aa8243a9a0a0bb27b3d6bf59b39e389c15211b92c93bc5aa8582c98fb1523427c84969a65e99775e9d97f382b9ab47ccfdaddac3e7201f917bb64843f2b804801a018c71bb6f632f4521c1a4e9a375d7bbc8a42e561843dd65ae3e7ec0da1db8751960a656876983986bb2be2dadca5bb312cb004eb10f1c608af4afa599d946bdf8bd52102c257871810f5c8a899580287074686738a5823f33e96d3c2a7c77b68e4a21a5ac50e06664944d0cd6bd40ae95a2e21236e671bdaef79f08da52e2a6f65131a2e80c6263ddbeb9759
+HASH: 00ab496891593584
+
+KEY: c6f1ee0aada0c076cb20cad883bbcf52
+IN: 912f5b270fe4f445134d1caad3cd04cebc9c377fd03d3e26b82d4449d4ce1914ea718a6fa8c179dcdce1d117e99f4e8cad2cc0f0c059fd94cd8b3149a38a6b271c7fe1c90943c7a8d40774817c272a9139a05e1c0aa74cfb5a7c3c93aed9303b9579200022527a0911908875e23b1d8bacd42a123fac2342723a77942523b4aa858ff284821e1ecdb82ab2858580227bdb2e297ba3680a1f1cb0d31cb8e6f6c45235dc64f4c3f986c24f9ae3a6cb9d3710d58125f20d8e8c4cf4f89adc091649bd7a33ecb58cd163baed98e7e4f4dc16955f7f09bee1a51e0f76e9575cbc2733bf58bb4be4a4e2a2b1285c1b928aae6d8d9becdbc3723b408fc01acccb95861d23a3940df799bd
+HASH: 792ff12185fc3326
+
+KEY: 5cad21c8c3805f62248b1e1758c9c392
+IN: ec943f6a3d3681cd718f532bea0e5e2839c9425d0381dc5808cbfb273f6730efd52496b0331a711fce232991dd0847b5b6fd58330f54c19f571346d1e100e2304ebf5fff1b1047ddbbbc4dae02ffa3a21f8655b8972f683a33676352908e007ae3d12ce62e77acb2ca8278161d7c6964d6b97332c19c1d1d7288542eec4743731144a0e8a0abd58d0df0664eeacd5e97e272d672e110734c84c075b9ca5ecc279010f22236063a8ed1a7ef31bd84637832ce375944eb672b51edd7b4b248dca106295d83f14f160b63f84a49cb6a0ab143494ccdf5444c464ede3ee7936f821857cb2f3cd7d29da414c10ab7db05e4d0817c1556804b1d3d9c771adafae48541f930464549cd2635
+HASH: e428c0fa3cf4f1cf
+
+KEY: f6f49ac81cbafda6579b81c9d34de602
+IN: b5e3e21c2b2104c50bdd9a96667d460c3a4c0a5975df60369ebfc0f6ace585d727292f1d37107ef0bdd7ef16fe8740e0453501405c2a863289773a4d121b37f01e1672b0339f57fb445f132262f6337f64e9df4ed7fa79ed1c722171cf350f1c1d78e7427904ff3b90bffa1eea64181c4e69440af712e37b7dea95953ebd4ef5efa7bb3b14f3f452e204f9a26fcfa104b80deb56e16e16af88fc710f68df3da7f162e3de3aa9f31a2485169411d6009014578cace09aacebda2e965cd8c4841df7e31704584aafc459e02afc978b812f8ba22e2887728243658b4adcc46a471aa357567505b17fa51fdbc4c5ddc67827cc86f66ce3a55257d00683ed2b67ea19ce1ba0eaaed8c4c1a7
+HASH: 1b44de49c45e9bc3
+
+KEY: 05f5a594c805575eca581fd6e8b16051
+IN: aefef8817ca1ec2ce86d04184ee9f5da020497d3296395cecec10af22696deb65dcf3cfa4a3b808eb1826ff421de79aeebfca796306eb3b39165ad11d8733cee89f30229f75c06dea935045439d5a69ac118c5ca26bf59a9cade2e6b80b0fcff911eaf7f4097f87751570362dc5aa42a379c5ad313fb403b15c9c6c517be4ec28a22039bafd618284b16e12bc38c39e8525b930f5a630d4d595980becae425229bee4c63706fc172f5f9ba6cd5fd36ab474112169c2ef52b2bd72b2ef3bceb8a82ca53a42abcb044b712cc1a6b37a8ca8894b9904076452ddaae641f9137a72987fe928c20b67c2ccaf9fd601fa10c5e4c968727357c4547fc68b0e11b35f4fcbdcc3784c2722eeebefb
+HASH: cd45d7070e5ecb55
+
+KEY: 0546eaf96bc197e372139dac59c9ca1f
+IN: 1e0622e2d160fe532cc789b76e99a5fc7e90078dfa0d3d9342ca8c625192afeacf6935e930bca3e7bb2d7a4db23371b6ee3e645b9c6939bc3d414ddbff363d4ecb2967962bbb1d3b86a476bedd5820d17b86747c886efb34692b87db843588d0db53c3d91f2c305e5d58cb7ab98d6387ac45400d2ad1f2435bdb0c4a76c6fd491c3f2bc049fe70426767cadb0fa897e29cfda1bcf4f18d0b15795de39ca05fd2318dcc8b9afb7a278e986521ad971a236745bb62a680b47707a7dd1ffeb98e8e61edaa038e7c2a568c4a0060c38752eae915e4f99c91d6de11574b3dee5f85209acb8a38a02d0f59f5192a3151ba8073682d191ddd26b01913e98b403a4ce771e5e020ffed080d2dcb7082
+HASH: 079a62a92c61d7ad
+
+KEY: bb1780c25e2afb8c9bc58a3dbec8867a
+IN: ed5287df6db34449db95528d55e64ff486f178d68135dcc996593592a40655c88dfbef41fecbbc09066adca3b3ceca2c9f7be48ba61587ac3c69bb5589fb257bb96c000ab5e5843e9e7257095ae087d084aa7e62e9e3f74079d1c4c10443c9d9bcbcae47c74e72c024e5cd13280f058a772afc379d2dee0e39ec0cfc219cbb2af7698162a3b84be52c90617760e84d3743909bdd5cf21bd3ea3e3e37c3734dda57a06db25ab81ff02823f6d39cf9e068698e1f597458d08a29aefef218b40e191dc3d05f089f133f5f337d98d1c0161f490849782248e915ebcf700d80667b2d0ba3fda022faeeb23ca87add7664666f4637571964d613bf76a22061c9f746f9fb00ff0f5c2d90f648717e1f
+HASH: fcda80db378102b4
+
+KEY: ea7247b6addced42c51c827dbb2cb66c
+IN: 0ec554948d3af7cbc2306d249861f6291d44ab2be6bb26ab629347ac0e892cc902b3c5bf9688e39d826e32609834b34644a8b39c9a91f47a0f24e73a13edeec9e224756f6cff77e1d07fac03c1d803649279e33b2f16dd2d100bd82e9806afadb022700d55f5fc9688193da02dcc9dce974b60ccc2caad064acaa0f1b9e4f24e9074ac7877b91b19c7abbe5bb7f889775cd72ab9334475dddba19d008fdb01ab9cbfb1038a486c76f3c542bac704cf795be628cb83a5d9d128c2b401cf23ce494584cfe5b4ed58d9c906ded22090ffed4894238c6d02835e6180ce662b24ea18fd84d5fcbed49790322260fcf3f193081ccdb60d8f768b6dbfa61867bf90fbb1f27e1a6cb83daad6243079904a
+HASH: f8181640a08f1343
diff --git a/src/crypto/test/abi_test.h b/src/crypto/test/abi_test.h
index 44547f8..a3c4bb8 100644
--- a/src/crypto/test/abi_test.h
+++ b/src/crypto/test/abi_test.h
@@ -149,11 +149,11 @@
// AAPCS64: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
// iOS64: https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html
//
-// In aarch64, r19 (x19 in a 64-bit context) is the platform register. iOS says
-// user code may not touch it. We found no clear reference for Linux. The iOS
-// behavior implies portable assembly cannot use it, and aarch64 has many
-// registers. Thus this framework ignores register's existence. We can test r19
-// violations with grep.
+// In aarch64, r18 (accessed as w18 or x18 in a 64-bit context) is the platform
+// register. iOS says user code may not touch it. We found no clear reference
+// for Linux. The iOS behavior implies portable assembly cannot use it, and
+// aarch64 has many registers. Thus this framework ignores register's existence.
+// We test r18 violations in arm-xlate.pl.
#define LOOP_CALLER_STATE_REGISTERS() \
/* Per AAPCS64, section 5.1.2, only the bottom 64 bits of v8-v15 */ \
/* are preserved. These are accessed as dN. */ \
diff --git a/src/crypto/test/asm/trampoline-armv4.pl b/src/crypto/test/asm/trampoline-armv4.pl
index 30f510e..6118dd7 100755
--- a/src/crypto/test/asm/trampoline-armv4.pl
+++ b/src/crypto/test/asm/trampoline-armv4.pl
@@ -179,4 +179,4 @@
}
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-armv8.pl b/src/crypto/test/asm/trampoline-armv8.pl
index aab5250..410b59e 100755
--- a/src/crypto/test/asm/trampoline-armv8.pl
+++ b/src/crypto/test/asm/trampoline-armv8.pl
@@ -206,4 +206,4 @@
}
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-x86.pl b/src/crypto/test/asm/trampoline-x86.pl
index 569a3dd..4244ac2 100755
--- a/src/crypto/test/asm/trampoline-x86.pl
+++ b/src/crypto/test/asm/trampoline-x86.pl
@@ -120,4 +120,4 @@
&asm_finish();
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-x86_64.pl b/src/crypto/test/asm/trampoline-x86_64.pl
index 8cb1410..5196141 100755
--- a/src/crypto/test/asm/trampoline-x86_64.pl
+++ b/src/crypto/test/asm/trampoline-x86_64.pl
@@ -556,4 +556,4 @@
}
print $code;
-close STDOUT;
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/x509/make_many_constraints.go b/src/crypto/x509/make_many_constraints.go
index e507403..578618d 100644
--- a/src/crypto/x509/make_many_constraints.go
+++ b/src/crypto/x509/make_many_constraints.go
@@ -137,10 +137,10 @@
}{
{"many_names1.pem", 513, 513},
{"many_names2.pem", 1025, 0},
- {"many_names3.pem", 0, 1025},
+ {"many_names3.pem", 1, 1025},
{"some_names1.pem", 256, 256},
{"some_names2.pem", 513, 0},
- {"some_names3.pem", 0, 513},
+ {"some_names3.pem", 1, 513},
}
for i, leaf := range leaves {
leafTemplate := x509.Certificate{
diff --git a/src/crypto/x509/many_names3.pem b/src/crypto/x509/many_names3.pem
index dbfa042..f15638f 100644
--- a/src/crypto/x509/many_names3.pem
+++ b/src/crypto/x509/many_names3.pem
@@ -1,5 +1,5 @@
-----BEGIN CERTIFICATE-----
-MIJqmDCCaYCgAwIBAgIBBDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJDQTAg
+MIJqrDCCaZSgAwIBAgIBBDANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJDQTAg
Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowgmfXMRAwDgYDVQQDEwd0
MC50ZXN0MRYwFAYJKoZIhvcNAQkBFgd0MEB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0
MUB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0MkB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0
@@ -560,12 +560,12 @@
oiXCzepBrhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lq
Z1Aky+aNlcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRz
YeIs2R65LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTO
-gnmET19WJH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABozUwMzAOBgNVHQ8BAf8E
-BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADANBgkqhkiG
-9w0BAQsFAAOCAQEAtMIpnGzOBkJXEBmCsRVbTrg9QgYRlGPG48+cXT2QbIutAmbj
-miF+OYg/bRsQtuqcKjnJYog+x6UCU3d34jaMEfEXfHSwF7xPQrqJm45MXhG3so4E
-+el5GMAS+SKFQK3w8NPoGhGwn82sz4XV6HMG+ANUxMlCrOcx2jh5UW+7ITjdRwJd
-ReJ/JaMpneJdwGFSU9Vn+t7PFb51/pOYqO/PuEANzphovjMVcFZ6mtAQwYDkQZBJ
-Vy1/7bPoNmbKD0GAS6HpS+xaJ/DnjjD6Kal2T7GMyvRMogj5BeZ/uEkXCEhvoaBT
-os1gaqqnGpZ6JSEDctzjgpCtEPR40yiz1wv1CA==
+gnmET19WJH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABo0kwRzAOBgNVHQ8BAf8E
+BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADASBgNVHREE
+CzAJggd0MC50ZXN0MA0GCSqGSIb3DQEBCwUAA4IBAQAi7LIMyX5Ec514hvjROZ8b
+7i4UR3xd5IbniVSej+PKZhG2inN6aX9bksdda0ddYZeRSHAkNJuoabeankQJ/x5x
+sxBntWSVLCxz6S8NRrLAPKKPBvFb/W5ns57LP9SrLIij9l/NSd+K/CQNTlfcdorg
+4ltPVNwSMp/XXjH6rQYJSbo9MhDoxeqPpv73e4jY0DfGn1a8uwyCXalLjh4EkUyS
+Ye0N7MoUKV0IucrXKdgj2sHgBFqNKJ/GVQ422xZRbYqsyIJ0bPD6Fc8VcqfVrvYg
+lCYJfu7Xij5n3mjQaSYcbVxH71X8fYhhNq1tk+WtQOXirz2EkSuh1rNGU/LT8Q6r
-----END CERTIFICATE-----
diff --git a/src/crypto/x509/some_names3.pem b/src/crypto/x509/some_names3.pem
index a6d3ee7..7b38bf3 100644
--- a/src/crypto/x509/some_names3.pem
+++ b/src/crypto/x509/some_names3.pem
@@ -1,5 +1,5 @@
-----BEGIN CERTIFICATE-----
-MII2fzCCNWegAwIBAgIBBzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJDQTAg
+MII2kzCCNXugAwIBAgIBBzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDEwJDQTAg
Fw0wMDAxMDEwMDAwMDBaGA8yMTAwMDEwMTAwMDAwMFowgjO+MRAwDgYDVQQDEwd0
MC50ZXN0MRYwFAYJKoZIhvcNAQkBFgd0MEB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0
MUB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0MkB0ZXN0MRYwFAYJKoZIhvcNAQkBFgd0
@@ -282,12 +282,13 @@
BEiEhBxIsaIlws3qQa4baeVEEoxw+A+ISrYHTIFcV/i0bcIFt5p7v7wbu686a/w0
vIqPfad5amdQJMvmjZXDI+jGMvFPmBRHr2/1dJUWPaKsJluwR514pJv74urIyEt+
dFPM2/5kc2HiLNkeuS1Hbky+dPlDIGrfaHHsCNnb/GjvQ6YfvDXRrYPCxWMk0x3F
-MSaDK9T0zoJ5hE9fViR+D6xcJO2RNUCUENS+Iipj3kIrLbkCAwEAAaM1MDMwDgYD
+MSaDK9T0zoJ5hE9fViR+D6xcJO2RNUCUENS+Iipj3kIrLbkCAwEAAaNJMEcwDgYD
VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAw
-DQYJKoZIhvcNAQELBQADggEBAH6ad2kFE0qGDe3ErMdwTGjbBz3T12dDvAUVhGHQ
-uZShOdPsXMHD2mUqFgLE0iJFeXB7jOSAKtzmKHNmxZ4W0UZ7eMPPogkgIbG3d3yR
-8zBO21CUyOQWChywpKcAou9ji3Kq6pb4+mqq0a5TGIYyGJKSUTv09KI+iHgwteCX
-DHzzhuTs8AhodmNO5K/F9YFWJWvQ1NrwyUmOFEw8/UcljyKxFrP2VEov0fWeiTRB
-Ps6VaFBW7SEEi8fAM9W5kfsl+iWRvwFcFdXGQt1HbeywCu58DLI4uceHCFb+3MMO
-Xv7wJ5UhQODuzwuq7CuZvlxR2tiFoPP+s5fPH0L8MBP5z6w=
+EgYDVR0RBAswCYIHdDAudGVzdDANBgkqhkiG9w0BAQsFAAOCAQEAQA/0vvY1gLA2
+0jrPkBVWte7OHzWVkwq7mqgQPR4L9qLLu7Vhelp4dW8n95s1wCbca5j5SJEGv4Uv
+0fI1OOK7XQeYdNlHBmvMVW47GoBSo6tuYNPI/y4xnM6ypEZiPKkdj9Ar9qNgURfV
+z3s1czip915dyTWgwBy7CTxOlG8NW0uiFgEc9iiDDfQsPwVXiVtxOPtjhPeI3F0J
+jh3wctFxBnAvLV9SsDxpWujM1dd/1SSQ25jKQhbKNtiDAC8v+Q043r8ZGHjRdxe8
+W2tVWH/iz9c+ze0P0ao7LKv8eGzoIsrBqICS86X4Zv5lGeTGaD2osF1oNvmmoSlh
+536yFa415g==
-----END CERTIFICATE-----
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index a53ed7a..1f664b9 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -32,6 +32,7 @@
#include "../internal.h"
#include "../test/test_util.h"
+#include "../x509v3/internal.h"
std::string GetTestData(const char *path);
@@ -660,6 +661,162 @@
"KStYq7X9PKseN+PvmfeoffIKc5R/Ha39oi7cGMVHCr8aiEhsf94=\n"
"-----END CERTIFICATE-----\n";
+// kCommonNameWithSANs is a leaf certificate signed by kSANTypesRoot, with
+// *.host1.test as the common name and a SAN list of *.host2.test and
+// foo.host3.test.
+static const char kCommonNameWithSANs[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIB2zCCAUSgAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+ "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+ "MDk5MDEwMTAwMDAwMFowNzEeMBwGA1UEChMVQ29tbW9uIG5hbWUgd2l0aCBTQU5z\n"
+ "MRUwEwYDVQQDDAwqLmhvc3QxLnRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\n"
+ "AASgWzfnFnpQrokSLIC+LhCKJDUAY/2usfIDpOnafYoYCasbYetkmOslgyY4Nn07\n"
+ "zjvjNROprA/0bdULXAkdL9bNo0gwRjAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQn\n"
+ "jFkBMCcGA1UdEQQgMB6CDCouaG9zdDIudGVzdIIOZm9vLmhvc3QzLnRlc3QwDQYJ\n"
+ "KoZIhvcNAQELBQADgYEAtv2e3hBhsslXB1HTxgusjoschWOVtvGZUaYlhkKzKTCL\n"
+ "4YpDn50BccnucBU/b9phYvaEZtyzOv4ZXhxTGyLnLrIVB9x5ikfCcfl+LNYNjDwM\n"
+ "enm/h1zOfJ7wXLyscD4kU29Wc/zxBd70thIgLYn16CC1S9NtXKsXXDXv5VVH/bg=\n"
+ "-----END CERTIFICATE-----\n";
+
+// kCommonNameWithSANs is a leaf certificate signed by kSANTypesRoot, with
+// *.host1.test as the common name and no SAN list.
+static const char kCommonNameWithoutSANs[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBtTCCAR6gAwIBAgIBAzANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+ "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+ "MDk5MDEwMTAwMDAwMFowOjEhMB8GA1UEChMYQ29tbW9uIG5hbWUgd2l0aG91dCBT\n"
+ "QU5zMRUwEwYDVQQDDAwqLmhvc3QxLnRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMB\n"
+ "BwNCAARt2vjlIrPE+kr11VS1rRP/AYQu4fvf1bNw/K9rwYlVBhmLMPYasEmpCtKE\n"
+ "0bDIFydtDYC3wZDpSS+YiaG40sdAox8wHTAbBgNVHSMEFDASgBBAN9cB+0AvuBx+\n"
+ "VAQnjFkBMA0GCSqGSIb3DQEBCwUAA4GBAHRbIeaCEytOpJpw9O2dlB656AHe1+t5\n"
+ "4JiS5mvtzoVOLn7fFk5EFQtZS7sG1Uc2XjlSw+iyvFoTFEqfKyU/mIdc2vBuPwA2\n"
+ "+YXT8aE4S+UZ9oz5j0gDpikGnkSCW0cyHD8L8fntNjaQRSaM482JpmtdmuxClmWO\n"
+ "pFFXI2B5usgI\n"
+ "-----END CERTIFICATE-----\n";
+
+// kCommonNameWithEmailSAN is a leaf certificate signed by kSANTypesRoot, with
+// *.host1.test as the common name and the email address test@host2.test in the
+// SAN list.
+static const char kCommonNameWithEmailSAN[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBvDCCASWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+ "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+ "MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI\n"
+ "zj0CAQYIKoZIzj0DAQcDQgAEtevOxcTjpPzlNGoUMFfZyr1k03/Hiuh+EsnuScDs\n"
+ "8XLKi6fDkvSaDClI99ycabQZRPIrvyT+dglDC6ugQd+CYqNJMEcwDAYDVR0TAQH/\n"
+ "BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMBoGA1UdEQQTMBGBD3Rl\n"
+ "c3RAaG9zdDIudGVzdDANBgkqhkiG9w0BAQsFAAOBgQCGbqb78OWJWl4zb+qw0Dz2\n"
+ "HJgZZJt6/+nNG/XJKdaYeS4eofsbwsJI4fuuOF6ZvYCJxVNtGqdfZDgycvFA9hjv\n"
+ "NGosBF1/spP17cmzTahLjxs71jDvHV/EQJbKGl/Zpta1Em1VrzSrwoOFabPXzZTJ\n"
+ "aet/mER21Z/9ZsTUoJQPJw==\n"
+ "-----END CERTIFICATE-----\n";
+
+// kCommonNameWithIPSAN is a leaf certificate signed by kSANTypesRoot, with
+// *.host1.test as the common name and the IP address 127.0.0.1 in the
+// SAN list.
+static const char kCommonNameWithIPSAN[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBsTCCARqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+ "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+ "MDk5MDEwMTAwMDAwMFowFzEVMBMGA1UEAwwMKi5ob3N0MS50ZXN0MFkwEwYHKoZI\n"
+ "zj0CAQYIKoZIzj0DAQcDQgAEFKrgkxm8PysXbwnHQeTD3p8YY0+sY4ssnZgmj8wX\n"
+ "KTyn893fdBHWlz71GO6t82wMTF5d+ZYwI2XU52pfl4SB2aM+MDwwDAYDVR0TAQH/\n"
+ "BAIwADAbBgNVHSMEFDASgBBAN9cB+0AvuBx+VAQnjFkBMA8GA1UdEQQIMAaHBH8A\n"
+ "AAEwDQYJKoZIhvcNAQELBQADgYEAQWZ8Oj059ZjS109V/ijMYT28xuAN5n6HHxCO\n"
+ "DopTP56Zu9+gme5wTETWEfocspZvgecoUOcedTFoKSQ7JafO09NcVLA+D6ddYpju\n"
+ "mgfuiLy9dDhqvX/NHaLBMxOBWWbOLwWE+ibyX+pOzjWRCw1L7eUXOr6PhZAOQsmU\n"
+ "D0+O6KI=\n"
+ "-----END CERTIFICATE-----\n";
+
+// kConstrainedIntermediate is an intermediate signed by kSANTypesRoot, with
+// permitted DNS names of permitted1.test and foo.permitted2.test and an
+// excluded DNS name of excluded.permitted1.test. Its private key is:
+//
+// -----BEGIN PRIVATE KEY-----
+// MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTXUM4tJWM7OzATty
+// JhNOfIv/d8heWFBeKOfMR+RfaROhRANCAASbbbWYiN6mn+BCpg4XNpibOH0D/DN4
+// kZ5C/Ml2YVomC9T83OKk2CzB8fPAabPb4P4Vv+fIabpEfjWS5nzKLY1y
+// -----END PRIVATE KEY-----
+static const char kConstrainedIntermediate[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIICDjCCAXegAwIBAgIBAjANBgkqhkiG9w0BAQsFADArMRcwFQYDVQQKEw5Cb3Jp\n"
+ "bmdTU0wgVGVzdDEQMA4GA1UEAxMHUm9vdCBDQTAgFw0wMDAxMDEwMDAwMDBaGA8y\n"
+ "MDk5MDEwMTAwMDAwMFowKDEmMCQGA1UEAxMdTmFtZSBDb25zdHJhaW50cyBJbnRl\n"
+ "cm1lZGlhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASbbbWYiN6mn+BCpg4X\n"
+ "NpibOH0D/DN4kZ5C/Ml2YVomC9T83OKk2CzB8fPAabPb4P4Vv+fIabpEfjWS5nzK\n"
+ "LY1yo4GJMIGGMA8GA1UdEwEB/wQFMAMBAf8wGwYDVR0jBBQwEoAQQDfXAftAL7gc\n"
+ "flQEJ4xZATBWBgNVHR4BAf8ETDBKoCowEYIPcGVybWl0dGVkMS50ZXN0MBWCE2Zv\n"
+ "by5wZXJtaXR0ZWQyLnRlc3ShHDAaghhleGNsdWRlZC5wZXJtaXR0ZWQxLnRlc3Qw\n"
+ "DQYJKoZIhvcNAQELBQADgYEAFq1Ka05hiKREwRpSceQPzIIH4B5a5IVBg5/EvmQI\n"
+ "9V0fXyAE1GmahPt70sIBxIgzNTEaY8P/IoOuCdlZWe0msmyEO3S6YSAzOWR5Van6\n"
+ "cXmFM1uMd95TlkxUMRdV+jKJTvG6R/BM2zltaV7Xt662k5HtzT5Svw0rZlFaggZz\n"
+ "UyM=\n"
+ "-----END CERTIFICATE-----\n";
+
+// kCommonNamePermittedLeaf is a leaf certificate signed by
+// kConstrainedIntermediate. Its common name is permitted by the name
+// constraints.
+static const char kCommonNamePermittedLeaf[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBaDCCAQ2gAwIBAgIBAzAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv\n"
+ "bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw\n"
+ "MTAwMDAwMFowPjEeMBwGA1UEChMVQ29tbW9uIG5hbWUgcGVybWl0dGVkMRwwGgYD\n"
+ "VQQDExNmb28ucGVybWl0dGVkMS50ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n"
+ "QgAENX5Ycs8q8MRzPYUz6DqLHhJR3wcmniFRgkiEa7MxE/mRe00y0VGwH7xi7Aoc\n"
+ "emXPrtD4JwN5bssbcxWGAKYYzaMQMA4wDAYDVR0TAQH/BAIwADAKBggqhkjOPQQD\n"
+ "AgNJADBGAiEAtsnWuRQXtw2xbieC78Y8SVEtTjcZUx8uZyQe1GPLfGICIQDR4fNY\n"
+ "yg3PC94ydPNQZVsFxAne32CbonWWsokalTFpUQ==\n"
+ "-----END CERTIFICATE-----\n";
+static const char kCommonNamePermitted[] = "foo.permitted1.test";
+
+// kCommonNameNotPermittedLeaf is a leaf certificate signed by
+// kConstrainedIntermediate. Its common name is not permitted by the name
+// constraints.
+static const char kCommonNameNotPermittedLeaf[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBazCCARCgAwIBAgIBBDAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv\n"
+ "bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw\n"
+ "MTAwMDAwMFowQTEiMCAGA1UEChMZQ29tbW9uIG5hbWUgbm90IHBlcm1pdHRlZDEb\n"
+ "MBkGA1UEAxMSbm90LXBlcm1pdHRlZC50ZXN0MFkwEwYHKoZIzj0CAQYIKoZIzj0D\n"
+ "AQcDQgAEzfghKuWf0JoXb0Drp09C3yXMSQQ1byt+AUaymvsHOWsxQ9v1Q+vkF/IM\n"
+ "HRqGTk2TyxrB2iClVEn/Uu+YtYox1KMQMA4wDAYDVR0TAQH/BAIwADAKBggqhkjO\n"
+ "PQQDAgNJADBGAiEAxaUslxmoWL1tIvnDz7gDkto/HcmdU0jHVuUQLXcCG8wCIQCN\n"
+ "5xZjitlCQU8UB5qSu9wH4B+0JcVO3Ss4Az76HEJWMw==\n"
+ "-----END CERTIFICATE-----\n";
+static const char kCommonNameNotPermitted[] = "not-permitted.test";
+
+// kCommonNameNotPermittedWithSANsLeaf is a leaf certificate signed by
+// kConstrainedIntermediate. Its common name is not permitted by the name
+// constraints but it has a SAN list.
+static const char kCommonNameNotPermittedWithSANsLeaf[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBqTCCAU+gAwIBAgIBBjAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv\n"
+ "bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw\n"
+ "MTAwMDAwMFowSzEsMCoGA1UEChMjQ29tbW9uIG5hbWUgbm90IHBlcm1pdHRlZCB3\n"
+ "aXRoIFNBTlMxGzAZBgNVBAMTEm5vdC1wZXJtaXR0ZWQudGVzdDBZMBMGByqGSM49\n"
+ "AgEGCCqGSM49AwEHA0IABKsn9wOApXFHrqhLdQgbFSeaSoAIbxgO0zVSRZUb5naR\n"
+ "93zoL3MFOvZEF8xiEqh7le+l3XuUig0fwqpcsZzRNJajRTBDMAwGA1UdEwEB/wQC\n"
+ "MAAwMwYDVR0RBCwwKoITZm9vLnBlcm1pdHRlZDEudGVzdIITZm9vLnBlcm1pdHRl\n"
+ "ZDIudGVzdDAKBggqhkjOPQQDAgNIADBFAiACk+1f184KkKAXuntmrz+Ygcq8MiZl\n"
+ "4delx44FtcNaegIhAIA5nYfzxNcTXxDo3U+x1vSLH6Y7faLvHiFySp7O//q+\n"
+ "-----END CERTIFICATE-----\n";
+static const char kCommonNameNotPermittedWithSANs[] = "not-permitted.test";
+
+// kCommonNameNotDNSLeaf is a leaf certificate signed by
+// kConstrainedIntermediate. Its common name is not a DNS name.
+static const char kCommonNameNotDNSLeaf[] =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIBYTCCAQagAwIBAgIBCDAKBggqhkjOPQQDAjAoMSYwJAYDVQQDEx1OYW1lIENv\n"
+ "bnN0cmFpbnRzIEludGVybWVkaWF0ZTAgFw0wMDAxMDEwMDAwMDBaGA8yMDk5MDEw\n"
+ "MTAwMDAwMFowNzEcMBoGA1UEChMTQ29tbW9uIG5hbWUgbm90IEROUzEXMBUGA1UE\n"
+ "AxMOTm90IGEgRE5TIG5hbWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASnueyc\n"
+ "Zxtnw5ke2J2T0/LwAK37auQP/RSFd9mem+BJVbgviawtAlignJmafp7Zw4/GdYEJ\n"
+ "Vm8qlriOJtluvXGcoxAwDjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0kAMEYC\n"
+ "IQChUAmVNI39VHe0zemRE09VDcSEgOxr1nTvjLcg/Q8pVQIhAJYZnJI0YZAi05QH\n"
+ "RHNlAkTK2TnUaVn3fGSylaLiFS1r\n"
+ "-----END CERTIFICATE-----\n";
+static const char kCommonNameNotDNS[] = "Not a DNS name";
+
// CertFromPEM parses the given, NUL-terminated pem block and returns an
// |X509*|.
static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
@@ -723,7 +880,8 @@
const std::vector<X509 *> &intermediates,
const std::vector<X509_CRL *> &crls, unsigned long flags,
bool use_additional_untrusted,
- std::function<void(X509_VERIFY_PARAM *)> configure_callback) {
+ std::function<void(X509_VERIFY_PARAM *)> configure_callback,
+ int (*verify_callback)(int, X509_STORE_CTX *) = nullptr) {
bssl::UniquePtr<STACK_OF(X509)> roots_stack(CertsToStack(roots));
bssl::UniquePtr<STACK_OF(X509)> intermediates_stack(
CertsToStack(intermediates));
@@ -1169,9 +1327,11 @@
uint8_t pub_bytes[32], priv_bytes[64];
ED25519_keypair(pub_bytes, priv_bytes);
- bssl::UniquePtr<EVP_PKEY> pub(EVP_PKEY_new_ed25519_public(pub_bytes));
+ bssl::UniquePtr<EVP_PKEY> pub(
+ EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32));
ASSERT_TRUE(pub);
- bssl::UniquePtr<EVP_PKEY> priv(EVP_PKEY_new_ed25519_private(priv_bytes));
+ bssl::UniquePtr<EVP_PKEY> priv(
+ EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_bytes, 32));
ASSERT_TRUE(priv);
bssl::ScopedEVP_MD_CTX md_ctx;
@@ -1744,3 +1904,172 @@
ASSERT_TRUE(cert2);
EXPECT_EQ(0, X509_cmp(cert.get(), cert2.get()));
}
+
+TEST(X509Test, CommonNameFallback) {
+ bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot);
+ ASSERT_TRUE(root);
+ bssl::UniquePtr<X509> with_sans = CertFromPEM(kCommonNameWithSANs);
+ ASSERT_TRUE(with_sans);
+ bssl::UniquePtr<X509> without_sans = CertFromPEM(kCommonNameWithoutSANs);
+ ASSERT_TRUE(without_sans);
+ bssl::UniquePtr<X509> with_email = CertFromPEM(kCommonNameWithEmailSAN);
+ ASSERT_TRUE(with_email);
+ bssl::UniquePtr<X509> with_ip = CertFromPEM(kCommonNameWithIPSAN);
+ ASSERT_TRUE(with_ip);
+
+ auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) {
+ return Verify(
+ leaf, {root.get()}, {}, {}, 0, false, [&](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(X509_VERIFY_PARAM_set1_host(param, host, strlen(host)));
+ X509_VERIFY_PARAM_set_hostflags(param, flags);
+ });
+ };
+
+ // By default, the common name is ignored if the SAN list is present but
+ // otherwise is checked.
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_sans.get(), 0 /* no flags */, "foo.host1.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), 0 /* no flags */, "foo.host2.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), 0 /* no flags */, "foo.host3.test"));
+ EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(), 0 /* no flags */,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_email.get(), 0 /* no flags */, "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_ip.get(), 0 /* no flags */, "foo.host1.test"));
+
+ // X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT is ignored.
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host2.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host3.test"));
+ EXPECT_EQ(X509_V_OK, verify_cert(without_sans.get(),
+ X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_email.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_ip.get(), X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
+ "foo.host1.test"));
+
+ // X509_CHECK_FLAG_NEVER_CHECK_SUBJECT implements the correct behavior: the
+ // common name is never checked.
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host2.test"));
+ EXPECT_EQ(X509_V_OK,
+ verify_cert(with_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host3.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(without_sans.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_email.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host1.test"));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(with_ip.get(), X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
+ "foo.host1.test"));
+}
+
+TEST(X509Test, LooksLikeDNSName) {
+ static const char *kValid[] = {
+ "example.com",
+ "eXample123-.com",
+ "*.example.com",
+ "exa_mple.com",
+ "example.com.",
+ "project-dev:us-central1:main",
+ };
+ static const char *kInvalid[] = {
+ "-eXample123-.com",
+ "",
+ ".",
+ "*",
+ "*.",
+ "example..com",
+ ".example.com",
+ "example.com..",
+ "*foo.example.com",
+ "foo.*.example.com",
+ "foo,bar",
+ };
+
+ for (const char *str : kValid) {
+ SCOPED_TRACE(str);
+ EXPECT_TRUE(x509v3_looks_like_dns_name(
+ reinterpret_cast<const uint8_t *>(str), strlen(str)));
+ }
+ for (const char *str : kInvalid) {
+ SCOPED_TRACE(str);
+ EXPECT_FALSE(x509v3_looks_like_dns_name(
+ reinterpret_cast<const uint8_t *>(str), strlen(str)));
+ }
+}
+
+TEST(X509Test, CommonNameAndNameConstraints) {
+ bssl::UniquePtr<X509> root = CertFromPEM(kSANTypesRoot);
+ ASSERT_TRUE(root);
+ bssl::UniquePtr<X509> intermediate = CertFromPEM(kConstrainedIntermediate);
+ ASSERT_TRUE(intermediate);
+ bssl::UniquePtr<X509> permitted = CertFromPEM(kCommonNamePermittedLeaf);
+ ASSERT_TRUE(permitted);
+ bssl::UniquePtr<X509> not_permitted =
+ CertFromPEM(kCommonNameNotPermittedLeaf);
+ ASSERT_TRUE(not_permitted);
+ bssl::UniquePtr<X509> not_permitted_with_sans =
+ CertFromPEM(kCommonNameNotPermittedWithSANsLeaf);
+ ASSERT_TRUE(not_permitted_with_sans);
+ bssl::UniquePtr<X509> not_dns = CertFromPEM(kCommonNameNotDNSLeaf);
+ ASSERT_TRUE(not_dns);
+
+ auto verify_cert = [&](X509 *leaf, unsigned flags, const char *host) {
+ return Verify(
+ leaf, {root.get()}, {intermediate.get()}, {}, 0, false,
+ [&](X509_VERIFY_PARAM *param) {
+ ASSERT_TRUE(X509_VERIFY_PARAM_set1_host(param, host, strlen(host)));
+ X509_VERIFY_PARAM_set_hostflags(param, flags);
+ });
+ };
+
+ // Certificates which would otherwise trigger the common name fallback are
+ // rejected whenever there are name constraints. We do this whether or not
+ // the common name matches the constraints.
+ EXPECT_EQ(
+ X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS,
+ verify_cert(permitted.get(), 0 /* no flags */, kCommonNamePermitted));
+ EXPECT_EQ(X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS,
+ verify_cert(not_permitted.get(), 0 /* no flags */,
+ kCommonNameNotPermitted));
+
+ // This occurs even if the built-in name checks aren't used. The caller may
+ // separately call |X509_check_host|.
+ EXPECT_EQ(X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS,
+ Verify(not_permitted.get(), {root.get()}, {intermediate.get()}, {},
+ 0 /* no flags */, false, nullptr));
+
+ // If the leaf certificate has SANs, the common name fallback is always
+ // disabled, so the name constraints do not apply.
+ EXPECT_EQ(X509_V_OK, Verify(not_permitted_with_sans.get(), {root.get()},
+ {intermediate.get()}, {}, 0, false, nullptr));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(not_permitted_with_sans.get(), 0 /* no flags */,
+ kCommonNameNotPermittedWithSANs));
+
+ // If the common name does not look like a DNS name, we apply neither name
+ // constraints nor common name fallback.
+ EXPECT_EQ(X509_V_OK, Verify(not_dns.get(), {root.get()}, {intermediate.get()},
+ {}, 0, false, nullptr));
+ EXPECT_EQ(X509_V_ERR_HOSTNAME_MISMATCH,
+ verify_cert(not_dns.get(), 0 /* no flags */, kCommonNameNotDNS));
+}
diff --git a/src/crypto/x509/x509_txt.c b/src/crypto/x509/x509_txt.c
index 753e720..8e6ac27 100644
--- a/src/crypto/x509/x509_txt.c
+++ b/src/crypto/x509/x509_txt.c
@@ -54,13 +54,10 @@
* copied and put under another distribution licence
* [including the GNU Public Licence.] */
-#include <openssl/mem.h>
#include <openssl/x509.h>
const char *X509_verify_cert_error_string(long n)
{
- static char buf[100];
-
switch ((int)n) {
case X509_V_OK:
return ("ok");
@@ -198,8 +195,10 @@
case X509_V_ERR_STORE_LOOKUP:
return ("Issuer certificate lookup error");
+ case X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS:
+ return "Issuer has name constraints but leaf has no SANs";
+
default:
- BIO_snprintf(buf, sizeof buf, "error number %ld", n);
- return (buf);
+ return "unknown certificate verification error";
}
}
diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c
index 5af3fb3..fff97fa 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -70,6 +70,7 @@
#include "vpm_int.h"
#include "../internal.h"
+#include "../x509v3/internal.h"
static CRYPTO_EX_DATA_CLASS g_ex_data_class =
CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA;
@@ -710,13 +711,40 @@
return ok;
}
+static int reject_dns_name_in_common_name(X509 *x509)
+{
+ X509_NAME *name = X509_get_subject_name(x509);
+ int i = -1;
+ for (;;) {
+ i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+ if (i == -1) {
+ return X509_V_OK;
+ }
+
+ X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, i);
+ ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(entry);
+ unsigned char *idval;
+ int idlen = ASN1_STRING_to_UTF8(&idval, common_name);
+ if (idlen < 0) {
+ return X509_V_ERR_OUT_OF_MEM;
+ }
+ /* Only process attributes that look like host names. Note it is
+ * important that this check be mirrored in |X509_check_host|. */
+ int looks_like_dns = x509v3_looks_like_dns_name(idval, (size_t)idlen);
+ OPENSSL_free(idval);
+ if (looks_like_dns) {
+ return X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS;
+ }
+ }
+}
+
static int check_name_constraints(X509_STORE_CTX *ctx)
{
- X509 *x;
int i, j, rv;
+ int has_name_constraints = 0;
/* Check name constraints for all certificates */
for (i = sk_X509_num(ctx->chain) - 1; i >= 0; i--) {
- x = sk_X509_value(ctx->chain, i);
+ X509 *x = sk_X509_value(ctx->chain, i);
/* Ignore self issued certs unless last in chain */
if (i && (x->ex_flags & EXFLAG_SI))
continue;
@@ -729,6 +757,7 @@
for (j = sk_X509_num(ctx->chain) - 1; j > i; j--) {
NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc;
if (nc) {
+ has_name_constraints = 1;
rv = NAME_CONSTRAINTS_check(x, nc);
switch (rv) {
case X509_V_OK:
@@ -747,6 +776,36 @@
}
}
}
+
+ /* Name constraints do not match against the common name, but
+ * |X509_check_host| still implements the legacy behavior where, on
+ * certificates lacking a SAN list, DNS-like names in the common name are
+ * checked instead.
+ *
+ * While we could apply the name constraints to the common name, name
+ * constraints are rare enough that can hold such certificates to a higher
+ * standard. Note this does not make "DNS-like" heuristic failures any
+ * worse. A decorative common-name misidentified as a DNS name would fail
+ * the name constraint anyway. */
+ X509 *leaf = sk_X509_value(ctx->chain, 0);
+ if (has_name_constraints && leaf->altname == NULL) {
+ rv = reject_dns_name_in_common_name(leaf);
+ switch (rv) {
+ case X509_V_OK:
+ break;
+ case X509_V_ERR_OUT_OF_MEM:
+ ctx->error = rv;
+ return 0;
+ default:
+ ctx->error = rv;
+ ctx->error_depth = i;
+ ctx->current_cert = leaf;
+ if (!ctx->verify_cb(0, ctx))
+ return 0;
+ break;
+ }
+ }
+
return 1;
}
diff --git a/src/crypto/x509v3/internal.h b/src/crypto/x509v3/internal.h
index e6be684..c143d73 100644
--- a/src/crypto/x509v3/internal.h
+++ b/src/crypto/x509v3/internal.h
@@ -43,6 +43,11 @@
// followed by '.'. Otherwise, it returns a non-zero number.
int x509v3_name_cmp(const char *name, const char *cmp);
+// x509v3_looks_like_dns_name returns one if |in| looks like a DNS name and zero
+// otherwise.
+OPENSSL_EXPORT int x509v3_looks_like_dns_name(const unsigned char *in,
+ size_t len);
+
#if defined(__cplusplus)
} /* extern C */
diff --git a/src/crypto/x509v3/v3_utl.c b/src/crypto/x509v3/v3_utl.c
index 2a293dc..86c4940 100644
--- a/src/crypto/x509v3/v3_utl.c
+++ b/src/crypto/x509v3/v3_utl.c
@@ -909,6 +909,53 @@
subject, subject_len, flags);
}
+int x509v3_looks_like_dns_name(const unsigned char *in, size_t len) {
+ /* This function is used as a heuristic for whether a common name is a
+ * hostname to be matched, or merely a decorative name to describe the
+ * subject. This heuristic must be applied to both name constraints and the
+ * common name fallback, so it must be loose enough to accept hostname
+ * common names, and tight enough to reject decorative common names. */
+
+ if (len > 0 && in[len - 1] == '.') {
+ len--;
+ }
+
+ /* Wildcards are allowed in front. */
+ if (len >= 2 && in[0] == '*' && in[1] == '.') {
+ in += 2;
+ len -= 2;
+ }
+
+ if (len == 0) {
+ return 0;
+ }
+
+ size_t label_start = 0;
+ for (size_t i = 0; i < len; i++) {
+ unsigned char c = in[i];
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c == '-' && i > label_start) ||
+ /* These are not valid characters in hostnames, but commonly found
+ * in deployments outside the Web PKI. */
+ c == '_' ||
+ c == ':') {
+ continue;
+ }
+
+ /* Labels must not be empty. */
+ if (c == '.' && i > label_start && i < len - 1) {
+ label_start = i + 1;
+ continue;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
/*
* Compare an ASN1_STRING to a supplied string. If they match return 1. If
* cmp_type > 0 only compare if string matches the type, otherwise convert it
@@ -916,8 +963,8 @@
*/
static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal,
- unsigned int flags, const char *b, size_t blen,
- char **peername)
+ unsigned int flags, int check_type, const char *b,
+ size_t blen, char **peername)
{
int rv = 0;
@@ -938,7 +985,17 @@
astrlen = ASN1_STRING_to_UTF8(&astr, a);
if (astrlen < 0)
return -1;
- rv = equal(astr, astrlen, (unsigned char *)b, blen, flags);
+ /*
+ * We check the common name against DNS name constraints if it passes
+ * |x509v3_looks_like_dns_name|. Thus we must not consider common names
+ * for DNS fallbacks if they fail this check.
+ */
+ if (check_type == GEN_DNS &&
+ !x509v3_looks_like_dns_name(astr, astrlen)) {
+ rv = 0;
+ } else {
+ rv = equal(astr, astrlen, (unsigned char *)b, blen, flags);
+ }
if (rv > 0 && peername)
*peername = BUF_strndup((char *)astr, astrlen);
OPENSSL_free(astr);
@@ -955,7 +1012,6 @@
int j;
int cnid = NID_undef;
int alt_type;
- int san_present = 0;
int rv = 0;
equal_fn equal;
@@ -988,7 +1044,6 @@
gen = sk_GENERAL_NAME_value(gens, i);
if (gen->type != check_type)
continue;
- san_present = 1;
if (check_type == GEN_EMAIL)
cstr = gen->d.rfc822Name;
else if (check_type == GEN_DNS)
@@ -996,21 +1051,16 @@
else
cstr = gen->d.iPAddress;
/* Positive on success, negative on error! */
- if ((rv = do_check_string(cstr, alt_type, equal, flags,
+ if ((rv = do_check_string(cstr, alt_type, equal, flags, check_type,
chk, chklen, peername)) != 0)
break;
}
GENERAL_NAMES_free(gens);
- if (rv != 0)
- return rv;
- if (cnid == NID_undef
- || (san_present
- && !(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT)))
- return 0;
+ return rv;
}
/* We're done if CN-ID is not pertinent */
- if (cnid == NID_undef)
+ if (cnid == NID_undef || (flags & X509_CHECK_FLAG_NEVER_CHECK_SUBJECT))
return 0;
j = -1;
@@ -1021,7 +1071,7 @@
ne = X509_NAME_get_entry(name, j);
str = X509_NAME_ENTRY_get_data(ne);
/* Positive on success, negative on error! */
- if ((rv = do_check_string(str, -1, equal, flags,
+ if ((rv = do_check_string(str, -1, equal, flags, check_type,
chk, chklen, peername)) != 0)
return rv;
}
diff --git a/src/crypto/x509v3/v3name_test.cc b/src/crypto/x509v3/v3name_test.cc
index 0736120..2dcdd87 100644
--- a/src/crypto/x509v3/v3name_test.cc
+++ b/src/crypto/x509v3/v3name_test.cc
@@ -65,6 +65,7 @@
#include <openssl/x509v3.h>
#include "../internal.h"
+#include "internal.h"
static const char *const names[] = {
@@ -344,7 +345,7 @@
ret = X509_check_host(crt, name, namelen, 0, NULL);
match = -1;
if (ret < 0) {
- fprintf(stderr, "internal error in X509_check_host");
+ fprintf(stderr, "internal error in X509_check_host\n");
++errors;
} else if (fn->host) {
if (ret == 1 && !samename)
@@ -359,7 +360,7 @@
X509_CHECK_FLAG_NO_WILDCARDS, NULL);
match = -1;
if (ret < 0) {
- fprintf(stderr, "internal error in X509_check_host");
+ fprintf(stderr, "internal error in X509_check_host\n");
++errors;
} else if (fn->host) {
if (ret == 1 && !samename)
@@ -385,12 +386,21 @@
}
}
-// TOOD(davidben): Convert this test to GTest more thoroughly.
+// TODO(davidben): Convert this test to GTest more thoroughly.
TEST(X509V3Test, NameTest) {
const struct set_name_fn *pfn = name_fns;
while (pfn->name) {
const char *const *pname = names;
while (*pname) {
+ // The common name fallback requires the name look sufficiently
+ // DNS-like.
+ if (strcmp(pfn->name, "set CN") == 0 &&
+ !x509v3_looks_like_dns_name(
+ reinterpret_cast<const unsigned char*>(*pname),
+ strlen(*pname))) {
+ ++pname;
+ continue;
+ }
bssl::UniquePtr<X509> crt(make_cert());
ASSERT_TRUE(crt);
ASSERT_TRUE(pfn->fn(crt.get(), *pname));
diff --git a/src/decrepit/blowfish/blowfish_test.cc b/src/decrepit/blowfish/blowfish_test.cc
index d19346a..4b08fe4 100644
--- a/src/decrepit/blowfish/blowfish_test.cc
+++ b/src/decrepit/blowfish/blowfish_test.cc
@@ -19,7 +19,7 @@
#include "../../crypto/internal.h"
#include "../../crypto/test/test_util.h"
-struct TestCase {
+struct BlowfishTestCase {
uint8_t key[16];
uint8_t plaintext[16];
uint8_t iv[8];
@@ -28,7 +28,7 @@
uint8_t cfb_ciphertext[16];
};
-static const TestCase kTests[] = {
+static const BlowfishTestCase kTests[] = {
// Randomly generated test cases. Checked against vanilla OpenSSL.
{
{0xbb, 0x56, 0xb1, 0x27, 0x7c, 0x4c, 0xdd, 0x5a, 0x99, 0x90, 0x1e, 0x6f,
diff --git a/src/decrepit/cast/cast_test.cc b/src/decrepit/cast/cast_test.cc
index 05a04cb..fd5f2a9 100644
--- a/src/decrepit/cast/cast_test.cc
+++ b/src/decrepit/cast/cast_test.cc
@@ -19,7 +19,7 @@
#include "../../crypto/internal.h"
#include "../../crypto/test/test_util.h"
-struct TestCase {
+struct CastTestCase {
uint8_t key[16];
uint8_t plaintext[16];
uint8_t iv[8];
@@ -27,7 +27,7 @@
uint8_t cbc_ciphertext[24];
};
-static const TestCase kTests[] = {
+static const CastTestCase kTests[] = {
// Randomly generated test cases. Checked against vanilla OpenSSL.
{
{0xbb, 0x56, 0xb1, 0x27, 0x7c, 0x4c, 0xdd, 0x5a, 0x99, 0x90, 0x1e, 0x6f,
diff --git a/src/include/openssl/aes.h b/src/include/openssl/aes.h
index 3606bfc..e560625 100644
--- a/src/include/openssl/aes.h
+++ b/src/include/openssl/aes.h
@@ -6,7 +6,7 @@
* are met:
*
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
@@ -163,6 +163,31 @@
size_t in_len);
+// AES key wrap with padding.
+//
+// These functions implement AES Key Wrap with Padding mode, as defined in RFC
+// 5649. They should never be used except to interoperate with existing systems
+// that use this mode.
+
+// AES_wrap_key_padded performs a padded AES key wrap on |in| which must be
+// between 1 and 2^32-1 bytes. |key| must have been configured for encryption.
+// On success it writes at most |max_out| bytes of ciphertext to |out|, sets
+// |*out_len| to the number of bytes written, and returns one. On failure it
+// returns zero. To ensure success, set |max_out| to at least |in_len| + 15.
+OPENSSL_EXPORT int AES_wrap_key_padded(const AES_KEY *key, uint8_t *out,
+ size_t *out_len, size_t max_out,
+ const uint8_t *in, size_t in_len);
+
+// AES_unwrap_key_padded performs a padded AES key unwrap on |in| which must be
+// a multiple of 8 bytes. |key| must have been configured for decryption. On
+// success it writes at most |max_out| bytes to |out|, sets |*out_len| to the
+// number of bytes written, and returns one. On failure it returns zero. Setting
+// |max_out| to |in_len| is a sensible estimate.
+OPENSSL_EXPORT int AES_unwrap_key_padded(const AES_KEY *key, uint8_t *out,
+ size_t *out_len, size_t max_out,
+ const uint8_t *in, size_t in_len);
+
+
#if defined(__cplusplus)
} // extern C
#endif
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index 7fe232f..cb1affa 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -291,6 +291,24 @@
#endif
#endif // OPENSSL_ASM_INCOMPATIBLE
+#if defined(__cplusplus)
+// enums can be predeclared, but only in C++ and only if given an explicit type.
+// C doesn't support setting an explicit type for enums thus a #define is used
+// to do this only for C++. However, the ABI type between C and C++ need to have
+// equal sizes, which is confirmed in a unittest.
+#define BORINGSSL_ENUM_INT : int
+enum ssl_early_data_reason_t BORINGSSL_ENUM_INT;
+enum ssl_encryption_level_t BORINGSSL_ENUM_INT;
+enum ssl_private_key_result_t BORINGSSL_ENUM_INT;
+enum ssl_renegotiate_mode_t BORINGSSL_ENUM_INT;
+enum ssl_select_cert_result_t BORINGSSL_ENUM_INT;
+enum ssl_select_cert_result_t BORINGSSL_ENUM_INT;
+enum ssl_ticket_aead_result_t BORINGSSL_ENUM_INT;
+enum ssl_verify_result_t BORINGSSL_ENUM_INT;
+#else
+#define BORINGSSL_ENUM_INT
+#endif
+
// CRYPTO_THREADID is a dummy value.
typedef int CRYPTO_THREADID;
diff --git a/src/include/openssl/cpu.h b/src/include/openssl/cpu.h
index ad5fc94..eb36a57 100644
--- a/src/include/openssl/cpu.h
+++ b/src/include/openssl/cpu.h
@@ -93,7 +93,7 @@
// bits in XCR0, so it is not necessary to check those.
extern uint32_t OPENSSL_ia32cap_P[4];
-#if defined(BORINGSSL_FIPS)
+#if defined(BORINGSSL_FIPS) && !defined(BORINGSSL_SHARED_LIBRARY)
const uint32_t *OPENSSL_ia32cap_get(void);
#else
OPENSSL_INLINE const uint32_t *OPENSSL_ia32cap_get(void) {
diff --git a/src/include/openssl/digest.h b/src/include/openssl/digest.h
index 1a1ca29..c7c6797 100644
--- a/src/include/openssl/digest.h
+++ b/src/include/openssl/digest.h
@@ -269,6 +269,11 @@
// EVP_MD_CTX_destroy calls |EVP_MD_CTX_free|.
OPENSSL_EXPORT void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx);
+// EVP_DigestFinalXOF returns zero and adds an error to the error queue.
+// BoringSSL does not support any XOF digests.
+OPENSSL_EXPORT int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, uint8_t *out,
+ size_t len);
+
struct evp_md_pctx_ops;
diff --git a/src/include/openssl/dsa.h b/src/include/openssl/dsa.h
index bed93c5..8e3b9b3 100644
--- a/src/include/openssl/dsa.h
+++ b/src/include/openssl/dsa.h
@@ -436,5 +436,6 @@
#define DSA_R_BAD_VERSION 104
#define DSA_R_DECODE_ERROR 105
#define DSA_R_ENCODE_ERROR 106
+#define DSA_R_INVALID_PARAMETERS 107
#endif // OPENSSL_HEADER_DSA_H
diff --git a/src/include/openssl/engine.h b/src/include/openssl/engine.h
index 9d45952..ce60de4 100644
--- a/src/include/openssl/engine.h
+++ b/src/include/openssl/engine.h
@@ -40,8 +40,8 @@
OPENSSL_EXPORT ENGINE *ENGINE_new(void);
// ENGINE_free decrements the reference counts for all methods linked from
-// |engine| and frees |engine| itself.
-OPENSSL_EXPORT void ENGINE_free(ENGINE *engine);
+// |engine| and frees |engine| itself. It returns one.
+OPENSSL_EXPORT int ENGINE_free(ENGINE *engine);
// Method accessors.
diff --git a/src/include/openssl/evp.h b/src/include/openssl/evp.h
index c446984..999e19d 100644
--- a/src/include/openssl/evp.h
+++ b/src/include/openssl/evp.h
@@ -170,22 +170,13 @@
OPENSSL_EXPORT EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
OPENSSL_EXPORT EC_KEY *EVP_PKEY_get1_EC_KEY(const EVP_PKEY *pkey);
-// EVP_PKEY_new_ed25519_public returns a newly allocated |EVP_PKEY| wrapping an
-// Ed25519 public key, or NULL on allocation error.
-OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_ed25519_public(
- const uint8_t public_key[32]);
-
-// EVP_PKEY_new_ed25519_private returns a newly allocated |EVP_PKEY| wrapping an
-// Ed25519 private key, or NULL on allocation error.
-OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_ed25519_private(
- const uint8_t private_key[64]);
-
#define EVP_PKEY_NONE NID_undef
#define EVP_PKEY_RSA NID_rsaEncryption
#define EVP_PKEY_RSA_PSS NID_rsassaPss
#define EVP_PKEY_DSA NID_dsa
#define EVP_PKEY_EC NID_X9_62_id_ecPublicKey
#define EVP_PKEY_ED25519 NID_ED25519
+#define EVP_PKEY_X25519 NID_X25519
// EVP_PKEY_assign sets the underlying key of |pkey| to |key|, which must be of
// the given type. It returns one if successful or zero if the |type| argument
@@ -241,6 +232,48 @@
OPENSSL_EXPORT int EVP_marshal_private_key(CBB *cbb, const EVP_PKEY *key);
+// Raw keys
+//
+// Some keys types support a "raw" serialization. Currently the only supported
+// raw format is Ed25519, where the public key and private key formats are those
+// specified in RFC 8032. Note the RFC 8032 private key format is the 32-byte
+// prefix of |ED25519_sign|'s 64-byte private key.
+
+// EVP_PKEY_new_raw_private_key returns a newly allocated |EVP_PKEY| wrapping a
+// private key of the specified type. It returns one on success and zero on
+// error.
+OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
+ const uint8_t *in,
+ size_t len);
+
+// EVP_PKEY_new_raw_public_key returns a newly allocated |EVP_PKEY| wrapping a
+// public key of the specified type. It returns one on success and zero on
+// error.
+OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
+ const uint8_t *in,
+ size_t len);
+
+// EVP_PKEY_get_raw_private_key outputs the private key for |pkey| in raw form.
+// If |out| is NULL, it sets |*out_len| to the size of the raw private key.
+// Otherwise, it writes at most |*out_len| bytes to |out| and sets |*out_len| to
+// the number of bytes written.
+//
+// It returns one on success and zero if |pkey| has no private key, the key
+// type does not support a raw format, or the buffer is too small.
+OPENSSL_EXPORT int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey,
+ uint8_t *out, size_t *out_len);
+
+// EVP_PKEY_get_raw_public_key outputs the public key for |pkey| in raw form.
+// If |out| is NULL, it sets |*out_len| to the size of the raw public key.
+// Otherwise, it writes at most |*out_len| bytes to |out| and sets |*out_len| to
+// the number of bytes written.
+//
+// It returns one on success and zero if |pkey| has no public key, the key
+// type does not support a raw format, or the buffer is too small.
+OPENSSL_EXPORT int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey,
+ uint8_t *out, size_t *out_len);
+
+
// Signing
// EVP_DigestSignInit sets up |ctx| for a signing operation with |type| and
@@ -874,6 +907,26 @@
OPENSSL_EXPORT int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx,
int encoding);
+// EVP_PKEY_set1_tls_encodedpoint replaces |pkey| with a public key encoded by
+// |in|. It returns one on success and zero on error.
+//
+// This function only works on X25519 keys.
+OPENSSL_EXPORT int EVP_PKEY_set1_tls_encodedpoint(EVP_PKEY *pkey,
+ const uint8_t *in,
+ size_t len);
+
+// EVP_PKEY_get1_tls_encodedpoint sets |*out_ptr| to a newly-allocated buffer
+// containing the raw encoded public key for |pkey|. The caller must call
+// |OPENSSL_free| to release this buffer. The function returns the length of the
+// buffer on success and zero on error.
+//
+// This function only works on X25519 keys.
+OPENSSL_EXPORT size_t EVP_PKEY_get1_tls_encodedpoint(const EVP_PKEY *pkey,
+ uint8_t **out_ptr);
+
+// EVP_PKEY_base_id calls |EVP_PKEY_id|.
+OPENSSL_EXPORT int EVP_PKEY_base_id(const EVP_PKEY *pkey);
+
// Preprocessor compatibility section (hidden).
//
@@ -962,5 +1015,6 @@
#define EVP_R_INVALID_SIGNATURE 131
#define EVP_R_MEMORY_LIMIT_EXCEEDED 132
#define EVP_R_INVALID_PARAMETERS 133
+#define EVP_R_INVALID_PEER_KEY 134
#endif // OPENSSL_HEADER_EVP_H
diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h
index 270d443..cea975a 100644
--- a/src/include/openssl/nid.h
+++ b/src/include/openssl/nid.h
@@ -4237,6 +4237,9 @@
#define SN_CECPQ2 "CECPQ2"
#define NID_CECPQ2 959
+#define SN_CECPQ2b "CECPQ2b"
+#define NID_CECPQ2b 960
+
#if defined(__cplusplus)
} /* extern C */
diff --git a/src/include/openssl/pkcs8.h b/src/include/openssl/pkcs8.h
index ee48f19..385b995 100644
--- a/src/include/openssl/pkcs8.h
+++ b/src/include/openssl/pkcs8.h
@@ -168,12 +168,18 @@
// PKCS12_parse calls |PKCS12_get_key_and_certs| on the ASN.1 data stored in
// |p12|. The |out_pkey| and |out_cert| arguments must not be NULL and, on
-// successful exit, the private key and first certificate will be stored in
+// successful exit, the private key and matching certificate will be stored in
// them. The |out_ca_certs| argument may be NULL but, if not, then any extra
// certificates will be appended to |*out_ca_certs|. If |*out_ca_certs| is NULL
// then it will be set to a freshly allocated stack containing the extra certs.
//
+// Note if |p12| does not contain a private key, both |*out_pkey| and
+// |*out_cert| will be set to NULL and all certificates will be returned via
+// |*out_ca_certs|.
+//
// It returns one on success and zero on error.
+//
+// Use |PKCS12_get_key_and_certs| instead.
OPENSSL_EXPORT int PKCS12_parse(const PKCS12 *p12, const char *password,
EVP_PKEY **out_pkey, X509 **out_cert,
STACK_OF(X509) **out_ca_certs);
diff --git a/src/include/openssl/siphash.h b/src/include/openssl/siphash.h
new file mode 100644
index 0000000..fe08aa7
--- /dev/null
+++ b/src/include/openssl/siphash.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_SIPHASH_H
+#define OPENSSL_HEADER_SIPHASH_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// SipHash is a fast, secure PRF that is often used for hash tables.
+
+
+// SIPHASH_24 implements SipHash-2-4. See https://131002.net/siphash/siphash.pdf
+OPENSSL_EXPORT uint64_t SIPHASH_24(const uint64_t key[2], const uint8_t *input,
+ size_t input_len);
+
+
+#if defined(__cplusplus)
+} // extern C
+#endif
+
+#endif // OPENSSL_HEADER_SIPHASH_H
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 4240c29..3d2bc07 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1160,7 +1160,7 @@
// Custom private keys.
-enum ssl_private_key_result_t {
+enum ssl_private_key_result_t BORINGSSL_ENUM_INT {
ssl_private_key_success,
ssl_private_key_retry,
ssl_private_key_failure,
@@ -1296,7 +1296,8 @@
OPENSSL_EXPORT const char *SSL_CIPHER_standard_name(const SSL_CIPHER *cipher);
// SSL_CIPHER_get_name returns the OpenSSL name of |cipher|. For example,
-// "ECDHE-RSA-AES128-GCM-SHA256".
+// "ECDHE-RSA-AES128-GCM-SHA256". Callers are recommended to use
+// |SSL_CIPHER_standard_name| instead.
OPENSSL_EXPORT const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher);
// SSL_CIPHER_get_kx_name returns a string that describes the key-exchange
@@ -1399,7 +1400,7 @@
// based on client preferences. An equal-preference is specified with square
// brackets, combining multiple selectors separated by |. For example:
//
-// [ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]
+// [TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]
//
// Once an equal-preference group is used, future directives must be
// opcode-less. Inside an equal-preference group, spaces are not allowed.
@@ -2039,13 +2040,13 @@
// On the client, ticket-based sessions use the same APIs as ID-based tickets.
// Callers do not need to handle them differently.
//
-// On the server, tickets are encrypted and authenticated with a secret key. By
-// default, an |SSL_CTX| generates a key on creation and uses it for the
-// lifetime of the |SSL_CTX|. Tickets are minted and processed
-// transparently. The following functions may be used to configure a persistent
-// key or implement more custom behavior, including key rotation and sharing
-// keys between multiple servers in a large deployment. There are three levels
-// of customisation possible:
+// On the server, tickets are encrypted and authenticated with a secret key.
+// By default, an |SSL_CTX| will manage session ticket encryption keys by
+// generating them internally and rotating every 48 hours. Tickets are minted
+// and processed transparently. The following functions may be used to configure
+// a persistent key or implement more custom behavior, including key rotation
+// and sharing keys between multiple servers in a large deployment. There are
+// three levels of customisation possible:
//
// 1) One can simply set the keys with |SSL_CTX_set_tlsext_ticket_keys|.
// 2) One can configure an |EVP_CIPHER_CTX| and |HMAC_CTX| directly for
@@ -2111,7 +2112,7 @@
// ssl_ticket_aead_result_t enumerates the possible results from decrypting a
// ticket with an |SSL_TICKET_AEAD_METHOD|.
-enum ssl_ticket_aead_result_t {
+enum ssl_ticket_aead_result_t BORINGSSL_ENUM_INT {
// ssl_ticket_aead_success indicates that the ticket was successfully
// decrypted.
ssl_ticket_aead_success,
@@ -2206,6 +2207,7 @@
#define SSL_CURVE_SECP521R1 25
#define SSL_CURVE_X25519 29
#define SSL_CURVE_CECPQ2 16696
+#define SSL_CURVE_CECPQ2b 65074
// SSL_get_curve_id returns the ID of the curve used by |ssl|'s most recently
// completed handshake or 0 if not applicable.
@@ -2285,7 +2287,7 @@
int (*callback)(int ok,
X509_STORE_CTX *store_ctx));
-enum ssl_verify_result_t {
+enum ssl_verify_result_t BORINGSSL_ENUM_INT {
ssl_verify_ok,
ssl_verify_invalid,
ssl_verify_retry,
@@ -3033,6 +3035,19 @@
OPENSSL_EXPORT const char *SSL_get_psk_identity(const SSL *ssl);
+// Post-quantum experiment signaling extension.
+//
+// *** EXPERIMENTAL ***
+//
+// In order to define a control group in an experiment of post-quantum key
+// agreements, clients and servers may send a non-IANA defined extension as a
+// signaling bit. These functions should not be used without explicit permission
+// from BoringSSL-team.
+
+OPENSSL_EXPORT void SSL_CTX_enable_pq_experiment_signal(SSL_CTX *ctx);
+OPENSSL_EXPORT int SSL_pq_experiment_signal_seen(const SSL *ssl);
+
+
// QUIC transport parameters.
//
// draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters
@@ -3099,6 +3114,10 @@
SSL *ssl, CRYPTO_BUFFER *dc, EVP_PKEY *pkey,
const SSL_PRIVATE_KEY_METHOD *key_method);
+// SSL_delegated_credential_used returns one if a delegated credential was used
+// and zero otherwise.
+OPENSSL_EXPORT int SSL_delegated_credential_used(const SSL *ssl);
+
// QUIC integration.
//
@@ -3135,7 +3154,7 @@
// ssl_encryption_level_t represents a specific QUIC encryption level used to
// transmit handshake messages.
-enum ssl_encryption_level_t {
+enum ssl_encryption_level_t BORINGSSL_ENUM_INT {
ssl_encryption_initial = 0,
ssl_encryption_early_data,
ssl_encryption_handshake,
@@ -3291,6 +3310,10 @@
// and |SSL_write| to send half-RTT data.
OPENSSL_EXPORT int SSL_in_early_data(const SSL *ssl);
+// SSL_SESSION_early_data_capable returns whether early data would have been
+// attempted with |session| if enabled.
+OPENSSL_EXPORT int SSL_SESSION_early_data_capable(const SSL_SESSION *session);
+
// SSL_early_data_accepted returns whether early data was accepted on the
// handshake performed by |ssl|.
OPENSSL_EXPORT int SSL_early_data_accepted(const SSL *ssl);
@@ -3304,12 +3327,45 @@
// |SSL_ERROR_EARLY_DATA_REJECTED|.
OPENSSL_EXPORT void SSL_reset_early_data_reject(SSL *ssl);
-// SSL_export_early_keying_material behaves like |SSL_export_keying_material|,
-// but it uses the early exporter. The operation will fail if |ssl| did not
-// negotiate TLS 1.3 or 0-RTT.
-OPENSSL_EXPORT int SSL_export_early_keying_material(
- SSL *ssl, uint8_t *out, size_t out_len, const char *label, size_t label_len,
- const uint8_t *context, size_t context_len);
+// SSL_get_ticket_age_skew returns the difference, in seconds, between the
+// client-sent ticket age and the server-computed value in TLS 1.3 server
+// connections which resumed a session.
+OPENSSL_EXPORT int32_t SSL_get_ticket_age_skew(const SSL *ssl);
+
+enum ssl_early_data_reason_t BORINGSSL_ENUM_INT {
+ // The handshake has not progressed far enough for the 0-RTT status to be
+ // known.
+ ssl_early_data_unknown,
+ // 0-RTT is disabled for this connection.
+ ssl_early_data_disabled,
+ // 0-RTT was accepted.
+ ssl_early_data_accepted,
+ // The negotiated protocol version does not support 0-RTT.
+ ssl_early_data_protocol_version,
+ // The peer declined to offer or accept 0-RTT for an unknown reason.
+ ssl_early_data_peer_declined,
+ // The client did not offer a session.
+ ssl_early_data_no_session_offered,
+ // The server declined to resume the session.
+ ssl_early_data_session_not_resumed,
+ // The session does not support 0-RTT.
+ ssl_early_data_unsupported_for_session,
+ // The server sent a HelloRetryRequest.
+ ssl_early_data_hello_retry_request,
+ // The negotiated ALPN protocol did not match the session.
+ ssl_early_data_alpn_mismatch,
+ // The connection negotiated Channel ID, which is incompatible with 0-RTT.
+ ssl_early_data_channel_id,
+ // The connection negotiated token binding, which is incompatible with 0-RTT.
+ ssl_early_data_token_binding,
+ // The client and server ticket age were too far apart.
+ ssl_early_data_ticket_age_skew,
+};
+
+// SSL_get_early_data_reason returns details why 0-RTT was accepted or rejected
+// on |ssl|. This is primarily useful on the server.
+OPENSSL_EXPORT enum ssl_early_data_reason_t SSL_get_early_data_reason(
+ const SSL *ssl);
// Alerts.
@@ -3522,7 +3578,7 @@
// such as HTTP/1.1, and not others, such as HTTP/2.
OPENSSL_EXPORT void SSL_set_shed_handshake_config(SSL *ssl, int enable);
-enum ssl_renegotiate_mode_t {
+enum ssl_renegotiate_mode_t BORINGSSL_ENUM_INT {
ssl_renegotiate_never = 0,
ssl_renegotiate_once,
ssl_renegotiate_freely,
@@ -3620,7 +3676,7 @@
// ssl_select_cert_result_t enumerates the possible results from selecting a
// certificate with |select_certificate_cb|.
-enum ssl_select_cert_result_t {
+enum ssl_select_cert_result_t BORINGSSL_ENUM_INT {
// ssl_select_cert_success indicates that the certificate selection was
// successful.
ssl_select_cert_success = 1,
@@ -3651,6 +3707,8 @@
// high-level operation on |ssl| to be retried at a later time, which will
// result in another call to |cb|.
//
+// |SSL_get_servername| may be used during this callback.
+//
// Note: The |SSL_CLIENT_HELLO| is only valid for the duration of the callback
// and is not valid while the handshake is paused.
OPENSSL_EXPORT void SSL_CTX_set_select_certificate_cb(
@@ -3815,11 +3873,6 @@
// record with |ssl|.
OPENSSL_EXPORT size_t SSL_max_seal_overhead(const SSL *ssl);
-// SSL_get_ticket_age_skew returns the difference, in seconds, between the
-// client-sent ticket age and the server-computed value in TLS 1.3 server
-// connections which resumed a session.
-OPENSSL_EXPORT int32_t SSL_get_ticket_age_skew(const SSL *ssl);
-
// SSL_CTX_set_false_start_allowed_without_alpn configures whether connections
// on |ctx| may use False Start (if |SSL_MODE_ENABLE_FALSE_START| is enabled)
// without negotiating ALPN.
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index 384d102..e3209b6 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -244,6 +244,9 @@
// This is not an IANA defined extension number
#define TLSEXT_TYPE_channel_id 30032
+// This is not an IANA defined extension number
+#define TLSEXT_TYPE_pq_experiment_signal 54538
+
// status request value from RFC 3546
#define TLSEXT_STATUSTYPE_nothing (-1)
#define TLSEXT_STATUSTYPE_ocsp 1
@@ -608,9 +611,9 @@
"ECDHE-PSK-CHACHA20-POLY1305"
// TLS 1.3 ciphersuites from RFC 8446.
-#define TLS1_TXT_AES_128_GCM_SHA256 "AEAD-AES128-GCM-SHA256"
-#define TLS1_TXT_AES_256_GCM_SHA384 "AEAD-AES256-GCM-SHA384"
-#define TLS1_TXT_CHACHA20_POLY1305_SHA256 "AEAD-CHACHA20-POLY1305-SHA256"
+#define TLS1_TXT_AES_128_GCM_SHA256 "TLS_AES_128_GCM_SHA256"
+#define TLS1_TXT_AES_256_GCM_SHA384 "TLS_AES_256_GCM_SHA384"
+#define TLS1_TXT_CHACHA20_POLY1305_SHA256 "TLS_CHACHA20_POLY1305_SHA256"
#define TLS_CT_RSA_SIGN 1
diff --git a/src/include/openssl/x509_vfy.h b/src/include/openssl/x509_vfy.h
index 86aa546..f262334 100644
--- a/src/include/openssl/x509_vfy.h
+++ b/src/include/openssl/x509_vfy.h
@@ -370,6 +370,8 @@
/* Issuer lookup error */
#define X509_V_ERR_STORE_LOOKUP 66
+#define X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS 67
+
/* Certificate verify flags */
/* Send issuer+subject checks to verify_cb */
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index d2d39f8..b5db715 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -703,8 +703,8 @@
OPENSSL_EXPORT STACK_OF(OPENSSL_STRING) *X509_get1_ocsp(X509 *x);
/* Flags for X509_check_* functions */
-/* Always check subject name for host match even if subject alt names present */
-#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1
+/* Deprecated: this flag does nothing */
+#define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0
/* Disable wildcard matching for dnsName fields and common name. */
#define X509_CHECK_FLAG_NO_WILDCARDS 0x2
/* Wildcards must not match a partial label. */
@@ -713,6 +713,8 @@
#define X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS 0x8
/* Constraint verifier subdomain patterns to match a single labels. */
#define X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS 0x10
+/* Skip the subject common name fallback if subjectAltNames is missing. */
+#define X509_CHECK_FLAG_NEVER_CHECK_SUBJECT 0x20
/*
* Match reference identifiers starting with "." to any sub-domain.
* This is a non-public flag, turned on implicitly when the subject
diff --git a/src/sources.cmake b/src/sources.cmake
index 6213512..1569ce9 100644
--- a/src/sources.cmake
+++ b/src/sources.cmake
@@ -54,6 +54,7 @@
crypto/fipsmodule/rand/ctrdrbg_vectors.txt
crypto/hmac_extra/hmac_tests.txt
crypto/poly1305/poly1305_tests.txt
+ crypto/siphash/siphash_tests.txt
crypto/x509/many_constraints.pem
crypto/x509/many_names1.pem
crypto/x509/many_names2.pem
@@ -80,6 +81,7 @@
third_party/wycheproof_testvectors/ecdsa_secp384r1_sha512_test.txt
third_party/wycheproof_testvectors/ecdsa_secp521r1_sha512_test.txt
third_party/wycheproof_testvectors/eddsa_test.txt
+ third_party/wycheproof_testvectors/kwp_test.txt
third_party/wycheproof_testvectors/kw_test.txt
third_party/wycheproof_testvectors/rsa_pss_2048_sha1_mgf1_20_test.txt
third_party/wycheproof_testvectors/rsa_pss_2048_sha256_mgf1_0_test.txt
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index dc89dca..0fb532e 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -50,6 +50,7 @@
span_test.cc
ssl_test.cc
+ ssl_c_test.c
$<TARGET_OBJECTS:boringssl_gtest_main>
)
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index be595b0..dfb8a67 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -256,7 +256,7 @@
if (ret <= 0) {
return ret;
}
- ssl->s3->alert_dispatch = 0;
+ ssl->s3->alert_dispatch = false;
// If the alert is fatal, flush the BIO now.
if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/handoff.cc b/src/ssl/handoff.cc
index 0928015..db5886a 100644
--- a/src/ssl/handoff.cc
+++ b/src/ssl/handoff.cc
@@ -450,6 +450,10 @@
s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
s3->hs->cert_request = cert_request;
+ // TODO(davidben): When handoff for TLS 1.3 is added, serialize
+ // |early_data_reason| and stabilize the constants.
+ s3->early_data_reason = ssl_early_data_protocol_version;
+
Array<uint8_t> key_block;
if ((type == handback_after_session_resumption ||
type == handback_after_handshake) &&
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 89be48f..b8e0070 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -648,6 +648,7 @@
return -1;
case ssl_hs_early_data_rejected:
+ assert(ssl->s3->early_data_reason != ssl_early_data_unknown);
ssl->s3->rwstate = SSL_EARLY_DATA_REJECTED;
// Cause |SSL_write| to start failing immediately.
hs->can_early_write = false;
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index b0de670..a53e430 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -1071,13 +1071,8 @@
return ssl_hs_error;
}
- bool sig_ok = ssl_public_key_verify(ssl, signature, signature_algorithm,
- hs->peer_pubkey.get(), transcript_data);
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- sig_ok = true;
- ERR_clear_error();
-#endif
- if (!sig_ok) {
+ if (!ssl_public_key_verify(ssl, signature, signature_algorithm,
+ hs->peer_pubkey.get(), transcript_data)) {
// bad signature
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index 4622ad0..36aa560 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -503,6 +503,54 @@
return true;
}
+static bool extract_sni(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ const SSL_CLIENT_HELLO *client_hello) {
+ SSL *const ssl = hs->ssl;
+ CBS sni;
+ if (!ssl_client_hello_get_extension(client_hello, &sni,
+ TLSEXT_TYPE_server_name)) {
+ // No SNI extension to parse.
+ return true;
+ }
+
+ CBS server_name_list, host_name;
+ uint8_t name_type;
+ if (!CBS_get_u16_length_prefixed(&sni, &server_name_list) ||
+ !CBS_get_u8(&server_name_list, &name_type) ||
+ // Although the server_name extension was intended to be extensible to
+ // new name types and multiple names, OpenSSL 1.0.x had a bug which meant
+ // different name types will cause an error. Further, RFC 4366 originally
+ // defined syntax inextensibly. RFC 6066 corrected this mistake, but
+ // adding new name types is no longer feasible.
+ //
+ // Act as if the extensibility does not exist to simplify parsing.
+ !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
+ CBS_len(&server_name_list) != 0 ||
+ CBS_len(&sni) != 0) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return false;
+ }
+
+ if (name_type != TLSEXT_NAMETYPE_host_name ||
+ CBS_len(&host_name) == 0 ||
+ CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
+ CBS_contains_zero_byte(&host_name)) {
+ *out_alert = SSL_AD_UNRECOGNIZED_NAME;
+ return false;
+ }
+
+ // Copy the hostname as a string.
+ char *raw = nullptr;
+ if (!CBS_strdup(&host_name, &raw)) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return false;
+ }
+ ssl->s3->hostname.reset(raw);
+
+ hs->should_ack_sni = true;
+ return true;
+}
+
static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
@@ -526,6 +574,12 @@
return ssl_hs_handoff;
}
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ if (!extract_sni(hs, &alert, &client_hello)) {
+ ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
+
// Run the early callback.
if (ssl->ctx->select_certificate_cb != NULL) {
switch (ssl->ctx->select_certificate_cb(&client_hello)) {
@@ -553,7 +607,6 @@
hs->apply_jdk11_workaround = true;
}
- uint8_t alert = SSL_AD_DECODE_ERROR;
if (!negotiate_version(hs, &alert, &client_hello)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
return ssl_hs_error;
@@ -635,6 +688,8 @@
return ssl_hs_ok;
}
+ ssl->s3->early_data_reason = ssl_early_data_protocol_version;
+
SSL_CLIENT_HELLO client_hello;
if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
return ssl_hs_error;
@@ -1408,14 +1463,8 @@
return ssl_hs_error;
}
- bool sig_ok =
- ssl_public_key_verify(ssl, signature, signature_algorithm,
- hs->peer_pubkey.get(), hs->transcript.buffer());
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- sig_ok = true;
- ERR_clear_error();
-#endif
- if (!sig_ok) {
+ if (!ssl_public_key_verify(ssl, signature, signature_algorithm,
+ hs->peer_pubkey.get(), hs->transcript.buffer())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
return ssl_hs_error;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index ee2952a..b355c7f 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -465,6 +465,9 @@
#define SSL_HANDSHAKE_MAC_SHA256 0x2
#define SSL_HANDSHAKE_MAC_SHA384 0x4
+// SSL_MAX_MD_SIZE is size of the largest hash function used in TLS, SHA-384.
+#define SSL_MAX_MD_SIZE 48
+
// An SSLCipherPreferenceList contains a list of SSL_CIPHERs with equal-
// preference groups. For TLS clients, the groups are moot because the server
// picks the cipher and groups cannot be expressed on the wire. However, for
@@ -560,6 +563,12 @@
// it returns zero.
size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher);
+// ssl_choose_tls13_cipher returns an |SSL_CIPHER| corresponding with the best
+// available from |cipher_suites| compatible with |version| and |group_id|. It
+// returns NULL if there isn't a compatible cipher.
+const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
+ uint16_t group_id);
+
// Transcript layer.
@@ -1446,13 +1455,13 @@
uint16_t max_version = 0;
size_t hash_len = 0;
- uint8_t secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t early_traffic_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t client_handshake_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t server_handshake_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE] = {0};
- uint8_t server_traffic_secret_0[EVP_MAX_MD_SIZE] = {0};
- uint8_t expected_client_finished[EVP_MAX_MD_SIZE] = {0};
+ uint8_t secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t early_traffic_secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t client_handshake_secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t server_handshake_secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t client_traffic_secret_0[SSL_MAX_MD_SIZE] = {0};
+ uint8_t server_traffic_secret_0[SSL_MAX_MD_SIZE] = {0};
+ uint8_t expected_client_finished[SSL_MAX_MD_SIZE] = {0};
union {
// sent is a bitset where the bits correspond to elements of kExtensions
@@ -2029,7 +2038,7 @@
// check_client_CA_list returns one if |names| is a good list of X.509
// distinguished names and zero otherwise. This is used to ensure that we can
// reject unparsable values at handshake time when using crypto/x509.
- int (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names);
+ bool (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names);
// cert_clear frees and NULLs all X509 certificate-related state.
void (*cert_clear)(CERT *cert);
@@ -2046,35 +2055,35 @@
// session_cache_objects fills out |sess->x509_peer| and |sess->x509_chain|
// from |sess->certs| and erases |sess->x509_chain_without_leaf|. It returns
- // one on success or zero on error.
- int (*session_cache_objects)(SSL_SESSION *session);
+ // true on success or false on error.
+ bool (*session_cache_objects)(SSL_SESSION *session);
// session_dup duplicates any needed fields from |session| to |new_session|.
- // It returns one on success or zero on error.
- int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
+ // It returns true on success or false on error.
+ bool (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session);
// session_clear frees any X509-related state from |session|.
void (*session_clear)(SSL_SESSION *session);
// session_verify_cert_chain verifies the certificate chain in |session|,
- // sets |session->verify_result| and returns one on success or zero on
+ // sets |session->verify_result| and returns true on success or false on
// error.
- int (*session_verify_cert_chain)(SSL_SESSION *session, SSL_HANDSHAKE *ssl,
- uint8_t *out_alert);
+ bool (*session_verify_cert_chain)(SSL_SESSION *session, SSL_HANDSHAKE *ssl,
+ uint8_t *out_alert);
// hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|.
void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs);
- // ssl_new does any neccessary initialisation of |hs|. It returns one on
- // success or zero on error.
- int (*ssl_new)(SSL_HANDSHAKE *hs);
+ // ssl_new does any necessary initialisation of |hs|. It returns true on
+ // success or false on error.
+ bool (*ssl_new)(SSL_HANDSHAKE *hs);
// ssl_free frees anything created by |ssl_new|.
void (*ssl_config_free)(SSL_CONFIG *cfg);
// ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|.
void (*ssl_flush_cached_client_CA)(SSL_CONFIG *cfg);
// ssl_auto_chain_if_needed runs the deprecated auto-chaining logic if
// necessary. On success, it updates |ssl|'s certificate configuration as
- // needed and returns one. Otherwise, it returns zero.
- int (*ssl_auto_chain_if_needed)(SSL_HANDSHAKE *hs);
- // ssl_ctx_new does any neccessary initialisation of |ctx|. It returns one on
- // success or zero on error.
- int (*ssl_ctx_new)(SSL_CTX *ctx);
+ // needed and returns true. Otherwise, it returns false.
+ bool (*ssl_auto_chain_if_needed)(SSL_HANDSHAKE *hs);
+ // ssl_ctx_new does any necessary initialisation of |ctx|. It returns true on
+ // success or false on error.
+ bool (*ssl_ctx_new)(SSL_CTX *ctx);
// ssl_ctx_free frees anything created by |ssl_ctx_new|.
void (*ssl_ctx_free)(SSL_CTX *ctx);
// ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|.
@@ -2164,8 +2173,6 @@
// the receive half of the connection.
UniquePtr<ERR_SAVE_STATE> read_error;
- int alert_dispatch = 0;
-
int total_renegotiations = 0;
// This holds a variable that indicates what we were doing when a 0 or -1 is
@@ -2221,6 +2228,10 @@
// session_reused indicates whether a session was resumed.
bool session_reused : 1;
+ // delegated_credential_used is whether we presented a delegated credential to
+ // the peer.
+ bool delegated_credential_used : 1;
+
bool send_connection_binding : 1;
// In a client, this means that the server supported Channel ID and that a
@@ -2244,6 +2255,13 @@
// token_binding_negotiated is set if Token Binding was negotiated.
bool token_binding_negotiated : 1;
+ // pq_experimental_signal_seen is true if the peer was observed
+ // sending/echoing the post-quantum experiment signal.
+ bool pq_experiment_signal_seen : 1;
+
+ // alert_dispatch is true there is an alert in |send_alert| to be sent.
+ bool alert_dispatch : 1;
+
// hs_buf is the buffer of handshake data to process.
UniquePtr<BUF_MEM> hs_buf;
@@ -2266,6 +2284,9 @@
// which resumed a session.
int32_t ticket_age_skew = 0;
+ // ssl_early_data_reason stores details on why 0-RTT was accepted or rejected.
+ enum ssl_early_data_reason_t early_data_reason = ssl_early_data_unknown;
+
// aead_read_ctx is the current read cipher state.
UniquePtr<SSLAEADContext> aead_read_ctx;
@@ -2276,14 +2297,12 @@
// one.
UniquePtr<SSL_HANDSHAKE> hs;
- uint8_t write_traffic_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t read_traffic_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t exporter_secret[EVP_MAX_MD_SIZE] = {0};
- uint8_t early_exporter_secret[EVP_MAX_MD_SIZE] = {0};
+ uint8_t write_traffic_secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t read_traffic_secret[SSL_MAX_MD_SIZE] = {0};
+ uint8_t exporter_secret[SSL_MAX_MD_SIZE] = {0};
uint8_t write_traffic_secret_len = 0;
uint8_t read_traffic_secret_len = 0;
uint8_t exporter_secret_len = 0;
- uint8_t early_exporter_secret_len = 0;
// Connection binding to prevent renegotiation attacks
uint8_t previous_client_finished[12] = {0};
@@ -2674,7 +2693,8 @@
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
-int ssl_send_alert(SSL *ssl, int level, int desc);
+void ssl_send_alert(SSL *ssl, int level, int desc);
+int ssl_send_alert_impl(SSL *ssl, int level, int desc);
bool ssl3_get_message(const SSL *ssl, SSLMessage *out);
ssl_open_record_t ssl3_open_handshake(SSL *ssl, size_t *out_consumed,
uint8_t *out_alert, Span<uint8_t> in);
@@ -3170,6 +3190,11 @@
// If enable_early_data is true, early data can be sent and accepted.
bool enable_early_data : 1;
+ // pq_experiment_signal indicates that an empty extension should be sent
+ // (for clients) or echoed (for servers) to indicate participation in an
+ // experiment of post-quantum key exchanges.
+ bool pq_experiment_signal : 1;
+
private:
~ssl_ctx_st();
friend void SSL_CTX_free(SSL_CTX *);
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 27e9454..842ec67 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -116,6 +116,8 @@
#include <limits.h>
#include <string.h>
+#include <tuple>
+
#include <openssl/buf.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
@@ -652,4 +654,72 @@
}
}
+// CipherScorer produces a "score" for each possible cipher suite offered by
+// the client.
+class CipherScorer {
+ public:
+ CipherScorer(uint16_t group_id)
+ : aes_is_fine_(EVP_has_aes_hardware()),
+ security_128_is_fine_(group_id != SSL_CURVE_CECPQ2 &&
+ group_id != SSL_CURVE_CECPQ2b) {}
+
+ typedef std::tuple<bool, bool, bool> Score;
+
+ // MinScore returns a |Score| that will compare less than the score of all
+ // cipher suites.
+ Score MinScore() const {
+ return Score(false, false, false);
+ }
+
+ Score Evaluate(const SSL_CIPHER *a) const {
+ return Score(
+ // Something is always preferable to nothing.
+ true,
+ // Either 128-bit is fine, or 256-bit is preferred.
+ security_128_is_fine_ || a->algorithm_enc != SSL_AES128GCM,
+ // Either AES is fine, or else ChaCha20 is preferred.
+ aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305);
+ }
+
+ private:
+ const bool aes_is_fine_;
+ const bool security_128_is_fine_;
+};
+
+const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version,
+ uint16_t group_id) {
+ if (CBS_len(&cipher_suites) % 2 != 0) {
+ return nullptr;
+ }
+
+ const SSL_CIPHER *best = nullptr;
+ CipherScorer scorer(group_id);
+ CipherScorer::Score best_score = scorer.MinScore();
+
+ while (CBS_len(&cipher_suites) > 0) {
+ uint16_t cipher_suite;
+ if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
+ return nullptr;
+ }
+
+ // Limit to TLS 1.3 ciphers we know about.
+ const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite);
+ if (candidate == nullptr ||
+ SSL_CIPHER_get_min_version(candidate) > version ||
+ SSL_CIPHER_get_max_version(candidate) < version) {
+ continue;
+ }
+
+ const CipherScorer::Score candidate_score = scorer.Evaluate(candidate);
+ // |candidate_score| must be larger to displace the current choice. That way
+ // the client's order controls between ciphers with an equal score.
+ if (candidate_score > best_score) {
+ best = candidate;
+ best_score = candidate_score;
+ }
+ }
+
+ return best;
+}
+
BSSL_NAMESPACE_END
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index 0e0770c..41dd588 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -172,13 +172,16 @@
has_message(false),
initial_handshake_complete(false),
session_reused(false),
+ delegated_credential_used(false),
send_connection_binding(false),
channel_id_valid(false),
key_update_pending(false),
wpend_pending(false),
early_data_accepted(false),
tls13_downgrade(false),
- token_binding_negotiated(false) {}
+ token_binding_negotiated(false),
+ pq_experiment_signal_seen(false),
+ alert_dispatch(false) {}
SSL3_STATE::~SSL3_STATE() {}
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index abc6798..a54bb00 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -118,6 +118,7 @@
#include <openssl/mem.h>
#include <openssl/rand.h>
+#include "../crypto/err/internal.h"
#include "../crypto/internal.h"
#include "internal.h"
@@ -381,7 +382,24 @@
return ssl_open_record_success;
}
-int ssl_send_alert(SSL *ssl, int level, int desc) {
+void ssl_send_alert(SSL *ssl, int level, int desc) {
+ // This function is called in response to a fatal error from the peer. Ignore
+ // any failures writing the alert and report only the original error. In
+ // particular, if the transport uses |SSL_write|, our existing error will be
+ // clobbered so we must save and restore the error queue. See
+ // https://crbug.com/959305.
+ //
+ // TODO(davidben): Return the alert out of the handshake, rather than calling
+ // this function internally everywhere.
+ //
+ // TODO(davidben): This does not allow retrying if the alert hit EAGAIN. See
+ // https://crbug.com/boringssl/130.
+ UniquePtr<ERR_SAVE_STATE> err_state(ERR_save_state());
+ ssl_send_alert_impl(ssl, level, desc);
+ ERR_restore_state(err_state.get());
+}
+
+int ssl_send_alert_impl(SSL *ssl, int level, int desc) {
// It is illegal to send an alert when we've already sent a closing one.
if (ssl->s3->write_shutdown != ssl_shutdown_none) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
@@ -396,7 +414,7 @@
ssl->s3->write_shutdown = ssl_shutdown_error;
}
- ssl->s3->alert_dispatch = 1;
+ ssl->s3->alert_dispatch = true;
ssl->s3->send_alert[0] = level;
ssl->s3->send_alert[1] = desc;
if (ssl->s3->write_buffer.empty()) {
@@ -423,7 +441,7 @@
}
}
- ssl->s3->alert_dispatch = 0;
+ ssl->s3->alert_dispatch = false;
// If the alert is fatal, flush the BIO now.
if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/ssl_c_test.c b/src/ssl/ssl_c_test.c
new file mode 100644
index 0000000..02f8655
--- /dev/null
+++ b/src/ssl/ssl_c_test.c
@@ -0,0 +1,15 @@
+#include <openssl/ssl.h>
+
+int BORINGSSL_enum_c_type_test(void);
+
+int BORINGSSL_enum_c_type_test(void) {
+#if defined(__cplusplus)
+#error "This is testing the behaviour of the C compiler."
+#error "It's pointless to build it in C++ mode."
+#endif
+
+ // In C++, the enums in ssl.h are explicitly typed as ints to allow them to
+ // be predeclared. This function confirms that the C compiler believes them
+ // to be the same size as ints. They may differ in signedness, however.
+ return sizeof(enum ssl_private_key_result_t) == sizeof(int);
+}
diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc
index 54df38f..b565a35 100644
--- a/src/ssl/ssl_cert.cc
+++ b/src/ssl/ssl_cert.cc
@@ -1010,3 +1010,7 @@
return cert_set_dc(ssl->config->cert.get(), dc, pkey, key_method);
}
+
+int SSL_delegated_credential_used(const SSL *ssl) {
+ return ssl->s3->delegated_credential_used;
+}
diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc
index 78d2aa1..826fb1a 100644
--- a/src/ssl/ssl_key_share.cc
+++ b/src/ssl/ssl_key_share.cc
@@ -31,7 +31,7 @@
#include "internal.h"
#include "../crypto/internal.h"
-
+#include "../third_party/sike/sike.h"
BSSL_NAMESPACE_BEGIN
@@ -300,6 +300,87 @@
HRSS_private_key hrss_private_key_;
};
+class CECPQ2bKeyShare : public SSLKeyShare {
+ public:
+ uint16_t GroupID() const override { return SSL_CURVE_CECPQ2b; }
+
+ bool Offer(CBB *out) override {
+ uint8_t public_x25519[32] = {0};
+ X25519_keypair(public_x25519, private_x25519_);
+ if (!SIKE_keypair(private_sike_, public_sike_)) {
+ return false;
+ }
+
+ return CBB_add_bytes(out, public_x25519, sizeof(public_x25519)) &&
+ CBB_add_bytes(out, public_sike_, sizeof(public_sike_));
+ }
+
+ bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
+ uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+ uint8_t public_x25519[32];
+ uint8_t private_x25519[32];
+ uint8_t sike_ciphertext[SIKE_CT_BYTESZ] = {0};
+
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ if (peer_key.size() != sizeof(public_x25519) + SIKE_PUB_BYTESZ) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ return false;
+ }
+
+ Array<uint8_t> secret;
+ if (!secret.Init(sizeof(private_x25519_) + SIKE_SS_BYTESZ)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return false;
+ }
+
+ X25519_keypair(public_x25519, private_x25519);
+ if (!X25519(secret.data(), private_x25519, peer_key.data())) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ return false;
+ }
+
+ SIKE_encaps(secret.data() + sizeof(private_x25519_), sike_ciphertext,
+ peer_key.data() + sizeof(public_x25519));
+ *out_secret = std::move(secret);
+
+ return CBB_add_bytes(out_public_key, public_x25519,
+ sizeof(public_x25519)) &&
+ CBB_add_bytes(out_public_key, sike_ciphertext,
+ sizeof(sike_ciphertext));
+ }
+
+ bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
+ Span<const uint8_t> peer_key) override {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ Array<uint8_t> secret;
+ if (!secret.Init(sizeof(private_x25519_) + SIKE_SS_BYTESZ)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return false;
+ }
+
+ if (peer_key.size() != 32 + SIKE_CT_BYTESZ ||
+ !X25519(secret.data(), private_x25519_, peer_key.data())) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ return false;
+ }
+
+ SIKE_decaps(secret.data() + sizeof(private_x25519_), peer_key.data() + 32,
+ public_sike_, private_sike_);
+ *out_secret = std::move(secret);
+ return true;
+ }
+
+ private:
+ uint8_t private_x25519_[32];
+ uint8_t private_sike_[SIKE_PRV_BYTESZ];
+ uint8_t public_sike_[SIKE_PUB_BYTESZ];
+};
+
CONSTEXPR_ARRAY NamedGroup kNamedGroups[] = {
{NID_secp224r1, SSL_CURVE_SECP224R1, "P-224", "secp224r1"},
{NID_X9_62_prime256v1, SSL_CURVE_SECP256R1, "P-256", "prime256v1"},
@@ -307,6 +388,7 @@
{NID_secp521r1, SSL_CURVE_SECP521R1, "P-521", "secp521r1"},
{NID_X25519, SSL_CURVE_X25519, "X25519", "x25519"},
{NID_CECPQ2, SSL_CURVE_CECPQ2, "CECPQ2", "CECPQ2"},
+ {NID_CECPQ2b, SSL_CURVE_CECPQ2b, "CECPQ2b", "CECPQ2b"},
};
} // namespace
@@ -333,6 +415,8 @@
return UniquePtr<SSLKeyShare>(New<X25519KeyShare>());
case SSL_CURVE_CECPQ2:
return UniquePtr<SSLKeyShare>(New<CECPQ2KeyShare>());
+ case SSL_CURVE_CECPQ2b:
+ return UniquePtr<SSLKeyShare>(New<CECPQ2bKeyShare>());
default:
return nullptr;
}
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index f9910f7..00ee7da 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -569,7 +569,8 @@
false_start_allowed_without_alpn(false),
ignore_tls13_downgrade(false),
handoff(false),
- enable_early_data(false) {
+ enable_early_data(false),
+ pq_experiment_signal(false) {
CRYPTO_MUTEX_init(&lock);
CRYPTO_new_ex_data(&ex_data);
}
@@ -1195,7 +1196,7 @@
if (ssl->s3->write_shutdown != ssl_shutdown_close_notify) {
// Send a close_notify.
- if (ssl_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
+ if (ssl_send_alert_impl(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
return -1;
}
} else if (ssl->s3->alert_dispatch) {
@@ -1242,7 +1243,15 @@
return ssl->method->dispatch_alert(ssl);
}
- return ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_send_alert_impl(ssl, SSL3_AL_FATAL, alert);
+}
+
+void SSL_CTX_enable_pq_experiment_signal(SSL_CTX *ctx) {
+ ctx->pq_experiment_signal = true;
+}
+
+int SSL_pq_experiment_signal_seen(const SSL *ssl) {
+ return ssl->s3->pq_experiment_signal_seen;
}
int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
@@ -1294,6 +1303,10 @@
ssl->s3->wpend_pending = false;
}
+enum ssl_early_data_reason_t SSL_get_early_data_reason(const SSL *ssl) {
+ return ssl->s3->early_data_reason;
+}
+
static int bio_retry_reason_to_error(int reason) {
switch (reason) {
case BIO_RR_CONNECT:
diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc
index 1ddb1b1..23f8d12 100644
--- a/src/ssl/ssl_privkey.cc
+++ b/src/ssl/ssl_privkey.cc
@@ -236,9 +236,16 @@
uint16_t sigalg, EVP_PKEY *pkey,
Span<const uint8_t> in) {
ScopedEVP_MD_CTX ctx;
- return setup_ctx(ssl, ctx.get(), pkey, sigalg, true /* verify */) &&
- EVP_DigestVerify(ctx.get(), signature.data(), signature.size(),
- in.data(), in.size());
+ if (!setup_ctx(ssl, ctx.get(), pkey, sigalg, true /* verify */)) {
+ return false;
+ }
+ bool ok = EVP_DigestVerify(ctx.get(), signature.data(), signature.size(),
+ in.data(), in.size());
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ ok = true;
+ ERR_clear_error();
+#endif
+ return ok;
}
enum ssl_private_key_result_t ssl_private_key_decrypt(SSL_HANDSHAKE *hs,
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index 927dd1b..bb04b1a 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -1044,6 +1044,11 @@
}
}
+int SSL_SESSION_early_data_capable(const SSL_SESSION *session) {
+ return ssl_session_protocol_version(session) >= TLS1_3_VERSION &&
+ session->ticket_max_early_data != 0;
+}
+
SSL_SESSION *SSL_magic_pending_session_ptr(void) {
return (SSL_SESSION *)&g_pending_session_magic;
}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index d01b649..6f180c7 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -737,103 +737,77 @@
return true;
}
-static bool TestSSL_SESSIONEncoding(const char *input_b64) {
- const uint8_t *cptr;
- uint8_t *ptr;
+TEST(SSLTest, SessionEncoding) {
+ for (const char *input_b64 : {
+ kOpenSSLSession,
+ kCustomSession,
+ kBoringSSLSession,
+ }) {
+ SCOPED_TRACE(std::string(input_b64));
+ // Decode the input.
+ std::vector<uint8_t> input;
+ ASSERT_TRUE(DecodeBase64(&input, input_b64));
- // Decode the input.
- std::vector<uint8_t> input;
- if (!DecodeBase64(&input, input_b64)) {
- return false;
+ // Verify the SSL_SESSION decodes.
+ bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(ssl_ctx);
+ bssl::UniquePtr<SSL_SESSION> session(
+ SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
+ ASSERT_TRUE(session) << "SSL_SESSION_from_bytes failed";
+
+ // Verify the SSL_SESSION encoding round-trips.
+ size_t encoded_len;
+ bssl::UniquePtr<uint8_t> encoded;
+ uint8_t *encoded_raw;
+ ASSERT_TRUE(SSL_SESSION_to_bytes(session.get(), &encoded_raw, &encoded_len))
+ << "SSL_SESSION_to_bytes failed";
+ encoded.reset(encoded_raw);
+ EXPECT_EQ(Bytes(encoded.get(), encoded_len), Bytes(input))
+ << "SSL_SESSION_to_bytes did not round-trip";
+
+ // Verify the SSL_SESSION also decodes with the legacy API.
+ const uint8_t *cptr = input.data();
+ session.reset(d2i_SSL_SESSION(NULL, &cptr, input.size()));
+ ASSERT_TRUE(session) << "d2i_SSL_SESSION failed";
+ EXPECT_EQ(cptr, input.data() + input.size());
+
+ // Verify the SSL_SESSION encoding round-trips via the legacy API.
+ int len = i2d_SSL_SESSION(session.get(), NULL);
+ ASSERT_GT(len, 0) << "i2d_SSL_SESSION failed";
+ ASSERT_EQ(static_cast<size_t>(len), input.size())
+ << "i2d_SSL_SESSION(NULL) returned invalid length";
+
+ encoded.reset((uint8_t *)OPENSSL_malloc(input.size()));
+ ASSERT_TRUE(encoded);
+
+ uint8_t *ptr = encoded.get();
+ len = i2d_SSL_SESSION(session.get(), &ptr);
+ ASSERT_GT(len, 0) << "i2d_SSL_SESSION failed";
+ ASSERT_EQ(static_cast<size_t>(len), input.size())
+ << "i2d_SSL_SESSION(NULL) returned invalid length";
+ ASSERT_EQ(ptr, encoded.get() + input.size())
+ << "i2d_SSL_SESSION did not advance ptr correctly";
+ EXPECT_EQ(Bytes(encoded.get(), encoded_len), Bytes(input))
+ << "SSL_SESSION_to_bytes did not round-trip";
}
- // Verify the SSL_SESSION decodes.
- bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
- if (!ssl_ctx) {
- return false;
- }
- bssl::UniquePtr<SSL_SESSION> session(
- SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
- if (!session) {
- fprintf(stderr, "SSL_SESSION_from_bytes failed\n");
- return false;
- }
+ for (const char *input_b64 : {
+ kBadSessionExtraField,
+ kBadSessionVersion,
+ kBadSessionTrailingData,
+ }) {
+ SCOPED_TRACE(std::string(input_b64));
+ std::vector<uint8_t> input;
+ ASSERT_TRUE(DecodeBase64(&input, input_b64));
- // Verify the SSL_SESSION encoding round-trips.
- size_t encoded_len;
- bssl::UniquePtr<uint8_t> encoded;
- uint8_t *encoded_raw;
- if (!SSL_SESSION_to_bytes(session.get(), &encoded_raw, &encoded_len)) {
- fprintf(stderr, "SSL_SESSION_to_bytes failed\n");
- return false;
+ // Verify that the SSL_SESSION fails to decode.
+ bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(ssl_ctx);
+ bssl::UniquePtr<SSL_SESSION> session(
+ SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
+ EXPECT_FALSE(session) << "SSL_SESSION_from_bytes unexpectedly succeeded";
+ ERR_clear_error();
}
- encoded.reset(encoded_raw);
- if (encoded_len != input.size() ||
- OPENSSL_memcmp(input.data(), encoded.get(), input.size()) != 0) {
- fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n");
- hexdump(stderr, "Before: ", input.data(), input.size());
- hexdump(stderr, "After: ", encoded_raw, encoded_len);
- return false;
- }
-
- // Verify the SSL_SESSION also decodes with the legacy API.
- cptr = input.data();
- session.reset(d2i_SSL_SESSION(NULL, &cptr, input.size()));
- if (!session || cptr != input.data() + input.size()) {
- fprintf(stderr, "d2i_SSL_SESSION failed\n");
- return false;
- }
-
- // Verify the SSL_SESSION encoding round-trips via the legacy API.
- int len = i2d_SSL_SESSION(session.get(), NULL);
- if (len < 0 || (size_t)len != input.size()) {
- fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n");
- return false;
- }
-
- encoded.reset((uint8_t *)OPENSSL_malloc(input.size()));
- if (!encoded) {
- fprintf(stderr, "malloc failed\n");
- return false;
- }
-
- ptr = encoded.get();
- len = i2d_SSL_SESSION(session.get(), &ptr);
- if (len < 0 || (size_t)len != input.size()) {
- fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n");
- return false;
- }
- if (ptr != encoded.get() + input.size()) {
- fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n");
- return false;
- }
- if (OPENSSL_memcmp(input.data(), encoded.get(), input.size()) != 0) {
- fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n");
- return false;
- }
-
- return true;
-}
-
-static bool TestBadSSL_SESSIONEncoding(const char *input_b64) {
- std::vector<uint8_t> input;
- if (!DecodeBase64(&input, input_b64)) {
- return false;
- }
-
- // Verify that the SSL_SESSION fails to decode.
- bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method()));
- if (!ssl_ctx) {
- return false;
- }
- bssl::UniquePtr<SSL_SESSION> session(
- SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get()));
- if (session) {
- fprintf(stderr, "SSL_SESSION_from_bytes unexpectedly succeeded\n");
- return false;
- }
- ERR_clear_error();
- return true;
}
static void ExpectDefaultVersion(uint16_t min_version, uint16_t max_version,
@@ -1087,63 +1061,67 @@
return client_hello.size() - SSL3_RT_HEADER_LENGTH;
}
-struct PaddingTest {
- size_t input_len, padded_len;
-};
+TEST(SSLTest, Padding) {
+ struct PaddingVersions {
+ uint16_t max_version, session_version;
+ };
+ static const PaddingVersions kPaddingVersions[] = {
+ // Test the padding extension at TLS 1.2.
+ {TLS1_2_VERSION, TLS1_2_VERSION},
+ // Test the padding extension at TLS 1.3 with a TLS 1.2 session, so there
+ // will be no PSK binder after the padding extension.
+ {TLS1_3_VERSION, TLS1_2_VERSION},
+ // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
+ // will be a PSK binder after the padding extension.
+ {TLS1_3_VERSION, TLS1_3_VERSION},
-static const PaddingTest kPaddingTests[] = {
- // ClientHellos of length below 0x100 do not require padding.
- {0xfe, 0xfe},
- {0xff, 0xff},
- // ClientHellos of length 0x100 through 0x1fb are padded up to 0x200.
- {0x100, 0x200},
- {0x123, 0x200},
- {0x1fb, 0x200},
- // ClientHellos of length 0x1fc through 0x1ff get padded beyond 0x200. The
- // padding extension takes a minimum of four bytes plus one required content
- // byte. (To work around yet more server bugs, we avoid empty final
- // extensions.)
- {0x1fc, 0x201},
- {0x1fd, 0x202},
- {0x1fe, 0x203},
- {0x1ff, 0x204},
- // Finally, larger ClientHellos need no padding.
- {0x200, 0x200},
- {0x201, 0x201},
-};
+ };
-static bool TestPaddingExtension(uint16_t max_version,
- uint16_t session_version) {
- // Sample a baseline length.
- size_t base_len = GetClientHelloLen(max_version, session_version, 1);
- if (base_len == 0) {
- return false;
- }
+ struct PaddingTest {
+ size_t input_len, padded_len;
+ };
+ static const PaddingTest kPaddingTests[] = {
+ // ClientHellos of length below 0x100 do not require padding.
+ {0xfe, 0xfe},
+ {0xff, 0xff},
+ // ClientHellos of length 0x100 through 0x1fb are padded up to 0x200.
+ {0x100, 0x200},
+ {0x123, 0x200},
+ {0x1fb, 0x200},
+ // ClientHellos of length 0x1fc through 0x1ff get padded beyond 0x200. The
+ // padding extension takes a minimum of four bytes plus one required
+ // content
+ // byte. (To work around yet more server bugs, we avoid empty final
+ // extensions.)
+ {0x1fc, 0x201},
+ {0x1fd, 0x202},
+ {0x1fe, 0x203},
+ {0x1ff, 0x204},
+ // Finally, larger ClientHellos need no padding.
+ {0x200, 0x200},
+ {0x201, 0x201},
+ };
- for (const PaddingTest &test : kPaddingTests) {
- if (base_len > test.input_len) {
- fprintf(stderr,
- "Baseline ClientHello too long (max_version = %04x, "
- "session_version = %04x).\n",
- max_version, session_version);
- return false;
- }
+ for (const PaddingVersions &versions : kPaddingVersions) {
+ SCOPED_TRACE(versions.max_version);
+ SCOPED_TRACE(versions.session_version);
- size_t padded_len = GetClientHelloLen(max_version, session_version,
- 1 + test.input_len - base_len);
- if (padded_len != test.padded_len) {
- fprintf(stderr,
- "%u-byte ClientHello padded to %u bytes, not %u (max_version = "
- "%04x, session_version = %04x).\n",
- static_cast<unsigned>(test.input_len),
- static_cast<unsigned>(padded_len),
- static_cast<unsigned>(test.padded_len), max_version,
- session_version);
- return false;
+ // Sample a baseline length.
+ size_t base_len =
+ GetClientHelloLen(versions.max_version, versions.session_version, 1);
+ ASSERT_NE(base_len, 0u) << "Baseline length could not be sampled";
+
+ for (const PaddingTest &test : kPaddingTests) {
+ SCOPED_TRACE(test.input_len);
+ ASSERT_LE(base_len, test.input_len) << "Baseline ClientHello too long";
+
+ size_t padded_len =
+ GetClientHelloLen(versions.max_version, versions.session_version,
+ 1 + test.input_len - base_len);
+ EXPECT_EQ(padded_len, test.padded_len)
+ << "ClientHello was not padded to expected length";
}
}
-
- return true;
}
static bssl::UniquePtr<X509> GetTestCertificate() {
@@ -1550,6 +1528,37 @@
return true;
}
+static bool FlushNewSessionTickets(SSL *client, SSL *server) {
+ // NewSessionTickets are deferred on the server to |SSL_write|, and clients do
+ // not pick them up until |SSL_read|.
+ for (;;) {
+ int server_ret = SSL_write(server, nullptr, 0);
+ int server_err = SSL_get_error(server, server_ret);
+ // The server may either succeed (|server_ret| is zero) or block on write
+ // (|server_ret| is -1 and |server_err| is |SSL_ERROR_WANT_WRITE|).
+ if (server_ret > 0 ||
+ (server_ret < 0 && server_err != SSL_ERROR_WANT_WRITE)) {
+ fprintf(stderr, "Unexpected server result: %d %d\n", server_ret,
+ server_err);
+ return false;
+ }
+
+ int client_ret = SSL_read(client, nullptr, 0);
+ int client_err = SSL_get_error(client, client_ret);
+ // The client must always block on read.
+ if (client_ret != -1 || client_err != SSL_ERROR_WANT_READ) {
+ fprintf(stderr, "Unexpected client result: %d %d\n", client_ret,
+ client_err);
+ return false;
+ }
+
+ // The server flushed everything it had to write.
+ if (server_ret == 0) {
+ return true;
+ }
+ }
+}
+
struct ClientConfig {
SSL_SESSION *session = nullptr;
std::string servername;
@@ -1661,9 +1670,7 @@
// Drain any post-handshake messages to ensure there are no unread records
// on either end.
- uint8_t byte = 0;
- ASSERT_LE(SSL_read(client_.get(), &byte, 1), 0);
- ASSERT_LE(SSL_read(server_.get(), &byte, 1), 0);
+ ASSERT_TRUE(FlushNewSessionTickets(client_.get(), server_.get()));
uint64_t client_read_seq = SSL_get_read_sequence(client_.get());
uint64_t client_write_seq = SSL_get_write_sequence(client_.get());
@@ -1687,6 +1694,7 @@
}
// Send a record from client to server.
+ uint8_t byte = 0;
EXPECT_EQ(SSL_write(client_.get(), &byte, 1), 1);
EXPECT_EQ(SSL_read(server_.get(), &byte, 1), 1);
@@ -2084,14 +2092,12 @@
// Connect client and server to get a session.
bssl::UniquePtr<SSL> client, server;
if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx,
- config)) {
+ config) ||
+ !FlushNewSessionTickets(client.get(), server.get())) {
fprintf(stderr, "Failed to connect client and server.\n");
return nullptr;
}
- // Run the read loop to account for post-handshake tickets in TLS 1.3.
- SSL_read(client.get(), nullptr, 0);
-
SSL_CTX_sess_set_new_cb(client_ctx, nullptr);
if (!g_last_session) {
@@ -2125,7 +2131,8 @@
ClientConfig config;
config.session = session;
if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx,
- config)) {
+ config) ||
+ !FlushNewSessionTickets(client.get(), server.get())) {
fprintf(stderr, "Failed to connect client and server.\n");
return nullptr;
}
@@ -2140,9 +2147,6 @@
return nullptr;
}
- // Run the read loop to account for post-handshake tickets in TLS 1.3.
- SSL_read(client.get(), nullptr, 0);
-
SSL_CTX_sess_set_new_cb(client_ctx, nullptr);
if (!g_last_session) {
@@ -3055,6 +3059,82 @@
EXPECT_FALSE(CreateClientSession(client_ctx_.get(), server_ctx_.get()));
}
+// Test that all versions survive tiny write buffers. In particular, TLS 1.3
+// NewSessionTickets are written post-handshake. Servers that block
+// |SSL_do_handshake| on writing them will deadlock if clients are not draining
+// the buffer. Test that we do not do this.
+TEST_P(SSLVersionTest, SmallBuffer) {
+ // DTLS is a datagram protocol and requires packet-sized buffers.
+ if (is_dtls()) {
+ return;
+ }
+
+ // Test both flushing NewSessionTickets with a zero-sized write and
+ // non-zero-sized write.
+ for (bool use_zero_write : {false, true}) {
+ SCOPED_TRACE(use_zero_write);
+
+ g_last_session = nullptr;
+ SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_sess_set_new_cb(client_ctx_.get(), SaveLastSession);
+
+ bssl::UniquePtr<SSL> client(SSL_new(client_ctx_.get())),
+ server(SSL_new(server_ctx_.get()));
+ ASSERT_TRUE(client);
+ ASSERT_TRUE(server);
+ SSL_set_connect_state(client.get());
+ SSL_set_accept_state(server.get());
+
+ // Use a tiny buffer.
+ BIO *bio1, *bio2;
+ ASSERT_TRUE(BIO_new_bio_pair(&bio1, 1, &bio2, 1));
+
+ // SSL_set_bio takes ownership.
+ SSL_set_bio(client.get(), bio1, bio1);
+ SSL_set_bio(server.get(), bio2, bio2);
+
+ ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
+ if (version() >= TLS1_3_VERSION) {
+ // The post-handshake ticket should not have been processed yet.
+ EXPECT_FALSE(g_last_session);
+ }
+
+ if (use_zero_write) {
+ ASSERT_TRUE(FlushNewSessionTickets(client.get(), server.get()));
+ EXPECT_TRUE(g_last_session);
+ }
+
+ // Send some data from server to client. If |use_zero_write| is false, this
+ // will also flush the NewSessionTickets.
+ static const char kMessage[] = "hello world";
+ char buf[sizeof(kMessage)];
+ for (;;) {
+ int server_ret = SSL_write(server.get(), kMessage, sizeof(kMessage));
+ int server_err = SSL_get_error(server.get(), server_ret);
+ int client_ret = SSL_read(client.get(), buf, sizeof(buf));
+ int client_err = SSL_get_error(client.get(), client_ret);
+
+ // The server will write a single record, so every iteration should see
+ // |SSL_ERROR_WANT_WRITE| and |SSL_ERROR_WANT_READ|, until the final
+ // iteration, where both will complete.
+ if (server_ret > 0) {
+ EXPECT_EQ(server_ret, static_cast<int>(sizeof(kMessage)));
+ EXPECT_EQ(client_ret, static_cast<int>(sizeof(kMessage)));
+ EXPECT_EQ(Bytes(buf), Bytes(kMessage));
+ break;
+ }
+
+ ASSERT_EQ(server_ret, -1);
+ ASSERT_EQ(server_err, SSL_ERROR_WANT_WRITE);
+ ASSERT_EQ(client_ret, -1);
+ ASSERT_EQ(client_err, SSL_ERROR_WANT_READ);
+ }
+
+ // The NewSessionTickets should have been flushed and processed.
+ EXPECT_TRUE(g_last_session);
+ }
+}
+
TEST(SSLTest, AddChainCertHack) {
// Ensure that we don't accidently break the hack that we have in place to
// keep curl and serf happy when they use an |X509| even after transfering
@@ -3514,9 +3594,7 @@
EXPECT_FALSE(SSL_session_reused(client.get()));
EXPECT_FALSE(SSL_session_reused(server.get()));
- // Run the read loop to account for post-handshake tickets in TLS 1.3.
- SSL_read(client.get(), nullptr, 0);
-
+ ASSERT_TRUE(FlushNewSessionTickets(client.get(), server.get()));
bssl::UniquePtr<SSL_SESSION> session = std::move(g_last_session);
ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(),
server_ctx.get(), retry_count,
@@ -4606,7 +4684,7 @@
thread.join();
}
}
-#endif
+#endif // OPENSSL_THREADS
constexpr size_t kNumQUICLevels = 4;
static_assert(ssl_encryption_initial < kNumQUICLevels,
@@ -5263,23 +5341,101 @@
EXPECT_EQ(SSL_process_quic_post_handshake(client_.get()), 0);
}
-// TODO(davidben): Convert this file to GTest properly.
-TEST(SSLTest, AllTests) {
- if (!TestSSL_SESSIONEncoding(kOpenSSLSession) ||
- !TestSSL_SESSIONEncoding(kCustomSession) ||
- !TestSSL_SESSIONEncoding(kBoringSSLSession) ||
- !TestBadSSL_SESSIONEncoding(kBadSessionExtraField) ||
- !TestBadSSL_SESSIONEncoding(kBadSessionVersion) ||
- !TestBadSSL_SESSIONEncoding(kBadSessionTrailingData) ||
- // Test the padding extension at TLS 1.2.
- !TestPaddingExtension(TLS1_2_VERSION, TLS1_2_VERSION) ||
- // Test the padding extension at TLS 1.3 with a TLS 1.2 session, so there
- // will be no PSK binder after the padding extension.
- !TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) ||
- // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there
- // will be a PSK binder after the padding extension.
- !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_VERSION)) {
- ADD_FAILURE() << "Tests failed";
+extern "C" {
+int BORINGSSL_enum_c_type_test(void);
+}
+
+TEST(SSLTest, EnumTypes) {
+ EXPECT_EQ(sizeof(int), sizeof(ssl_private_key_result_t));
+ EXPECT_EQ(1, BORINGSSL_enum_c_type_test());
+}
+
+TEST_P(SSLVersionTest, DoubleSSLError) {
+ // Connect the inner SSL connections.
+ ASSERT_TRUE(Connect());
+
+ // Make a pair of |BIO|s which wrap |client_| and |server_|.
+ UniquePtr<BIO_METHOD> bio_method(BIO_meth_new(0, nullptr));
+ ASSERT_TRUE(bio_method);
+ ASSERT_TRUE(BIO_meth_set_read(
+ bio_method.get(), [](BIO *bio, char *out, int len) -> int {
+ SSL *ssl = static_cast<SSL *>(BIO_get_data(bio));
+ int ret = SSL_read(ssl, out, len);
+ int ssl_ret = SSL_get_error(ssl, ret);
+ if (ssl_ret == SSL_ERROR_WANT_READ) {
+ BIO_set_retry_read(bio);
+ }
+ return ret;
+ }));
+ ASSERT_TRUE(BIO_meth_set_write(
+ bio_method.get(), [](BIO *bio, const char *in, int len) -> int {
+ SSL *ssl = static_cast<SSL *>(BIO_get_data(bio));
+ int ret = SSL_write(ssl, in, len);
+ int ssl_ret = SSL_get_error(ssl, ret);
+ if (ssl_ret == SSL_ERROR_WANT_WRITE) {
+ BIO_set_retry_write(bio);
+ }
+ return ret;
+ }));
+ ASSERT_TRUE(BIO_meth_set_ctrl(
+ bio_method.get(), [](BIO *bio, int cmd, long larg, void *parg) -> long {
+ // |SSL| objects require |BIO_flush| support.
+ if (cmd == BIO_CTRL_FLUSH) {
+ return 1;
+ }
+ return 0;
+ }));
+
+ UniquePtr<BIO> client_bio(BIO_new(bio_method.get()));
+ ASSERT_TRUE(client_bio);
+ BIO_set_data(client_bio.get(), client_.get());
+ BIO_set_init(client_bio.get(), 1);
+
+ UniquePtr<BIO> server_bio(BIO_new(bio_method.get()));
+ ASSERT_TRUE(server_bio);
+ BIO_set_data(server_bio.get(), server_.get());
+ BIO_set_init(server_bio.get(), 1);
+
+ // Wrap the inner connections in another layer of SSL.
+ UniquePtr<SSL> client_outer(SSL_new(client_ctx_.get()));
+ ASSERT_TRUE(client_outer);
+ SSL_set_connect_state(client_outer.get());
+ SSL_set_bio(client_outer.get(), client_bio.get(), client_bio.get());
+ client_bio.release(); // |SSL_set_bio| takes ownership.
+
+ UniquePtr<SSL> server_outer(SSL_new(server_ctx_.get()));
+ ASSERT_TRUE(server_outer);
+ SSL_set_accept_state(server_outer.get());
+ SSL_set_bio(server_outer.get(), server_bio.get(), server_bio.get());
+ server_bio.release(); // |SSL_set_bio| takes ownership.
+
+ // Configure |client_outer| to reject the server certificate.
+ SSL_set_custom_verify(
+ client_outer.get(), SSL_VERIFY_PEER,
+ [](SSL *ssl, uint8_t *out_alert) -> ssl_verify_result_t {
+ return ssl_verify_invalid;
+ });
+
+ for (;;) {
+ int client_ret = SSL_do_handshake(client_outer.get());
+ int client_err = SSL_get_error(client_outer.get(), client_ret);
+ if (client_err != SSL_ERROR_WANT_READ &&
+ client_err != SSL_ERROR_WANT_WRITE) {
+ // The client handshake should terminate on a certificate verification
+ // error.
+ EXPECT_EQ(SSL_ERROR_SSL, client_err);
+ uint32_t err = ERR_peek_error();
+ EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(err));
+ EXPECT_EQ(SSL_R_CERTIFICATE_VERIFY_FAILED, ERR_GET_REASON(err));
+ break;
+ }
+
+ // Run the server handshake and continue.
+ int server_ret = SSL_do_handshake(server_outer.get());
+ int server_err = SSL_get_error(server_outer.get(), server_ret);
+ ASSERT_TRUE(server_err == SSL_ERROR_NONE ||
+ server_err == SSL_ERROR_WANT_READ ||
+ server_err == SSL_ERROR_WANT_WRITE);
}
}
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index 841482f..cda7611 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -200,19 +200,19 @@
// forms of elements of |chain|. It returns one on success or zero on error, in
// which case no change to |cert->chain| is made. It preverses the existing
// leaf from |cert->chain|, if any.
-static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+static bool ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
UniquePtr<STACK_OF(CRYPTO_BUFFER)> new_chain;
if (cert->chain != nullptr) {
new_chain.reset(sk_CRYPTO_BUFFER_new_null());
if (!new_chain) {
- return 0;
+ return false;
}
// |leaf| might be NULL if it's a “leafless” chain.
CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain.get(), 0);
if (!PushToStack(new_chain.get(), UpRef(leaf))) {
- return 0;
+ return false;
}
}
@@ -220,32 +220,32 @@
if (!new_chain) {
new_chain = new_leafless_chain();
if (!new_chain) {
- return 0;
+ return false;
}
}
UniquePtr<CRYPTO_BUFFER> buffer = x509_to_buffer(x509);
if (!buffer ||
!PushToStack(new_chain.get(), std::move(buffer))) {
- return 0;
+ return false;
}
}
cert->chain = std::move(new_chain);
- return 1;
+ return true;
}
static void ssl_crypto_x509_cert_flush_cached_leaf(CERT *cert) {
X509_free(cert->x509_leaf);
- cert->x509_leaf = NULL;
+ cert->x509_leaf = nullptr;
}
static void ssl_crypto_x509_cert_flush_cached_chain(CERT *cert) {
sk_X509_pop_free(cert->x509_chain, X509_free);
- cert->x509_chain = NULL;
+ cert->x509_chain = nullptr;
}
-static int ssl_crypto_x509_check_client_CA_list(
+static bool ssl_crypto_x509_check_client_CA_list(
STACK_OF(CRYPTO_BUFFER) *names) {
for (const CRYPTO_BUFFER *buffer : names) {
const uint8_t *inp = CRYPTO_BUFFER_data(buffer);
@@ -253,11 +253,11 @@
d2i_X509_NAME(nullptr, &inp, CRYPTO_BUFFER_len(buffer)));
if (name == nullptr ||
inp != CRYPTO_BUFFER_data(buffer) + CRYPTO_BUFFER_len(buffer)) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
static void ssl_crypto_x509_cert_clear(CERT *cert) {
@@ -265,7 +265,7 @@
ssl_crypto_x509_cert_flush_cached_chain(cert);
X509_free(cert->x509_stash);
- cert->x509_stash = NULL;
+ cert->x509_stash = nullptr;
}
static void ssl_crypto_x509_cert_free(CERT *cert) {
@@ -274,19 +274,19 @@
}
static void ssl_crypto_x509_cert_dup(CERT *new_cert, const CERT *cert) {
- if (cert->verify_store != NULL) {
+ if (cert->verify_store != nullptr) {
X509_STORE_up_ref(cert->verify_store);
new_cert->verify_store = cert->verify_store;
}
}
-static int ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) {
+static bool ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) {
bssl::UniquePtr<STACK_OF(X509)> chain, chain_without_leaf;
if (sk_CRYPTO_BUFFER_num(sess->certs.get()) > 0) {
chain.reset(sk_X509_new_null());
if (!chain) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
+ return false;
}
if (sess->is_server) {
// chain_without_leaf is only needed for server sessions. See
@@ -294,7 +294,7 @@
chain_without_leaf.reset(sk_X509_new_null());
if (!chain_without_leaf) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
+ return false;
}
}
}
@@ -304,18 +304,18 @@
UniquePtr<X509> x509(X509_parse_from_buffer(cert));
if (!x509) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- return 0;
+ return false;
}
if (leaf == nullptr) {
leaf = UpRef(x509);
} else if (chain_without_leaf &&
!PushToStack(chain_without_leaf.get(), UpRef(x509))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
+ return false;
}
if (!PushToStack(chain.get(), std::move(x509))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return 0;
+ return false;
}
}
@@ -327,80 +327,76 @@
X509_free(sess->x509_peer);
sess->x509_peer = leaf.release();
- return 1;
+ return true;
}
-static int ssl_crypto_x509_session_dup(SSL_SESSION *new_session,
- const SSL_SESSION *session) {
+static bool ssl_crypto_x509_session_dup(SSL_SESSION *new_session,
+ const SSL_SESSION *session) {
new_session->x509_peer = UpRef(session->x509_peer).release();
if (session->x509_chain != nullptr) {
new_session->x509_chain = X509_chain_up_ref(session->x509_chain);
if (new_session->x509_chain == nullptr) {
- return 0;
+ return false;
}
}
if (session->x509_chain_without_leaf != nullptr) {
new_session->x509_chain_without_leaf =
X509_chain_up_ref(session->x509_chain_without_leaf);
if (new_session->x509_chain_without_leaf == nullptr) {
- return 0;
+ return false;
}
}
- return 1;
+ return true;
}
static void ssl_crypto_x509_session_clear(SSL_SESSION *session) {
X509_free(session->x509_peer);
- session->x509_peer = NULL;
+ session->x509_peer = nullptr;
sk_X509_pop_free(session->x509_chain, X509_free);
- session->x509_chain = NULL;
+ session->x509_chain = nullptr;
sk_X509_pop_free(session->x509_chain_without_leaf, X509_free);
- session->x509_chain_without_leaf = NULL;
+ session->x509_chain_without_leaf = nullptr;
}
-static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
- SSL_HANDSHAKE *hs,
- uint8_t *out_alert) {
+static bool ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
+ SSL_HANDSHAKE *hs,
+ uint8_t *out_alert) {
*out_alert = SSL_AD_INTERNAL_ERROR;
STACK_OF(X509) *const cert_chain = session->x509_chain;
- if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) {
- return 0;
+ if (cert_chain == nullptr || sk_X509_num(cert_chain) == 0) {
+ return false;
}
SSL_CTX *ssl_ctx = hs->ssl->ctx.get();
X509_STORE *verify_store = ssl_ctx->cert_store;
- if (hs->config->cert->verify_store != NULL) {
+ if (hs->config->cert->verify_store != nullptr) {
verify_store = hs->config->cert->verify_store;
}
X509 *leaf = sk_X509_value(cert_chain, 0);
ScopedX509_STORE_CTX ctx;
- if (!X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain)) {
+ if (!X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||
+ !X509_STORE_CTX_set_ex_data(
+ ctx.get(), SSL_get_ex_data_X509_STORE_CTX_idx(), hs->ssl) ||
+ // We need to inherit the verify parameters. These can be determined by
+ // the context: if its a server it will verify SSL client certificates or
+ // vice versa.
+ !X509_STORE_CTX_set_default(
+ ctx.get(), hs->ssl->server ? "ssl_client" : "ssl_server") ||
+ // Anything non-default in "param" should overwrite anything in the ctx.
+ !X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),
+ hs->config->param)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
- return 0;
+ return false;
}
- if (!X509_STORE_CTX_set_ex_data(
- ctx.get(), SSL_get_ex_data_X509_STORE_CTX_idx(), hs->ssl)) {
- return 0;
- }
-
- // We need to inherit the verify parameters. These can be determined by the
- // context: if its a server it will verify SSL client certificates or vice
- // versa.
- X509_STORE_CTX_set_default(ctx.get(),
- hs->ssl->server ? "ssl_client" : "ssl_server");
-
- // Anything non-default in "param" should overwrite anything in the ctx.
- X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),
- hs->config->param);
if (hs->config->verify_callback) {
X509_STORE_CTX_set_verify_cb(ctx.get(), hs->config->verify_callback);
}
int verify_ret;
- if (ssl_ctx->app_verify_callback != NULL) {
+ if (ssl_ctx->app_verify_callback != nullptr) {
verify_ret =
ssl_ctx->app_verify_callback(ctx.get(), ssl_ctx->app_verify_arg);
} else {
@@ -412,59 +408,59 @@
// If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {
*out_alert = SSL_alert_from_verify_result(ctx->error);
- return 0;
+ return false;
}
ERR_clear_error();
- return 1;
+ return true;
}
static void ssl_crypto_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {
sk_X509_NAME_pop_free(hs->cached_x509_ca_names, X509_NAME_free);
- hs->cached_x509_ca_names = NULL;
+ hs->cached_x509_ca_names = nullptr;
}
-static int ssl_crypto_x509_ssl_new(SSL_HANDSHAKE *hs) {
+static bool ssl_crypto_x509_ssl_new(SSL_HANDSHAKE *hs) {
hs->config->param = X509_VERIFY_PARAM_new();
- if (hs->config->param == NULL) {
- return 0;
+ if (hs->config->param == nullptr) {
+ return false;
}
X509_VERIFY_PARAM_inherit(hs->config->param, hs->ssl->ctx->param);
- return 1;
+ return true;
}
static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {
sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
- cfg->cached_x509_client_CA = NULL;
+ cfg->cached_x509_client_CA = nullptr;
}
static void ssl_crypto_x509_ssl_config_free(SSL_CONFIG *cfg) {
sk_X509_NAME_pop_free(cfg->cached_x509_client_CA, X509_NAME_free);
- cfg->cached_x509_client_CA = NULL;
+ cfg->cached_x509_client_CA = nullptr;
X509_VERIFY_PARAM_free(cfg->param);
}
-static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
+static bool ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
// Only build a chain if there are no intermediates configured and the feature
// isn't disabled.
if ((hs->ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
!ssl_has_certificate(hs) || hs->config->cert->chain == NULL ||
sk_CRYPTO_BUFFER_num(hs->config->cert->chain.get()) > 1) {
- return 1;
+ return true;
}
UniquePtr<X509> leaf(X509_parse_from_buffer(
sk_CRYPTO_BUFFER_value(hs->config->cert->chain.get(), 0)));
if (!leaf) {
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
- return 0;
+ return false;
}
ScopedX509_STORE_CTX ctx;
if (!X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store, leaf.get(),
NULL)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
- return 0;
+ return false;
}
// Attempt to build a chain, ignoring the result.
@@ -475,23 +471,23 @@
X509_free(sk_X509_shift(ctx->chain));
if (!ssl_cert_set_chain(hs->config->cert.get(), ctx->chain)) {
- return 0;
+ return false;
}
ssl_crypto_x509_cert_flush_cached_chain(hs->config->cert.get());
- return 1;
+ return true;
}
static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {
sk_X509_NAME_pop_free(ctx->cached_x509_client_CA, X509_NAME_free);
- ctx->cached_x509_client_CA = NULL;
+ ctx->cached_x509_client_CA = nullptr;
}
-static int ssl_crypto_x509_ssl_ctx_new(SSL_CTX *ctx) {
+static bool ssl_crypto_x509_ssl_ctx_new(SSL_CTX *ctx) {
ctx->cert_store = X509_STORE_new();
ctx->param = X509_VERIFY_PARAM_new();
- return (ctx->cert_store != NULL && ctx->param != NULL);
+ return (ctx->cert_store != nullptr && ctx->param != nullptr);
}
static void ssl_crypto_x509_ssl_ctx_free(SSL_CTX *ctx) {
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index c6b2844..4c2fffb 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -359,27 +359,3 @@
MakeConstSpan(session->master_key, session->master_key_length),
MakeConstSpan(label, label_len), seed, {});
}
-
-int SSL_export_early_keying_material(
- SSL *ssl, uint8_t *out, size_t out_len, const char *label, size_t label_len,
- const uint8_t *context, size_t context_len) {
- if (!SSL_in_early_data(ssl) &&
- (!ssl->s3->have_version ||
- ssl_protocol_version(ssl) < TLS1_3_VERSION)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
- return 0;
- }
-
- // The early exporter only exists if we accepted early data or offered it as
- // a client.
- if (!SSL_in_early_data(ssl) && !SSL_early_data_accepted(ssl)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_EARLY_DATA_NOT_IN_USE);
- return 0;
- }
-
- return tls13_export_keying_material(
- ssl, MakeSpan(out, out_len),
- MakeConstSpan(ssl->s3->early_exporter_secret,
- ssl->s3->early_exporter_secret_len),
- MakeConstSpan(label, label_len), MakeConstSpan(context, context_len));
-}
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 87f1888..c1c41a8 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -199,6 +199,10 @@
return true;
}
+static bool is_post_quantum_group(uint16_t id) {
+ return id == SSL_CURVE_CECPQ2 || id == SSL_CURVE_CECPQ2b;
+}
+
bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
const SSLMessage &msg) {
OPENSSL_memset(out, 0, sizeof(*out));
@@ -325,10 +329,10 @@
for (uint16_t pref_group : pref) {
for (uint16_t supp_group : supp) {
if (pref_group == supp_group &&
- // CECPQ2 doesn't fit in the u8-length-prefixed ECPoint field in TLS
- // 1.2 and below.
+ // CECPQ2(b) doesn't fit in the u8-length-prefixed ECPoint field in
+ // TLS 1.2 and below.
(ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
- pref_group != SSL_CURVE_CECPQ2)) {
+ !is_post_quantum_group(pref_group))) {
*out_group_id = pref_group;
return true;
}
@@ -390,9 +394,9 @@
}
bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) {
- if (group_id == SSL_CURVE_CECPQ2 &&
+ if (is_post_quantum_group(group_id) &&
ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) {
- // CECPQ2 requires TLS 1.3.
+ // CECPQ2(b) requires TLS 1.3.
return false;
}
@@ -629,45 +633,7 @@
static bool ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
CBS *contents) {
- SSL *const ssl = hs->ssl;
- if (contents == NULL) {
- return true;
- }
-
- CBS server_name_list, host_name;
- uint8_t name_type;
- if (!CBS_get_u16_length_prefixed(contents, &server_name_list) ||
- !CBS_get_u8(&server_name_list, &name_type) ||
- // Although the server_name extension was intended to be extensible to
- // new name types and multiple names, OpenSSL 1.0.x had a bug which meant
- // different name types will cause an error. Further, RFC 4366 originally
- // defined syntax inextensibly. RFC 6066 corrected this mistake, but
- // adding new name types is no longer feasible.
- //
- // Act as if the extensibility does not exist to simplify parsing.
- !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
- CBS_len(&server_name_list) != 0 ||
- CBS_len(contents) != 0) {
- return false;
- }
-
- if (name_type != TLSEXT_NAMETYPE_host_name ||
- CBS_len(&host_name) == 0 ||
- CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
- CBS_contains_zero_byte(&host_name)) {
- *out_alert = SSL_AD_UNRECOGNIZED_NAME;
- return false;
- }
-
- // Copy the hostname as a string.
- char *raw = nullptr;
- if (!CBS_strdup(&host_name, &raw)) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- return false;
- }
- ssl->s3->hostname.reset(raw);
-
- hs->should_ack_sni = true;
+ // SNI has already been parsed earlier in the handshake. See |extract_sni|.
return true;
}
@@ -1790,7 +1756,7 @@
}
static bool ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
- // The point format extension is unneccessary in TLS 1.3.
+ // The point format extension is unnecessary in TLS 1.3.
if (hs->min_version >= TLS1_3_VERSION) {
return true;
}
@@ -2057,20 +2023,46 @@
static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (!ssl->enable_early_data ||
- // Session must be 0-RTT capable.
- ssl->session == nullptr ||
- ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
- ssl->session->ticket_max_early_data == 0 ||
- // The second ClientHello never offers early data.
- hs->received_hello_retry_request ||
- // In case ALPN preferences changed since this session was established,
- // avoid reporting a confusing value in |SSL_get0_alpn_selected|.
- (!ssl->session->early_alpn.empty() &&
- !ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn))) {
+ // The second ClientHello never offers early data, and we must have already
+ // filled in |early_data_reason| by this point.
+ if (hs->received_hello_retry_request) {
+ assert(ssl->s3->early_data_reason != ssl_early_data_unknown);
return true;
}
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
+ return true;
+ }
+
+ if (hs->max_version < TLS1_3_VERSION) {
+ // We discard inapplicable sessions, so this is redundant with the session
+ // checks below, but we check give a more useful reason.
+ ssl->s3->early_data_reason = ssl_early_data_protocol_version;
+ return true;
+ }
+
+ if (ssl->session == nullptr) {
+ ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
+ return true;
+ }
+
+ if (ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
+ ssl->session->ticket_max_early_data == 0) {
+ ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
+ return true;
+ }
+
+ // In case ALPN preferences changed since this session was established, avoid
+ // reporting a confusing value in |SSL_get0_alpn_selected| and sending early
+ // data we know will be rejected.
+ if (!ssl->session->early_alpn.empty() &&
+ !ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn)) {
+ ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
+ return true;
+ }
+
+ // |early_data_reason| will be filled in later when the server responds.
hs->early_data_offered = true;
if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
@@ -2083,12 +2075,27 @@
}
static bool ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
- uint8_t *out_alert, CBS *contents) {
+ uint8_t *out_alert,
+ CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == NULL) {
+ if (hs->early_data_offered && !hs->received_hello_retry_request) {
+ ssl->s3->early_data_reason = ssl->s3->session_reused
+ ? ssl_early_data_peer_declined
+ : ssl_early_data_session_not_resumed;
+ } else {
+ // We already filled in |early_data_reason| when declining to offer 0-RTT
+ // or handling the implicit HelloRetryRequest reject.
+ assert(ssl->s3->early_data_reason != ssl_early_data_unknown);
+ }
return true;
}
+ // If we received an HRR, the second ClientHello never offers early data, so
+ // the extensions logic will automatically reject early data extensions as
+ // unsolicited. This covered by the ServerAcceptsEarlyDataOnHRR test.
+ assert(!hs->received_hello_retry_request);
+
if (CBS_len(contents) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
@@ -2100,6 +2107,7 @@
return false;
}
+ ssl->s3->early_data_reason = ssl_early_data_accepted;
ssl->s3->early_data_accepted = true;
return true;
}
@@ -2186,8 +2194,8 @@
group_id = groups[0];
- if (group_id == SSL_CURVE_CECPQ2 && groups.size() >= 2) {
- // CECPQ2 is not sent as the only initial key share. We'll include the
+ if (is_post_quantum_group(group_id) && groups.size() >= 2) {
+ // CECPQ2(b) is not sent as the only initial key share. We'll include the
// 2nd preference group too to avoid round-trips.
second_group_id = groups[1];
assert(second_group_id != group_id);
@@ -2424,7 +2432,7 @@
}
for (uint16_t group : tls1_get_grouplist(hs)) {
- if (group == SSL_CURVE_CECPQ2 &&
+ if (is_post_quantum_group(group) &&
hs->max_version < TLS1_3_VERSION) {
continue;
}
@@ -2840,6 +2848,67 @@
return true;
}
+
+// Post-quantum experiment signal
+//
+// This extension may be used in order to identify a control group for
+// experimenting with post-quantum key exchange algorithms.
+
+static bool ext_pq_experiment_signal_add_clienthello(SSL_HANDSHAKE *hs,
+ CBB *out) {
+ if (hs->ssl->ctx->pq_experiment_signal &&
+ (!CBB_add_u16(out, TLSEXT_TYPE_pq_experiment_signal) ||
+ !CBB_add_u16(out, 0))) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ext_pq_experiment_signal_parse_serverhello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ if (!hs->ssl->ctx->pq_experiment_signal || CBS_len(contents) != 0) {
+ return false;
+ }
+
+ hs->ssl->s3->pq_experiment_signal_seen = true;
+ return true;
+}
+
+static bool ext_pq_experiment_signal_parse_clienthello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ if (CBS_len(contents) != 0) {
+ return false;
+ }
+
+ if (hs->ssl->ctx->pq_experiment_signal) {
+ hs->ssl->s3->pq_experiment_signal_seen = true;
+ }
+
+ return true;
+}
+
+static bool ext_pq_experiment_signal_add_serverhello(SSL_HANDSHAKE *hs,
+ CBB *out) {
+ if (hs->ssl->s3->pq_experiment_signal_seen &&
+ (!CBB_add_u16(out, TLSEXT_TYPE_pq_experiment_signal) ||
+ !CBB_add_u16(out, 0))) {
+ return false;
+ }
+
+ return true;
+}
+
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
{
@@ -3028,6 +3097,14 @@
ext_delegated_credential_parse_clienthello,
dont_add_serverhello,
},
+ {
+ TLSEXT_TYPE_pq_experiment_signal,
+ NULL,
+ ext_pq_experiment_signal_add_clienthello,
+ ext_pq_experiment_signal_parse_serverhello,
+ ext_pq_experiment_signal_parse_clienthello,
+ ext_pq_experiment_signal_add_serverhello,
+ },
};
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -3061,6 +3138,9 @@
return false;
}
+ // Note we may send multiple ClientHellos for DTLS HelloVerifyRequest and TLS
+ // 1.3 HelloRetryRequest. For the latter, the extensions may change, so it is
+ // important to reset this value.
hs->extensions.sent = 0;
for (size_t i = 0; i < kNumExtensions; i++) {
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 62db076..f58c151 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -279,23 +279,23 @@
// after a renegotiation, that authentication-related properties match |config|.
static bool CheckAuthProperties(SSL *ssl, bool is_resume,
const TestConfig *config) {
- if (!config->expected_ocsp_response.empty()) {
+ if (!config->expect_ocsp_response.empty()) {
const uint8_t *data;
size_t len;
SSL_get0_ocsp_response(ssl, &data, &len);
- if (config->expected_ocsp_response.size() != len ||
- OPENSSL_memcmp(config->expected_ocsp_response.data(), data, len) != 0) {
+ if (config->expect_ocsp_response.size() != len ||
+ OPENSSL_memcmp(config->expect_ocsp_response.data(), data, len) != 0) {
fprintf(stderr, "OCSP response mismatch\n");
return false;
}
}
- if (!config->expected_signed_cert_timestamps.empty()) {
+ if (!config->expect_signed_cert_timestamps.empty()) {
const uint8_t *data;
size_t len;
SSL_get0_signed_cert_timestamp_list(ssl, &data, &len);
- if (config->expected_signed_cert_timestamps.size() != len ||
- OPENSSL_memcmp(config->expected_signed_cert_timestamps.data(), data,
+ if (config->expect_signed_cert_timestamps.size() != len ||
+ OPENSSL_memcmp(config->expect_signed_cert_timestamps.data(), data,
len) != 0) {
fprintf(stderr, "SCT list mismatch\n");
return false;
@@ -389,6 +389,39 @@
return true;
}
+static const char *EarlyDataReasonToString(ssl_early_data_reason_t reason) {
+ switch (reason) {
+ case ssl_early_data_unknown:
+ return "unknown";
+ case ssl_early_data_disabled:
+ return "disabled";
+ case ssl_early_data_accepted:
+ return "accepted";
+ case ssl_early_data_protocol_version:
+ return "protocol_version";
+ case ssl_early_data_peer_declined:
+ return "peer_declined";
+ case ssl_early_data_no_session_offered:
+ return "no_session_offered";
+ case ssl_early_data_session_not_resumed:
+ return "session_not_resumed";
+ case ssl_early_data_unsupported_for_session:
+ return "unsupported_for_session";
+ case ssl_early_data_hello_retry_request:
+ return "hello_retry_request";
+ case ssl_early_data_alpn_mismatch:
+ return "alpn_mismatch";
+ case ssl_early_data_channel_id:
+ return "channel_id";
+ case ssl_early_data_token_binding:
+ return "token_binding";
+ case ssl_early_data_ticket_age_skew:
+ return "ticket_age_skew";
+ }
+
+ abort();
+}
+
// CheckHandshakeProperties checks, immediately after |ssl| completes its
// initial handshake (or False Starts), whether all the properties are
// consistent with the test configuration and invariants.
@@ -459,23 +492,23 @@
return false;
}
- if (!config->expected_server_name.empty()) {
+ if (!config->expect_server_name.empty()) {
const char *server_name =
SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (server_name == nullptr ||
- server_name != config->expected_server_name) {
+ server_name != config->expect_server_name) {
fprintf(stderr, "servername mismatch (got %s; want %s)\n",
- server_name, config->expected_server_name.c_str());
+ server_name, config->expect_server_name.c_str());
return false;
}
}
- if (!config->expected_next_proto.empty()) {
+ if (!config->expect_next_proto.empty()) {
const uint8_t *next_proto;
unsigned next_proto_len;
SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len);
- if (next_proto_len != config->expected_next_proto.size() ||
- OPENSSL_memcmp(next_proto, config->expected_next_proto.data(),
+ if (next_proto_len != config->expect_next_proto.size() ||
+ OPENSSL_memcmp(next_proto, config->expect_next_proto.data(),
next_proto_len) != 0) {
fprintf(stderr, "negotiated next proto mismatch\n");
return false;
@@ -486,48 +519,48 @@
const uint8_t *alpn_proto;
unsigned alpn_proto_len;
SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len);
- if (alpn_proto_len != config->expected_alpn.size() ||
- OPENSSL_memcmp(alpn_proto, config->expected_alpn.data(),
+ if (alpn_proto_len != config->expect_alpn.size() ||
+ OPENSSL_memcmp(alpn_proto, config->expect_alpn.data(),
alpn_proto_len) != 0) {
fprintf(stderr, "negotiated alpn proto mismatch\n");
return false;
}
}
- if (!config->expected_quic_transport_params.empty()) {
+ if (!config->expect_quic_transport_params.empty()) {
const uint8_t *peer_params;
size_t peer_params_len;
SSL_get_peer_quic_transport_params(ssl, &peer_params, &peer_params_len);
- if (peer_params_len != config->expected_quic_transport_params.size() ||
+ if (peer_params_len != config->expect_quic_transport_params.size() ||
OPENSSL_memcmp(peer_params,
- config->expected_quic_transport_params.data(),
+ config->expect_quic_transport_params.data(),
peer_params_len) != 0) {
fprintf(stderr, "QUIC transport params mismatch\n");
return false;
}
}
- if (!config->expected_channel_id.empty()) {
+ if (!config->expect_channel_id.empty()) {
uint8_t channel_id[64];
if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) {
fprintf(stderr, "no channel id negotiated\n");
return false;
}
- if (config->expected_channel_id.size() != 64 ||
- OPENSSL_memcmp(config->expected_channel_id.data(), channel_id, 64) !=
+ if (config->expect_channel_id.size() != 64 ||
+ OPENSSL_memcmp(config->expect_channel_id.data(), channel_id, 64) !=
0) {
fprintf(stderr, "channel id mismatch\n");
return false;
}
}
- if (config->expected_token_binding_param != -1) {
+ if (config->expect_token_binding_param != -1) {
if (!SSL_is_token_binding_negotiated(ssl)) {
fprintf(stderr, "no Token Binding negotiated\n");
return false;
}
if (SSL_get_negotiated_token_binding_param(ssl) !=
- static_cast<uint8_t>(config->expected_token_binding_param)) {
+ static_cast<uint8_t>(config->expect_token_binding_param)) {
fprintf(stderr, "Token Binding param mismatch\n");
return false;
}
@@ -587,7 +620,8 @@
return false;
}
- if (is_resume && !SSL_in_early_data(ssl)) {
+ // The early data status is only applicable after the handshake is confirmed.
+ if (!SSL_in_early_data(ssl)) {
if ((config->expect_accept_early_data && !SSL_early_data_accepted(ssl)) ||
(config->expect_reject_early_data && SSL_early_data_accepted(ssl))) {
fprintf(stderr,
@@ -595,6 +629,15 @@
SSL_early_data_accepted(ssl) ? "" : " not");
return false;
}
+
+ const char *early_data_reason =
+ EarlyDataReasonToString(SSL_get_early_data_reason(ssl));
+ if (!config->expect_early_data_reason.empty() &&
+ config->expect_early_data_reason != early_data_reason) {
+ fprintf(stderr, "Early data reason was \"%s\", expected \"%s\"\n",
+ early_data_reason, config->expect_early_data_reason.c_str());
+ return false;
+ }
}
if (!config->psk.empty()) {
@@ -622,6 +665,21 @@
return false;
}
+ if (config->expect_delegated_credential_used !=
+ !!SSL_delegated_credential_used(ssl)) {
+ fprintf(stderr,
+ "Got %s delegated credential usage, but wanted opposite. \n",
+ SSL_delegated_credential_used(ssl) ? "" : "no");
+ return false;
+ }
+
+ if (config->expect_pq_experiment_signal !=
+ !!SSL_pq_experiment_signal_seen(ssl)) {
+ fprintf(stderr, "Got %sPQ experiment signal, but wanted opposite. \n",
+ SSL_pq_experiment_signal_seen(ssl) ? "" : "no ");
+ return false;
+ }
+
return true;
}
@@ -701,6 +759,16 @@
return false;
}
+ // Client pre- and post-0-RTT reject states are considered logically
+ // different connections with different test expections. Check that the test
+ // did not mistakenly configure reason expectations on the wrong one.
+ if (!config->expect_early_data_reason.empty()) {
+ fprintf(stderr,
+ "Test error: client reject -expect-early-data-reason flags "
+ "should be configured with -on-retry, not -on-resume.\n");
+ return false;
+ }
+
// Reset the connection and try again at 1-RTT.
SSL_reset_early_data_reject(ssl.get());
GetTestState(ssl.get())->cert_verified = false;
@@ -804,22 +872,6 @@
GetTestState(ssl)->got_new_session = false;
}
- if (config->export_early_keying_material > 0) {
- std::vector<uint8_t> result(
- static_cast<size_t>(config->export_early_keying_material));
- if (!SSL_export_early_keying_material(
- ssl, result.data(), result.size(), config->export_label.data(),
- config->export_label.size(),
- reinterpret_cast<const uint8_t *>(config->export_context.data()),
- config->export_context.size())) {
- fprintf(stderr, "failed to export keying material\n");
- return false;
- }
- if (WriteAll(ssl, result.data(), result.size()) < 0) {
- return false;
- }
- }
-
if (config->export_keying_material > 0) {
std::vector<uint8_t> result(
static_cast<size_t>(config->export_keying_material));
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index bbcacf5..b56b9b3 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -126,6 +126,7 @@
extensionQUICTransportParams uint16 = 0xffa5 // draft-ietf-quic-tls-13
extensionChannelID uint16 = 30032 // not IANA assigned
extensionDelegatedCredentials uint16 = 0xff02 // not IANA assigned
+ extensionPQExperimentSignal uint16 = 54538
)
// TLS signaling cipher suite values
@@ -144,12 +145,13 @@
type CurveID uint16
const (
- CurveP224 CurveID = 21
- CurveP256 CurveID = 23
- CurveP384 CurveID = 24
- CurveP521 CurveID = 25
- CurveX25519 CurveID = 29
- CurveCECPQ2 CurveID = 16696
+ CurveP224 CurveID = 21
+ CurveP256 CurveID = 23
+ CurveP384 CurveID = 24
+ CurveP521 CurveID = 25
+ CurveX25519 CurveID = 29
+ CurveCECPQ2 CurveID = 16696
+ CurveCECPQ2b CurveID = 65074
)
// TLS Elliptic Curve Point Formats
@@ -499,6 +501,11 @@
CertCompressionAlgs map[uint16]CertCompressionAlg
+ // PQExperimentSignal instructs a client to send a non-IANA defined extension
+ // that signals participation in an experiment of post-quantum key exchange
+ // methods.
+ PQExperimentSignal bool
+
// Bugs specifies optional misbehaviour to be used for testing other
// implementations.
Bugs ProtocolBugs
@@ -1319,21 +1326,6 @@
// it was accepted.
SendEarlyDataExtension bool
- // ExpectEarlyKeyingMaterial, if non-zero, causes a TLS 1.3 server to
- // read an application data record after the ClientHello before it sends
- // a ServerHello. The record's contents have the specified length and
- // match the corresponding early exporter value. This is used to test
- // the client using the early exporter in the 0-RTT state.
- ExpectEarlyKeyingMaterial int
-
- // ExpectEarlyKeyingLabel is the label to use with
- // ExpectEarlyKeyingMaterial.
- ExpectEarlyKeyingLabel string
-
- // ExpectEarlyKeyingContext is the context string to use with
- // ExpectEarlyKeyingMaterial
- ExpectEarlyKeyingContext string
-
// ExpectEarlyData causes a TLS 1.3 server to read application
// data after the ClientHello (assuming the server is able to
// derive the key under which the data is encrypted) before it
@@ -1649,6 +1641,10 @@
// DisableDelegatedCredentials, if true, disables client support for delegated
// credentials.
DisableDelegatedCredentials bool
+
+ // ExpectPQExperimentSignal specifies whether or not the post-quantum
+ // experiment signal should be received by a client or server.
+ ExpectPQExperimentSignal bool
}
func (c *Config) serverInit() {
@@ -1728,7 +1724,7 @@
return ret
}
-var defaultCurvePreferences = []CurveID{CurveCECPQ2, CurveX25519, CurveP256, CurveP384, CurveP521}
+var defaultCurvePreferences = []CurveID{CurveCECPQ2b, CurveCECPQ2, CurveX25519, CurveP256, CurveP384, CurveP521}
func (c *Config) curvePreferences() []CurveID {
if c == nil || len(c.CurvePreferences) == 0 {
diff --git a/src/ssl/test/runner/ecdsa_p224_key.pem b/src/ssl/test/runner/ecdsa_p224_key.pem
index cfe411b..d62594b 100644
--- a/src/ssl/test/runner/ecdsa_p224_key.pem
+++ b/src/ssl/test/runner/ecdsa_p224_key.pem
@@ -1,5 +1,5 @@
------BEGIN EC PRIVATE KEY-----
-MGgCAQEEHGi+rNLi+gHJqmRRtdlLBOw1WYv7H/VnlYGAZ0+gBwYFK4EEACGhPAM6
-AATp26Xp0vT4LKigWIorhX4Rg1g9sxmgtPFFyNGCvDd1vpDMEC+INLEoANNW7JGZ
-pULvgx/pugEcOQ==
------END EC PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MHgCAQAwEAYHKoZIzj0CAQYFK4EEACEEYTBfAgEBBBxovqzS4voByapkUbXZSwTs
+NVmL+x/1Z5WBgGdPoTwDOgAE6dul6dL0+CyooFiKK4V+EYNYPbMZoLTxRcjRgrw3
+db6QzBAviDSxKADTVuyRmaVC74Mf6boBHDk=
+-----END PRIVATE KEY-----
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index 1a154c2..0a50722 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -18,9 +18,6 @@
"BadECDSA-*": "Fuzzer mode always accepts a signature.",
"*-InvalidSignature-*": "Fuzzer mode always accepts a signature.",
- "*Auth-Verify-RSA-PKCS1-*-TLS13*": "Fuzzer mode always accepts a signature.",
- "*Auth-Verify-ECDSA-SHA1-TLS13*": "Fuzzer mode always accepts a signature.",
- "*Auth-Verify-ECDSA-P224-*-TLS13*": "Fuzzer mode always accepts a signature.",
"Verify-*Auth-SignatureType*": "Fuzzer mode always accepts a signature.",
"ECDSACurveMismatch-Verify-TLS13*": "Fuzzer mode always accepts a signature.",
"InvalidChannelIDSignature-*": "Fuzzer mode always accepts a signature.",
@@ -30,6 +27,7 @@
"Resume-Server-DeclineCrossVersion*": "Fuzzer mode does not encrypt tickets.",
"TicketCallback-SingleCall-*": "Fuzzer mode does not encrypt tickets.",
"CorruptTicket-*": "Fuzzer mode does not encrypt tickets.",
+ "*RejectTicket-Server-*": "Fuzzer mode does not encrypt tickets.",
"ShimTicketRewritable*": "Fuzzer mode does not encrypt tickets.",
"Resume-Server-*Binder*": "Fuzzer mode does not check binders.",
@@ -46,6 +44,7 @@
"*-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
"EarlyData-Reject*-Client-*": "Trial decryption does not work with the NULL cipher.",
"CustomExtensions-Server-EarlyDataOffered": "Trial decryption does not work with the NULL cipher.",
+ "*-TicketAgeSkew-*-Reject": "Trial decryption does not work with the NULL cipher.",
"Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info.",
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 45dc75d..2574ec3 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -129,6 +129,7 @@
omitExtensions: c.config.Bugs.OmitExtensions,
emptyExtensions: c.config.Bugs.EmptyExtensions,
delegatedCredentials: !c.config.Bugs.DisableDelegatedCredentials,
+ pqExperimentSignal: c.config.PQExperimentSignal,
}
if maxVersion >= VersionTLS13 {
@@ -1666,6 +1667,10 @@
c.quicTransportParams = serverExtensions.quicTransportParams
}
+ if c.config.Bugs.ExpectPQExperimentSignal != serverExtensions.pqExperimentSignal {
+ return fmt.Errorf("tls: PQ experiment signal presence (%t) was not what was expected", serverExtensions.pqExperimentSignal)
+ }
+
return nil
}
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index f12ca1a..ac52eed 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -298,6 +298,7 @@
pad int
compressedCertAlgs []uint16
delegatedCredentials bool
+ pqExperimentSignal bool
}
func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -352,7 +353,8 @@
m.emptyExtensions == m1.emptyExtensions &&
m.pad == m1.pad &&
eqUint16s(m.compressedCertAlgs, m1.compressedCertAlgs) &&
- m.delegatedCredentials == m1.delegatedCredentials
+ m.delegatedCredentials == m1.delegatedCredentials &&
+ m.pqExperimentSignal == m1.pqExperimentSignal
}
func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) {
@@ -598,6 +600,11 @@
extensions.addU16(extensionDelegatedCredentials)
extensions.addU16(0) // Length is always 0
}
+ if m.pqExperimentSignal {
+ extensions.addU16(extensionPQExperimentSignal)
+ extensions.addU16(0) // Length is always 0
+ }
+
// The PSK extension must be last. See https://tools.ietf.org/html/rfc8446#section-4.2.11
if len(m.pskIdentities) > 0 && !m.pskBinderFirst {
extensions.addU16(extensionPreSharedKey)
@@ -724,6 +731,7 @@
m.extendedMasterSecret = false
m.customExtension = ""
m.delegatedCredentials = false
+ m.pqExperimentSignal = false
if len(reader) == 0 {
// ClientHello is optionally followed by extension data
@@ -959,6 +967,11 @@
return false
}
m.delegatedCredentials = true
+ case extensionPQExperimentSignal:
+ if len(body) != 0 {
+ return false
+ }
+ m.pqExperimentSignal = true
}
if isGREASEValue(extension) {
@@ -1226,6 +1239,7 @@
supportedCurves []CurveID
quicTransportParams []byte
serverNameAck bool
+ pqExperimentSignal bool
}
func (m *serverExtensions) marshal(extensions *byteBuilder) {
@@ -1360,6 +1374,10 @@
extensions.addU16(extensionServerName)
extensions.addU16(0) // zero length
}
+ if m.pqExperimentSignal {
+ extensions.addU16(extensionPQExperimentSignal)
+ extensions.addU16(0) // zero length
+ }
}
func (m *serverExtensions) unmarshal(data byteReader, version uint16) bool {
@@ -1468,6 +1486,11 @@
return false
}
m.hasEarlyData = true
+ case extensionPQExperimentSignal:
+ if len(body) != 0 {
+ return false
+ }
+ m.pqExperimentSignal = true
default:
// Unknown extensions are illegal from the server.
return false
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index d2ef9b4..3a6c810 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -210,8 +210,8 @@
if config.Bugs.FailIfCECPQ2Offered {
for _, offeredCurve := range hs.clientHello.supportedCurves {
- if offeredCurve == CurveCECPQ2 {
- return errors.New("tls: CECPQ2 was offered")
+ if isPqGroup(offeredCurve) {
+ return errors.New("tls: CECPQ2 or CECPQ2b was offered")
}
}
}
@@ -228,6 +228,10 @@
}
}
+ if c.config.Bugs.ExpectPQExperimentSignal != hs.clientHello.pqExperimentSignal {
+ return fmt.Errorf("tls: PQ experiment signal presence (%t) was not what was expected", hs.clientHello.pqExperimentSignal)
+ }
+
c.clientVersion = hs.clientHello.vers
// Use the versions extension if supplied, otherwise use the legacy ClientHello version.
@@ -722,16 +726,7 @@
}
c.earlyCipherSuite = hs.suite
- expectEarlyData := config.Bugs.ExpectEarlyData
- if n := config.Bugs.ExpectEarlyKeyingMaterial; n > 0 {
- exporter, err := c.ExportEarlyKeyingMaterial(n, []byte(config.Bugs.ExpectEarlyKeyingLabel), []byte(config.Bugs.ExpectEarlyKeyingContext))
- if err != nil {
- return err
- }
- expectEarlyData = append([][]byte{exporter}, expectEarlyData...)
- }
-
- for _, expectedMsg := range expectEarlyData {
+ for _, expectedMsg := range config.Bugs.ExpectEarlyData {
if err := c.readRecord(recordTypeApplicationData); err != nil {
return err
}
@@ -1232,8 +1227,8 @@
preferredCurves := config.curvePreferences()
Curves:
for _, curve := range hs.clientHello.supportedCurves {
- if curve == CurveCECPQ2 && c.vers < VersionTLS13 {
- // CECPQ2 is TLS 1.3-only.
+ if isPqGroup(curve) && c.vers < VersionTLS13 {
+ // CECPQ2 and CECPQ2b is TLS 1.3-only.
continue
}
@@ -1456,6 +1451,7 @@
}
serverExtensions.serverNameAck = c.config.Bugs.SendServerNameAck
+ serverExtensions.pqExperimentSignal = hs.clientHello.pqExperimentSignal
return nil
}
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 13e78bc..f4789b6 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -19,6 +19,7 @@
"boringssl.googlesource.com/boringssl/ssl/test/runner/curve25519"
"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
"boringssl.googlesource.com/boringssl/ssl/test/runner/hrss"
+ "boringssl.googlesource.com/boringssl/ssl/test/runner/sike"
)
type keyType int
@@ -433,6 +434,98 @@
return preMasterSecret, nil
}
+// cecpq2BCurve implements CECPQ2b, which is SIKE combined with X25519.
+type cecpq2BCurve struct {
+ // Both public key and shared secret size
+ x25519PrivateKey [32]byte
+ sikePrivateKey *sike.PrivateKey
+}
+
+func (e *cecpq2BCurve) offer(rand io.Reader) (publicKey []byte, err error) {
+ if _, err = io.ReadFull(rand, e.x25519PrivateKey[:]); err != nil {
+ return nil, err
+ }
+
+ var x25519Public [32]byte
+ curve25519.ScalarBaseMult(&x25519Public, &e.x25519PrivateKey)
+
+ e.sikePrivateKey = sike.NewPrivateKey(sike.KeyVariant_SIKE)
+ if err = e.sikePrivateKey.Generate(rand); err != nil {
+ return nil, err
+ }
+
+ sikePublic := e.sikePrivateKey.GeneratePublicKey().Export()
+ var ret []byte
+ ret = append(ret, x25519Public[:]...)
+ ret = append(ret, sikePublic...)
+ return ret, nil
+}
+
+func (e *cecpq2BCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+ if len(peerKey) != 32+sike.Params.PublicKeySize {
+ return nil, nil, errors.New("tls: bad length CECPQ2b offer")
+ }
+
+ if _, err = io.ReadFull(rand, e.x25519PrivateKey[:]); err != nil {
+ return nil, nil, err
+ }
+
+ var x25519Shared, x25519PeerKey, x25519Public [32]byte
+ copy(x25519PeerKey[:], peerKey)
+ curve25519.ScalarBaseMult(&x25519Public, &e.x25519PrivateKey)
+ curve25519.ScalarMult(&x25519Shared, &e.x25519PrivateKey, &x25519PeerKey)
+
+ // Per RFC 7748, reject the all-zero value in constant time.
+ var zeros [32]byte
+ if subtle.ConstantTimeCompare(zeros[:], x25519Shared[:]) == 1 {
+ return nil, nil, errors.New("tls: X25519 value with wrong order")
+ }
+
+ var sikePubKey = sike.NewPublicKey(sike.KeyVariant_SIKE)
+ if err = sikePubKey.Import(peerKey[32:]); err != nil {
+ // should never happen as size was already checked
+ return nil, nil, errors.New("tls: implementation error")
+ }
+ sikeCiphertext, sikeShared, err := sike.Encapsulate(rand, sikePubKey)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ publicKey = append(publicKey, x25519Public[:]...)
+ publicKey = append(publicKey, sikeCiphertext...)
+ preMasterSecret = append(preMasterSecret, x25519Shared[:]...)
+ preMasterSecret = append(preMasterSecret, sikeShared...)
+
+ return publicKey, preMasterSecret, nil
+}
+
+func (e *cecpq2BCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
+ if len(peerKey) != 32+(sike.Params.PublicKeySize+sike.Params.MsgLen) {
+ return nil, errors.New("tls: bad length CECPQ2b reply")
+ }
+
+ var x25519Shared, x25519PeerKey [32]byte
+ copy(x25519PeerKey[:], peerKey)
+ curve25519.ScalarMult(&x25519Shared, &e.x25519PrivateKey, &x25519PeerKey)
+
+ // Per RFC 7748, reject the all-zero value in constant time.
+ var zeros [32]byte
+ if subtle.ConstantTimeCompare(zeros[:], x25519Shared[:]) == 1 {
+ return nil, errors.New("tls: X25519 value with wrong order")
+ }
+
+ var sikePubKey = e.sikePrivateKey.GeneratePublicKey()
+ sikeShared, err := sike.Decapsulate(e.sikePrivateKey, sikePubKey, peerKey[32:])
+ if err != nil {
+ return nil, errors.New("tls: invalid SIKE ciphertext")
+ }
+
+ preMasterSecret = append(preMasterSecret, x25519Shared[:]...)
+ preMasterSecret = append(preMasterSecret, sikeShared...)
+
+ return preMasterSecret, nil
+}
+
func curveForCurveID(id CurveID, config *Config) (ecdhCurve, bool) {
switch id {
case CurveP224:
@@ -447,6 +540,8 @@
return &x25519ECDHCurve{setHighBit: config.Bugs.SetX25519HighBit}, true
case CurveCECPQ2:
return &cecpq2Curve{}, true
+ case CurveCECPQ2b:
+ return &cecpq2BCurve{}, true
default:
return nil, false
}
@@ -594,8 +689,8 @@
NextCandidate:
for _, candidate := range preferredCurves {
- if candidate == CurveCECPQ2 && version < VersionTLS13 {
- // CECPQ2 is TLS 1.3-only.
+ if isPqGroup(candidate) && version < VersionTLS13 {
+ // CECPQ2 and CECPQ2b is TLS 1.3-only.
continue
}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 8461bd8..877a239 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -589,9 +589,6 @@
exportLabel string
exportContext string
useExportContext bool
- // exportEarlyKeyingMaterial, if non-zero, behaves like
- // exportKeyingMaterial, but for the early exporter.
- exportEarlyKeyingMaterial int
// flags, if not empty, contains a list of command-line flags that will
// be passed to the shim program.
flags []string
@@ -881,20 +878,6 @@
}
}
- if isResume && test.exportEarlyKeyingMaterial > 0 {
- actual := make([]byte, test.exportEarlyKeyingMaterial)
- if _, err := io.ReadFull(tlsConn, actual); err != nil {
- return err
- }
- expected, err := tlsConn.ExportEarlyKeyingMaterial(test.exportEarlyKeyingMaterial, []byte(test.exportLabel), []byte(test.exportContext))
- if err != nil {
- return err
- }
- if !bytes.Equal(actual, expected) {
- return fmt.Errorf("early keying material mismatch; got %x, wanted %x", actual, expected)
- }
- }
-
if test.exportKeyingMaterial > 0 {
actual := make([]byte, test.exportKeyingMaterial)
if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -1272,10 +1255,7 @@
flags = append(flags, "-use-export-context")
}
}
- if test.exportEarlyKeyingMaterial > 0 {
- flags = append(flags, "-on-resume-export-early-keying-material", strconv.Itoa(test.exportEarlyKeyingMaterial))
- }
- if test.exportKeyingMaterial > 0 || test.exportEarlyKeyingMaterial > 0 {
+ if test.exportKeyingMaterial > 0 {
flags = append(flags, "-export-label", test.exportLabel)
flags = append(flags, "-export-context", test.exportContext)
}
@@ -1569,34 +1549,34 @@
}
var testCipherSuites = []testCipherSuite{
- {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
- {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256},
- {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
- {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384},
- {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
- {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
- {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
- {"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
- {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
- {"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
- {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
- {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
- {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
- {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
- {"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
- {"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
- {"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
- {"ECDHE-PSK-AES128-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
- {"ECDHE-PSK-AES256-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA},
- {"ECDHE-PSK-CHACHA20-POLY1305", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256},
- {"AEAD-CHACHA20-POLY1305", TLS_CHACHA20_POLY1305_SHA256},
- {"AEAD-AES128-GCM-SHA256", TLS_AES_128_GCM_SHA256},
- {"AEAD-AES256-GCM-SHA384", TLS_AES_256_GCM_SHA384},
- {"NULL-SHA", TLS_RSA_WITH_NULL_SHA},
+ {"RSA_WITH_3DES_EDE_CBC_SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"RSA_WITH_AES_128_GCM_SHA256", TLS_RSA_WITH_AES_128_GCM_SHA256},
+ {"RSA_WITH_AES_128_CBC_SHA", TLS_RSA_WITH_AES_128_CBC_SHA},
+ {"RSA_WITH_AES_256_GCM_SHA384", TLS_RSA_WITH_AES_256_GCM_SHA384},
+ {"RSA_WITH_AES_256_CBC_SHA", TLS_RSA_WITH_AES_256_CBC_SHA},
+ {"ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ {"ECDHE_ECDSA_WITH_AES_128_CBC_SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
+ {"ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
+ {"ECDHE_ECDSA_WITH_AES_256_CBC_SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
+ {"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"ECDHE_RSA_WITH_AES_128_GCM_SHA256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ {"ECDHE_RSA_WITH_AES_128_CBC_SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ {"ECDHE_RSA_WITH_AES_256_GCM_SHA384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
+ {"ECDHE_RSA_WITH_AES_256_CBC_SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+ {"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"PSK_WITH_AES_128_CBC_SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
+ {"PSK_WITH_AES_256_CBC_SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
+ {"ECDHE_PSK_WITH_AES_128_CBC_SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
+ {"ECDHE_PSK_WITH_AES_256_CBC_SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA},
+ {"ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256},
+ {"CHACHA20_POLY1305_SHA256", TLS_CHACHA20_POLY1305_SHA256},
+ {"AES_128_GCM_SHA256", TLS_AES_128_GCM_SHA256},
+ {"AES_256_GCM_SHA384", TLS_AES_256_GCM_SHA384},
+ {"RSA_WITH_NULL_SHA", TLS_RSA_WITH_NULL_SHA},
}
func hasComponent(suiteName, component string) bool {
- return strings.Contains("-"+suiteName+"-", "-"+component+"-")
+ return strings.Contains("_"+suiteName+"_", "_"+component+"_")
}
func isTLS12Only(suiteName string) bool {
@@ -1607,7 +1587,7 @@
}
func isTLS13Suite(suiteName string) bool {
- return strings.HasPrefix(suiteName, "AEAD-")
+ return !hasComponent(suiteName, "WITH")
}
func bigFromHex(hex string) *big.Int {
@@ -4466,6 +4446,8 @@
},
resumeSession: true,
resumeRenewedSession: true,
+ // 0-RTT being disabled overrides all other 0-RTT reasons.
+ flags: []string{"-expect-early-data-reason", "disabled"},
})
tests = append(tests, testCase{
@@ -4477,9 +4459,13 @@
},
resumeSession: true,
resumeRenewedSession: true,
- // TLS 1.3 uses tickets, so the session should not be
- // cached statefully.
- flags: []string{"-expect-no-session-id"},
+ flags: []string{
+ // TLS 1.3 uses tickets, so the session should not be
+ // cached statefully.
+ "-expect-no-session-id",
+ // 0-RTT being disabled overrides all other 0-RTT reasons.
+ "-expect-early-data-reason", "disabled",
+ },
})
tests = append(tests, testCase{
@@ -4532,7 +4518,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-on-resume-shim-writes-first",
},
})
@@ -4561,7 +4547,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-on-resume-read-with-unfinished-write",
"-on-resume-shim-writes-first",
},
@@ -4612,7 +4598,7 @@
resumeSession: true,
flags: []string{
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
},
shouldFail: true,
expectedError: ":TOO_MUCH_READ_EARLY_DATA:",
@@ -6663,7 +6649,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
})
@@ -6712,7 +6698,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
})
@@ -6748,7 +6734,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
})
@@ -6766,7 +6752,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
})
@@ -6797,7 +6783,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
shouldFail: true,
@@ -6817,7 +6803,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
shouldFail: true,
@@ -6837,7 +6823,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
shouldFail: true,
@@ -6873,7 +6859,7 @@
flags: []string{
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{0, 1, 2}),
- "-expected-token-binding-param",
+ "-expect-token-binding-param",
"2",
},
})
@@ -7018,6 +7004,8 @@
"-expect-ticket-supports-early-data",
"-token-binding-params",
base64.StdEncoding.EncodeToString([]byte{2, 1, 0}),
+ "-expect-reject-early-data",
+ "-on-retry-expect-early-data-reason", "token_binding",
},
})
}
@@ -7036,7 +7024,7 @@
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
- "-expected-quic-transport-params",
+ "-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
expectedQUICTransportParams: []byte{3, 4},
@@ -7053,7 +7041,7 @@
flags: []string{
"-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{3, 4}),
- "-expected-quic-transport-params",
+ "-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
expectedQUICTransportParams: []byte{3, 4},
@@ -7097,7 +7085,7 @@
QUICTransportParams: []byte{1, 2},
},
flags: []string{
- "-expected-quic-transport-params",
+ "-expect-quic-transport-params",
base64.StdEncoding.EncodeToString([]byte{1, 2}),
},
shouldFail: true,
@@ -8712,21 +8700,21 @@
id signatureAlgorithm
cert testCert
}{
- {"RSA-PKCS1-SHA1", signatureRSAPKCS1WithSHA1, testCertRSA},
- {"RSA-PKCS1-SHA256", signatureRSAPKCS1WithSHA256, testCertRSA},
- {"RSA-PKCS1-SHA384", signatureRSAPKCS1WithSHA384, testCertRSA},
- {"RSA-PKCS1-SHA512", signatureRSAPKCS1WithSHA512, testCertRSA},
- {"ECDSA-SHA1", signatureECDSAWithSHA1, testCertECDSAP256},
+ {"RSA_PKCS1_SHA1", signatureRSAPKCS1WithSHA1, testCertRSA},
+ {"RSA_PKCS1_SHA256", signatureRSAPKCS1WithSHA256, testCertRSA},
+ {"RSA_PKCS1_SHA384", signatureRSAPKCS1WithSHA384, testCertRSA},
+ {"RSA_PKCS1_SHA512", signatureRSAPKCS1WithSHA512, testCertRSA},
+ {"ECDSA_SHA1", signatureECDSAWithSHA1, testCertECDSAP256},
// The “P256” in the following line is not a mistake. In TLS 1.2 the
// hash function doesn't have to match the curve and so the same
// signature algorithm works with P-224.
- {"ECDSA-P224-SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP224},
- {"ECDSA-P256-SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP256},
- {"ECDSA-P384-SHA384", signatureECDSAWithP384AndSHA384, testCertECDSAP384},
- {"ECDSA-P521-SHA512", signatureECDSAWithP521AndSHA512, testCertECDSAP521},
- {"RSA-PSS-SHA256", signatureRSAPSSWithSHA256, testCertRSA},
- {"RSA-PSS-SHA384", signatureRSAPSSWithSHA384, testCertRSA},
- {"RSA-PSS-SHA512", signatureRSAPSSWithSHA512, testCertRSA},
+ {"ECDSA_P224_SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP224},
+ {"ECDSA_P256_SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP256},
+ {"ECDSA_P384_SHA384", signatureECDSAWithP384AndSHA384, testCertECDSAP384},
+ {"ECDSA_P521_SHA512", signatureECDSAWithP521AndSHA512, testCertECDSAP521},
+ {"RSA_PSS_SHA256", signatureRSAPSSWithSHA256, testCertRSA},
+ {"RSA_PSS_SHA384", signatureRSAPSSWithSHA384, testCertRSA},
+ {"RSA_PSS_SHA512", signatureRSAPSSWithSHA512, testCertRSA},
{"Ed25519", signatureEd25519, testCertEd25519},
// Tests for key types prior to TLS 1.2.
{"RSA", 0, testCertRSA},
@@ -10129,7 +10117,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-on-resume-export-keying-material", "1024",
"-on-resume-export-label", "label",
"-on-resume-export-context", "context",
@@ -10138,106 +10126,6 @@
expectedError: ":HANDSHAKE_NOT_COMPLETE:",
})
- // Test the early exporter works while the client is
- // sending 0-RTT data. This data arrives during the
- // server handshake, so we test it with ProtocolBugs.
- testCases = append(testCases, testCase{
- name: "ExportEarlyKeyingMaterial-Client-InEarlyData-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: vers.version,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- ExpectEarlyKeyingMaterial: 1024,
- ExpectEarlyKeyingLabel: "label",
- ExpectEarlyKeyingContext: "context",
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- "-on-resume-export-early-keying-material", "1024",
- "-on-resume-export-label", "label",
- "-on-resume-export-context", "context",
- },
- })
-
- // Test the early exporter still works on the client
- // after the handshake is confirmed. This arrives after
- // the server handshake, so the normal hooks work.
- testCases = append(testCases, testCase{
- name: "ExportEarlyKeyingMaterial-Client-EarlyDataAccept-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- MaxEarlyDataSize: 16384,
- },
- resumeConfig: &Config{
- MaxVersion: vers.version,
- MaxEarlyDataSize: 16384,
- },
- resumeSession: true,
- exportEarlyKeyingMaterial: 1024,
- exportLabel: "label",
- exportContext: "context",
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
- // Handshake twice on the client to force
- // handshake confirmation.
- "-handshake-twice",
- },
- })
-
- // Test the early exporter does not work on the client
- // if 0-RTT was not offered.
- testCases = append(testCases, testCase{
- name: "NoExportEarlyKeyingMaterial-Client-Initial-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- flags: []string{"-export-early-keying-material", "1024"},
- shouldFail: true,
- expectedError: ":EARLY_DATA_NOT_IN_USE:",
- })
- testCases = append(testCases, testCase{
- name: "NoExportEarlyKeyingMaterial-Client-Resume-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- resumeSession: true,
- flags: []string{"-on-resume-export-early-keying-material", "1024"},
- shouldFail: true,
- expectedError: ":EARLY_DATA_NOT_IN_USE:",
- })
-
- // Test the early exporter does not work on the client
- // after a 0-RTT reject.
- testCases = append(testCases, testCase{
- name: "NoExportEarlyKeyingMaterial-Client-EarlyDataReject-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- MaxEarlyDataSize: 16384,
- Bugs: ProtocolBugs{
- AlwaysRejectEarlyData: true,
- },
- },
- resumeSession: true,
- flags: []string{
- "-enable-early-data",
- "-expect-ticket-supports-early-data",
- "-expect-reject-early-data",
- "-on-retry-export-early-keying-material", "1024",
- },
- shouldFail: true,
- expectedError: ":EARLY_DATA_NOT_IN_USE:",
- })
-
// Test the normal exporter on the server in half-RTT.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -10256,75 +10144,6 @@
useExportContext: true,
flags: []string{"-enable-early-data"},
})
-
- // Test the early exporter works on the server in half-RTT.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "ExportEarlyKeyingMaterial-Server-HalfRTT-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- Bugs: ProtocolBugs{
- SendEarlyData: [][]byte{},
- ExpectEarlyDataAccepted: true,
- },
- },
- resumeSession: true,
- exportEarlyKeyingMaterial: 1024,
- exportLabel: "label",
- exportContext: "context",
- flags: []string{"-enable-early-data"},
- })
-
- // Test the early exporter does not work on the server
- // if 0-RTT was not offered.
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "NoExportEarlyKeyingMaterial-Server-Initial-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- flags: []string{"-export-early-keying-material", "1024"},
- shouldFail: true,
- expectedError: ":EARLY_DATA_NOT_IN_USE:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "NoExportEarlyKeyingMaterial-Server-Resume-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- resumeSession: true,
- flags: []string{"-on-resume-export-early-keying-material", "1024"},
- shouldFail: true,
- expectedError: ":EARLY_DATA_NOT_IN_USE:",
- })
- } else {
- // Test the early exporter fails before TLS 1.3.
- testCases = append(testCases, testCase{
- name: "NoExportEarlyKeyingMaterial-Client-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- resumeSession: true,
- exportEarlyKeyingMaterial: 1024,
- exportLabel: "label",
- exportContext: "context",
- shouldFail: true,
- expectedError: ":WRONG_SSL_VERSION:",
- })
- testCases = append(testCases, testCase{
- testType: serverTest,
- name: "NoExportEarlyKeyingMaterial-Server-" + vers.name,
- config: Config{
- MaxVersion: vers.version,
- },
- resumeSession: true,
- exportEarlyKeyingMaterial: 1024,
- exportLabel: "label",
- exportContext: "context",
- shouldFail: true,
- expectedError: ":WRONG_SSL_VERSION:",
- })
}
}
@@ -10580,14 +10399,19 @@
{"P-521", CurveP521},
{"X25519", CurveX25519},
{"CECPQ2", CurveCECPQ2},
+ {"CECPQ2b", CurveCECPQ2b},
}
const bogusCurve = 0x1234
+func isPqGroup(r CurveID) bool {
+ return r == CurveCECPQ2 || r == CurveCECPQ2b
+}
+
func addCurveTests() {
for _, curve := range testCurves {
for _, ver := range tlsVersions {
- if curve.id == CurveCECPQ2 && ver.version < VersionTLS13 {
+ if isPqGroup(curve.id) && ver.version < VersionTLS13 {
continue
}
@@ -10629,7 +10453,7 @@
expectedCurveID: curve.id,
})
- if curve.id != CurveX25519 && curve.id != CurveCECPQ2 {
+ if curve.id != CurveX25519 && !isPqGroup(curve.id) {
testCases = append(testCases, testCase{
name: "CurveTest-Client-Compressed-" + suffix,
config: Config{
@@ -11054,6 +10878,21 @@
},
})
+ // CECPQ2b should not be offered by a TLS < 1.3 client.
+ testCases = append(testCases, testCase{
+ name: "CECPQ2bNotInTLS12",
+ config: Config{
+ Bugs: ProtocolBugs{
+ FailIfCECPQ2Offered: true,
+ },
+ },
+ flags: []string{
+ "-max-version", strconv.Itoa(VersionTLS12),
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-curves", strconv.Itoa(int(CurveX25519)),
+ },
+ })
+
// CECPQ2 should not crash a TLS < 1.3 client if the server mistakenly
// selects it.
testCases = append(testCases, testCase{
@@ -11072,6 +10911,24 @@
expectedError: ":WRONG_CURVE:",
})
+ // CECPQ2b should not crash a TLS < 1.3 client if the server mistakenly
+ // selects it.
+ testCases = append(testCases, testCase{
+ name: "CECPQ2bNotAcceptedByTLS12Client",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendCurve: CurveCECPQ2b,
+ },
+ },
+ flags: []string{
+ "-max-version", strconv.Itoa(VersionTLS12),
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-curves", strconv.Itoa(int(CurveX25519)),
+ },
+ shouldFail: true,
+ expectedError: ":WRONG_CURVE:",
+ })
+
// CECPQ2 should not be offered by default as a client.
testCases = append(testCases, testCase{
name: "CECPQ2NotEnabledByDefaultInClients",
@@ -11083,6 +10940,17 @@
},
})
+ // CECPQ2b should not be offered by default as a client.
+ testCases = append(testCases, testCase{
+ name: "CECPQ2bNotEnabledByDefaultInClients",
+ config: Config{
+ MinVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ FailIfCECPQ2Offered: true,
+ },
+ },
+ })
+
// If CECPQ2 is offered, both X25519 and CECPQ2 should have a key-share.
testCases = append(testCases, testCase{
name: "NotJustCECPQ2KeyShare",
@@ -11115,6 +10983,38 @@
},
})
+ // If CECPQ2b is offered, both X25519 and CECPQ2b should have a key-share.
+ testCases = append(testCases, testCase{
+ name: "NotJustCECPQ2bKeyShare",
+ config: Config{
+ MinVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectedKeyShares: []CurveID{CurveCECPQ2b, CurveX25519},
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-curves", strconv.Itoa(int(CurveX25519)),
+ "-expect-curve-id", strconv.Itoa(int(CurveCECPQ2b)),
+ },
+ })
+
+ // ... but only if CECPQ2b is listed first.
+ testCases = append(testCases, testCase{
+ name: "CECPQ2bKeyShareNotIncludedSecond",
+ config: Config{
+ MinVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectedKeyShares: []CurveID{CurveX25519},
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveX25519)),
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-expect-curve-id", strconv.Itoa(int(CurveX25519)),
+ },
+ })
+
// If CECPQ2 is the only configured curve, the key share is sent.
testCases = append(testCases, testCase{
name: "JustConfiguringCECPQ2Works",
@@ -11130,6 +11030,21 @@
},
})
+ // If CECPQ2b is the only configured curve, the key share is sent.
+ testCases = append(testCases, testCase{
+ name: "JustConfiguringCECPQ2bWorks",
+ config: Config{
+ MinVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectedKeyShares: []CurveID{CurveCECPQ2b},
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-expect-curve-id", strconv.Itoa(int(CurveCECPQ2b)),
+ },
+ })
+
// As a server, CECPQ2 is not yet supported by default.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -11144,6 +11059,21 @@
"-expect-curve-id", strconv.Itoa(int(CurveX25519)),
},
})
+
+ // As a server, CECPQ2b is not yet supported by default.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CECPQ2bNotEnabledByDefaultForAServer",
+ config: Config{
+ MinVersion: VersionTLS13,
+ CurvePreferences: []CurveID{CurveCECPQ2b, CurveX25519},
+ DefaultCurves: []CurveID{CurveCECPQ2b},
+ },
+ flags: []string{
+ "-server-preference",
+ "-expect-curve-id", strconv.Itoa(int(CurveX25519)),
+ },
+ })
}
func addTLS13RecordTests() {
@@ -11322,9 +11252,99 @@
},
})
+ // Test that ticket age skew up to 60 seconds in either direction is accepted.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Forward-60-Accept",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 70 * time.Second,
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "10",
+ "-expect-ticket-age-skew", "60",
+ // 0-RTT is accepted.
+ "-enable-early-data",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
+ },
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Backward-60-Accept",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 10 * time.Second,
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: true,
+ ExpectHalfRTTData: [][]byte{{254, 253, 252, 251}},
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "70",
+ "-expect-ticket-age-skew", "-60",
+ // 0-RTT is accepted.
+ "-enable-early-data",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
+ },
+ })
+
+ // Test that ticket age skew beyond 60 seconds in either direction is rejected.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Forward-61-Reject",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 71 * time.Second,
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "10",
+ "-expect-ticket-age-skew", "61",
+ // 0-RTT is rejected.
+ "-enable-early-data",
+ "-expect-reject-early-data",
+ "-on-resume-expect-early-data-reason", "ticket_age_skew",
+ },
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Backward-61-Reject",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 10 * time.Second,
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "71",
+ "-expect-ticket-age-skew", "-61",
+ // 0-RTT is rejected.
+ "-enable-early-data",
+ "-expect-reject-early-data",
+ "-on-resume-expect-early-data-reason", "ticket_age_skew",
+ },
+ })
+
testCases = append(testCases, testCase{
testType: clientTest,
- name: "TLS13-SendTicketEarlyDataInfo",
+ name: "TLS13-SendTicketEarlyDataSupport",
config: Config{
MaxVersion: VersionTLS13,
MaxEarlyDataSize: 16384,
@@ -11335,19 +11355,22 @@
},
})
- // Test that 0-RTT tickets are ignored in clients unless opted in.
+ // Test that 0-RTT tickets are still recorded as such when early data is disabled overall.
testCases = append(testCases, testCase{
testType: clientTest,
- name: "TLS13-SendTicketEarlyDataInfo-Disabled",
+ name: "TLS13-SendTicketEarlyDataSupport-Disabled",
config: Config{
MaxVersion: VersionTLS13,
MaxEarlyDataSize: 16384,
},
+ flags: []string{
+ "-expect-ticket-supports-early-data",
+ },
})
testCases = append(testCases, testCase{
testType: clientTest,
- name: "TLS13-DuplicateTicketEarlyDataInfo",
+ name: "TLS13-DuplicateTicketEarlyDataSupport",
config: Config{
MaxVersion: VersionTLS13,
MaxEarlyDataSize: 16384,
@@ -11362,7 +11385,7 @@
testCases = append(testCases, testCase{
testType: serverTest,
- name: "TLS13-ExpectTicketEarlyDataInfo",
+ name: "TLS13-ExpectTicketEarlyDataSupport",
config: Config{
MaxVersion: VersionTLS13,
Bugs: ProtocolBugs{
@@ -12329,7 +12352,9 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-initial-expect-early-data-reason", "no_session_offered",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
"-on-resume-shim-writes-first",
},
})
@@ -12354,6 +12379,7 @@
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
"-on-resume-shim-writes-first",
+ "-on-retry-expect-early-data-reason", "peer_declined",
},
})
@@ -12373,10 +12399,14 @@
resumeSession: true,
flags: []string{
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-initial-expect-early-data-reason", "no_session_offered",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
},
})
+ // The above tests the most recent ticket. Additionally test that 0-RTT
+ // works on the first ticket issued by the server.
testCases = append(testCases, testCase{
testType: serverTest,
name: "EarlyData-FirstTicket-Server-TLS13",
@@ -12394,7 +12424,8 @@
resumeSession: true,
flags: []string{
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
},
})
@@ -12463,6 +12494,9 @@
},
DefaultCurves: []CurveID{},
},
+ // Though the session is not resumed and we send HelloRetryRequest,
+ // early data being disabled takes priority as the reject reason.
+ flags: []string{"-expect-early-data-reason", "disabled"},
})
testCases = append(testCases, testCase{
@@ -12979,6 +13013,8 @@
},
},
})
+
+ // Test the client handles 0-RTT being rejected by a full handshake.
testCases = append(testCases, testCase{
testType: clientTest,
name: "EarlyData-RejectTicket-Client-TLS13",
@@ -13000,6 +13036,9 @@
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
"-on-resume-shim-writes-first",
+ "-on-retry-expect-early-data-reason", "session_not_resumed",
+ // Test the peer certificate is reported correctly in each of the
+ // three logical connections.
"-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
"-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
"-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
@@ -13008,6 +13047,34 @@
},
})
+ // Test the server rejects 0-RTT if it does not recognize the ticket.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-RejectTicket-Server-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ // Corrupt the ticket.
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in[len(in)-1] ^= 1
+ return in, nil
+ },
+ },
+ },
+ messageCount: 2,
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-resume-expect-reject-early-data",
+ "-on-resume-expect-early-data-reason", "session_not_resumed",
+ },
+ })
+
+ // Test the client handles 0-RTT being rejected via a HelloRetryRequest.
testCases = append(testCases, testCase{
testType: clientTest,
name: "EarlyData-HRR-Client-TLS13",
@@ -13027,6 +13094,99 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
+ "-on-retry-expect-early-data-reason", "hello_retry_request",
+ },
+ })
+
+ // Test the server rejects 0-RTT if it needs to send a HelloRetryRequest.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-HRR-Server-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ // Require a HelloRetryRequest for every curve.
+ DefaultCurves: []CurveID{},
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ },
+ },
+ messageCount: 2,
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-resume-expect-reject-early-data",
+ "-on-resume-expect-early-data-reason", "hello_retry_request",
+ },
+ })
+
+ // Test the client handles a 0-RTT reject from both ticket rejection and
+ // HelloRetryRequest.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-HRR-RejectTicket-Client-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Certificates: []Certificate{rsaCertificate},
+ },
+ resumeConfig: &Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ Certificates: []Certificate{ecdsaP256Certificate},
+ SessionTicketsDisabled: true,
+ Bugs: ProtocolBugs{
+ SendHelloRetryRequestCookie: []byte{1, 2, 3, 4},
+ },
+ },
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-ticket-supports-early-data",
+ "-expect-reject-early-data",
+ // The client sees HelloRetryRequest before the resumption result,
+ // though neither value is inherently preferable.
+ "-on-retry-expect-early-data-reason", "hello_retry_request",
+ // Test the peer certificate is reported correctly in each of the
+ // three logical connections.
+ "-on-initial-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-on-resume-expect-peer-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-on-retry-expect-peer-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile),
+ // Session tickets are disabled, so the runner will not send a ticket.
+ "-on-retry-expect-no-session",
+ },
+ })
+
+ // Test the server rejects 0-RTT if it needs to send a HelloRetryRequest.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyData-HRR-RejectTicket-Server-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MinVersion: VersionTLS13,
+ // Require a HelloRetryRequest for every curve.
+ DefaultCurves: []CurveID{},
+ Bugs: ProtocolBugs{
+ SendEarlyData: [][]byte{{1, 2, 3, 4}},
+ ExpectEarlyDataAccepted: false,
+ // Corrupt the ticket.
+ FilterTicket: func(in []byte) ([]byte, error) {
+ in[len(in)-1] ^= 1
+ return in, nil
+ },
+ },
+ },
+ messageCount: 2,
+ resumeSession: true,
+ expectResumeRejected: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-resume-expect-reject-early-data",
+ // The server sees the missed resumption before HelloRetryRequest,
+ // though neither value is inherently preferable.
+ "-on-resume-expect-early-data-reason", "session_not_resumed",
},
})
@@ -13192,6 +13352,10 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
+ // The client does not learn ALPN was the cause.
+ "-on-retry-expect-early-data-reason", "peer_declined",
+ // In the 0-RTT state, we surface the predicted ALPN. After
+ // processing the reject, we surface the real one.
"-on-initial-expect-alpn", "foo",
"-on-resume-expect-alpn", "foo",
"-on-retry-expect-alpn", "bar",
@@ -13218,6 +13382,10 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
+ // The client does not learn ALPN was the cause.
+ "-on-retry-expect-early-data-reason", "peer_declined",
+ // In the 0-RTT state, we surface the predicted ALPN. After
+ // processing the reject, we surface the real one.
"-on-initial-expect-alpn", "",
"-on-resume-expect-alpn", "",
"-on-retry-expect-alpn", "foo",
@@ -13245,6 +13413,10 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-reject-early-data",
+ // The client does not learn ALPN was the cause.
+ "-on-retry-expect-early-data-reason", "peer_declined",
+ // In the 0-RTT state, we surface the predicted ALPN. After
+ // processing the reject, we surface the real one.
"-on-initial-expect-alpn", "foo",
"-on-resume-expect-alpn", "foo",
"-on-retry-expect-alpn", "",
@@ -13299,10 +13471,31 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-expect-no-offer-early-data",
+ // Offer different ALPN values in the initial and resumption.
"-on-initial-advertise-alpn", "\x03foo",
- "-on-resume-advertise-alpn", "\x03bar",
"-on-initial-expect-alpn", "foo",
+ "-on-resume-advertise-alpn", "\x03bar",
"-on-resume-expect-alpn", "bar",
+ // The ALPN mismatch comes from the client, so it reports it as the
+ // reason.
+ "-on-resume-expect-early-data-reason", "alpn_mismatch",
+ },
+ })
+
+ // Test that the client does not offer 0-RTT to servers which never
+ // advertise it.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyData-NonZeroRTTSession-Client-TLS13",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ },
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-on-resume-expect-no-offer-early-data",
+ // The client declines to offer 0-RTT because of the session.
+ "-on-resume-expect-early-data-reason", "unsupported_for_session",
},
})
@@ -13325,6 +13518,8 @@
flags: []string{
"-on-resume-enable-early-data",
"-expect-reject-early-data",
+ // The server rejects 0-RTT because of the session.
+ "-on-resume-expect-early-data-reason", "unsupported_for_session",
},
})
@@ -13350,6 +13545,7 @@
"-enable-early-data",
"-on-initial-select-alpn", "",
"-on-resume-select-alpn", "foo",
+ "-on-resume-expect-early-data-reason", "alpn_mismatch",
},
})
@@ -13375,6 +13571,7 @@
"-enable-early-data",
"-on-initial-select-alpn", "foo",
"-on-resume-select-alpn", "",
+ "-on-resume-expect-early-data-reason", "alpn_mismatch",
},
})
@@ -13399,6 +13596,7 @@
"-enable-early-data",
"-on-initial-select-alpn", "foo",
"-on-resume-select-alpn", "bar",
+ "-on-resume-expect-early-data-reason", "alpn_mismatch",
},
})
@@ -13443,6 +13641,8 @@
"-expect-ticket-supports-early-data",
"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
"-expect-reject-early-data",
+ // The client never learns the reason was Channel ID.
+ "-on-retry-expect-early-data-reason", "peer_declined",
},
})
@@ -13460,7 +13660,8 @@
"-enable-early-data",
"-expect-ticket-supports-early-data",
"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile),
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
+ "-on-resume-expect-early-data-reason", "accept",
},
})
@@ -13484,6 +13685,7 @@
"-expect-reject-early-data",
"-expect-channel-id",
base64.StdEncoding.EncodeToString(channelIDBytes),
+ "-on-resume-expect-early-data-reason", "channel_id",
},
})
@@ -13504,8 +13706,9 @@
expectChannelID: false,
flags: []string{
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-enable-channel-id",
+ "-on-resume-expect-early-data-reason", "accept",
},
})
@@ -13565,7 +13768,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-expect-version", strconv.Itoa(VersionTLS13),
},
})
@@ -13590,7 +13793,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
},
shouldFail: true,
expectedError: ":DIGEST_CHECK_FAILED:",
@@ -13614,7 +13817,7 @@
resumeSession: true,
flags: []string{
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
},
shouldFail: true,
expectedError: ":DIGEST_CHECK_FAILED:",
@@ -13639,7 +13842,7 @@
flags: []string{
"-enable-early-data",
"-expect-ticket-supports-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
},
shouldFail: true,
expectedError: ":DECODE_ERROR:",
@@ -13687,6 +13890,45 @@
expectedLocalError: "remote error: unexpected message",
})
+ // If the client or server has 0-RTT enabled but disabled TLS 1.3, it should
+ // report a reason of protocol_version.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "EarlyDataEnabled-Client-MaxTLS12",
+ expectedVersion: VersionTLS12,
+ flags: []string{
+ "-enable-early-data",
+ "-max-version", strconv.Itoa(VersionTLS12),
+ "-expect-early-data-reason", "protocol_version",
+ },
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyDataEnabled-Server-MaxTLS12",
+ expectedVersion: VersionTLS12,
+ flags: []string{
+ "-enable-early-data",
+ "-max-version", strconv.Itoa(VersionTLS12),
+ "-expect-early-data-reason", "protocol_version",
+ },
+ })
+
+ // The server additionally reports protocol_version if it enabled TLS 1.3,
+ // but the peer negotiated TLS 1.2. (The corresponding situation does not
+ // exist on the client because negotiating TLS 1.2 with a 0-RTT ClientHello
+ // is a fatal error.)
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "EarlyDataEnabled-Server-NegotiateTLS12",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ },
+ expectedVersion: VersionTLS12,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-early-data-reason", "protocol_version",
+ },
+ })
}
func addTLS13CipherPreferenceTests() {
@@ -13756,6 +13998,22 @@
"-curves", strconv.Itoa(int(CurveCECPQ2)),
},
})
+
+ // CECPQ2b prefers 256-bit ciphers but will use AES-128 if there's nothing else.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-CECPQ2b-AES128Only",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ },
+ })
+
// When a 256-bit cipher is offered, even if not in first place, it should be
// picked.
testCases = append(testCases, testCase{
@@ -13790,6 +14048,40 @@
expectedCipher: TLS_AES_128_GCM_SHA256,
})
+ // When a 256-bit cipher is offered, even if not in first place, it should be
+ // picked.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-CECPQ2b-AES256Preferred",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ },
+ expectedCipher: TLS_AES_256_GCM_SHA384,
+ })
+ // ... but when CECPQ2b isn't being used, the client's preference controls.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-CECPQ2b-AES128PreferredOtherwise",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveX25519)),
+ },
+ expectedCipher: TLS_AES_128_GCM_SHA256,
+ })
+
// Test that CECPQ2 continues to honor AES vs ChaCha20 logic.
testCases = append(testCases, testCase{
testType: serverTest,
@@ -13825,6 +14117,42 @@
"-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
},
})
+
+ // Test that CECPQ2b continues to honor AES vs ChaCha20 logic.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-CECPQ2b-AES128-ChaCha20-AES256",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-expect-cipher-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ },
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-CipherPreference-CECPQ2b-AES128-AES256-ChaCha20",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ CipherSuites: []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ TLS_CHACHA20_POLY1305_SHA256,
+ },
+ },
+ flags: []string{
+ "-curves", strconv.Itoa(int(CurveCECPQ2b)),
+ "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_256_GCM_SHA384)),
+ "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)),
+ },
+ })
}
func addPeekTests() {
@@ -14390,7 +14718,7 @@
flags: []string{
"-async",
"-enable-early-data",
- "-expect-accept-early-data",
+ "-on-resume-expect-accept-early-data",
"-no-op-extra-handshake",
},
})
@@ -14963,6 +15291,7 @@
},
flags: []string{
"-delegated-credential", ecdsaFlagValue,
+ "-expect-delegated-credential-used",
},
})
@@ -15013,6 +15342,67 @@
})
}
+func addPQExperimentSignalTests() {
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "PQExperimentSignal-Server-NoEchoIfNotConfigured",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectPQExperimentSignal: false,
+ },
+ PQExperimentSignal: true,
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "PQExperimentSignal-Server-Echo",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectPQExperimentSignal: true,
+ },
+ PQExperimentSignal: true,
+ },
+ flags: []string{
+ "-enable-pq-experiment-signal",
+ "-expect-pq-experiment-signal",
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "PQExperimentSignal-Client-NotDefault",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectPQExperimentSignal: false,
+ },
+ PQExperimentSignal: true,
+ },
+ })
+
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "PQExperimentSignal-Client",
+ config: Config{
+ MinVersion: VersionTLS13,
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ ExpectPQExperimentSignal: true,
+ },
+ },
+ flags: []string{
+ "-enable-pq-experiment-signal",
+ "-expect-pq-experiment-signal",
+ },
+ })
+}
+
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
defer wg.Done()
@@ -15150,6 +15540,7 @@
addCertCompressionTests()
addJDK11WorkaroundTests()
addDelegatedCredentialTests()
+ addPQExperimentSignalTests()
testCases = append(testCases, convertToSplitHandshakeTests(testCases)...)
diff --git a/src/ssl/test/runner/sike/arith.go b/src/ssl/test/runner/sike/arith.go
new file mode 100644
index 0000000..10a2ca6
--- /dev/null
+++ b/src/ssl/test/runner/sike/arith.go
@@ -0,0 +1,374 @@
+// Copyright (c) 2019, Cloudflare Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package sike
+
+import (
+ "math/bits"
+)
+
+// Compute z = x + y (mod 2*p).
+func fpAddRdc(z, x, y *Fp) {
+ var carry uint64
+
+ // z=x+y % p
+ for i := 0; i < FP_WORDS; i++ {
+ z[i], carry = bits.Add64(x[i], y[i], carry)
+ }
+
+ // z = z - pX2
+ carry = 0
+ for i := 0; i < FP_WORDS; i++ {
+ z[i], carry = bits.Sub64(z[i], pX2[i], carry)
+ }
+
+ // if z<0 add pX2 back
+ mask := uint64(0 - carry)
+ carry = 0
+ for i := 0; i < FP_WORDS; i++ {
+ z[i], carry = bits.Add64(z[i], pX2[i]&mask, carry)
+ }
+}
+
+// Compute z = x - y (mod 2*p).
+func fpSubRdc(z, x, y *Fp) {
+ var borrow uint64
+
+ // z = z - pX2
+ for i := 0; i < FP_WORDS; i++ {
+ z[i], borrow = bits.Sub64(x[i], y[i], borrow)
+ }
+
+ // if z<0 add pX2 back
+ mask := uint64(0 - borrow)
+ borrow = 0
+ for i := 0; i < FP_WORDS; i++ {
+ z[i], borrow = bits.Add64(z[i], pX2[i]&mask, borrow)
+ }
+}
+
+// Reduce a field element in [0, 2*p) to one in [0,p).
+func fpRdcP(x *Fp) {
+ var borrow, mask uint64
+ for i := 0; i < FP_WORDS; i++ {
+ x[i], borrow = bits.Sub64(x[i], p[i], borrow)
+ }
+
+ // Sets all bits if borrow = 1
+ mask = 0 - borrow
+ borrow = 0
+ for i := 0; i < FP_WORDS; i++ {
+ x[i], borrow = bits.Add64(x[i], p[i]&mask, borrow)
+ }
+}
+
+// Implementation doesn't actually depend on a prime field.
+func fpSwapCond(x, y *Fp, mask uint8) {
+ if mask != 0 {
+ var tmp Fp
+ copy(tmp[:], y[:])
+ copy(y[:], x[:])
+ copy(x[:], tmp[:])
+ }
+}
+
+// Compute z = x * y.
+func fpMul(z *FpX2, x, y *Fp) {
+ var carry, t, u, v uint64
+ var hi, lo uint64
+
+ for i := uint64(0); i < FP_WORDS; i++ {
+ for j := uint64(0); j <= i; j++ {
+ hi, lo = bits.Mul64(x[j], y[i-j])
+ v, carry = bits.Add64(lo, v, 0)
+ u, carry = bits.Add64(hi, u, carry)
+ t += carry
+ }
+ z[i] = v
+ v = u
+ u = t
+ t = 0
+ }
+
+ for i := FP_WORDS; i < (2*FP_WORDS)-1; i++ {
+ for j := i - FP_WORDS + 1; j < FP_WORDS; j++ {
+ hi, lo = bits.Mul64(x[j], y[i-j])
+ v, carry = bits.Add64(lo, v, 0)
+ u, carry = bits.Add64(hi, u, carry)
+ t += carry
+ }
+ z[i] = v
+ v = u
+ u = t
+ t = 0
+ }
+ z[2*FP_WORDS-1] = v
+}
+
+// Perform Montgomery reduction: set z = x R^{-1} (mod 2*p)
+// with R=2^512. Destroys the input value.
+func fpMontRdc(z *Fp, x *FpX2) {
+ var carry, t, u, v uint64
+ var hi, lo uint64
+ var count int
+
+ count = 3 // number of 0 digits in the least significat part of p + 1
+
+ for i := 0; i < FP_WORDS; i++ {
+ for j := 0; j < i; j++ {
+ if j < (i - count + 1) {
+ hi, lo = bits.Mul64(z[j], p1[i-j])
+ v, carry = bits.Add64(lo, v, 0)
+ u, carry = bits.Add64(hi, u, carry)
+ t += carry
+ }
+ }
+ v, carry = bits.Add64(v, x[i], 0)
+ u, carry = bits.Add64(u, 0, carry)
+ t += carry
+
+ z[i] = v
+ v = u
+ u = t
+ t = 0
+ }
+
+ for i := FP_WORDS; i < 2*FP_WORDS-1; i++ {
+ if count > 0 {
+ count--
+ }
+ for j := i - FP_WORDS + 1; j < FP_WORDS; j++ {
+ if j < (FP_WORDS - count) {
+ hi, lo = bits.Mul64(z[j], p1[i-j])
+ v, carry = bits.Add64(lo, v, 0)
+ u, carry = bits.Add64(hi, u, carry)
+ t += carry
+ }
+ }
+ v, carry = bits.Add64(v, x[i], 0)
+ u, carry = bits.Add64(u, 0, carry)
+
+ t += carry
+ z[i-FP_WORDS] = v
+ v = u
+ u = t
+ t = 0
+ }
+ v, carry = bits.Add64(v, x[2*FP_WORDS-1], 0)
+ z[FP_WORDS-1] = v
+}
+
+// Compute z = x + y, without reducing mod p.
+func fp2Add(z, x, y *FpX2) {
+ var carry uint64
+ for i := 0; i < 2*FP_WORDS; i++ {
+ z[i], carry = bits.Add64(x[i], y[i], carry)
+ }
+}
+
+// Compute z = x - y, without reducing mod p.
+func fp2Sub(z, x, y *FpX2) {
+ var borrow, mask uint64
+ for i := 0; i < 2*FP_WORDS; i++ {
+ z[i], borrow = bits.Sub64(x[i], y[i], borrow)
+ }
+
+ // Sets all bits if borrow = 1
+ mask = 0 - borrow
+ borrow = 0
+ for i := FP_WORDS; i < 2*FP_WORDS; i++ {
+ z[i], borrow = bits.Add64(z[i], p[i-FP_WORDS]&mask, borrow)
+ }
+}
+
+// Montgomery multiplication. Input values must be already
+// in Montgomery domain.
+func fpMulRdc(dest, lhs, rhs *Fp) {
+ a := lhs // = a*R
+ b := rhs // = b*R
+
+ var ab FpX2
+ fpMul(&ab, a, b) // = a*b*R*R
+ fpMontRdc(dest, &ab) // = a*b*R mod p
+}
+
+// Set dest = x^((p-3)/4). If x is square, this is 1/sqrt(x).
+// Uses variation of sliding-window algorithm from with window size
+// of 5 and least to most significant bit sliding (left-to-right)
+// See HAC 14.85 for general description.
+//
+// Allowed to overlap x with dest.
+// All values in Montgomery domains
+// Set dest = x^(2^k), for k >= 1, by repeated squarings.
+func p34(dest, x *Fp) {
+ var lookup [16]Fp
+
+ // This performs sum(powStrategy) + 1 squarings and len(lookup) + len(mulStrategy)
+ // multiplications.
+ powStrategy := []uint8{
+ 0x03, 0x0A, 0x07, 0x05, 0x06, 0x05, 0x03, 0x08, 0x04, 0x07,
+ 0x05, 0x06, 0x04, 0x05, 0x09, 0x06, 0x03, 0x0B, 0x05, 0x05,
+ 0x02, 0x08, 0x04, 0x07, 0x07, 0x08, 0x05, 0x06, 0x04, 0x08,
+ 0x05, 0x02, 0x0A, 0x06, 0x05, 0x04, 0x08, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x01}
+ mulStrategy := []uint8{
+ 0x02, 0x0F, 0x09, 0x08, 0x0E, 0x0C, 0x02, 0x08, 0x05, 0x0F,
+ 0x08, 0x0F, 0x06, 0x06, 0x03, 0x02, 0x00, 0x0A, 0x09, 0x0D,
+ 0x01, 0x0C, 0x03, 0x07, 0x01, 0x0A, 0x08, 0x0B, 0x02, 0x0F,
+ 0x0E, 0x01, 0x0B, 0x0C, 0x0E, 0x03, 0x0B, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00}
+ initialMul := uint8(8)
+
+ // Precompute lookup table of odd multiples of x for window
+ // size k=5.
+ var xx Fp
+ fpMulRdc(&xx, x, x)
+ lookup[0] = *x
+ for i := 1; i < 16; i++ {
+ fpMulRdc(&lookup[i], &lookup[i-1], &xx)
+ }
+
+ // Now lookup = {x, x^3, x^5, ... }
+ // so that lookup[i] = x^{2*i + 1}
+ // so that lookup[k/2] = x^k, for odd k
+ *dest = lookup[initialMul]
+ for i := uint8(0); i < uint8(len(powStrategy)); i++ {
+ fpMulRdc(dest, dest, dest)
+ for j := uint8(1); j < powStrategy[i]; j++ {
+ fpMulRdc(dest, dest, dest)
+ }
+ fpMulRdc(dest, dest, &lookup[mulStrategy[i]])
+ }
+}
+
+func add(dest, lhs, rhs *Fp2) {
+ fpAddRdc(&dest.A, &lhs.A, &rhs.A)
+ fpAddRdc(&dest.B, &lhs.B, &rhs.B)
+}
+
+func sub(dest, lhs, rhs *Fp2) {
+ fpSubRdc(&dest.A, &lhs.A, &rhs.A)
+ fpSubRdc(&dest.B, &lhs.B, &rhs.B)
+}
+
+func mul(dest, lhs, rhs *Fp2) {
+ // Let (a,b,c,d) = (lhs.a,lhs.b,rhs.a,rhs.b).
+ a := &lhs.A
+ b := &lhs.B
+ c := &rhs.A
+ d := &rhs.B
+
+ // We want to compute
+ //
+ // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i
+ //
+ // Use Karatsuba's trick: note that
+ //
+ // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d
+ //
+ // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d.
+
+ var ac, bd FpX2
+ fpMul(&ac, a, c) // = a*c*R*R
+ fpMul(&bd, b, d) // = b*d*R*R
+
+ var b_minus_a, c_minus_d Fp
+ fpSubRdc(&b_minus_a, b, a) // = (b-a)*R
+ fpSubRdc(&c_minus_d, c, d) // = (c-d)*R
+
+ var ad_plus_bc FpX2
+ fpMul(&ad_plus_bc, &b_minus_a, &c_minus_d) // = (b-a)*(c-d)*R*R
+ fp2Add(&ad_plus_bc, &ad_plus_bc, &ac) // = ((b-a)*(c-d) + a*c)*R*R
+ fp2Add(&ad_plus_bc, &ad_plus_bc, &bd) // = ((b-a)*(c-d) + a*c + b*d)*R*R
+
+ fpMontRdc(&dest.B, &ad_plus_bc) // = (a*d + b*c)*R mod p
+
+ var ac_minus_bd FpX2
+ fp2Sub(&ac_minus_bd, &ac, &bd) // = (a*c - b*d)*R*R
+ fpMontRdc(&dest.A, &ac_minus_bd) // = (a*c - b*d)*R mod p
+}
+
+func inv(dest, x *Fp2) {
+ var a2PlusB2 Fp
+ var asq, bsq FpX2
+ var ac FpX2
+ var minusB Fp
+ var minusBC FpX2
+
+ a := &x.A
+ b := &x.B
+
+ // We want to compute
+ //
+ // 1 1 (a - bi) (a - bi)
+ // -------- = -------- -------- = -----------
+ // (a + bi) (a + bi) (a - bi) (a^2 + b^2)
+ //
+ // Letting c = 1/(a^2 + b^2), this is
+ //
+ // 1/(a+bi) = a*c - b*ci.
+
+ fpMul(&asq, a, a) // = a*a*R*R
+ fpMul(&bsq, b, b) // = b*b*R*R
+ fp2Add(&asq, &asq, &bsq) // = (a^2 + b^2)*R*R
+ fpMontRdc(&a2PlusB2, &asq) // = (a^2 + b^2)*R mod p
+ // Now a2PlusB2 = a^2 + b^2
+
+ inv := a2PlusB2
+ fpMulRdc(&inv, &a2PlusB2, &a2PlusB2)
+ p34(&inv, &inv)
+ fpMulRdc(&inv, &inv, &inv)
+ fpMulRdc(&inv, &inv, &a2PlusB2)
+
+ fpMul(&ac, a, &inv)
+ fpMontRdc(&dest.A, &ac)
+
+ fpSubRdc(&minusB, &minusB, b)
+ fpMul(&minusBC, &minusB, &inv)
+ fpMontRdc(&dest.B, &minusBC)
+}
+
+func sqr(dest, x *Fp2) {
+ var a2, aPlusB, aMinusB Fp
+ var a2MinB2, ab2 FpX2
+
+ a := &x.A
+ b := &x.B
+
+ // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi.
+ fpAddRdc(&a2, a, a) // = a*R + a*R = 2*a*R
+ fpAddRdc(&aPlusB, a, b) // = a*R + b*R = (a+b)*R
+ fpSubRdc(&aMinusB, a, b) // = a*R - b*R = (a-b)*R
+ fpMul(&a2MinB2, &aPlusB, &aMinusB) // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R
+ fpMul(&ab2, &a2, b) // = 2*a*b*R*R
+ fpMontRdc(&dest.A, &a2MinB2) // = (a^2 - b^2)*R mod p
+ fpMontRdc(&dest.B, &ab2) // = 2*a*b*R mod p
+}
+
+// In case choice == 1, performs following swap in constant time:
+// xPx <-> xQx
+// xPz <-> xQz
+// Otherwise returns xPx, xPz, xQx, xQz unchanged
+func condSwap(xPx, xPz, xQx, xQz *Fp2, choice uint8) {
+ fpSwapCond(&xPx.A, &xQx.A, choice)
+ fpSwapCond(&xPx.B, &xQx.B, choice)
+ fpSwapCond(&xPz.A, &xQz.A, choice)
+ fpSwapCond(&xPz.B, &xQz.B, choice)
+}
diff --git a/src/ssl/test/runner/sike/consts.go b/src/ssl/test/runner/sike/consts.go
new file mode 100644
index 0000000..9d68a4f
--- /dev/null
+++ b/src/ssl/test/runner/sike/consts.go
@@ -0,0 +1,317 @@
+// Copyright (c) 2019, Cloudflare Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package sike
+
+// I keep it bool in order to be able to apply logical NOT
+type KeyVariant uint
+
+// Representation of an element of the base field F_p.
+//
+// No particular meaning is assigned to the representation -- it could represent
+// an element in Montgomery form, or not. Tracking the meaning of the field
+// element is left to higher types.
+type Fp [FP_WORDS]uint64
+
+// Represents an intermediate product of two elements of the base field F_p.
+type FpX2 [2 * FP_WORDS]uint64
+
+// Represents an element of the extended field Fp^2 = Fp(x+i)
+type Fp2 struct {
+ A Fp
+ B Fp
+}
+
+type DomainParams struct {
+ // P, Q and R=P-Q base points
+ Affine_P, Affine_Q, Affine_R Fp2
+ // Size of a compuatation strategy for x-torsion group
+ IsogenyStrategy []uint32
+ // Max size of secret key for x-torsion group
+ SecretBitLen uint
+ // Max size of secret key for x-torsion group
+ SecretByteLen uint
+}
+
+type SidhParams struct {
+ Id uint8
+ // Bytelen of P
+ Bytelen int
+ // The public key size, in bytes.
+ PublicKeySize int
+ // The shared secret size, in bytes.
+ SharedSecretSize int
+ // Defines A,C constant for starting curve Cy^2 = x^3 + Ax^2 + x
+ InitCurve ProjectiveCurveParameters
+ // 2- and 3-torsion group parameter definitions
+ A, B DomainParams
+ // Precomputed 1/2 in the Fp2 in Montgomery domain
+ HalfFp2 Fp2
+ // Precomputed identity element in the Fp2 in Montgomery domain
+ OneFp2 Fp2
+ // Length of SIKE secret message. Must be one of {24,32,40},
+ // depending on size of prime field used (see [SIKE], 1.4 and 5.1)
+ MsgLen int
+ // Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1)
+ KemSize int
+ // Size of a ciphertext returned by encapsulation in bytes
+ CiphertextSize int
+}
+
+// Stores curve projective parameters equivalent to A/C. Meaning of the
+// values depends on the context. When working with isogenies over
+// subgroup that are powers of:
+// * three then (A:C) ~ (A+2C:A-2C)
+// * four then (A:C) ~ (A+2C: 4C)
+// See Appendix A of SIKE for more details
+type CurveCoefficientsEquiv struct {
+ A Fp2
+ C Fp2
+}
+
+// A point on the projective line P^1(F_{p^2}).
+//
+// This represents a point on the Kummer line of a Montgomery curve. The
+// curve is specified by a ProjectiveCurveParameters struct.
+type ProjectivePoint struct {
+ X Fp2
+ Z Fp2
+}
+
+// Base type for public and private key. Used mainly to carry domain
+// parameters.
+type key struct {
+ // Domain parameters of the algorithm to be used with a key
+ params *SidhParams
+ // Flag indicates whether corresponds to 2-, 3-torsion group or SIKE
+ keyVariant KeyVariant
+}
+
+// Defines operations on private key
+type PrivateKey struct {
+ key
+ // Secret key
+ Scalar []byte
+ // Used only by KEM
+ S []byte
+}
+
+// Defines operations on public key
+type PublicKey struct {
+ key
+ affine_xP Fp2
+ affine_xQ Fp2
+ affine_xQmP Fp2
+}
+
+// A point on the projective line P^1(F_{p^2}).
+//
+// This is used to work projectively with the curve coefficients.
+type ProjectiveCurveParameters struct {
+ A Fp2
+ C Fp2
+}
+
+const (
+ // First 2 bits identify SIDH variant third bit indicates
+ // whether key is a SIKE variant (set) or SIDH (not set)
+
+ // 001 - SIDH: corresponds to 2-torsion group
+ KeyVariant_SIDH_A KeyVariant = 1 << 0
+ // 010 - SIDH: corresponds to 3-torsion group
+ KeyVariant_SIDH_B = 1 << 1
+ // 110 - SIKE
+ KeyVariant_SIKE = 1<<2 | KeyVariant_SIDH_B
+ // Number of uint64 limbs used to store field element
+ FP_WORDS = 7
+)
+
+// Used internally by this package
+// -------------------------------
+
+var (
+ p = Fp{
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFDC1767AE2FFFFFF,
+ 0x7BC65C783158AEA3, 0x6CFC5FD681C52056, 0x2341F27177344,
+ }
+
+ // 2*p434
+ pX2 = Fp{
+ 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFB82ECF5C5FFFFFF,
+ 0xF78CB8F062B15D47, 0xD9F8BFAD038A40AC, 0x4683E4E2EE688,
+ }
+
+ // p434 + 1
+ p1 = Fp{
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0xFDC1767AE3000000,
+ 0x7BC65C783158AEA3, 0x6CFC5FD681C52056, 0x0002341F27177344,
+ }
+
+ // R^2=(2^448)^2 mod p
+ R2 = Fp{
+ 0x28E55B65DCD69B30, 0xACEC7367768798C2, 0xAB27973F8311688D, 0x175CC6AF8D6C7C0B,
+ 0xABCD92BF2DDE347E, 0x69E16A61C7686D9A, 0x000025A89BCDD12A,
+ }
+
+ // 1/2 * R mod p
+ half = Fp2{
+ A: Fp{
+ 0x0000000000003A16, 0x0000000000000000, 0x0000000000000000, 0x5C87FA027E000000,
+ 0x6C00D27DAACFD66A, 0x74992A2A2FBBA086, 0x0000767753DE976D},
+ }
+
+ // 1*R mod p
+ one = Fp2{
+ A: Fp{
+ 0x000000000000742C, 0x0000000000000000, 0x0000000000000000, 0xB90FF404FC000000,
+ 0xD801A4FB559FACD4, 0xE93254545F77410C, 0x0000ECEEA7BD2EDA},
+ }
+
+ // 6*R mod p
+ six = Fp2{
+ A: Fp{
+ 0x000000000002B90A, 0x0000000000000000, 0x0000000000000000, 0x5ADCCB2822000000,
+ 0x187D24F39F0CAFB4, 0x9D353A4D394145A0, 0x00012559A0403298},
+ }
+
+ Params SidhParams
+)
+
+func init() {
+ Params = SidhParams{
+ // SIDH public key byte size.
+ PublicKeySize: 330,
+ // SIDH shared secret byte size.
+ SharedSecretSize: 110,
+ InitCurve: ProjectiveCurveParameters{
+ A: six,
+ C: one,
+ },
+ A: DomainParams{
+ // The x-coordinate of PA
+ Affine_P: Fp2{
+ A: Fp{
+ 0x05ADF455C5C345BF, 0x91935C5CC767AC2B, 0xAFE4E879951F0257, 0x70E792DC89FA27B1,
+ 0xF797F526BB48C8CD, 0x2181DB6131AF621F, 0x00000A1C08B1ECC4,
+ },
+ B: Fp{
+ 0x74840EB87CDA7788, 0x2971AA0ECF9F9D0B, 0xCB5732BDF41715D5, 0x8CD8E51F7AACFFAA,
+ 0xA7F424730D7E419F, 0xD671EB919A179E8C, 0x0000FFA26C5A924A,
+ },
+ },
+ // The x-coordinate of QA
+ Affine_Q: Fp2{
+ A: Fp{
+ 0xFEC6E64588B7273B, 0xD2A626D74CBBF1C6, 0xF8F58F07A78098C7, 0xE23941F470841B03,
+ 0x1B63EDA2045538DD, 0x735CFEB0FFD49215, 0x0001C4CB77542876,
+ },
+ B: Fp{
+ 0xADB0F733C17FFDD6, 0x6AFFBD037DA0A050, 0x680EC43DB144E02F, 0x1E2E5D5FF524E374,
+ 0xE2DDA115260E2995, 0xA6E4B552E2EDE508, 0x00018ECCDDF4B53E,
+ },
+ },
+ // The x-coordinate of RA = PA-QA
+ Affine_R: Fp2{
+ A: Fp{
+ 0x01BA4DB518CD6C7D, 0x2CB0251FE3CC0611, 0x259B0C6949A9121B, 0x60E17AC16D2F82AD,
+ 0x3AA41F1CE175D92D, 0x413FBE6A9B9BC4F3, 0x00022A81D8D55643,
+ },
+ B: Fp{
+ 0xB8ADBC70FC82E54A, 0xEF9CDDB0D5FADDED, 0x5820C734C80096A0, 0x7799994BAA96E0E4,
+ 0x044961599E379AF8, 0xDB2B94FBF09F27E2, 0x0000B87FC716C0C6,
+ },
+ },
+ // Max size of secret key for 2-torsion group, corresponds to 2^e2 - 1
+ SecretBitLen: 216,
+ // SecretBitLen in bytes.
+ SecretByteLen: 27,
+ // 2-torsion group computation strategy
+ IsogenyStrategy: []uint32{
+ 0x30, 0x1C, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x0D, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
+ 0x15, 0x0C, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x03, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x05, 0x03,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01},
+ },
+ B: DomainParams{
+ // The x-coordinate of PB
+ Affine_P: Fp2{
+ A: Fp{
+ 0x6E5497556EDD48A3, 0x2A61B501546F1C05, 0xEB919446D049887D, 0x5864A4A69D450C4F,
+ 0xB883F276A6490D2B, 0x22CC287022D5F5B9, 0x0001BED4772E551F,
+ },
+ B: Fp{
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ },
+ },
+ // The x-coordinate of QB
+ Affine_Q: Fp2{
+ A: Fp{
+ 0xFAE2A3F93D8B6B8E, 0x494871F51700FE1C, 0xEF1A94228413C27C, 0x498FF4A4AF60BD62,
+ 0xB00AD2A708267E8A, 0xF4328294E017837F, 0x000034080181D8AE,
+ },
+ B: Fp{
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ },
+ },
+ // The x-coordinate of RB = PB - QB
+ Affine_R: Fp2{
+ A: Fp{
+ 0x283B34FAFEFDC8E4, 0x9208F44977C3E647, 0x7DEAE962816F4E9A, 0x68A2BA8AA262EC9D,
+ 0x8176F112EA43F45B, 0x02106D022634F504, 0x00007E8A50F02E37,
+ },
+ B: Fp{
+ 0xB378B7C1DA22CCB1, 0x6D089C99AD1D9230, 0xEBE15711813E2369, 0x2B35A68239D48A53,
+ 0x445F6FD138407C93, 0xBEF93B29A3F6B54B, 0x000173FA910377D3,
+ },
+ },
+ // Size of secret key for 3-torsion group, corresponds to log_2(3^e3) - 1.
+ SecretBitLen: 217,
+ // SecretBitLen in bytes.
+ SecretByteLen: 28,
+ // 3-torsion group computation strategy
+ IsogenyStrategy: []uint32{
+ 0x42, 0x21, 0x11, 0x09, 0x05, 0x03, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x08, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x10,
+ 0x08, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x04, 0x02, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x20, 0x10, 0x08, 0x04, 0x03, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x10, 0x08, 0x04, 0x02, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01},
+ },
+ OneFp2: one,
+ HalfFp2: half,
+ MsgLen: 16,
+ // SIKEp434 provides 128 bit of classical security ([SIKE], 5.1)
+ KemSize: 16,
+ // ceil(434+7/8)
+ Bytelen: 55,
+ CiphertextSize: 16 + 330,
+ }
+}
diff --git a/src/ssl/test/runner/sike/curve.go b/src/ssl/test/runner/sike/curve.go
new file mode 100644
index 0000000..8172546
--- /dev/null
+++ b/src/ssl/test/runner/sike/curve.go
@@ -0,0 +1,422 @@
+// Copyright (c) 2019, Cloudflare Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package sike
+
+// Interface for working with isogenies.
+type isogeny interface {
+ // Given a torsion point on a curve computes isogenous curve.
+ // Returns curve coefficients (A:C), so that E_(A/C) = E_(A/C)/<P>,
+ // where P is a provided projective point. Sets also isogeny constants
+ // that are needed for isogeny evaluation.
+ GenerateCurve(*ProjectivePoint) CurveCoefficientsEquiv
+ // Evaluates isogeny at caller provided point. Requires isogeny curve constants
+ // to be earlier computed by GenerateCurve.
+ EvaluatePoint(*ProjectivePoint) ProjectivePoint
+}
+
+// Stores isogeny 3 curve constants
+type isogeny3 struct {
+ K1 Fp2
+ K2 Fp2
+}
+
+// Stores isogeny 4 curve constants
+type isogeny4 struct {
+ isogeny3
+ K3 Fp2
+}
+
+// Constructs isogeny3 objects
+func NewIsogeny3() isogeny {
+ return &isogeny3{}
+}
+
+// Constructs isogeny4 objects
+func NewIsogeny4() isogeny {
+ return &isogeny4{}
+}
+
+// Helper function for RightToLeftLadder(). Returns A+2C / 4.
+func calcAplus2Over4(cparams *ProjectiveCurveParameters) (ret Fp2) {
+ var tmp Fp2
+
+ // 2C
+ add(&tmp, &cparams.C, &cparams.C)
+ // A+2C
+ add(&ret, &cparams.A, &tmp)
+ // 1/4C
+ add(&tmp, &tmp, &tmp)
+ inv(&tmp, &tmp)
+ // A+2C/4C
+ mul(&ret, &ret, &tmp)
+ return
+}
+
+// Converts values in x.A and x.B to Montgomery domain
+// x.A = x.A * R mod p
+// x.B = x.B * R mod p
+// Performs v = v*R^2*R^(-1) mod p, for both x.A and x.B
+func toMontDomain(x *Fp2) {
+ var aRR FpX2
+
+ // convert to montgomery domain
+ fpMul(&aRR, &x.A, &R2) // = a*R*R
+ fpMontRdc(&x.A, &aRR) // = a*R mod p
+ fpMul(&aRR, &x.B, &R2)
+ fpMontRdc(&x.B, &aRR)
+}
+
+// Converts values in x.A and x.B from Montgomery domain
+// a = x.A mod p
+// b = x.B mod p
+//
+// After returning from the call x is not modified.
+func fromMontDomain(x *Fp2, out *Fp2) {
+ var aR FpX2
+
+ // convert from montgomery domain
+ copy(aR[:], x.A[:])
+ fpMontRdc(&out.A, &aR) // = a mod p in [0, 2p)
+ fpRdcP(&out.A) // = a mod p in [0, p)
+ for i := range aR {
+ aR[i] = 0
+ }
+ copy(aR[:], x.B[:])
+ fpMontRdc(&out.B, &aR)
+ fpRdcP(&out.B)
+}
+
+// Computes j-invariant for a curve y2=x3+A/Cx+x with A,C in F_(p^2). Result
+// is returned in 'j'. Implementation corresponds to Algorithm 9 from SIKE.
+func Jinvariant(cparams *ProjectiveCurveParameters, j *Fp2) {
+ var t0, t1 Fp2
+
+ sqr(j, &cparams.A) // j = A^2
+ sqr(&t1, &cparams.C) // t1 = C^2
+ add(&t0, &t1, &t1) // t0 = t1 + t1
+ sub(&t0, j, &t0) // t0 = j - t0
+ sub(&t0, &t0, &t1) // t0 = t0 - t1
+ sub(j, &t0, &t1) // t0 = t0 - t1
+ sqr(&t1, &t1) // t1 = t1^2
+ mul(j, j, &t1) // j = j * t1
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ sqr(&t1, &t0) // t1 = t0^2
+ mul(&t0, &t0, &t1) // t0 = t0 * t1
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ inv(j, j) // j = 1/j
+ mul(j, &t0, j) // j = t0 * j
+}
+
+// Given affine points x(P), x(Q) and x(Q-P) in a extension field F_{p^2}, function
+// recorvers projective coordinate A of a curve. This is Algorithm 10 from SIKE.
+func RecoverCoordinateA(curve *ProjectiveCurveParameters, xp, xq, xr *Fp2) {
+ var t0, t1 Fp2
+
+ add(&t1, xp, xq) // t1 = Xp + Xq
+ mul(&t0, xp, xq) // t0 = Xp * Xq
+ mul(&curve.A, xr, &t1) // A = X(q-p) * t1
+ add(&curve.A, &curve.A, &t0) // A = A + t0
+ mul(&t0, &t0, xr) // t0 = t0 * X(q-p)
+ sub(&curve.A, &curve.A, &Params.OneFp2) // A = A - 1
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ add(&t1, &t1, xr) // t1 = t1 + X(q-p)
+ add(&t0, &t0, &t0) // t0 = t0 + t0
+ sqr(&curve.A, &curve.A) // A = A^2
+ inv(&t0, &t0) // t0 = 1/t0
+ mul(&curve.A, &curve.A, &t0) // A = A * t0
+ sub(&curve.A, &curve.A, &t1) // A = A - t1
+}
+
+// Computes equivalence (A:C) ~ (A+2C : A-2C)
+func CalcCurveParamsEquiv3(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv {
+ var coef CurveCoefficientsEquiv
+ var c2 Fp2
+
+ add(&c2, &cparams.C, &cparams.C)
+ // A24p = A+2*C
+ add(&coef.A, &cparams.A, &c2)
+ // A24m = A-2*C
+ sub(&coef.C, &cparams.A, &c2)
+ return coef
+}
+
+// Computes equivalence (A:C) ~ (A+2C : 4C)
+func CalcCurveParamsEquiv4(cparams *ProjectiveCurveParameters) CurveCoefficientsEquiv {
+ var coefEq CurveCoefficientsEquiv
+
+ add(&coefEq.C, &cparams.C, &cparams.C)
+ // A24p = A+2C
+ add(&coefEq.A, &cparams.A, &coefEq.C)
+ // C24 = 4*C
+ add(&coefEq.C, &coefEq.C, &coefEq.C)
+ return coefEq
+}
+
+// Recovers (A:C) curve parameters from projectively equivalent (A+2C:A-2C).
+func RecoverCurveCoefficients3(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) {
+ add(&cparams.A, &coefEq.A, &coefEq.C)
+ // cparams.A = 2*(A+2C+A-2C) = 4A
+ add(&cparams.A, &cparams.A, &cparams.A)
+ // cparams.C = (A+2C-A+2C) = 4C
+ sub(&cparams.C, &coefEq.A, &coefEq.C)
+ return
+}
+
+// Recovers (A:C) curve parameters from projectively equivalent (A+2C:4C).
+func RecoverCurveCoefficients4(cparams *ProjectiveCurveParameters, coefEq *CurveCoefficientsEquiv) {
+ // cparams.C = (4C)*1/2=2C
+ mul(&cparams.C, &coefEq.C, &Params.HalfFp2)
+ // cparams.A = A+2C - 2C = A
+ sub(&cparams.A, &coefEq.A, &cparams.C)
+ // cparams.C = 2C * 1/2 = C
+ mul(&cparams.C, &cparams.C, &Params.HalfFp2)
+ return
+}
+
+// Combined coordinate doubling and differential addition. Takes projective points
+// P,Q,Q-P and (A+2C)/4C curve E coefficient. Returns 2*P and P+Q calculated on E.
+// Function is used only by RightToLeftLadder. Corresponds to Algorithm 5 of SIKE
+func xDbladd(P, Q, QmP *ProjectivePoint, a24 *Fp2) (dblP, PaQ ProjectivePoint) {
+ var t0, t1, t2 Fp2
+ xQmP, zQmP := &QmP.X, &QmP.Z
+ xPaQ, zPaQ := &PaQ.X, &PaQ.Z
+ x2P, z2P := &dblP.X, &dblP.Z
+ xP, zP := &P.X, &P.Z
+ xQ, zQ := &Q.X, &Q.Z
+
+ add(&t0, xP, zP) // t0 = Xp+Zp
+ sub(&t1, xP, zP) // t1 = Xp-Zp
+ sqr(x2P, &t0) // 2P.X = t0^2
+ sub(&t2, xQ, zQ) // t2 = Xq-Zq
+ add(xPaQ, xQ, zQ) // Xp+q = Xq+Zq
+ mul(&t0, &t0, &t2) // t0 = t0 * t2
+ mul(z2P, &t1, &t1) // 2P.Z = t1 * t1
+ mul(&t1, &t1, xPaQ) // t1 = t1 * Xp+q
+ sub(&t2, x2P, z2P) // t2 = 2P.X - 2P.Z
+ mul(x2P, x2P, z2P) // 2P.X = 2P.X * 2P.Z
+ mul(xPaQ, a24, &t2) // Xp+q = A24 * t2
+ sub(zPaQ, &t0, &t1) // Zp+q = t0 - t1
+ add(z2P, xPaQ, z2P) // 2P.Z = Xp+q + 2P.Z
+ add(xPaQ, &t0, &t1) // Xp+q = t0 + t1
+ mul(z2P, z2P, &t2) // 2P.Z = 2P.Z * t2
+ sqr(zPaQ, zPaQ) // Zp+q = Zp+q ^ 2
+ sqr(xPaQ, xPaQ) // Xp+q = Xp+q ^ 2
+ mul(zPaQ, xQmP, zPaQ) // Zp+q = Xq-p * Zp+q
+ mul(xPaQ, zQmP, xPaQ) // Xp+q = Zq-p * Xp+q
+ return
+}
+
+// Given the curve parameters, xP = x(P), computes xP = x([2^k]P)
+// Safe to overlap xP, x2P.
+func Pow2k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) {
+ var t0, t1 Fp2
+
+ x, z := &xP.X, &xP.Z
+ for i := uint32(0); i < k; i++ {
+ sub(&t0, x, z) // t0 = Xp - Zp
+ add(&t1, x, z) // t1 = Xp + Zp
+ sqr(&t0, &t0) // t0 = t0 ^ 2
+ sqr(&t1, &t1) // t1 = t1 ^ 2
+ mul(z, ¶ms.C, &t0) // Z2p = C24 * t0
+ mul(x, z, &t1) // X2p = Z2p * t1
+ sub(&t1, &t1, &t0) // t1 = t1 - t0
+ mul(&t0, ¶ms.A, &t1) // t0 = A24+ * t1
+ add(z, z, &t0) // Z2p = Z2p + t0
+ mul(z, z, &t1) // Zp = Z2p * t1
+ }
+}
+
+// Given the curve parameters, xP = x(P), and k >= 0, compute xP = x([3^k]P).
+//
+// Safe to overlap xP, xR.
+func Pow3k(xP *ProjectivePoint, params *CurveCoefficientsEquiv, k uint32) {
+ var t0, t1, t2, t3, t4, t5, t6 Fp2
+
+ x, z := &xP.X, &xP.Z
+ for i := uint32(0); i < k; i++ {
+ sub(&t0, x, z) // t0 = Xp - Zp
+ sqr(&t2, &t0) // t2 = t0^2
+ add(&t1, x, z) // t1 = Xp + Zp
+ sqr(&t3, &t1) // t3 = t1^2
+ add(&t4, &t1, &t0) // t4 = t1 + t0
+ sub(&t0, &t1, &t0) // t0 = t1 - t0
+ sqr(&t1, &t4) // t1 = t4^2
+ sub(&t1, &t1, &t3) // t1 = t1 - t3
+ sub(&t1, &t1, &t2) // t1 = t1 - t2
+ mul(&t5, &t3, ¶ms.A) // t5 = t3 * A24+
+ mul(&t3, &t3, &t5) // t3 = t5 * t3
+ mul(&t6, &t2, ¶ms.C) // t6 = t2 * A24-
+ mul(&t2, &t2, &t6) // t2 = t2 * t6
+ sub(&t3, &t2, &t3) // t3 = t2 - t3
+ sub(&t2, &t5, &t6) // t2 = t5 - t6
+ mul(&t1, &t2, &t1) // t1 = t2 * t1
+ add(&t2, &t3, &t1) // t2 = t3 + t1
+ sqr(&t2, &t2) // t2 = t2^2
+ mul(x, &t2, &t4) // X3p = t2 * t4
+ sub(&t1, &t3, &t1) // t1 = t3 - t1
+ sqr(&t1, &t1) // t1 = t1^2
+ mul(z, &t1, &t0) // Z3p = t1 * t0
+ }
+}
+
+// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3).
+//
+// All xi, yi must be distinct.
+func Fp2Batch3Inv(x1, x2, x3, y1, y2, y3 *Fp2) {
+ var x1x2, t Fp2
+
+ mul(&x1x2, x1, x2) // x1*x2
+ mul(&t, &x1x2, x3) // 1/(x1*x2*x3)
+ inv(&t, &t)
+ mul(y1, &t, x2) // 1/x1
+ mul(y1, y1, x3)
+ mul(y2, &t, x1) // 1/x2
+ mul(y2, y2, x3)
+ mul(y3, &t, &x1x2) // 1/x3
+}
+
+// ScalarMul3Pt is a right-to-left point multiplication that given the
+// x-coordinate of P, Q and P-Q calculates the x-coordinate of R=Q+[scalar]P.
+// nbits must be smaller or equal to len(scalar).
+func ScalarMul3Pt(cparams *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint, nbits uint, scalar []uint8) ProjectivePoint {
+ var R0, R2, R1 ProjectivePoint
+ aPlus2Over4 := calcAplus2Over4(cparams)
+ R1 = *P
+ R2 = *PmQ
+ R0 = *Q
+
+ // Iterate over the bits of the scalar, bottom to top
+ prevBit := uint8(0)
+ for i := uint(0); i < nbits; i++ {
+ bit := (scalar[i>>3] >> (i & 7) & 1)
+ swap := prevBit ^ bit
+ prevBit = bit
+ condSwap(&R1.X, &R1.Z, &R2.X, &R2.Z, swap)
+ R0, R2 = xDbladd(&R0, &R2, &R1, &aPlus2Over4)
+ }
+ condSwap(&R1.X, &R1.Z, &R2.X, &R2.Z, prevBit)
+ return R1
+}
+
+// Given a three-torsion point p = x(PB) on the curve E_(A:C), construct the
+// three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C').
+//
+// Input: (XP_3: ZP_3), where P_3 has exact order 3 on E_A/C
+// Output: * Curve coordinates (A' + 2C', A' - 2C') corresponding to E_A'/C' = A_E/C/<P3>
+// * isogeny phi with constants in F_p^2
+func (phi *isogeny3) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
+ var t0, t1, t2, t3, t4 Fp2
+ var coefEq CurveCoefficientsEquiv
+ var K1, K2 = &phi.K1, &phi.K2
+
+ sub(K1, &p.X, &p.Z) // K1 = XP3 - ZP3
+ sqr(&t0, K1) // t0 = K1^2
+ add(K2, &p.X, &p.Z) // K2 = XP3 + ZP3
+ sqr(&t1, K2) // t1 = K2^2
+ add(&t2, &t0, &t1) // t2 = t0 + t1
+ add(&t3, K1, K2) // t3 = K1 + K2
+ sqr(&t3, &t3) // t3 = t3^2
+ sub(&t3, &t3, &t2) // t3 = t3 - t2
+ add(&t2, &t1, &t3) // t2 = t1 + t3
+ add(&t3, &t3, &t0) // t3 = t3 + t0
+ add(&t4, &t3, &t0) // t4 = t3 + t0
+ add(&t4, &t4, &t4) // t4 = t4 + t4
+ add(&t4, &t1, &t4) // t4 = t1 + t4
+ mul(&coefEq.C, &t2, &t4) // A24m = t2 * t4
+ add(&t4, &t1, &t2) // t4 = t1 + t2
+ add(&t4, &t4, &t4) // t4 = t4 + t4
+ add(&t4, &t0, &t4) // t4 = t0 + t4
+ mul(&t4, &t3, &t4) // t4 = t3 * t4
+ sub(&t0, &t4, &coefEq.C) // t0 = t4 - A24m
+ add(&coefEq.A, &coefEq.C, &t0) // A24p = A24m + t0
+ return coefEq
+}
+
+// Given a 3-isogeny phi and a point pB = x(PB), compute x(QB), the x-coordinate
+// of the image QB = phi(PB) of PB under phi : E_(A:C) -> E_(A':C').
+//
+// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve
+// parameters are returned by the GenerateCurve function used to construct phi.
+func (phi *isogeny3) EvaluatePoint(p *ProjectivePoint) ProjectivePoint {
+ var t0, t1, t2 Fp2
+ var q ProjectivePoint
+ var K1, K2 = &phi.K1, &phi.K2
+ var px, pz = &p.X, &p.Z
+
+ add(&t0, px, pz) // t0 = XQ + ZQ
+ sub(&t1, px, pz) // t1 = XQ - ZQ
+ mul(&t0, K1, &t0) // t2 = K1 * t0
+ mul(&t1, K2, &t1) // t1 = K2 * t1
+ add(&t2, &t0, &t1) // t2 = t0 + t1
+ sub(&t0, &t1, &t0) // t0 = t1 - t0
+ sqr(&t2, &t2) // t2 = t2 ^ 2
+ sqr(&t0, &t0) // t0 = t0 ^ 2
+ mul(&q.X, px, &t2) // XQ'= XQ * t2
+ mul(&q.Z, pz, &t0) // ZQ'= ZQ * t0
+ return q
+}
+
+// Given a four-torsion point p = x(PB) on the curve E_(A:C), construct the
+// four-isogeny phi : E_(A:C) -> E_(A:C)/<P_4> = E_(A':C').
+//
+// Input: (XP_4: ZP_4), where P_4 has exact order 4 on E_A/C
+// Output: * Curve coordinates (A' + 2C', 4C') corresponding to E_A'/C' = A_E/C/<P4>
+// * isogeny phi with constants in F_p^2
+func (phi *isogeny4) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
+ var coefEq CurveCoefficientsEquiv
+ var xp4, zp4 = &p.X, &p.Z
+ var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3
+
+ sub(K2, xp4, zp4)
+ add(K3, xp4, zp4)
+ sqr(K1, zp4)
+ add(K1, K1, K1)
+ sqr(&coefEq.C, K1)
+ add(K1, K1, K1)
+ sqr(&coefEq.A, xp4)
+ add(&coefEq.A, &coefEq.A, &coefEq.A)
+ sqr(&coefEq.A, &coefEq.A)
+ return coefEq
+}
+
+// Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate
+// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C').
+//
+// Input: isogeny returned by GenerateCurve and point q=(Qx,Qz) from E0_A/C
+// Output: Corresponding point q from E1_A'/C', where E1 is 4-isogenous to E0
+func (phi *isogeny4) EvaluatePoint(p *ProjectivePoint) ProjectivePoint {
+ var t0, t1 Fp2
+ var q = *p
+ var xq, zq = &q.X, &q.Z
+ var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3
+
+ add(&t0, xq, zq)
+ sub(&t1, xq, zq)
+ mul(xq, &t0, K2)
+ mul(zq, &t1, K3)
+ mul(&t0, &t0, &t1)
+ mul(&t0, &t0, K1)
+ add(&t1, xq, zq)
+ sub(zq, xq, zq)
+ sqr(&t1, &t1)
+ sqr(zq, zq)
+ add(xq, &t0, &t1)
+ sub(&t0, zq, &t0)
+ mul(xq, xq, &t1)
+ mul(zq, zq, &t0)
+ return q
+}
diff --git a/src/ssl/test/runner/sike/sike.go b/src/ssl/test/runner/sike/sike.go
new file mode 100644
index 0000000..dcd6cfc
--- /dev/null
+++ b/src/ssl/test/runner/sike/sike.go
@@ -0,0 +1,683 @@
+// Copyright (c) 2019, Cloudflare Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package sike
+
+import (
+ "crypto/sha256"
+ "crypto/subtle"
+ "errors"
+ "io"
+)
+
+// Zeroize Fp2
+func zeroize(fp *Fp2) {
+ // Zeroizing in 2 separated loops tells compiler to
+ // use fast runtime.memclr()
+ for i := range fp.A {
+ fp.A[i] = 0
+ }
+ for i := range fp.B {
+ fp.B[i] = 0
+ }
+}
+
+// Convert the input to wire format.
+//
+// The output byte slice must be at least 2*bytelen(p) bytes long.
+func convFp2ToBytes(output []byte, fp2 *Fp2) {
+ if len(output) < 2*Params.Bytelen {
+ panic("output byte slice too short")
+ }
+ var a Fp2
+ fromMontDomain(fp2, &a)
+
+ // convert to bytes in little endian form
+ for i := 0; i < Params.Bytelen; i++ {
+ // set i = j*8 + k
+ tmp := i / 8
+ k := uint64(i % 8)
+ output[i] = byte(a.A[tmp] >> (8 * k))
+ output[i+Params.Bytelen] = byte(a.B[tmp] >> (8 * k))
+ }
+}
+
+// Read 2*bytelen(p) bytes into the given ExtensionFieldElement.
+//
+// It is an error to call this function if the input byte slice is less than 2*bytelen(p) bytes long.
+func convBytesToFp2(fp2 *Fp2, input []byte) {
+ if len(input) < 2*Params.Bytelen {
+ panic("input byte slice too short")
+ }
+
+ for i := 0; i < Params.Bytelen; i++ {
+ j := i / 8
+ k := uint64(i % 8)
+ fp2.A[j] |= uint64(input[i]) << (8 * k)
+ fp2.B[j] |= uint64(input[i+Params.Bytelen]) << (8 * k)
+ }
+ toMontDomain(fp2)
+}
+
+// -----------------------------------------------------------------------------
+// Functions for traversing isogeny trees acoording to strategy. Key type 'A' is
+//
+
+// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
+// for public key generation.
+func traverseTreePublicKeyA(curve *ProjectiveCurveParameters, xR, phiP, phiQ, phiR *ProjectivePoint, pub *PublicKey) {
+ var points = make([]ProjectivePoint, 0, 8)
+ var indices = make([]int, 0, 8)
+ var i, sidx int
+
+ cparam := CalcCurveParamsEquiv4(curve)
+ phi := NewIsogeny4()
+ strat := pub.params.A.IsogenyStrategy
+ stratSz := len(strat)
+
+ for j := 1; j <= stratSz; j++ {
+ for i <= stratSz-j {
+ points = append(points, *xR)
+ indices = append(indices, i)
+
+ k := strat[sidx]
+ sidx++
+ Pow2k(xR, &cparam, 2*k)
+ i += int(k)
+ }
+
+ cparam = phi.GenerateCurve(xR)
+ for k := 0; k < len(points); k++ {
+ points[k] = phi.EvaluatePoint(&points[k])
+ }
+
+ *phiP = phi.EvaluatePoint(phiP)
+ *phiQ = phi.EvaluatePoint(phiQ)
+ *phiR = phi.EvaluatePoint(phiR)
+
+ // pop xR from points
+ *xR, points = points[len(points)-1], points[:len(points)-1]
+ i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
+ }
+}
+
+// Traverses isogeny tree in order to compute xR needed
+// for public key generation.
+func traverseTreeSharedKeyA(curve *ProjectiveCurveParameters, xR *ProjectivePoint, pub *PublicKey) {
+ var points = make([]ProjectivePoint, 0, 8)
+ var indices = make([]int, 0, 8)
+ var i, sidx int
+
+ cparam := CalcCurveParamsEquiv4(curve)
+ phi := NewIsogeny4()
+ strat := pub.params.A.IsogenyStrategy
+ stratSz := len(strat)
+
+ for j := 1; j <= stratSz; j++ {
+ for i <= stratSz-j {
+ points = append(points, *xR)
+ indices = append(indices, i)
+
+ k := strat[sidx]
+ sidx++
+ Pow2k(xR, &cparam, 2*k)
+ i += int(k)
+ }
+
+ cparam = phi.GenerateCurve(xR)
+ for k := 0; k < len(points); k++ {
+ points[k] = phi.EvaluatePoint(&points[k])
+ }
+
+ // pop xR from points
+ *xR, points = points[len(points)-1], points[:len(points)-1]
+ i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
+ }
+}
+
+// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
+// for public key generation.
+func traverseTreePublicKeyB(curve *ProjectiveCurveParameters, xR, phiP, phiQ, phiR *ProjectivePoint, pub *PublicKey) {
+ var points = make([]ProjectivePoint, 0, 8)
+ var indices = make([]int, 0, 8)
+ var i, sidx int
+
+ cparam := CalcCurveParamsEquiv3(curve)
+ phi := NewIsogeny3()
+ strat := pub.params.B.IsogenyStrategy
+ stratSz := len(strat)
+
+ for j := 1; j <= stratSz; j++ {
+ for i <= stratSz-j {
+ points = append(points, *xR)
+ indices = append(indices, i)
+
+ k := strat[sidx]
+ sidx++
+ Pow3k(xR, &cparam, k)
+ i += int(k)
+ }
+
+ cparam = phi.GenerateCurve(xR)
+ for k := 0; k < len(points); k++ {
+ points[k] = phi.EvaluatePoint(&points[k])
+ }
+
+ *phiP = phi.EvaluatePoint(phiP)
+ *phiQ = phi.EvaluatePoint(phiQ)
+ *phiR = phi.EvaluatePoint(phiR)
+
+ // pop xR from points
+ *xR, points = points[len(points)-1], points[:len(points)-1]
+ i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
+ }
+}
+
+// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
+// for public key generation.
+func traverseTreeSharedKeyB(curve *ProjectiveCurveParameters, xR *ProjectivePoint, pub *PublicKey) {
+ var points = make([]ProjectivePoint, 0, 8)
+ var indices = make([]int, 0, 8)
+ var i, sidx int
+
+ cparam := CalcCurveParamsEquiv3(curve)
+ phi := NewIsogeny3()
+ strat := pub.params.B.IsogenyStrategy
+ stratSz := len(strat)
+
+ for j := 1; j <= stratSz; j++ {
+ for i <= stratSz-j {
+ points = append(points, *xR)
+ indices = append(indices, i)
+
+ k := strat[sidx]
+ sidx++
+ Pow3k(xR, &cparam, k)
+ i += int(k)
+ }
+
+ cparam = phi.GenerateCurve(xR)
+ for k := 0; k < len(points); k++ {
+ points[k] = phi.EvaluatePoint(&points[k])
+ }
+
+ // pop xR from points
+ *xR, points = points[len(points)-1], points[:len(points)-1]
+ i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
+ }
+}
+
+// Generate a public key in the 2-torsion group
+func publicKeyGenA(prv *PrivateKey) (pub *PublicKey) {
+ var xPA, xQA, xRA ProjectivePoint
+ var xPB, xQB, xRB, xK ProjectivePoint
+ var invZP, invZQ, invZR Fp2
+
+ pub = NewPublicKey(KeyVariant_SIDH_A)
+ var phi = NewIsogeny4()
+
+ // Load points for A
+ xPA = ProjectivePoint{X: prv.params.A.Affine_P, Z: prv.params.OneFp2}
+ xQA = ProjectivePoint{X: prv.params.A.Affine_Q, Z: prv.params.OneFp2}
+ xRA = ProjectivePoint{X: prv.params.A.Affine_R, Z: prv.params.OneFp2}
+
+ // Load points for B
+ xRB = ProjectivePoint{X: prv.params.B.Affine_R, Z: prv.params.OneFp2}
+ xQB = ProjectivePoint{X: prv.params.B.Affine_Q, Z: prv.params.OneFp2}
+ xPB = ProjectivePoint{X: prv.params.B.Affine_P, Z: prv.params.OneFp2}
+
+ // Find isogeny kernel
+ xK = ScalarMul3Pt(&pub.params.InitCurve, &xPA, &xQA, &xRA, prv.params.A.SecretBitLen, prv.Scalar)
+ traverseTreePublicKeyA(&pub.params.InitCurve, &xK, &xPB, &xQB, &xRB, pub)
+
+ // Secret isogeny
+ phi.GenerateCurve(&xK)
+ xPA = phi.EvaluatePoint(&xPB)
+ xQA = phi.EvaluatePoint(&xQB)
+ xRA = phi.EvaluatePoint(&xRB)
+ Fp2Batch3Inv(&xPA.Z, &xQA.Z, &xRA.Z, &invZP, &invZQ, &invZR)
+
+ mul(&pub.affine_xP, &xPA.X, &invZP)
+ mul(&pub.affine_xQ, &xQA.X, &invZQ)
+ mul(&pub.affine_xQmP, &xRA.X, &invZR)
+ return
+}
+
+// Generate a public key in the 3-torsion group
+func publicKeyGenB(prv *PrivateKey) (pub *PublicKey) {
+ var xPB, xQB, xRB, xK ProjectivePoint
+ var xPA, xQA, xRA ProjectivePoint
+ var invZP, invZQ, invZR Fp2
+
+ pub = NewPublicKey(prv.keyVariant)
+ var phi = NewIsogeny3()
+
+ // Load points for B
+ xRB = ProjectivePoint{X: prv.params.B.Affine_R, Z: prv.params.OneFp2}
+ xQB = ProjectivePoint{X: prv.params.B.Affine_Q, Z: prv.params.OneFp2}
+ xPB = ProjectivePoint{X: prv.params.B.Affine_P, Z: prv.params.OneFp2}
+
+ // Load points for A
+ xPA = ProjectivePoint{X: prv.params.A.Affine_P, Z: prv.params.OneFp2}
+ xQA = ProjectivePoint{X: prv.params.A.Affine_Q, Z: prv.params.OneFp2}
+ xRA = ProjectivePoint{X: prv.params.A.Affine_R, Z: prv.params.OneFp2}
+
+ xK = ScalarMul3Pt(&pub.params.InitCurve, &xPB, &xQB, &xRB, prv.params.B.SecretBitLen, prv.Scalar)
+ traverseTreePublicKeyB(&pub.params.InitCurve, &xK, &xPA, &xQA, &xRA, pub)
+
+ phi.GenerateCurve(&xK)
+ xPB = phi.EvaluatePoint(&xPA)
+ xQB = phi.EvaluatePoint(&xQA)
+ xRB = phi.EvaluatePoint(&xRA)
+ Fp2Batch3Inv(&xPB.Z, &xQB.Z, &xRB.Z, &invZP, &invZQ, &invZR)
+
+ mul(&pub.affine_xP, &xPB.X, &invZP)
+ mul(&pub.affine_xQ, &xQB.X, &invZQ)
+ mul(&pub.affine_xQmP, &xRB.X, &invZR)
+ return
+}
+
+// -----------------------------------------------------------------------------
+// Key agreement functions
+//
+
+// Establishing shared keys in in 2-torsion group
+func deriveSecretA(prv *PrivateKey, pub *PublicKey) []byte {
+ var sharedSecret = make([]byte, pub.params.SharedSecretSize)
+ var xP, xQ, xQmP ProjectivePoint
+ var xK ProjectivePoint
+ var cparam ProjectiveCurveParameters
+ var phi = NewIsogeny4()
+ var jInv Fp2
+
+ // Recover curve coefficients
+ RecoverCoordinateA(&cparam, &pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP)
+ // C=1
+ cparam.C = Params.OneFp2
+
+ // Find kernel of the morphism
+ xP = ProjectivePoint{X: pub.affine_xP, Z: pub.params.OneFp2}
+ xQ = ProjectivePoint{X: pub.affine_xQ, Z: pub.params.OneFp2}
+ xQmP = ProjectivePoint{X: pub.affine_xQmP, Z: pub.params.OneFp2}
+ xK = ScalarMul3Pt(&cparam, &xP, &xQ, &xQmP, pub.params.A.SecretBitLen, prv.Scalar)
+
+ // Traverse isogeny tree
+ traverseTreeSharedKeyA(&cparam, &xK, pub)
+
+ // Calculate j-invariant on isogeneus curve
+ c := phi.GenerateCurve(&xK)
+ RecoverCurveCoefficients4(&cparam, &c)
+ Jinvariant(&cparam, &jInv)
+ convFp2ToBytes(sharedSecret, &jInv)
+ return sharedSecret
+}
+
+// Establishing shared keys in in 3-torsion group
+func deriveSecretB(prv *PrivateKey, pub *PublicKey) []byte {
+ var sharedSecret = make([]byte, pub.params.SharedSecretSize)
+ var xP, xQ, xQmP ProjectivePoint
+ var xK ProjectivePoint
+ var cparam ProjectiveCurveParameters
+ var phi = NewIsogeny3()
+ var jInv Fp2
+
+ // Recover curve A coefficient
+ RecoverCoordinateA(&cparam, &pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP)
+ // C=1
+ cparam.C = Params.OneFp2
+
+ // Find kernel of the morphism
+ xP = ProjectivePoint{X: pub.affine_xP, Z: pub.params.OneFp2}
+ xQ = ProjectivePoint{X: pub.affine_xQ, Z: pub.params.OneFp2}
+ xQmP = ProjectivePoint{X: pub.affine_xQmP, Z: pub.params.OneFp2}
+ xK = ScalarMul3Pt(&cparam, &xP, &xQ, &xQmP, pub.params.B.SecretBitLen, prv.Scalar)
+
+ // Traverse isogeny tree
+ traverseTreeSharedKeyB(&cparam, &xK, pub)
+
+ // Calculate j-invariant on isogeneus curve
+ c := phi.GenerateCurve(&xK)
+ RecoverCurveCoefficients3(&cparam, &c)
+ Jinvariant(&cparam, &jInv)
+ convFp2ToBytes(sharedSecret, &jInv)
+ return sharedSecret
+}
+
+func encrypt(skA *PrivateKey, pkA, pkB *PublicKey, ptext []byte) ([]byte, error) {
+ if pkB.keyVariant != KeyVariant_SIKE {
+ return nil, errors.New("wrong key type")
+ }
+
+ j, err := DeriveSecret(skA, pkB)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(ptext) != pkA.params.KemSize {
+ panic("Implementation error")
+ }
+
+ digest := sha256.Sum256(j)
+ // Uses truncated digest (first 16-bytes)
+ for i, _ := range ptext {
+ digest[i] ^= ptext[i]
+ }
+
+ ret := make([]byte, pkA.Size()+len(ptext))
+ copy(ret, pkA.Export())
+ copy(ret[pkA.Size():], digest[:pkA.params.KemSize])
+ return ret, nil
+}
+
+// NewPrivateKey initializes private key.
+// Usage of this function guarantees that the object is correctly initialized.
+func NewPrivateKey(v KeyVariant) *PrivateKey {
+ prv := &PrivateKey{key: key{params: &Params, keyVariant: v}}
+ if (v & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
+ prv.Scalar = make([]byte, prv.params.A.SecretByteLen)
+ } else {
+ prv.Scalar = make([]byte, prv.params.B.SecretByteLen)
+ }
+ if v == KeyVariant_SIKE {
+ prv.S = make([]byte, prv.params.MsgLen)
+ }
+ return prv
+}
+
+// NewPublicKey initializes public key.
+// Usage of this function guarantees that the object is correctly initialized.
+func NewPublicKey(v KeyVariant) *PublicKey {
+ return &PublicKey{key: key{params: &Params, keyVariant: v}}
+}
+
+// Import clears content of the public key currently stored in the structure
+// and imports key stored in the byte string. Returns error in case byte string
+// size is wrong. Doesn't perform any validation.
+func (pub *PublicKey) Import(input []byte) error {
+ if len(input) != pub.Size() {
+ return errors.New("sidh: input to short")
+ }
+ ssSz := pub.params.SharedSecretSize
+ convBytesToFp2(&pub.affine_xP, input[0:ssSz])
+ convBytesToFp2(&pub.affine_xQ, input[ssSz:2*ssSz])
+ convBytesToFp2(&pub.affine_xQmP, input[2*ssSz:3*ssSz])
+ return nil
+}
+
+// Exports currently stored key. In case structure hasn't been filled with key data
+// returned byte string is filled with zeros.
+func (pub *PublicKey) Export() []byte {
+ output := make([]byte, pub.params.PublicKeySize)
+ ssSz := pub.params.SharedSecretSize
+ convFp2ToBytes(output[0:ssSz], &pub.affine_xP)
+ convFp2ToBytes(output[ssSz:2*ssSz], &pub.affine_xQ)
+ convFp2ToBytes(output[2*ssSz:3*ssSz], &pub.affine_xQmP)
+ return output
+}
+
+// Size returns size of the public key in bytes
+func (pub *PublicKey) Size() int {
+ return pub.params.PublicKeySize
+}
+
+// Exports currently stored key. In case structure hasn't been filled with key data
+// returned byte string is filled with zeros.
+func (prv *PrivateKey) Export() []byte {
+ ret := make([]byte, len(prv.Scalar)+len(prv.S))
+ copy(ret, prv.S)
+ copy(ret[len(prv.S):], prv.Scalar)
+ return ret
+}
+
+// Size returns size of the private key in bytes
+func (prv *PrivateKey) Size() int {
+ tmp := len(prv.Scalar)
+ if prv.keyVariant == KeyVariant_SIKE {
+ tmp += int(prv.params.MsgLen)
+ }
+ return tmp
+}
+
+// Import clears content of the private key currently stored in the structure
+// and imports key from octet string. In case of SIKE, the random value 'S'
+// must be prepended to the value of actual private key (see SIKE spec for details).
+// Function doesn't import public key value to PrivateKey object.
+func (prv *PrivateKey) Import(input []byte) error {
+ if len(input) != prv.Size() {
+ return errors.New("sidh: input to short")
+ }
+ copy(prv.S, input[:len(prv.S)])
+ copy(prv.Scalar, input[len(prv.S):])
+ return nil
+}
+
+// Generates random private key for SIDH or SIKE. Generated value is
+// formed as little-endian integer from key-space <2^(e2-1)..2^e2 - 1>
+// for KeyVariant_A or <2^(s-1)..2^s - 1>, where s = floor(log_2(3^e3)),
+// for KeyVariant_B.
+//
+// Returns error in case user provided RNG fails.
+func (prv *PrivateKey) Generate(rand io.Reader) error {
+ var err error
+ var dp *DomainParams
+
+ if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
+ dp = &prv.params.A
+ } else {
+ dp = &prv.params.B
+ }
+
+ if prv.keyVariant == KeyVariant_SIKE {
+ _, err = io.ReadFull(rand, prv.S)
+ }
+
+ // Private key generation takes advantage of the fact that keyspace for secret
+ // key is (0, 2^x - 1), for some possitivite value of 'x' (see SIKE, 1.3.8).
+ // It means that all bytes in the secret key, but the last one, can take any
+ // value between <0x00,0xFF>. Similarily for the last byte, but generation
+ // needs to chop off some bits, to make sure generated value is an element of
+ // a key-space.
+ _, err = io.ReadFull(rand, prv.Scalar)
+ if err != nil {
+ return err
+ }
+ prv.Scalar[len(prv.Scalar)-1] &= (1 << (dp.SecretBitLen % 8)) - 1
+ // Make sure scalar is SecretBitLen long. SIKE spec says that key
+ // space starts from 0, but I'm not confortable with having low
+ // value scalars used for private keys. It is still secrure as per
+ // table 5.1 in [SIKE].
+ prv.Scalar[len(prv.Scalar)-1] |= 1 << ((dp.SecretBitLen % 8) - 1)
+ return err
+}
+
+// Generates public key.
+//
+// Constant time.
+func (prv *PrivateKey) GeneratePublicKey() *PublicKey {
+ if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
+ return publicKeyGenA(prv)
+ }
+ return publicKeyGenB(prv)
+}
+
+// Computes a shared secret which is a j-invariant. Function requires that pub has
+// different KeyVariant than prv. Length of returned output is 2*ceil(log_2 P)/8),
+// where P is a prime defining finite field.
+//
+// It's important to notice that each keypair must not be used more than once
+// to calculate shared secret.
+//
+// Function may return error. This happens only in case provided input is invalid.
+// Constant time for properly initialized private and public key.
+func DeriveSecret(prv *PrivateKey, pub *PublicKey) ([]byte, error) {
+
+ if (pub == nil) || (prv == nil) {
+ return nil, errors.New("sidh: invalid arguments")
+ }
+
+ if (pub.keyVariant == prv.keyVariant) || (pub.params.Id != prv.params.Id) {
+ return nil, errors.New("sidh: public and private are incompatbile")
+ }
+
+ if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
+ return deriveSecretA(prv, pub), nil
+ } else {
+ return deriveSecretB(prv, pub), nil
+ }
+}
+
+// Uses SIKE public key to encrypt plaintext. Requires cryptographically secure PRNG
+// Returns ciphertext in case encryption succeeds. Returns error in case PRNG fails
+// or wrongly formatted input was provided.
+func Encrypt(rng io.Reader, pub *PublicKey, ptext []byte) ([]byte, error) {
+ var ptextLen = len(ptext)
+ // c1 must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3)
+ if ptextLen != pub.params.KemSize {
+ return nil, errors.New("Unsupported message length")
+ }
+
+ skA := NewPrivateKey(KeyVariant_SIDH_A)
+ err := skA.Generate(rng)
+ if err != nil {
+ return nil, err
+ }
+
+ pkA := skA.GeneratePublicKey()
+ return encrypt(skA, pkA, pub, ptext)
+}
+
+// Uses SIKE private key to decrypt ciphertext. Returns plaintext in case
+// decryption succeeds or error in case unexptected input was provided.
+// Constant time
+func Decrypt(prv *PrivateKey, ctext []byte) ([]byte, error) {
+ var c1_len int
+ n := make([]byte, prv.params.KemSize)
+ pk_len := prv.params.PublicKeySize
+
+ if prv.keyVariant != KeyVariant_SIKE {
+ return nil, errors.New("wrong key type")
+ }
+
+ // ctext is a concatenation of (pubkey_A || c1=ciphertext)
+ // it must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3)
+ c1_len = len(ctext) - pk_len
+ if c1_len != int(prv.params.KemSize) {
+ return nil, errors.New("wrong size of cipher text")
+ }
+
+ c0 := NewPublicKey(KeyVariant_SIDH_A)
+ err := c0.Import(ctext[:pk_len])
+ if err != nil {
+ return nil, err
+ }
+ j, err := DeriveSecret(prv, c0)
+ if err != nil {
+ return nil, err
+ }
+
+ digest := sha256.Sum256(j)
+ copy(n, digest[:])
+
+ for i, _ := range n {
+ n[i] ^= ctext[pk_len+i]
+ }
+ return n[:c1_len], nil
+}
+
+// Encapsulation receives the public key and generates SIKE ciphertext and shared secret.
+// The generated ciphertext is used for authentication.
+// The rng must be cryptographically secure PRNG.
+// Error is returned in case PRNG fails or wrongly formatted input was provided.
+func Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, err error) {
+ // Buffer for random, secret message
+ ptext := make([]byte, pub.params.MsgLen)
+ // SHA256 hash context object
+ d := sha256.New()
+
+ // Generate ephemeral value
+ _, err = io.ReadFull(rng, ptext)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Implementation uses first 28-bytes of secret
+ d.Write(ptext)
+ d.Write(pub.Export())
+ digest := d.Sum(nil)
+ // r = G(ptext||pub)
+ r := digest[:pub.params.A.SecretByteLen]
+
+ // (c0 || c1) = Enc(pkA, ptext; r)
+ skA := NewPrivateKey(KeyVariant_SIDH_A)
+ err = skA.Import(r)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ pkA := skA.GeneratePublicKey()
+ ctext, err = encrypt(skA, pkA, pub, ptext)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // K = H(ptext||(c0||c1))
+ d.Reset()
+ d.Write(ptext)
+ d.Write(ctext)
+ digest = d.Sum(digest[:0])
+ return ctext, digest[:pub.params.KemSize], nil
+}
+
+// Decapsulate given the keypair and ciphertext as inputs, Decapsulate outputs a shared
+// secret if plaintext verifies correctly, otherwise function outputs random value.
+// Decapsulation may fail in case input is wrongly formatted.
+// Constant time for properly initialized input.
+func Decapsulate(prv *PrivateKey, pub *PublicKey, ctext []byte) ([]byte, error) {
+ var skA = NewPrivateKey(KeyVariant_SIDH_A)
+ // SHA256 hash context object
+ d := sha256.New()
+
+ m, err := Decrypt(prv, ctext)
+ if err != nil {
+ return nil, err
+ }
+
+ // r' = G(m'||pub)
+ d.Write(m)
+ d.Write(pub.Export())
+ digest := d.Sum(nil)
+ // Never fails
+ skA.Import(digest[:pub.params.A.SecretByteLen])
+
+ // Never fails
+ pkA := skA.GeneratePublicKey()
+ c0 := pkA.Export()
+
+ d.Reset()
+ if subtle.ConstantTimeCompare(c0, ctext[:len(c0)]) == 1 {
+ d.Write(m)
+ } else {
+ // S is chosen at random when generating a key and is unknown to the other party. It
+ // may seem weird, but it's correct. It is important that S is unpredictable
+ // to other party. Without this check, it is possible to recover a secret, by
+ // providing series of invalid ciphertexts. It is also important that in case
+ //
+ // See more details in "On the security of supersingular isogeny cryptosystems"
+ // (S. Galbraith, et al., 2016, ePrint #859).
+ d.Write(prv.S)
+ }
+ d.Write(ctext)
+ digest = d.Sum(digest[:0])
+ return digest[:pub.params.KemSize], nil
+}
diff --git a/src/ssl/test/runner/sike/sike_test.go b/src/ssl/test/runner/sike/sike_test.go
new file mode 100644
index 0000000..2e146bc
--- /dev/null
+++ b/src/ssl/test/runner/sike/sike_test.go
@@ -0,0 +1,698 @@
+// Copyright (c) 2019, Cloudflare Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package sike
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/rand"
+ "encoding/hex"
+ "math/big"
+ "strings"
+ "testing"
+)
+
+var tdata = struct {
+ name string
+ PrB_sidh string
+ PkB_sidh string
+ PrA_sidh string
+ PkA_sidh string
+ PkB_sike string
+ PrB_sike string
+}{
+ name: "P-434",
+ PrA_sidh: "3A727E04EA9B7E2A766A6F846489E7E7B915263BCEED308BB10FC9",
+ PkA_sidh: "9E668D1E6750ED4B91EE052C32839CA9DD2E56D52BC24DECC950AA" +
+ "AD24CEED3F9049C77FE80F0B9B01E7F8DAD7833EEC2286544D6380" +
+ "009C379CDD3E7517CEF5E20EB01F8231D52FC30DC61D2F63FB357F" +
+ "85DC6396E8A95DB9740BD3A972C8DB7901B31F074CD3E45345CA78" +
+ "F900817130E688A29A7CF0073B5C00FF2C65FBE776918EF9BD8E75" +
+ "B29EF7FAB791969B60B0C5B37A8992EDEF95FA7BAC40A95DAFE02E" +
+ "237301FEE9A7A43FD0B73477E8035DD12B73FAFEF18D39904DDE36" +
+ "53A754F36BE1888F6607C6A7951349A414352CF31A29F2C40302DB" +
+ "406C48018C905EB9DC46AFBF42A9187A9BB9E51B587622A2862DC7" +
+ "D5CC598BF38ED6320FB51D8697AD3D7A72ABCC32A393F0133DA8DF" +
+ "5E253D9E00B760B2DF342FCE974DCFE946CFE4727783531882800F" +
+ "9E5DD594D6D5A6275EEFEF9713ED838F4A06BB34D7B8D46E0B385A" +
+ "AEA1C7963601",
+ PrB_sidh: "E37BFE55B43B32448F375903D8D226EC94ADBFEA1D2B3536EB987001",
+ PkB_sidh: "C9F73E4497AAA3FDF9EB688135866A8A83934BA10E273B8CC3808C" +
+ "F0C1F5FAB3E9BB295885881B73DEBC875670C0F51C4BB40DF5FEDE" +
+ "01B8AF32D1BF10508B8C17B2734EB93B2B7F5D84A4A0F2F816E9E2" +
+ "C32AC253C0B6025B124D05A87A9E2A8567930F44BAA14219B941B6" +
+ "B400B4AED1D796DA12A5A9F0B8F3F5EE9DD43F64CB24A3B1719DF2" +
+ "78ADF56B5F3395187829DA2319DEABF6BBD6EDA244DE2B62CC5AC2" +
+ "50C1009DD1CD4712B0B37406612AD002B5E51A62B51AC9C0374D14" +
+ "3ABBBD58275FAFC4A5E959C54838C2D6D9FB43B7B2609061267B6A" +
+ "2E6C6D01D295C4223E0D3D7A4CDCFB28A7818A737935279751A6DD" +
+ "8290FD498D1F6AD5F4FFF6BDFA536713F509DCE8047252F1E7D0DD" +
+ "9FCC414C0070B5DCCE3665A21A032D7FBE749181032183AFAD240B" +
+ "7E671E87FBBEC3A8CA4C11AA7A9A23AC69AE2ACF54B664DECD2775" +
+ "3D63508F1B02",
+ PrB_sike: "4B622DE1350119C45A9F2E2EF3DC5DF56A27FCDFCDDAF58CD69B90" +
+ "3752D68C200934E160B234E49EDE247601",
+ PkB_sike: "1BD0A2E81307B6F96461317DDF535ACC0E59C742627BAE60D27605" +
+ "E10FAF722D22A73E184CB572A12E79DCD58C6B54FB01442114CBE9" +
+ "010B6CAEC25D04C16C5E42540C1524C545B8C67614ED4183C9FA5B" +
+ "D0BE45A7F89FBC770EE8E7E5E391C7EE6F35F74C29E6D9E35B1663" +
+ "DA01E48E9DEB2347512D366FDE505161677055E3EF23054D276E81" +
+ "7E2C57025DA1C10D2461F68617F2D11256EEE4E2D7DBDF6C8E34F3" +
+ "A0FD00C625428CB41857002159DAB94267ABE42D630C6AAA91AF83" +
+ "7C7A6740754EA6634C45454C51B0BB4D44C3CCCCE4B32C00901CF6" +
+ "9C008D013348379B2F9837F428A01B6173584691F2A6F3A3C4CF48" +
+ "7D20D261B36C8CDB1BC158E2A5162A9DA4F7A97AA0879B9897E2B6" +
+ "891B672201F9AEFBF799C27B2587120AC586A511360926FB7DA8EB" +
+ "F5CB5272F396AE06608422BE9792E2CE9BEF21BF55B7EFF8DC7EC8" +
+ "C99910D3F800",
+}
+
+/* -------------------------------------------------------------------------
+ Helpers
+ -------------------------------------------------------------------------*/
+// Fail if err !=nil. Display msg as an error message
+func checkErr(t testing.TB, err error, msg string) {
+ t.Helper()
+ if err != nil {
+ t.Error(msg)
+ }
+}
+
+// Utility used for running same test with all registered prime fields
+type MultiIdTestingFunc func(testing.TB)
+
+// Converts string to private key
+func convToPrv(s string, v KeyVariant) *PrivateKey {
+ key := NewPrivateKey(v)
+ hex, e := hex.DecodeString(s)
+ if e != nil {
+ panic("non-hex number provided")
+ }
+ e = key.Import(hex)
+ if e != nil {
+ panic("Can't import private key")
+ }
+ return key
+}
+
+// Converts string to public key
+func convToPub(s string, v KeyVariant) *PublicKey {
+ key := NewPublicKey(v)
+ hex, e := hex.DecodeString(s)
+ if e != nil {
+ panic("non-hex number provided")
+ }
+ e = key.Import(hex)
+ if e != nil {
+ panic("Can't import public key")
+ }
+ return key
+}
+
+/* -------------------------------------------------------------------------
+ Unit tests
+ -------------------------------------------------------------------------*/
+func TestKeygen(t *testing.T) {
+ alicePrivate := convToPrv(tdata.PrA_sidh, KeyVariant_SIDH_A)
+ bobPrivate := convToPrv(tdata.PrB_sidh, KeyVariant_SIDH_B)
+ expPubA := convToPub(tdata.PkA_sidh, KeyVariant_SIDH_A)
+ expPubB := convToPub(tdata.PkB_sidh, KeyVariant_SIDH_B)
+
+ pubA := alicePrivate.GeneratePublicKey()
+ pubB := bobPrivate.GeneratePublicKey()
+
+ if !bytes.Equal(pubA.Export(), expPubA.Export()) {
+ t.Fatalf("unexpected value of public key A")
+ }
+ if !bytes.Equal(pubB.Export(), expPubB.Export()) {
+ t.Fatalf("unexpected value of public key B")
+ }
+}
+
+func TestImportExport(t *testing.T) {
+ var err error
+ a := NewPublicKey(KeyVariant_SIDH_A)
+ b := NewPublicKey(KeyVariant_SIDH_B)
+
+ // Import keys
+ a_hex, err := hex.DecodeString(tdata.PkA_sidh)
+ checkErr(t, err, "invalid hex-number provided")
+
+ err = a.Import(a_hex)
+ checkErr(t, err, "import failed")
+
+ b_hex, err := hex.DecodeString(tdata.PkB_sike)
+ checkErr(t, err, "invalid hex-number provided")
+
+ err = b.Import(b_hex)
+ checkErr(t, err, "import failed")
+
+ // Export and check if same
+ if !bytes.Equal(b.Export(), b_hex) || !bytes.Equal(a.Export(), a_hex) {
+ t.Fatalf("export/import failed")
+ }
+
+ if (len(b.Export()) != b.Size()) || (len(a.Export()) != a.Size()) {
+ t.Fatalf("wrong size of exported keys")
+ }
+}
+
+func testPrivateKeyBelowMax(t testing.TB) {
+ for variant, keySz := range map[KeyVariant]*DomainParams{
+ KeyVariant_SIDH_A: &Params.A,
+ KeyVariant_SIDH_B: &Params.B} {
+
+ func(v KeyVariant, dp *DomainParams) {
+ var blen = int(dp.SecretByteLen)
+ var prv = NewPrivateKey(v)
+
+ // Calculate either (2^e2 - 1) or (2^s - 1); where s=ceil(log_2(3^e3)))
+ maxSecertVal := big.NewInt(int64(dp.SecretBitLen))
+ maxSecertVal.Exp(big.NewInt(int64(2)), maxSecertVal, nil)
+ maxSecertVal.Sub(maxSecertVal, big.NewInt(1))
+
+ // Do same test 1000 times
+ for i := 0; i < 1000; i++ {
+ err := prv.Generate(rand.Reader)
+ checkErr(t, err, "Private key generation")
+
+ // Convert to big-endian, as that's what expected by (*Int)SetBytes()
+ secretBytes := prv.Export()
+ for i := 0; i < int(blen/2); i++ {
+ tmp := secretBytes[i] ^ secretBytes[blen-i-1]
+ secretBytes[i] = tmp ^ secretBytes[i]
+ secretBytes[blen-i-1] = tmp ^ secretBytes[blen-i-1]
+ }
+ prvBig := new(big.Int).SetBytes(secretBytes)
+ // Check if generated key is bigger than acceptable
+ if prvBig.Cmp(maxSecertVal) == 1 {
+ t.Error("Generated private key is wrong")
+ }
+ }
+ }(variant, keySz)
+ }
+}
+
+func testKeyAgreement(t *testing.T, pkA, prA, pkB, prB string) {
+ var e error
+
+ // KeyPairs
+ alicePublic := convToPub(pkA, KeyVariant_SIDH_A)
+ bobPublic := convToPub(pkB, KeyVariant_SIDH_B)
+ alicePrivate := convToPrv(prA, KeyVariant_SIDH_A)
+ bobPrivate := convToPrv(prB, KeyVariant_SIDH_B)
+
+ // Do actual test
+ s1, e := DeriveSecret(bobPrivate, alicePublic)
+ checkErr(t, e, "derivation s1")
+ s2, e := DeriveSecret(alicePrivate, bobPublic)
+ checkErr(t, e, "derivation s1")
+
+ if !bytes.Equal(s1[:], s2[:]) {
+ t.Fatalf("two shared keys: %d, %d do not match", s1, s2)
+ }
+
+ // Negative case
+ dec, e := hex.DecodeString(tdata.PkA_sidh)
+ if e != nil {
+ t.FailNow()
+ }
+ dec[0] = ^dec[0]
+ e = alicePublic.Import(dec)
+ if e != nil {
+ t.FailNow()
+ }
+
+ s1, e = DeriveSecret(bobPrivate, alicePublic)
+ checkErr(t, e, "derivation of s1 failed")
+ s2, e = DeriveSecret(alicePrivate, bobPublic)
+ checkErr(t, e, "derivation of s2 failed")
+
+ if bytes.Equal(s1[:], s2[:]) {
+ t.Fatalf("The two shared keys: %d, %d match", s1, s2)
+ }
+}
+
+func TestDerivationRoundTrip(t *testing.T) {
+ var err error
+
+ prvA := NewPrivateKey(KeyVariant_SIDH_A)
+ prvB := NewPrivateKey(KeyVariant_SIDH_B)
+
+ // Generate private keys
+ err = prvA.Generate(rand.Reader)
+ checkErr(t, err, "key generation failed")
+ err = prvB.Generate(rand.Reader)
+ checkErr(t, err, "key generation failed")
+
+ // Generate public keys
+ pubA := prvA.GeneratePublicKey()
+ pubB := prvB.GeneratePublicKey()
+
+ // Derive shared secret
+ s1, err := DeriveSecret(prvB, pubA)
+ checkErr(t, err, "")
+
+ s2, err := DeriveSecret(prvA, pubB)
+ checkErr(t, err, "")
+
+ if !bytes.Equal(s1[:], s2[:]) {
+ t.Fatalf("Two shared keys: \n%X, \n%X do not match", s1, s2)
+ }
+}
+
+// Encrypt, Decrypt, check if input/output plaintext is the same
+func testPKERoundTrip(t testing.TB, id uint8) {
+ // Message to be encrypted
+ var msg = make([]byte, Params.MsgLen)
+ for i, _ := range msg {
+ msg[i] = byte(i)
+ }
+
+ // Import keys
+ pkB := NewPublicKey(KeyVariant_SIKE)
+ skB := NewPrivateKey(KeyVariant_SIKE)
+ pk_hex, err := hex.DecodeString(tdata.PkB_sike)
+ if err != nil {
+ t.Fatal(err)
+ }
+ sk_hex, err := hex.DecodeString(tdata.PrB_sike)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if pkB.Import(pk_hex) != nil || skB.Import(sk_hex) != nil {
+ t.Error("Import")
+ }
+
+ ct, err := Encrypt(rand.Reader, pkB, msg[:])
+ if err != nil {
+ t.Fatal(err)
+ }
+ pt, err := Decrypt(skB, ct)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(pt[:], msg[:]) {
+ t.Errorf("Decryption failed \n got : %X\n exp : %X", pt, msg)
+ }
+}
+
+// Generate key and check if can encrypt
+func TestPKEKeyGeneration(t *testing.T) {
+ // Message to be encrypted
+ var msg = make([]byte, Params.MsgLen)
+ var err error
+ for i, _ := range msg {
+ msg[i] = byte(i)
+ }
+
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ err = sk.Generate(rand.Reader)
+ checkErr(t, err, "PEK key generation")
+ pk := sk.GeneratePublicKey()
+
+ // Try to encrypt
+ ct, err := Encrypt(rand.Reader, pk, msg[:])
+ checkErr(t, err, "PEK encryption")
+ pt, err := Decrypt(sk, ct)
+ checkErr(t, err, "PEK key decryption")
+
+ if !bytes.Equal(pt[:], msg[:]) {
+ t.Fatalf("Decryption failed \n got : %X\n exp : %X", pt, msg)
+ }
+}
+
+func TestNegativePKE(t *testing.T) {
+ var msg [40]byte
+ var err error
+
+ // Generate key
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ err = sk.Generate(rand.Reader)
+ checkErr(t, err, "key generation")
+
+ pk := sk.GeneratePublicKey()
+
+ // bytelen(msg) - 1
+ ct, err := Encrypt(rand.Reader, pk, msg[:Params.KemSize+8-1])
+ if err == nil {
+ t.Fatal("Error hasn't been returned")
+ }
+ if ct != nil {
+ t.Fatal("Ciphertext must be nil")
+ }
+
+ // KemSize - 1
+ pt, err := Decrypt(sk, msg[:Params.KemSize+8-1])
+ if err == nil {
+ t.Fatal("Error hasn't been returned")
+ }
+ if pt != nil {
+ t.Fatal("Ciphertext must be nil")
+ }
+}
+
+func testKEMRoundTrip(t *testing.T, pkB, skB []byte) {
+ // Import keys
+ pk := NewPublicKey(KeyVariant_SIKE)
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ if pk.Import(pkB) != nil || sk.Import(skB) != nil {
+ t.Error("Import failed")
+ }
+
+ ct, ss_e, err := Encapsulate(rand.Reader, pk)
+ if err != nil {
+ t.Error("Encapsulate failed")
+ }
+
+ ss_d, err := Decapsulate(sk, pk, ct)
+ if err != nil {
+ t.Error("Decapsulate failed")
+ }
+ if !bytes.Equal(ss_e, ss_d) {
+ t.Error("Shared secrets from decapsulation and encapsulation differ")
+ }
+}
+
+func TestKEMRoundTrip(t *testing.T) {
+ pk, err := hex.DecodeString(tdata.PkB_sike)
+ checkErr(t, err, "public key B not a number")
+ sk, err := hex.DecodeString(tdata.PrB_sike)
+ checkErr(t, err, "private key B not a number")
+ testKEMRoundTrip(t, pk, sk)
+}
+
+func TestKEMKeyGeneration(t *testing.T) {
+ // Generate key
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ checkErr(t, sk.Generate(rand.Reader), "error: key generation")
+ pk := sk.GeneratePublicKey()
+
+ // calculated shared secret
+ ct, ss_e, err := Encapsulate(rand.Reader, pk)
+
+ checkErr(t, err, "encapsulation failed")
+ ss_d, err := Decapsulate(sk, pk, ct)
+ checkErr(t, err, "decapsulation failed")
+
+ if !bytes.Equal(ss_e, ss_d) {
+ t.Fatalf("KEM failed \n encapsulated: %X\n decapsulated: %X", ss_d, ss_e)
+ }
+}
+
+func TestNegativeKEM(t *testing.T) {
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ checkErr(t, sk.Generate(rand.Reader), "error: key generation")
+ pk := sk.GeneratePublicKey()
+
+ ct, ss_e, err := Encapsulate(rand.Reader, pk)
+ checkErr(t, err, "pre-requisite for a test failed")
+
+ ct[0] = ct[0] - 1
+ ss_d, err := Decapsulate(sk, pk, ct)
+ checkErr(t, err, "decapsulation returns error when invalid ciphertext provided")
+
+ if bytes.Equal(ss_e, ss_d) {
+ // no idea how this could ever happen, but it would be very bad
+ t.Error("critical error")
+ }
+
+ // Try encapsulating with SIDH key
+ pkSidh := NewPublicKey(KeyVariant_SIDH_B)
+ prSidh := NewPrivateKey(KeyVariant_SIDH_B)
+ _, _, err = Encapsulate(rand.Reader, pkSidh)
+ if err == nil {
+ t.Error("encapsulation accepts SIDH public key")
+ }
+ // Try decapsulating with SIDH key
+ _, err = Decapsulate(prSidh, pk, ct)
+ if err == nil {
+ t.Error("decapsulation accepts SIDH private key key")
+ }
+}
+
+// In case invalid ciphertext is provided, SIKE's decapsulation must
+// return same (but unpredictable) result for a given key.
+func TestNegativeKEMSameWrongResult(t *testing.T) {
+ sk := NewPrivateKey(KeyVariant_SIKE)
+ checkErr(t, sk.Generate(rand.Reader), "error: key generation")
+ pk := sk.GeneratePublicKey()
+
+ ct, encSs, err := Encapsulate(rand.Reader, pk)
+ checkErr(t, err, "pre-requisite for a test failed")
+
+ // make ciphertext wrong
+ ct[0] = ct[0] - 1
+ decSs1, err := Decapsulate(sk, pk, ct)
+ checkErr(t, err, "pre-requisite for a test failed")
+
+ // second decapsulation must be done with same, but imported private key
+ expSk := sk.Export()
+
+ // creat new private key
+ sk = NewPrivateKey(KeyVariant_SIKE)
+ err = sk.Import(expSk)
+ checkErr(t, err, "import failed")
+
+ // try decapsulating again. ss2 must be same as ss1 and different than
+ // original plaintext
+ decSs2, err := Decapsulate(sk, pk, ct)
+ checkErr(t, err, "pre-requisite for a test failed")
+
+ if !bytes.Equal(decSs1, decSs2) {
+ t.Error("decapsulation is insecure")
+ }
+
+ if bytes.Equal(encSs, decSs1) || bytes.Equal(encSs, decSs2) {
+ // this test requires that decapsulation returns wrong result
+ t.Errorf("test implementation error")
+ }
+}
+
+func readAndCheckLine(r *bufio.Reader) []byte {
+ // Read next line from buffer
+ line, isPrefix, err := r.ReadLine()
+ if err != nil || isPrefix {
+ panic("Wrong format of input file")
+ }
+
+ // Function expects that line is in format "KEY = HEX_VALUE". Get
+ // value, which should be a hex string
+ hexst := strings.Split(string(line), "=")[1]
+ hexst = strings.TrimSpace(hexst)
+ // Convert value to byte string
+ ret, err := hex.DecodeString(hexst)
+ if err != nil {
+ panic("Wrong format of input file")
+ }
+ return ret
+}
+
+func testKeygenSIKE(pk, sk []byte, id uint8) bool {
+ // Import provided private key
+ var prvKey = NewPrivateKey(KeyVariant_SIKE)
+ if prvKey.Import(sk) != nil {
+ panic("sike test: can't load KAT")
+ }
+
+ // Generate public key
+ pubKey := prvKey.GeneratePublicKey()
+ return bytes.Equal(pubKey.Export(), pk)
+}
+
+func testDecapsulation(pk, sk, ct, ssExpected []byte, id uint8) bool {
+ var pubKey = NewPublicKey(KeyVariant_SIKE)
+ var prvKey = NewPrivateKey(KeyVariant_SIKE)
+ if pubKey.Import(pk) != nil || prvKey.Import(sk) != nil {
+ panic("sike test: can't load KAT")
+ }
+
+ ssGot, err := Decapsulate(prvKey, pubKey, ct)
+ if err != nil {
+ panic("sike test: can't perform degcapsulation KAT")
+ }
+
+ return bytes.Equal(ssGot, ssExpected)
+}
+
+func TestKeyAgreement(t *testing.T) {
+ testKeyAgreement(t, tdata.PkA_sidh, tdata.PrA_sidh, tdata.PkB_sidh, tdata.PrB_sidh)
+}
+
+// Same values as in sike_test.cc
+func TestDecapsulation(t *testing.T) {
+ var sk = [16 + 28]byte{
+ 0x04, 0x5E, 0x01, 0x42, 0xB8, 0x2F, 0xE1, 0x9A, 0x38, 0x25,
+ 0x92, 0xE7, 0xDC, 0xBA, 0xF7, 0x1B, 0xB1, 0xFD, 0x34, 0x42,
+ 0xDB, 0x02, 0xBC, 0x9D, 0x4C, 0xD0, 0x72, 0x34, 0x4D, 0xBD,
+ 0x06, 0xDF, 0x1C, 0x7D, 0x0A, 0x88, 0xB2, 0x50, 0xC4, 0xF6,
+ 0xAE, 0xE8, 0x25, 0x01,
+ }
+
+ var pk = [330]byte{
+ 0x6D, 0x8D, 0xF5, 0x7B, 0xCD, 0x47, 0xCA, 0xCB, 0x7A, 0x38,
+ 0xB7, 0xA6, 0x90, 0xB7, 0x37, 0x03, 0xD4, 0x6F, 0x27, 0x73,
+ 0x74, 0x17, 0x5A, 0xA4, 0x0D, 0xC6, 0x81, 0xAD, 0xDB, 0xF7,
+ 0x18, 0xB2, 0x3C, 0x30, 0xCF, 0xAA, 0x08, 0x11, 0x91, 0xCC,
+ 0x27, 0x4E, 0xF1, 0xA6, 0xB7, 0xDA, 0xD2, 0xCF, 0x99, 0x7F,
+ 0xF7, 0xE1, 0xD0, 0xCE, 0x00, 0xD2, 0x4B, 0xA4, 0x33, 0xB4,
+ 0x87, 0x01, 0x3F, 0x02, 0xF7, 0xF9, 0xDE, 0xC3, 0x60, 0x62,
+ 0xDA, 0x3F, 0x74, 0xA9, 0x44, 0xBE, 0x19, 0xD5, 0x03, 0x2A,
+ 0x79, 0x8C, 0xA7, 0xFF, 0xEA, 0xB3, 0xBB, 0xB5, 0xD4, 0x1D,
+ 0x8F, 0x92, 0xCE, 0x62, 0x6E, 0x99, 0x24, 0xD7, 0x57, 0xFA,
+ 0xCD, 0xB6, 0xE2, 0x8E, 0xFD, 0x22, 0x0E, 0x31, 0x21, 0x01,
+ 0x8D, 0x79, 0xF8, 0x3E, 0x27, 0xEC, 0x43, 0x40, 0xDB, 0x82,
+ 0xE5, 0xEB, 0x6C, 0x97, 0x66, 0x29, 0x15, 0x68, 0xB7, 0x4D,
+ 0x84, 0xD1, 0x8A, 0x0B, 0x12, 0x36, 0x2C, 0x0C, 0x0A, 0x6E,
+ 0x4E, 0xDE, 0xA5, 0x8A, 0xDE, 0x77, 0xDD, 0x70, 0x49, 0x73,
+ 0xAC, 0x27, 0x6D, 0x8D, 0x25, 0x9A, 0xE4, 0x25, 0xE8, 0x95,
+ 0x8F, 0xFE, 0x90, 0x3B, 0x00, 0x69, 0x20, 0xE8, 0x7C, 0xA5,
+ 0xF5, 0x79, 0xC0, 0x61, 0x51, 0x91, 0x35, 0x25, 0x3F, 0x17,
+ 0x2F, 0x70, 0x73, 0xF0, 0x89, 0xB5, 0xC8, 0x25, 0xB8, 0xE5,
+ 0x7E, 0x34, 0xDD, 0x11, 0xE5, 0xD6, 0xC3, 0xD5, 0x29, 0x89,
+ 0xC6, 0x2C, 0x99, 0x53, 0x1D, 0x2C, 0x77, 0xB0, 0xB6, 0xA1,
+ 0xBD, 0x79, 0xFB, 0x4A, 0xC2, 0x48, 0x4C, 0x62, 0x51, 0x00,
+ 0xE3, 0x91, 0x2A, 0xCB, 0x84, 0x03, 0x5D, 0x2D, 0xC8, 0x33,
+ 0xE9, 0x14, 0xBF, 0x74, 0x21, 0xBC, 0xF4, 0x76, 0xE5, 0x42,
+ 0xB8, 0xBD, 0xE2, 0xE7, 0x20, 0x95, 0x54, 0xF2, 0xED, 0xC0,
+ 0x79, 0x38, 0x1E, 0xD2, 0xEA, 0x1A, 0x63, 0x85, 0xE7, 0x3A,
+ 0xDA, 0xAD, 0xAB, 0x1B, 0x1E, 0x19, 0x9E, 0x73, 0xD0, 0x10,
+ 0x2E, 0x38, 0xAC, 0x8B, 0x00, 0x6A, 0x30, 0x2C, 0x3D, 0x70,
+ 0x8E, 0x39, 0x6D, 0xC0, 0x12, 0x61, 0x7D, 0x2A, 0x0A, 0x04,
+ 0x95, 0x8E, 0x09, 0x3C, 0x7B, 0xEC, 0x2E, 0xBC, 0xE8, 0xE8,
+ 0xE8, 0x37, 0x29, 0xC4, 0x7E, 0x76, 0x48, 0xB9, 0x3B, 0x72,
+ 0xE5, 0x99, 0x9B, 0xF9, 0xE3, 0x99, 0x72, 0x3F, 0x35, 0x29,
+ 0x85, 0xE0, 0xC8, 0xBF, 0xB1, 0x6B, 0xB1, 0x6E, 0x72, 0x00,
+ }
+
+ var ct = [330 + 16]byte{
+ 0xFF, 0xEB, 0xEF, 0x4A, 0xC0, 0x57, 0x0F, 0x26, 0xAC, 0x76,
+ 0xA8, 0xB0, 0xA3, 0x5D, 0x9C, 0xD9, 0x25, 0xD1, 0x7F, 0x92,
+ 0x5D, 0xF4, 0x23, 0x34, 0xC3, 0x03, 0x10, 0xE1, 0xB0, 0x24,
+ 0x9B, 0x44, 0x58, 0x26, 0x13, 0x56, 0x83, 0x43, 0x72, 0x69,
+ 0x28, 0x0D, 0x55, 0x07, 0x1F, 0xDB, 0xC0, 0x23, 0x34, 0x83,
+ 0x1A, 0x09, 0x9B, 0x80, 0x00, 0x64, 0x56, 0xDC, 0x79, 0x7A,
+ 0xD2, 0xCE, 0x23, 0xC9, 0x72, 0x27, 0xFC, 0x8D, 0xAB, 0xBF,
+ 0xD3, 0x17, 0xF6, 0x91, 0x7B, 0x15, 0x93, 0x83, 0x8A, 0x4F,
+ 0x6C, 0xCA, 0x4A, 0x94, 0xDA, 0xC7, 0x9D, 0xB6, 0xD6, 0xBA,
+ 0xBD, 0x81, 0x9A, 0x78, 0xE5, 0xE5, 0xBE, 0x17, 0xBC, 0xCB,
+ 0xC8, 0x23, 0x80, 0x5F, 0x75, 0xF8, 0xDB, 0x51, 0x55, 0x00,
+ 0x25, 0x33, 0x52, 0x64, 0xB2, 0xD6, 0xD8, 0x9A, 0x2A, 0x9E,
+ 0x29, 0x99, 0x13, 0x33, 0xE2, 0xA7, 0x98, 0xAC, 0xD7, 0x79,
+ 0x5C, 0x2F, 0xBA, 0x07, 0xC3, 0x03, 0x37, 0xD6, 0xE6, 0xB5,
+ 0xA1, 0xF5, 0x29, 0xB6, 0xF6, 0xC0, 0x5C, 0x44, 0x68, 0x2B,
+ 0x0B, 0xF5, 0x00, 0x01, 0x44, 0xD5, 0xCC, 0x23, 0xB5, 0x27,
+ 0x4F, 0xCA, 0xB4, 0x05, 0x01, 0xF9, 0xD4, 0x41, 0xE0, 0xE1,
+ 0x1E, 0xCF, 0xA9, 0xBC, 0x79, 0xD7, 0xD5, 0xF5, 0x3C, 0xE6,
+ 0x93, 0xF4, 0x6C, 0x84, 0x5A, 0x2C, 0x4B, 0xE4, 0x91, 0xB2,
+ 0xB2, 0xB8, 0xAD, 0x74, 0x9A, 0x69, 0x79, 0x4C, 0x84, 0xB7,
+ 0xBF, 0xF1, 0x68, 0x4B, 0xAE, 0x0F, 0x7F, 0x45, 0x3B, 0x18,
+ 0x3F, 0xFA, 0x00, 0x48, 0xE0, 0x3A, 0xE2, 0xC0, 0xAE, 0x00,
+ 0xCE, 0x90, 0x28, 0xA4, 0x1B, 0xBE, 0xCA, 0x0C, 0x21, 0x29,
+ 0x64, 0x30, 0x5E, 0x35, 0xAD, 0xFD, 0x83, 0x47, 0x40, 0x6D,
+ 0x15, 0x56, 0xFC, 0xF8, 0x5F, 0xAB, 0x81, 0xFE, 0x6B, 0xE9,
+ 0x6B, 0xED, 0x27, 0x35, 0x7C, 0xD8, 0x2C, 0xD4, 0xF2, 0x11,
+ 0xE6, 0xAF, 0xDF, 0xB8, 0x91, 0x96, 0xEB, 0xF7, 0x4C, 0x8D,
+ 0x70, 0x77, 0x90, 0x81, 0x00, 0x09, 0x19, 0x27, 0x8A, 0x9E,
+ 0xB6, 0x1A, 0xE9, 0xAC, 0x6C, 0xC9, 0xF8, 0xEA, 0xA2, 0x34,
+ 0xB8, 0xAC, 0xB3, 0xB3, 0x68, 0xA1, 0xB7, 0x29, 0x55, 0xCA,
+ 0x40, 0x23, 0x92, 0x5C, 0x0C, 0x79, 0x6B, 0xD6, 0x9F, 0x5B,
+ 0xD2, 0xE6, 0xAE, 0x04, 0xCB, 0xEC, 0xC7, 0x88, 0x18, 0xDB,
+ 0x7A, 0xE6, 0xD6, 0xC9, 0x39, 0xFD, 0x93, 0x9B, 0xC8, 0x01,
+ 0x6F, 0x3E, 0x6C, 0x90, 0x3E, 0x73, 0x76, 0x99, 0x7C, 0x48,
+ 0xDA, 0x68, 0x48, 0x80, 0x2B, 0x63,
+ }
+ var ssExp = [16]byte{
+ 0xA1, 0xF9, 0x5A, 0x67, 0xB9, 0x3D, 0x1E, 0x72, 0xE8, 0xC5,
+ 0x71, 0xF1, 0x4C, 0xB2, 0xAA, 0x6D,
+ }
+
+ var prvObj = NewPrivateKey(KeyVariant_SIKE)
+ var pubObj = NewPublicKey(KeyVariant_SIKE)
+
+ if pubObj.Import(pk[:]) != nil || prvObj.Import(sk[:]) != nil {
+ t.Error("Can't import one of the keys")
+ }
+
+ res, _ := Decapsulate(prvObj, pubObj, ct[:])
+ if !bytes.Equal(ssExp[:], res) {
+ t.Error("Wrong decapsulation result")
+ }
+}
+
+/* -------------------------------------------------------------------------
+ Benchmarking
+ -------------------------------------------------------------------------*/
+
+func BenchmarkSidhKeyAgreement(b *testing.B) {
+ // KeyPairs
+ alicePublic := convToPub(tdata.PkA_sidh, KeyVariant_SIDH_A)
+ alicePrivate := convToPrv(tdata.PrA_sidh, KeyVariant_SIDH_A)
+ bobPublic := convToPub(tdata.PkB_sidh, KeyVariant_SIDH_B)
+ bobPrivate := convToPrv(tdata.PrB_sidh, KeyVariant_SIDH_B)
+
+ for i := 0; i < b.N; i++ {
+ // Derive shared secret
+ DeriveSecret(bobPrivate, alicePublic)
+ DeriveSecret(alicePrivate, bobPublic)
+ }
+}
+
+func BenchmarkAliceKeyGenPrv(b *testing.B) {
+ prv := NewPrivateKey(KeyVariant_SIDH_A)
+ for n := 0; n < b.N; n++ {
+ prv.Generate(rand.Reader)
+ }
+}
+
+func BenchmarkBobKeyGenPrv(b *testing.B) {
+ prv := NewPrivateKey(KeyVariant_SIDH_B)
+ for n := 0; n < b.N; n++ {
+ prv.Generate(rand.Reader)
+ }
+}
+
+func BenchmarkAliceKeyGenPub(b *testing.B) {
+ prv := NewPrivateKey(KeyVariant_SIDH_A)
+ prv.Generate(rand.Reader)
+ for n := 0; n < b.N; n++ {
+ prv.GeneratePublicKey()
+ }
+}
+
+func BenchmarkBobKeyGenPub(b *testing.B) {
+ prv := NewPrivateKey(KeyVariant_SIDH_B)
+ prv.Generate(rand.Reader)
+ for n := 0; n < b.N; n++ {
+ prv.GeneratePublicKey()
+ }
+}
+
+func BenchmarkSharedSecretAlice(b *testing.B) {
+ aPr := convToPrv(tdata.PrA_sidh, KeyVariant_SIDH_A)
+ bPk := convToPub(tdata.PkB_sike, KeyVariant_SIDH_B)
+ for n := 0; n < b.N; n++ {
+ DeriveSecret(aPr, bPk)
+ }
+}
+
+func BenchmarkSharedSecretBob(b *testing.B) {
+ // m_B = 3*randint(0,3^238)
+ aPk := convToPub(tdata.PkA_sidh, KeyVariant_SIDH_A)
+ bPr := convToPrv(tdata.PrB_sidh, KeyVariant_SIDH_B)
+ for n := 0; n < b.N; n++ {
+ DeriveSecret(bPr, aPk)
+ }
+}
diff --git a/src/ssl/test/runner/tls.go b/src/ssl/test/runner/tls.go
index 128d22e..1862b3d 100644
--- a/src/ssl/test/runner/tls.go
+++ b/src/ssl/test/runner/tls.go
@@ -276,15 +276,13 @@
}
func getCertificatePublicKey(cert *x509.Certificate) crypto.PublicKey {
- if cert.PublicKey != nil {
- return cert.PublicKey
- }
-
+ // TODO(davidben): When Go 1.13 is released, use the Ed25519 support in
+ // the standard library.
if isEd25519Certificate(cert) {
return ed25519.PublicKey(cert.RawSubjectPublicKeyInfo[len(ed25519SPKIPrefix):])
}
- return nil
+ return cert.PublicKey
}
var ed25519PKCS8Prefix = []byte{0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70,
@@ -294,6 +292,12 @@
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
+ // TODO(davidben): When Go 1.13 is released, use the Ed25519 support in
+ // the standard library.
+ if bytes.HasPrefix(der, ed25519PKCS8Prefix) && len(der) == len(ed25519PKCS8Prefix)+32 {
+ seed := der[len(ed25519PKCS8Prefix):]
+ return ed25519.NewKeyFromSeed(seed), nil
+ }
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
return key, nil
}
@@ -309,10 +313,5 @@
return key, nil
}
- if bytes.HasPrefix(der, ed25519PKCS8Prefix) && len(der) == len(ed25519PKCS8Prefix)+32 {
- seed := der[len(ed25519PKCS8Prefix):]
- return ed25519.NewKeyFromSeed(seed), nil
- }
-
return nil, errors.New("crypto/tls: failed to parse private key")
}
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 70e061b..bd32ce9 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -51,181 +51,181 @@
}
const Flag<bool> kBoolFlags[] = {
- { "-server", &TestConfig::is_server },
- { "-dtls", &TestConfig::is_dtls },
- { "-fallback-scsv", &TestConfig::fallback_scsv },
- { "-require-any-client-certificate",
- &TestConfig::require_any_client_certificate },
- { "-false-start", &TestConfig::false_start },
- { "-async", &TestConfig::async },
- { "-write-different-record-sizes",
- &TestConfig::write_different_record_sizes },
- { "-cbc-record-splitting", &TestConfig::cbc_record_splitting },
- { "-partial-write", &TestConfig::partial_write },
- { "-no-tls13", &TestConfig::no_tls13 },
- { "-no-tls12", &TestConfig::no_tls12 },
- { "-no-tls11", &TestConfig::no_tls11 },
- { "-no-tls1", &TestConfig::no_tls1 },
- { "-no-ticket", &TestConfig::no_ticket },
- { "-enable-channel-id", &TestConfig::enable_channel_id },
- { "-shim-writes-first", &TestConfig::shim_writes_first },
- { "-expect-session-miss", &TestConfig::expect_session_miss },
- { "-decline-alpn", &TestConfig::decline_alpn },
- { "-select-empty-alpn", &TestConfig::select_empty_alpn },
- { "-expect-extended-master-secret",
- &TestConfig::expect_extended_master_secret },
- { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling },
- { "-enable-signed-cert-timestamps",
- &TestConfig::enable_signed_cert_timestamps },
- { "-implicit-handshake", &TestConfig::implicit_handshake },
- { "-use-early-callback", &TestConfig::use_early_callback },
- { "-fail-early-callback", &TestConfig::fail_early_callback },
- { "-install-ddos-callback", &TestConfig::install_ddos_callback },
- { "-fail-ddos-callback", &TestConfig::fail_ddos_callback },
- { "-fail-cert-callback", &TestConfig::fail_cert_callback },
- { "-handshake-never-done", &TestConfig::handshake_never_done },
- { "-use-export-context", &TestConfig::use_export_context },
- { "-tls-unique", &TestConfig::tls_unique },
- { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal },
- { "-expect-no-session", &TestConfig::expect_no_session },
- { "-expect-ticket-supports-early-data",
- &TestConfig::expect_ticket_supports_early_data },
- { "-use-ticket-callback", &TestConfig::use_ticket_callback },
- { "-renew-ticket", &TestConfig::renew_ticket },
- { "-enable-early-data", &TestConfig::enable_early_data },
- { "-check-close-notify", &TestConfig::check_close_notify },
- { "-shim-shuts-down", &TestConfig::shim_shuts_down },
- { "-verify-fail", &TestConfig::verify_fail },
- { "-verify-peer", &TestConfig::verify_peer },
- { "-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc },
- { "-expect-verify-result", &TestConfig::expect_verify_result },
- { "-renegotiate-once", &TestConfig::renegotiate_once },
- { "-renegotiate-freely", &TestConfig::renegotiate_freely },
- { "-renegotiate-ignore", &TestConfig::renegotiate_ignore },
- { "-forbid-renegotiation-after-handshake",
- &TestConfig::forbid_renegotiation_after_handshake },
- { "-enable-all-curves", &TestConfig::enable_all_curves },
- { "-use-old-client-cert-callback",
- &TestConfig::use_old_client_cert_callback },
- { "-send-alert", &TestConfig::send_alert },
- { "-peek-then-read", &TestConfig::peek_then_read },
- { "-enable-grease", &TestConfig::enable_grease },
- { "-use-exporter-between-reads", &TestConfig::use_exporter_between_reads },
- { "-retain-only-sha256-client-cert",
- &TestConfig::retain_only_sha256_client_cert },
- { "-expect-sha256-client-cert",
- &TestConfig::expect_sha256_client_cert },
- { "-read-with-unfinished-write", &TestConfig::read_with_unfinished_write },
- { "-expect-secure-renegotiation",
- &TestConfig::expect_secure_renegotiation },
- { "-expect-no-secure-renegotiation",
- &TestConfig::expect_no_secure_renegotiation },
- { "-expect-session-id", &TestConfig::expect_session_id },
- { "-expect-no-session-id", &TestConfig::expect_no_session_id },
- { "-expect-accept-early-data", &TestConfig::expect_accept_early_data },
- { "-expect-reject-early-data", &TestConfig::expect_reject_early_data },
- { "-expect-no-offer-early-data", &TestConfig::expect_no_offer_early_data },
- { "-no-op-extra-handshake", &TestConfig::no_op_extra_handshake },
- { "-handshake-twice", &TestConfig::handshake_twice },
- { "-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos },
- { "-enable-ed25519", &TestConfig::enable_ed25519 },
- { "-use-custom-verify-callback", &TestConfig::use_custom_verify_callback },
- { "-allow-false-start-without-alpn",
- &TestConfig::allow_false_start_without_alpn },
- { "-ignore-tls13-downgrade", &TestConfig::ignore_tls13_downgrade },
- { "-expect-tls13-downgrade", &TestConfig::expect_tls13_downgrade },
- { "-handoff", &TestConfig::handoff },
- { "-no-rsa-pss-rsae-certs", &TestConfig::no_rsa_pss_rsae_certs },
- { "-use-ocsp-callback", &TestConfig::use_ocsp_callback },
- { "-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback },
- { "-decline-ocsp-callback", &TestConfig::decline_ocsp_callback },
- { "-fail-ocsp-callback", &TestConfig::fail_ocsp_callback },
- { "-install-cert-compression-algs",
- &TestConfig::install_cert_compression_algs },
- { "-is-handshaker-supported", &TestConfig::is_handshaker_supported },
- { "-handshaker-resume", &TestConfig::handshaker_resume },
- { "-reverify-on-resume", &TestConfig::reverify_on_resume },
- { "-enforce-rsa-key-usage", &TestConfig::enforce_rsa_key_usage },
- { "-jdk11-workaround", &TestConfig::jdk11_workaround },
- { "-server-preference", &TestConfig::server_preference },
- { "-export-traffic-secrets", &TestConfig::export_traffic_secrets },
- { "-key-update", &TestConfig::key_update },
+ {"-server", &TestConfig::is_server},
+ {"-dtls", &TestConfig::is_dtls},
+ {"-fallback-scsv", &TestConfig::fallback_scsv},
+ {"-require-any-client-certificate",
+ &TestConfig::require_any_client_certificate},
+ {"-false-start", &TestConfig::false_start},
+ {"-async", &TestConfig::async},
+ {"-write-different-record-sizes",
+ &TestConfig::write_different_record_sizes},
+ {"-cbc-record-splitting", &TestConfig::cbc_record_splitting},
+ {"-partial-write", &TestConfig::partial_write},
+ {"-no-tls13", &TestConfig::no_tls13},
+ {"-no-tls12", &TestConfig::no_tls12},
+ {"-no-tls11", &TestConfig::no_tls11},
+ {"-no-tls1", &TestConfig::no_tls1},
+ {"-no-ticket", &TestConfig::no_ticket},
+ {"-enable-channel-id", &TestConfig::enable_channel_id},
+ {"-shim-writes-first", &TestConfig::shim_writes_first},
+ {"-expect-session-miss", &TestConfig::expect_session_miss},
+ {"-decline-alpn", &TestConfig::decline_alpn},
+ {"-select-empty-alpn", &TestConfig::select_empty_alpn},
+ {"-expect-extended-master-secret",
+ &TestConfig::expect_extended_master_secret},
+ {"-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling},
+ {"-enable-signed-cert-timestamps",
+ &TestConfig::enable_signed_cert_timestamps},
+ {"-implicit-handshake", &TestConfig::implicit_handshake},
+ {"-use-early-callback", &TestConfig::use_early_callback},
+ {"-fail-early-callback", &TestConfig::fail_early_callback},
+ {"-install-ddos-callback", &TestConfig::install_ddos_callback},
+ {"-fail-ddos-callback", &TestConfig::fail_ddos_callback},
+ {"-fail-cert-callback", &TestConfig::fail_cert_callback},
+ {"-handshake-never-done", &TestConfig::handshake_never_done},
+ {"-use-export-context", &TestConfig::use_export_context},
+ {"-tls-unique", &TestConfig::tls_unique},
+ {"-expect-ticket-renewal", &TestConfig::expect_ticket_renewal},
+ {"-expect-no-session", &TestConfig::expect_no_session},
+ {"-expect-ticket-supports-early-data",
+ &TestConfig::expect_ticket_supports_early_data},
+ {"-use-ticket-callback", &TestConfig::use_ticket_callback},
+ {"-renew-ticket", &TestConfig::renew_ticket},
+ {"-enable-early-data", &TestConfig::enable_early_data},
+ {"-check-close-notify", &TestConfig::check_close_notify},
+ {"-shim-shuts-down", &TestConfig::shim_shuts_down},
+ {"-verify-fail", &TestConfig::verify_fail},
+ {"-verify-peer", &TestConfig::verify_peer},
+ {"-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc},
+ {"-expect-verify-result", &TestConfig::expect_verify_result},
+ {"-renegotiate-once", &TestConfig::renegotiate_once},
+ {"-renegotiate-freely", &TestConfig::renegotiate_freely},
+ {"-renegotiate-ignore", &TestConfig::renegotiate_ignore},
+ {"-forbid-renegotiation-after-handshake",
+ &TestConfig::forbid_renegotiation_after_handshake},
+ {"-enable-all-curves", &TestConfig::enable_all_curves},
+ {"-use-old-client-cert-callback",
+ &TestConfig::use_old_client_cert_callback},
+ {"-send-alert", &TestConfig::send_alert},
+ {"-peek-then-read", &TestConfig::peek_then_read},
+ {"-enable-grease", &TestConfig::enable_grease},
+ {"-use-exporter-between-reads", &TestConfig::use_exporter_between_reads},
+ {"-retain-only-sha256-client-cert",
+ &TestConfig::retain_only_sha256_client_cert},
+ {"-expect-sha256-client-cert", &TestConfig::expect_sha256_client_cert},
+ {"-read-with-unfinished-write", &TestConfig::read_with_unfinished_write},
+ {"-expect-secure-renegotiation", &TestConfig::expect_secure_renegotiation},
+ {"-expect-no-secure-renegotiation",
+ &TestConfig::expect_no_secure_renegotiation},
+ {"-expect-session-id", &TestConfig::expect_session_id},
+ {"-expect-no-session-id", &TestConfig::expect_no_session_id},
+ {"-expect-accept-early-data", &TestConfig::expect_accept_early_data},
+ {"-expect-reject-early-data", &TestConfig::expect_reject_early_data},
+ {"-expect-no-offer-early-data", &TestConfig::expect_no_offer_early_data},
+ {"-no-op-extra-handshake", &TestConfig::no_op_extra_handshake},
+ {"-handshake-twice", &TestConfig::handshake_twice},
+ {"-allow-unknown-alpn-protos", &TestConfig::allow_unknown_alpn_protos},
+ {"-enable-ed25519", &TestConfig::enable_ed25519},
+ {"-use-custom-verify-callback", &TestConfig::use_custom_verify_callback},
+ {"-allow-false-start-without-alpn",
+ &TestConfig::allow_false_start_without_alpn},
+ {"-ignore-tls13-downgrade", &TestConfig::ignore_tls13_downgrade},
+ {"-expect-tls13-downgrade", &TestConfig::expect_tls13_downgrade},
+ {"-handoff", &TestConfig::handoff},
+ {"-no-rsa-pss-rsae-certs", &TestConfig::no_rsa_pss_rsae_certs},
+ {"-use-ocsp-callback", &TestConfig::use_ocsp_callback},
+ {"-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback},
+ {"-decline-ocsp-callback", &TestConfig::decline_ocsp_callback},
+ {"-fail-ocsp-callback", &TestConfig::fail_ocsp_callback},
+ {"-install-cert-compression-algs",
+ &TestConfig::install_cert_compression_algs},
+ {"-is-handshaker-supported", &TestConfig::is_handshaker_supported},
+ {"-handshaker-resume", &TestConfig::handshaker_resume},
+ {"-reverify-on-resume", &TestConfig::reverify_on_resume},
+ {"-enforce-rsa-key-usage", &TestConfig::enforce_rsa_key_usage},
+ {"-jdk11-workaround", &TestConfig::jdk11_workaround},
+ {"-server-preference", &TestConfig::server_preference},
+ {"-export-traffic-secrets", &TestConfig::export_traffic_secrets},
+ {"-key-update", &TestConfig::key_update},
+ {"-expect-delegated-credential-used",
+ &TestConfig::expect_delegated_credential_used},
+ {"-enable-pq-experiment-signal", &TestConfig::enable_pq_experiment_signal},
+ {"-expect-pq-experiment-signal", &TestConfig::expect_pq_experiment_signal},
};
const Flag<std::string> kStringFlags[] = {
- { "-write-settings", &TestConfig::write_settings },
- { "-key-file", &TestConfig::key_file },
- { "-cert-file", &TestConfig::cert_file },
- { "-expect-server-name", &TestConfig::expected_server_name },
- { "-advertise-npn", &TestConfig::advertise_npn },
- { "-expect-next-proto", &TestConfig::expected_next_proto },
- { "-select-next-proto", &TestConfig::select_next_proto },
- { "-send-channel-id", &TestConfig::send_channel_id },
- { "-host-name", &TestConfig::host_name },
- { "-advertise-alpn", &TestConfig::advertise_alpn },
- { "-expect-alpn", &TestConfig::expected_alpn },
- { "-expect-late-alpn", &TestConfig::expected_late_alpn },
- { "-expect-advertised-alpn", &TestConfig::expected_advertised_alpn },
- { "-select-alpn", &TestConfig::select_alpn },
- { "-psk", &TestConfig::psk },
- { "-psk-identity", &TestConfig::psk_identity },
- { "-srtp-profiles", &TestConfig::srtp_profiles },
- { "-cipher", &TestConfig::cipher },
- { "-export-label", &TestConfig::export_label },
- { "-export-context", &TestConfig::export_context },
- { "-expect-peer-cert-file", &TestConfig::expect_peer_cert_file },
- { "-use-client-ca-list", &TestConfig::use_client_ca_list },
- { "-expect-client-ca-list", &TestConfig::expected_client_ca_list },
- { "-expect-msg-callback", &TestConfig::expect_msg_callback },
- { "-handshaker-path", &TestConfig::handshaker_path },
- { "-delegated-credential", &TestConfig::delegated_credential },
+ {"-write-settings", &TestConfig::write_settings},
+ {"-key-file", &TestConfig::key_file},
+ {"-cert-file", &TestConfig::cert_file},
+ {"-expect-server-name", &TestConfig::expect_server_name},
+ {"-advertise-npn", &TestConfig::advertise_npn},
+ {"-expect-next-proto", &TestConfig::expect_next_proto},
+ {"-select-next-proto", &TestConfig::select_next_proto},
+ {"-send-channel-id", &TestConfig::send_channel_id},
+ {"-host-name", &TestConfig::host_name},
+ {"-advertise-alpn", &TestConfig::advertise_alpn},
+ {"-expect-alpn", &TestConfig::expect_alpn},
+ {"-expect-late-alpn", &TestConfig::expect_late_alpn},
+ {"-expect-advertised-alpn", &TestConfig::expect_advertised_alpn},
+ {"-select-alpn", &TestConfig::select_alpn},
+ {"-psk", &TestConfig::psk},
+ {"-psk-identity", &TestConfig::psk_identity},
+ {"-srtp-profiles", &TestConfig::srtp_profiles},
+ {"-cipher", &TestConfig::cipher},
+ {"-export-label", &TestConfig::export_label},
+ {"-export-context", &TestConfig::export_context},
+ {"-expect-peer-cert-file", &TestConfig::expect_peer_cert_file},
+ {"-use-client-ca-list", &TestConfig::use_client_ca_list},
+ {"-expect-client-ca-list", &TestConfig::expect_client_ca_list},
+ {"-expect-msg-callback", &TestConfig::expect_msg_callback},
+ {"-handshaker-path", &TestConfig::handshaker_path},
+ {"-delegated-credential", &TestConfig::delegated_credential},
+ {"-expect-early-data-reason", &TestConfig::expect_early_data_reason},
};
const Flag<std::string> kBase64Flags[] = {
- { "-expect-certificate-types", &TestConfig::expected_certificate_types },
- { "-expect-channel-id", &TestConfig::expected_channel_id },
- { "-token-binding-params", &TestConfig::send_token_binding_params },
- { "-expect-ocsp-response", &TestConfig::expected_ocsp_response },
- { "-expect-signed-cert-timestamps",
- &TestConfig::expected_signed_cert_timestamps },
- { "-ocsp-response", &TestConfig::ocsp_response },
- { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps },
- { "-ticket-key", &TestConfig::ticket_key },
- { "-quic-transport-params", &TestConfig::quic_transport_params },
- { "-expected-quic-transport-params",
- &TestConfig::expected_quic_transport_params },
+ {"-expect-certificate-types", &TestConfig::expect_certificate_types},
+ {"-expect-channel-id", &TestConfig::expect_channel_id},
+ {"-token-binding-params", &TestConfig::send_token_binding_params},
+ {"-expect-ocsp-response", &TestConfig::expect_ocsp_response},
+ {"-expect-signed-cert-timestamps",
+ &TestConfig::expect_signed_cert_timestamps},
+ {"-ocsp-response", &TestConfig::ocsp_response},
+ {"-signed-cert-timestamps", &TestConfig::signed_cert_timestamps},
+ {"-ticket-key", &TestConfig::ticket_key},
+ {"-quic-transport-params", &TestConfig::quic_transport_params},
+ {"-expect-quic-transport-params",
+ &TestConfig::expect_quic_transport_params},
};
const Flag<int> kIntFlags[] = {
- { "-port", &TestConfig::port },
- { "-resume-count", &TestConfig::resume_count },
- { "-expected-token-binding-param",
- &TestConfig::expected_token_binding_param },
- { "-min-version", &TestConfig::min_version },
- { "-max-version", &TestConfig::max_version },
- { "-expect-version", &TestConfig::expect_version },
- { "-mtu", &TestConfig::mtu },
- { "-export-early-keying-material",
- &TestConfig::export_early_keying_material },
- { "-export-keying-material", &TestConfig::export_keying_material },
- { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
- { "-expect-peer-signature-algorithm",
- &TestConfig::expect_peer_signature_algorithm },
- { "-expect-curve-id", &TestConfig::expect_curve_id },
- { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
- { "-max-cert-list", &TestConfig::max_cert_list },
- { "-expect-cipher-aes", &TestConfig::expect_cipher_aes },
- { "-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes },
- { "-resumption-delay", &TestConfig::resumption_delay },
- { "-max-send-fragment", &TestConfig::max_send_fragment },
- { "-read-size", &TestConfig::read_size },
- { "-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew },
+ {"-port", &TestConfig::port},
+ {"-resume-count", &TestConfig::resume_count},
+ {"-expect-token-binding-param", &TestConfig::expect_token_binding_param},
+ {"-min-version", &TestConfig::min_version},
+ {"-max-version", &TestConfig::max_version},
+ {"-expect-version", &TestConfig::expect_version},
+ {"-mtu", &TestConfig::mtu},
+ {"-export-keying-material", &TestConfig::export_keying_material},
+ {"-expect-total-renegotiations", &TestConfig::expect_total_renegotiations},
+ {"-expect-peer-signature-algorithm",
+ &TestConfig::expect_peer_signature_algorithm},
+ {"-expect-curve-id", &TestConfig::expect_curve_id},
+ {"-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms},
+ {"-max-cert-list", &TestConfig::max_cert_list},
+ {"-expect-cipher-aes", &TestConfig::expect_cipher_aes},
+ {"-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes},
+ {"-resumption-delay", &TestConfig::resumption_delay},
+ {"-max-send-fragment", &TestConfig::max_send_fragment},
+ {"-read-size", &TestConfig::read_size},
+ {"-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew},
};
const Flag<std::vector<int>> kIntVectorFlags[] = {
{"-signing-prefs", &TestConfig::signing_prefs},
{"-verify-prefs", &TestConfig::verify_prefs},
- {"-expect-peer-verify-pref", &TestConfig::expected_peer_verify_prefs},
+ {"-expect-peer-verify-pref", &TestConfig::expect_peer_verify_prefs},
{"-curves", &TestConfig::curves},
};
@@ -243,7 +243,7 @@
if (string_field != NULL) {
*i = *i + 1;
if (*i >= argc) {
- fprintf(stderr, "Missing parameter\n");
+ fprintf(stderr, "Missing parameter.\n");
return false;
}
if (!skip) {
@@ -256,19 +256,19 @@
if (base64_field != NULL) {
*i = *i + 1;
if (*i >= argc) {
- fprintf(stderr, "Missing parameter\n");
+ fprintf(stderr, "Missing parameter.\n");
return false;
}
size_t len;
if (!EVP_DecodedLength(&len, strlen(argv[*i]))) {
- fprintf(stderr, "Invalid base64: %s\n", argv[*i]);
+ fprintf(stderr, "Invalid base64: %s.\n", argv[*i]);
return false;
}
std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
if (!EVP_DecodeBase64(decoded.get(), &len, len,
reinterpret_cast<const uint8_t *>(argv[*i]),
strlen(argv[*i]))) {
- fprintf(stderr, "Invalid base64: %s\n", argv[*i]);
+ fprintf(stderr, "Invalid base64: %s.\n", argv[*i]);
return false;
}
if (!skip) {
@@ -282,7 +282,7 @@
if (int_field) {
*i = *i + 1;
if (*i >= argc) {
- fprintf(stderr, "Missing parameter\n");
+ fprintf(stderr, "Missing parameter.\n");
return false;
}
if (!skip) {
@@ -296,7 +296,7 @@
if (int_vector_field) {
*i = *i + 1;
if (*i >= argc) {
- fprintf(stderr, "Missing parameter\n");
+ fprintf(stderr, "Missing parameter.\n");
return false;
}
@@ -307,7 +307,7 @@
return true;
}
- fprintf(stderr, "Unknown argument: %s\n", flag);
+ fprintf(stderr, "Unknown argument: %s.\n", flag);
return false;
}
@@ -403,9 +403,9 @@
const TestConfig *config = GetTestConfig(ssl);
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
if (server_name == nullptr ||
- std::string(server_name) != config->expected_server_name) {
- fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name,
- config->expected_server_name.c_str());
+ std::string(server_name) != config->expect_server_name) {
+ fprintf(stderr, "servername mismatch (got %s; want %s).\n", server_name,
+ config->expect_server_name.c_str());
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
@@ -449,7 +449,7 @@
if (content_type == SSL3_RT_HEADER) {
if (len !=
(config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH)) {
- fprintf(stderr, "Incorrect length for record header: %zu\n", len);
+ fprintf(stderr, "Incorrect length for record header: %zu.\n", len);
state->msg_callback_ok = false;
}
return;
@@ -459,7 +459,7 @@
switch (content_type) {
case 0:
if (version != SSL2_VERSION) {
- fprintf(stderr, "Incorrect version for V2ClientHello: %x\n", version);
+ fprintf(stderr, "Incorrect version for V2ClientHello: %x.\n", version);
state->msg_callback_ok = false;
return;
}
@@ -509,7 +509,7 @@
return;
default:
- fprintf(stderr, "Invalid content_type: %d\n", content_type);
+ fprintf(stderr, "Invalid content_type: %d.\n", content_type);
state->msg_callback_ok = false;
}
}
@@ -618,11 +618,11 @@
return SSL_TLSEXT_ERR_NOACK;
}
- if (!config->expected_advertised_alpn.empty() &&
- (config->expected_advertised_alpn.size() != inlen ||
- OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) !=
+ if (!config->expect_advertised_alpn.empty() &&
+ (config->expect_advertised_alpn.size() != inlen ||
+ OPENSSL_memcmp(config->expect_advertised_alpn.data(), in, inlen) !=
0)) {
- fprintf(stderr, "bad ALPN select callback inputs\n");
+ fprintf(stderr, "bad ALPN select callback inputs.\n");
exit(1);
}
@@ -634,12 +634,12 @@
static bool CheckVerifyCallback(SSL *ssl) {
const TestConfig *config = GetTestConfig(ssl);
- if (!config->expected_ocsp_response.empty()) {
+ if (!config->expect_ocsp_response.empty()) {
const uint8_t *data;
size_t len;
SSL_get0_ocsp_response(ssl, &data, &len);
if (len == 0) {
- fprintf(stderr, "OCSP response not available in verify callback\n");
+ fprintf(stderr, "OCSP response not available in verify callback.\n");
return false;
}
}
@@ -808,7 +808,7 @@
for (const auto &part : parts) {
std::string binary;
if (!HexDecode(&binary, part)) {
- fprintf(stderr, "Bad hex string: %s\n", part.c_str());
+ fprintf(stderr, "Bad hex string: %s.\n", part.c_str());
return ret;
}
@@ -847,22 +847,22 @@
static bool CheckPeerVerifyPrefs(SSL *ssl) {
const TestConfig *config = GetTestConfig(ssl);
- if (!config->expected_peer_verify_prefs.empty()) {
+ if (!config->expect_peer_verify_prefs.empty()) {
const uint16_t *peer_sigalgs;
size_t num_peer_sigalgs =
SSL_get0_peer_verify_algorithms(ssl, &peer_sigalgs);
- if (config->expected_peer_verify_prefs.size() != num_peer_sigalgs) {
+ if (config->expect_peer_verify_prefs.size() != num_peer_sigalgs) {
fprintf(stderr,
"peer verify preferences length mismatch (got %zu, wanted %zu)\n",
- num_peer_sigalgs, config->expected_peer_verify_prefs.size());
+ num_peer_sigalgs, config->expect_peer_verify_prefs.size());
return false;
}
for (size_t i = 0; i < num_peer_sigalgs; i++) {
if (static_cast<int>(peer_sigalgs[i]) !=
- config->expected_peer_verify_prefs[i]) {
+ config->expect_peer_verify_prefs[i]) {
fprintf(stderr,
"peer verify preference %zu mismatch (got %04x, wanted %04x\n",
- i, peer_sigalgs[i], config->expected_peer_verify_prefs[i]);
+ i, peer_sigalgs[i], config->expect_peer_verify_prefs[i]);
return false;
}
}
@@ -877,29 +877,29 @@
return false;
}
- if (!config->expected_certificate_types.empty()) {
+ if (!config->expect_certificate_types.empty()) {
const uint8_t *certificate_types;
size_t certificate_types_len =
SSL_get0_certificate_types(ssl, &certificate_types);
- if (certificate_types_len != config->expected_certificate_types.size() ||
+ if (certificate_types_len != config->expect_certificate_types.size() ||
OPENSSL_memcmp(certificate_types,
- config->expected_certificate_types.data(),
+ config->expect_certificate_types.data(),
certificate_types_len) != 0) {
- fprintf(stderr, "certificate types mismatch\n");
+ fprintf(stderr, "certificate types mismatch.\n");
return false;
}
}
- if (!config->expected_client_ca_list.empty()) {
+ if (!config->expect_client_ca_list.empty()) {
bssl::UniquePtr<STACK_OF(X509_NAME)> expected =
- DecodeHexX509Names(config->expected_client_ca_list);
+ DecodeHexX509Names(config->expect_client_ca_list);
const size_t num_expected = sk_X509_NAME_num(expected.get());
const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl);
const size_t num_received = sk_X509_NAME_num(received);
if (num_received != num_expected) {
- fprintf(stderr, "expected %u names in CertificateRequest but got %u\n",
+ fprintf(stderr, "expected %u names in CertificateRequest but got %u.\n",
static_cast<unsigned>(num_expected),
static_cast<unsigned>(num_received));
return false;
@@ -908,7 +908,7 @@
for (size_t i = 0; i < num_received; i++) {
if (X509_NAME_cmp(sk_X509_NAME_value(received, i),
sk_X509_NAME_value(expected.get(), i)) != 0) {
- fprintf(stderr, "names in CertificateRequest differ at index #%d\n",
+ fprintf(stderr, "names in CertificateRequest differ at index #%d.\n",
static_cast<unsigned>(i));
return false;
}
@@ -1099,35 +1099,16 @@
const TestConfig *config = GetTestConfig(client_hello->ssl);
GetTestState(client_hello->ssl)->early_callback_called = true;
- if (!config->expected_server_name.empty()) {
- const uint8_t *extension_data;
- size_t extension_len;
- CBS extension, server_name_list, host_name;
- uint8_t name_type;
-
- if (!SSL_early_callback_ctx_extension_get(
- client_hello, TLSEXT_TYPE_server_name, &extension_data,
- &extension_len)) {
- fprintf(stderr, "Could not find server_name extension.\n");
+ if (!config->expect_server_name.empty()) {
+ const char *server_name =
+ SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
+ if (server_name == nullptr ||
+ std::string(server_name) != config->expect_server_name) {
+ fprintf(stderr,
+ "Server name mismatch in early callback (got %s; want %s).\n",
+ server_name, config->expect_server_name.c_str());
return ssl_select_cert_error;
}
-
- CBS_init(&extension, extension_data, extension_len);
- if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) ||
- CBS_len(&extension) != 0 ||
- !CBS_get_u8(&server_name_list, &name_type) ||
- name_type != TLSEXT_NAMETYPE_host_name ||
- !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
- CBS_len(&server_name_list) != 0) {
- fprintf(stderr, "Could not decode server_name extension.\n");
- return ssl_select_cert_error;
- }
-
- if (!CBS_mem_equal(&host_name,
- (const uint8_t *)config->expected_server_name.data(),
- config->expected_server_name.size())) {
- fprintf(stderr, "Server name mismatch.\n");
- }
}
if (config->fail_early_callback) {
@@ -1240,7 +1221,7 @@
SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
}
- if (!expected_server_name.empty()) {
+ if (!expect_server_name.empty()) {
SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
}
@@ -1344,6 +1325,10 @@
SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
}
+ if (enable_pq_experiment_signal) {
+ SSL_CTX_enable_pq_experiment_signal(ssl_ctx.get());
+ }
+
return ssl_ctx;
}
@@ -1371,7 +1356,7 @@
// Account for the trailing '\0' for the identity.
if (config->psk_identity.size() >= max_identity_len ||
config->psk.size() > max_psk_len) {
- fprintf(stderr, "PSK buffers too small\n");
+ fprintf(stderr, "PSK buffers too small.\n");
return 0;
}
@@ -1390,7 +1375,7 @@
}
if (config->psk.size() > max_psk_len) {
- fprintf(stderr, "PSK buffers too small\n");
+ fprintf(stderr, "PSK buffers too small.\n");
return 0;
}
@@ -1520,7 +1505,7 @@
if (no_ticket) {
SSL_set_options(ssl.get(), SSL_OP_NO_TICKET);
}
- if (!expected_channel_id.empty() || enable_channel_id) {
+ if (!expect_channel_id.empty() || enable_channel_id) {
SSL_set_tls_channel_id_enabled(ssl.get(), 1);
}
if (!send_channel_id.empty()) {
@@ -1622,6 +1607,9 @@
case SSL_CURVE_CECPQ2:
nids.push_back(NID_CECPQ2);
break;
+ case SSL_CURVE_CECPQ2b:
+ nids.push_back(NID_CECPQ2b);
+ break;
}
if (!SSL_set1_curves(ssl.get(), &nids[0], nids.size())) {
return nullptr;
@@ -1630,8 +1618,8 @@
}
if (enable_all_curves) {
static const int kAllCurves[] = {
- NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1,
- NID_secp521r1, NID_X25519, NID_CECPQ2,
+ NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
+ NID_X25519, NID_CECPQ2, NID_CECPQ2b,
};
if (!SSL_set1_curves(ssl.get(), kAllCurves,
OPENSSL_ARRAY_SIZE(kAllCurves))) {
@@ -1678,7 +1666,8 @@
if (!delegated_credential.empty()) {
std::string::size_type comma = delegated_credential.find(',');
if (comma == std::string::npos) {
- fprintf(stderr, "failed to find comma in delegated credential argument");
+ fprintf(stderr,
+ "failed to find comma in delegated credential argument.\n");
return nullptr;
}
@@ -1686,7 +1675,7 @@
const std::string pkcs8_hex = delegated_credential.substr(comma + 1);
std::string dc, pkcs8;
if (!HexDecode(&dc, dc_hex) || !HexDecode(&pkcs8, pkcs8_hex)) {
- fprintf(stderr, "failed to hex decode delegated credential argument");
+ fprintf(stderr, "failed to hex decode delegated credential argument.\n");
return nullptr;
}
@@ -1697,7 +1686,7 @@
bssl::UniquePtr<EVP_PKEY> priv(EVP_parse_private_key(&pkcs8_cbs));
if (!priv) {
- fprintf(stderr, "failed to parse delegated credential private key");
+ fprintf(stderr, "failed to parse delegated credential private key.\n");
return nullptr;
}
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 9221d6f..ce4b416 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -32,15 +32,15 @@
bool fallback_scsv = false;
std::vector<int> signing_prefs;
std::vector<int> verify_prefs;
- std::vector<int> expected_peer_verify_prefs;
+ std::vector<int> expect_peer_verify_prefs;
std::vector<int> curves;
std::string key_file;
std::string cert_file;
- std::string expected_server_name;
- std::string expected_certificate_types;
+ std::string expect_server_name;
+ std::string expect_certificate_types;
bool require_any_client_certificate = false;
std::string advertise_npn;
- std::string expected_next_proto;
+ std::string expect_next_proto;
bool false_start = false;
std::string select_next_proto;
bool async = false;
@@ -52,31 +52,31 @@
bool no_tls11 = false;
bool no_tls1 = false;
bool no_ticket = false;
- std::string expected_channel_id;
+ std::string expect_channel_id;
bool enable_channel_id = false;
std::string send_channel_id;
- int expected_token_binding_param = -1;
+ int expect_token_binding_param = -1;
std::string send_token_binding_params;
bool shim_writes_first = false;
std::string host_name;
std::string advertise_alpn;
- std::string expected_alpn;
- std::string expected_late_alpn;
- std::string expected_advertised_alpn;
+ std::string expect_alpn;
+ std::string expect_late_alpn;
+ std::string expect_advertised_alpn;
std::string select_alpn;
bool decline_alpn = false;
bool select_empty_alpn = false;
std::string quic_transport_params;
- std::string expected_quic_transport_params;
+ std::string expect_quic_transport_params;
bool expect_session_miss = false;
bool expect_extended_master_secret = false;
std::string psk;
std::string psk_identity;
std::string srtp_profiles;
bool enable_ocsp_stapling = false;
- std::string expected_ocsp_response;
+ std::string expect_ocsp_response;
bool enable_signed_cert_timestamps = false;
- std::string expected_signed_cert_timestamps;
+ std::string expect_signed_cert_timestamps;
int min_version = 0;
int max_version = 0;
int expect_version = 0;
@@ -89,7 +89,6 @@
bool fail_cert_callback = false;
std::string cipher;
bool handshake_never_done = false;
- int export_early_keying_material = 0;
int export_keying_material = 0;
std::string export_label;
std::string export_context;
@@ -127,7 +126,7 @@
bool use_old_client_cert_callback = false;
int initial_timeout_duration_ms = 0;
std::string use_client_ca_list;
- std::string expected_client_ca_list;
+ std::string expect_client_ca_list;
bool send_alert = false;
bool peek_then_read = false;
bool enable_grease = false;
@@ -173,7 +172,11 @@
bool server_preference = false;
bool export_traffic_secrets = false;
bool key_update = false;
+ bool expect_delegated_credential_used = false;
std::string delegated_credential;
+ std::string expect_early_data_reason;
+ bool enable_pq_experiment_signal = false;
+ bool expect_pq_experiment_signal = false;
int argc;
char **argv;
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index ba5719f..1a49e4c 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -370,13 +370,8 @@
return false;
}
- bool sig_ok = ssl_public_key_verify(ssl, signature, signature_algorithm,
- hs->peer_pubkey.get(), input);
-#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
- sig_ok = true;
- ERR_clear_error();
-#endif
- if (!sig_ok) {
+ if (!ssl_public_key_verify(ssl, signature, signature_algorithm,
+ hs->peer_pubkey.get(), input)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
return false;
@@ -488,15 +483,16 @@
if (ssl_signing_with_dc(hs)) {
const CRYPTO_BUFFER *raw = dc->raw.get();
+ CBB child;
if (!CBB_add_u16(&extensions, TLSEXT_TYPE_delegated_credential) ||
- !CBB_add_u16(&extensions, CRYPTO_BUFFER_len(raw)) ||
- !CBB_add_bytes(&extensions,
- CRYPTO_BUFFER_data(raw),
+ !CBB_add_u16_length_prefixed(&extensions, &child) ||
+ !CBB_add_bytes(&child, CRYPTO_BUFFER_data(raw),
CRYPTO_BUFFER_len(raw)) ||
!CBB_flush(&extensions)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
+ ssl->s3->delegated_credential_used = true;
}
for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain.get()); i++) {
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index ac97165..f411e19 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -188,6 +188,7 @@
hs->tls13_state = state_send_second_client_hello;
// 0-RTT is rejected if we receive a HelloRetryRequest.
if (hs->in_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
return ssl_hs_early_data_rejected;
}
return ssl_hs_ok;
@@ -900,7 +901,7 @@
return false;
}
- if (have_early_data_info && ssl->enable_early_data) {
+ if (have_early_data_info) {
if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
CBS_len(&early_data_info) != 0) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 7353561..b6a402f 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -38,6 +38,7 @@
return false;
}
+ assert(hs->transcript.DigestLen() <= SSL_MAX_MD_SIZE);
hs->hash_len = hs->transcript.DigestLen();
// Initialize the secret to the zero key.
@@ -215,7 +216,6 @@
static const char kTLS13LabelExporter[] = "exp master";
-static const char kTLS13LabelEarlyExporter[] = "e exp master";
static const char kTLS13LabelClientEarlyTraffic[] = "c e traffic";
static const char kTLS13LabelClientHandshakeTraffic[] = "c hs traffic";
@@ -229,13 +229,9 @@
kTLS13LabelClientEarlyTraffic,
strlen(kTLS13LabelClientEarlyTraffic)) ||
!ssl_log_secret(ssl, "CLIENT_EARLY_TRAFFIC_SECRET",
- hs->early_traffic_secret, hs->hash_len) ||
- !derive_secret(hs, ssl->s3->early_exporter_secret, hs->hash_len,
- kTLS13LabelEarlyExporter,
- strlen(kTLS13LabelEarlyExporter))) {
+ hs->early_traffic_secret, hs->hash_len)) {
return false;
}
- ssl->s3->early_exporter_secret_len = hs->hash_len;
if (ssl->quic_method != nullptr) {
if (ssl->server) {
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index caaf0c7..4ebea5b 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -53,6 +53,12 @@
static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0};
+// Allow a minute of ticket age skew in either direction. This covers
+// transmission delays in ClientHello and NewSessionTicket, as well as
+// drift between client and server clock rate since the ticket was issued.
+// See RFC 8446, section 8.3.
+static const int32_t kMaxTicketAgeSkewSeconds = 60;
+
static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, bool *out_need_retry,
SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
@@ -97,77 +103,15 @@
return 1;
}
-// CipherScorer produces a "score" for each possible cipher suite offered by
-// the client.
-class CipherScorer {
- public:
- CipherScorer(uint16_t group_id)
- : aes_is_fine_(EVP_has_aes_hardware()),
- security_128_is_fine_(group_id != SSL_CURVE_CECPQ2) {}
-
- typedef std::tuple<bool, bool, bool> Score;
-
- // MinScore returns a |Score| that will compare less than the score of all
- // cipher suites.
- Score MinScore() const {
- return Score(false, false, false);
- }
-
- Score Evaluate(const SSL_CIPHER *a) const {
- return Score(
- // Something is always preferable to nothing.
- true,
- // Either 128-bit is fine, or 256-bit is preferred.
- security_128_is_fine_ || a->algorithm_enc != SSL_AES128GCM,
- // Either AES is fine, or else ChaCha20 is preferred.
- aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305);
- }
-
- private:
- const bool aes_is_fine_;
- const bool security_128_is_fine_;
-};
-
static const SSL_CIPHER *choose_tls13_cipher(
const SSL *ssl, const SSL_CLIENT_HELLO *client_hello, uint16_t group_id) {
- if (client_hello->cipher_suites_len % 2 != 0) {
- return nullptr;
- }
-
CBS cipher_suites;
CBS_init(&cipher_suites, client_hello->cipher_suites,
client_hello->cipher_suites_len);
const uint16_t version = ssl_protocol_version(ssl);
- const SSL_CIPHER *best = nullptr;
- CipherScorer scorer(group_id);
- CipherScorer::Score best_score = scorer.MinScore();
-
- while (CBS_len(&cipher_suites) > 0) {
- uint16_t cipher_suite;
- if (!CBS_get_u16(&cipher_suites, &cipher_suite)) {
- return nullptr;
- }
-
- // Limit to TLS 1.3 ciphers we know about.
- const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite);
- if (candidate == nullptr ||
- SSL_CIPHER_get_min_version(candidate) > version ||
- SSL_CIPHER_get_max_version(candidate) < version) {
- continue;
- }
-
- const CipherScorer::Score candidate_score = scorer.Evaluate(candidate);
- // |candidate_score| must be larger to displace the current choice. That way
- // the client's order controls between ciphers with an equal score.
- if (candidate_score > best_score) {
- best = candidate;
- best_score = candidate_score;
- }
- }
-
- return best;
+ return ssl_choose_tls13_cipher(cipher_suites, version, group_id);
}
static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {
@@ -307,16 +251,15 @@
static enum ssl_ticket_aead_result_t select_session(
SSL_HANDSHAKE *hs, uint8_t *out_alert, UniquePtr<SSL_SESSION> *out_session,
- int32_t *out_ticket_age_skew, const SSLMessage &msg,
- const SSL_CLIENT_HELLO *client_hello) {
+ int32_t *out_ticket_age_skew, bool *out_offered_ticket,
+ const SSLMessage &msg, const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
- *out_session = NULL;
+ *out_session = nullptr;
- // Decode the ticket if we agreed on a PSK key exchange mode.
CBS pre_shared_key;
- if (!hs->accept_psk_mode ||
- !ssl_client_hello_get_extension(client_hello, &pre_shared_key,
- TLSEXT_TYPE_pre_shared_key)) {
+ *out_offered_ticket = ssl_client_hello_get_extension(
+ client_hello, &pre_shared_key, TLSEXT_TYPE_pre_shared_key);
+ if (!*out_offered_ticket) {
return ssl_ticket_aead_ignore_ticket;
}
@@ -337,6 +280,11 @@
return ssl_ticket_aead_error;
}
+ // If the peer did not offer psk_dhe, ignore the resumption.
+ if (!hs->accept_psk_mode) {
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
// TLS 1.3 session tickets are renewed separately as part of the
// NewSessionTicket.
bool unused_renew;
@@ -406,10 +354,18 @@
uint8_t alert = SSL_AD_DECODE_ERROR;
UniquePtr<SSL_SESSION> session;
- switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew, msg,
- &client_hello)) {
+ bool offered_ticket = false;
+ switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew,
+ &offered_ticket, msg, &client_hello)) {
case ssl_ticket_aead_ignore_ticket:
assert(!session);
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
+ } else if (!offered_ticket) {
+ ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
+ } else {
+ ssl->s3->early_data_reason = ssl_early_data_session_not_resumed;
+ }
if (!ssl_get_new_session(hs, 1 /* server */)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -421,26 +377,34 @@
// a fresh session.
hs->new_session =
SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
-
- if (ssl->enable_early_data &&
- // Early data must be acceptable for this ticket.
- session->ticket_max_early_data != 0 &&
- // The client must have offered early data.
- hs->early_data_offered &&
- // Channel ID is incompatible with 0-RTT.
- !ssl->s3->channel_id_valid &&
- // If Token Binding is negotiated, reject 0-RTT.
- !ssl->s3->token_binding_negotiated &&
- // The negotiated ALPN must match the one in the ticket.
- MakeConstSpan(ssl->s3->alpn_selected) == session->early_alpn) {
- ssl->s3->early_data_accepted = true;
- }
-
- if (hs->new_session == NULL) {
+ if (hs->new_session == nullptr) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
+ } else if (session->ticket_max_early_data == 0) {
+ ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
+ } else if (!hs->early_data_offered) {
+ ssl->s3->early_data_reason = ssl_early_data_peer_declined;
+ } else if (ssl->s3->channel_id_valid) {
+ // Channel ID is incompatible with 0-RTT.
+ ssl->s3->early_data_reason = ssl_early_data_channel_id;
+ } else if (ssl->s3->token_binding_negotiated) {
+ // Token Binding is incompatible with 0-RTT.
+ ssl->s3->early_data_reason = ssl_early_data_token_binding;
+ } else if (MakeConstSpan(ssl->s3->alpn_selected) != session->early_alpn) {
+ // The negotiated ALPN must match the one in the ticket.
+ ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
+ } else if (ssl->s3->ticket_age_skew < -kMaxTicketAgeSkewSeconds ||
+ kMaxTicketAgeSkewSeconds < ssl->s3->ticket_age_skew) {
+ ssl->s3->early_data_reason = ssl_early_data_ticket_age_skew;
+ } else {
+ ssl->s3->early_data_reason = ssl_early_data_accepted;
+ ssl->s3->early_data_accepted = true;
+ }
+
ssl->s3->session_reused = true;
// Resumption incorporates fresh key material, so refresh the timeout.
@@ -499,7 +463,10 @@
bool need_retry;
if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
if (need_retry) {
- ssl->s3->early_data_accepted = false;
+ if (ssl->s3->early_data_accepted) {
+ ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
+ ssl->s3->early_data_accepted = false;
+ }
ssl->s3->skip_early_data = true;
ssl->method->next_message(ssl);
if (!hs->transcript.UpdateForHelloRetryRequest()) {
@@ -950,7 +917,15 @@
}
hs->tls13_state = state_done;
- return sent_tickets ? ssl_hs_flush : ssl_hs_ok;
+ // In TLS 1.3, the NewSessionTicket isn't flushed until the server performs a
+ // write, to prevent a non-reading client from causing the server to hang in
+ // the case of a small server write buffer. Consumers which don't write data
+ // to the client will need to do a zero-byte write if they wish to flush the
+ // tickets.
+ if (hs->ssl->ctx->quic_method != nullptr && sent_tickets) {
+ return ssl_hs_flush;
+ }
+ return ssl_hs_ok;
}
enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index bc9410b..95fac4d 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -125,9 +125,9 @@
ssl3_set_write_state,
};
-static int ssl_noop_x509_check_client_CA_names(
+static bool ssl_noop_x509_check_client_CA_names(
STACK_OF(CRYPTO_BUFFER) *names) {
- return 1;
+ return true;
}
static void ssl_noop_x509_clear(CERT *cert) {}
@@ -135,29 +135,29 @@
static void ssl_noop_x509_dup(CERT *new_cert, const CERT *cert) {}
static void ssl_noop_x509_flush_cached_leaf(CERT *cert) {}
static void ssl_noop_x509_flush_cached_chain(CERT *cert) {}
-static int ssl_noop_x509_session_cache_objects(SSL_SESSION *sess) {
- return 1;
+static bool ssl_noop_x509_session_cache_objects(SSL_SESSION *sess) {
+ return true;
}
-static int ssl_noop_x509_session_dup(SSL_SESSION *new_session,
- const SSL_SESSION *session) {
- return 1;
+static bool ssl_noop_x509_session_dup(SSL_SESSION *new_session,
+ const SSL_SESSION *session) {
+ return true;
}
static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
-static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
- SSL_HANDSHAKE *hs,
- uint8_t *out_alert) {
- return 0;
+static bool ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
+ SSL_HANDSHAKE *hs,
+ uint8_t *out_alert) {
+ return false;
}
static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}
-static int ssl_noop_x509_ssl_new(SSL_HANDSHAKE *hs) { return 1; }
+static bool ssl_noop_x509_ssl_new(SSL_HANDSHAKE *hs) { return true; }
static void ssl_noop_x509_ssl_config_free(SSL_CONFIG *cfg) {}
static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL_CONFIG *cfg) {}
-static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
- return 1;
+static bool ssl_noop_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) {
+ return true;
}
-static int ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return 1; }
-static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) { }
+static bool ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return true; }
+static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) {}
static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {}
const SSL_X509_METHOD ssl_noop_x509_method = {
diff --git a/src/third_party/fiat/curve25519_32.h b/src/third_party/fiat/curve25519_32.h
index 820a5c9..5377242 100644
--- a/src/third_party/fiat/curve25519_32.h
+++ b/src/third_party/fiat/curve25519_32.h
@@ -90,7 +90,13 @@
static void fiat_25519_cmovznz_u32(uint32_t* out1, fiat_25519_uint1 arg1, uint32_t arg2, uint32_t arg3) {
fiat_25519_uint1 x1 = (!(!arg1));
uint32_t x2 = ((fiat_25519_int1)(0x0 - x1) & UINT32_C(0xffffffff));
- uint32_t x3 = ((x2 & arg3) | ((~x2) & arg2));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint32_t x3 = ((value_barrier_u32(x2) & arg3) | (value_barrier_u32(~x2) & arg2));
*out1 = x3;
}
diff --git a/src/third_party/fiat/curve25519_64.h b/src/third_party/fiat/curve25519_64.h
index 23bf361..7c31ff9 100644
--- a/src/third_party/fiat/curve25519_64.h
+++ b/src/third_party/fiat/curve25519_64.h
@@ -58,7 +58,13 @@
static void fiat_25519_cmovznz_u64(uint64_t* out1, fiat_25519_uint1 arg1, uint64_t arg2, uint64_t arg3) {
fiat_25519_uint1 x1 = (!(!arg1));
uint64_t x2 = ((fiat_25519_int1)(0x0 - x1) & UINT64_C(0xffffffffffffffff));
- uint64_t x3 = ((x2 & arg3) | ((~x2) & arg2));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint64_t x3 = ((value_barrier_u64(x2) & arg3) | (value_barrier_u64(~x2) & arg2));
*out1 = x3;
}
diff --git a/src/third_party/fiat/p256.c b/src/third_party/fiat/p256.c
index ebc5de6..23ec71f 100644
--- a/src/third_party/fiat/p256.c
+++ b/src/third_party/fiat/p256.c
@@ -321,7 +321,10 @@
limb_t yneq = fe_nz(r);
- if (!xneq && !yneq && z1nz && z2nz) {
+ limb_t is_nontrivial_double = constant_time_is_zero_w(xneq | yneq) &
+ ~constant_time_is_zero_w(z1nz) &
+ ~constant_time_is_zero_w(z2nz);
+ if (is_nontrivial_double) {
point_double(x3, y3, z3, x1, y1, z1);
return;
}
@@ -731,98 +734,6 @@
return (in[i >> 3] >> (i & 7)) & 1;
}
-// Interleaved point multiplication using precomputed point multiples: The
-// small point multiples 0*P, 1*P, ..., 17*P are in p_pre_comp, the scalar
-// in p_scalar, if non-NULL. If g_scalar is non-NULL, we also add this multiple
-// of the generator, using certain (large) precomputed multiples in g_pre_comp.
-// Output point (X, Y, Z) is stored in x_out, y_out, z_out.
-static void batch_mul(fe x_out, fe y_out, fe z_out,
- const uint8_t *p_scalar, const uint8_t *g_scalar,
- const fe p_pre_comp[17][3]) {
- // set nq to the point at infinity
- fe nq[3] = {{0},{0},{0}}, ftmp, tmp[3];
- uint64_t bits;
- uint8_t sign, digit;
-
- // Loop over both scalars msb-to-lsb, interleaving additions of multiples
- // of the generator (two in each of the last 32 rounds) and additions of p
- // (every 5th round).
-
- int skip = 1; // save two point operations in the first round
- size_t i = p_scalar != NULL ? 255 : 31;
- for (;;) {
- // double
- if (!skip) {
- point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
- }
-
- // add multiples of the generator
- if (g_scalar != NULL && i <= 31) {
- // first, look 32 bits upwards
- bits = get_bit(g_scalar, i + 224) << 3;
- bits |= get_bit(g_scalar, i + 160) << 2;
- bits |= get_bit(g_scalar, i + 96) << 1;
- bits |= get_bit(g_scalar, i + 32);
- // select the point to add, in constant time
- select_point(bits, 16, g_pre_comp[1], tmp);
-
- if (!skip) {
- point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */,
- tmp[0], tmp[1], tmp[2]);
- } else {
- fe_copy(nq[0], tmp[0]);
- fe_copy(nq[1], tmp[1]);
- fe_copy(nq[2], tmp[2]);
- skip = 0;
- }
-
- // second, look at the current position
- bits = get_bit(g_scalar, i + 192) << 3;
- bits |= get_bit(g_scalar, i + 128) << 2;
- bits |= get_bit(g_scalar, i + 64) << 1;
- bits |= get_bit(g_scalar, i);
- // select the point to add, in constant time
- select_point(bits, 16, g_pre_comp[0], tmp);
- point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */, tmp[0],
- tmp[1], tmp[2]);
- }
-
- // do other additions every 5 doublings
- if (p_scalar != NULL && i % 5 == 0) {
- bits = get_bit(p_scalar, i + 4) << 5;
- bits |= get_bit(p_scalar, i + 3) << 4;
- bits |= get_bit(p_scalar, i + 2) << 3;
- bits |= get_bit(p_scalar, i + 1) << 2;
- bits |= get_bit(p_scalar, i) << 1;
- bits |= get_bit(p_scalar, i - 1);
- ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
-
- // select the point to add or subtract, in constant time.
- select_point(digit, 17, p_pre_comp, tmp);
- fe_opp(ftmp, tmp[1]); // (X, -Y, Z) is the negative point.
- fe_cmovznz(tmp[1], sign, tmp[1], ftmp);
-
- if (!skip) {
- point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 0 /* mixed */,
- tmp[0], tmp[1], tmp[2]);
- } else {
- fe_copy(nq[0], tmp[0]);
- fe_copy(nq[1], tmp[1]);
- fe_copy(nq[2], tmp[2]);
- skip = 0;
- }
- }
-
- if (i == 0) {
- break;
- }
- --i;
- }
- fe_copy(x_out, nq[0]);
- fe_copy(y_out, nq[1]);
- fe_copy(z_out, nq[2]);
-}
-
// OPENSSL EC_METHOD FUNCTIONS
// Takes the Jacobian coordinates (X, Y, Z) of a point and returns (X', Y') =
@@ -890,45 +801,116 @@
fe_to_generic(&r->Z, z);
}
-static void ec_GFp_nistp256_points_mul(const EC_GROUP *group, EC_RAW_POINT *r,
- const EC_SCALAR *g_scalar,
- const EC_RAW_POINT *p,
- const EC_SCALAR *p_scalar) {
+static void ec_GFp_nistp256_point_mul(const EC_GROUP *group, EC_RAW_POINT *r,
+ const EC_RAW_POINT *p,
+ const EC_SCALAR *scalar) {
fe p_pre_comp[17][3];
- fe x_out, y_out, z_out;
+ OPENSSL_memset(&p_pre_comp, 0, sizeof(p_pre_comp));
+ // Precompute multiples.
+ fe_from_generic(p_pre_comp[1][0], &p->X);
+ fe_from_generic(p_pre_comp[1][1], &p->Y);
+ fe_from_generic(p_pre_comp[1][2], &p->Z);
+ for (size_t j = 2; j <= 16; ++j) {
+ if (j & 1) {
+ point_add(p_pre_comp[j][0], p_pre_comp[j][1], p_pre_comp[j][2],
+ p_pre_comp[1][0], p_pre_comp[1][1], p_pre_comp[1][2], 0,
+ p_pre_comp[j - 1][0], p_pre_comp[j - 1][1],
+ p_pre_comp[j - 1][2]);
+ } else {
+ point_double(p_pre_comp[j][0], p_pre_comp[j][1], p_pre_comp[j][2],
+ p_pre_comp[j / 2][0], p_pre_comp[j / 2][1],
+ p_pre_comp[j / 2][2]);
+ }
+ }
- if (p != NULL && p_scalar != NULL) {
- // We treat NULL scalars as 0, and NULL points as points at infinity, i.e.,
- // they contribute nothing to the linear combination.
- OPENSSL_memset(&p_pre_comp, 0, sizeof(p_pre_comp));
- // Precompute multiples.
- fe_from_generic(p_pre_comp[1][0], &p->X);
- fe_from_generic(p_pre_comp[1][1], &p->Y);
- fe_from_generic(p_pre_comp[1][2], &p->Z);
- for (size_t j = 2; j <= 16; ++j) {
- if (j & 1) {
- point_add(p_pre_comp[j][0], p_pre_comp[j][1],
- p_pre_comp[j][2], p_pre_comp[1][0],
- p_pre_comp[1][1], p_pre_comp[1][2],
- 0,
- p_pre_comp[j - 1][0], p_pre_comp[j - 1][1],
- p_pre_comp[j - 1][2]);
+ // Set nq to the point at infinity.
+ fe nq[3] = {{0}, {0}, {0}}, ftmp, tmp[3];
+
+ // Loop over |scalar| msb-to-lsb, incorporating |p_pre_comp| every 5th round.
+ int skip = 1; // Save two point operations in the first round.
+ for (size_t i = 255; i < 256; i--) {
+ // double
+ if (!skip) {
+ point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+ }
+
+ // do other additions every 5 doublings
+ if (i % 5 == 0) {
+ uint64_t bits = get_bit(scalar->bytes, i + 4) << 5;
+ bits |= get_bit(scalar->bytes, i + 3) << 4;
+ bits |= get_bit(scalar->bytes, i + 2) << 3;
+ bits |= get_bit(scalar->bytes, i + 1) << 2;
+ bits |= get_bit(scalar->bytes, i) << 1;
+ bits |= get_bit(scalar->bytes, i - 1);
+ uint8_t sign, digit;
+ ec_GFp_nistp_recode_scalar_bits(&sign, &digit, bits);
+
+ // select the point to add or subtract, in constant time.
+ select_point(digit, 17, (const fe(*)[3])p_pre_comp, tmp);
+ fe_opp(ftmp, tmp[1]); // (X, -Y, Z) is the negative point.
+ fe_cmovznz(tmp[1], sign, tmp[1], ftmp);
+
+ if (!skip) {
+ point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 0 /* mixed */,
+ tmp[0], tmp[1], tmp[2]);
} else {
- point_double(p_pre_comp[j][0], p_pre_comp[j][1],
- p_pre_comp[j][2], p_pre_comp[j / 2][0],
- p_pre_comp[j / 2][1], p_pre_comp[j / 2][2]);
+ fe_copy(nq[0], tmp[0]);
+ fe_copy(nq[1], tmp[1]);
+ fe_copy(nq[2], tmp[2]);
+ skip = 0;
}
}
}
- batch_mul(x_out, y_out, z_out,
- (p != NULL && p_scalar != NULL) ? p_scalar->bytes : NULL,
- g_scalar != NULL ? g_scalar->bytes : NULL,
- (const fe (*) [3])p_pre_comp);
+ fe_to_generic(&r->X, nq[0]);
+ fe_to_generic(&r->Y, nq[1]);
+ fe_to_generic(&r->Z, nq[2]);
+}
- fe_to_generic(&r->X, x_out);
- fe_to_generic(&r->Y, y_out);
- fe_to_generic(&r->Z, z_out);
+static void ec_GFp_nistp256_point_mul_base(const EC_GROUP *group,
+ EC_RAW_POINT *r,
+ const EC_SCALAR *scalar) {
+ // Set nq to the point at infinity.
+ fe nq[3] = {{0}, {0}, {0}}, tmp[3];
+
+ int skip = 1; // Save two point operations in the first round.
+ for (size_t i = 31; i < 32; i--) {
+ if (!skip) {
+ point_double(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2]);
+ }
+
+ // First, look 32 bits upwards.
+ uint64_t bits = get_bit(scalar->bytes, i + 224) << 3;
+ bits |= get_bit(scalar->bytes, i + 160) << 2;
+ bits |= get_bit(scalar->bytes, i + 96) << 1;
+ bits |= get_bit(scalar->bytes, i + 32);
+ // Select the point to add, in constant time.
+ select_point(bits, 16, g_pre_comp[1], tmp);
+
+ if (!skip) {
+ point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */, tmp[0],
+ tmp[1], tmp[2]);
+ } else {
+ fe_copy(nq[0], tmp[0]);
+ fe_copy(nq[1], tmp[1]);
+ fe_copy(nq[2], tmp[2]);
+ skip = 0;
+ }
+
+ // Second, look at the current position.
+ bits = get_bit(scalar->bytes, i + 192) << 3;
+ bits |= get_bit(scalar->bytes, i + 128) << 2;
+ bits |= get_bit(scalar->bytes, i + 64) << 1;
+ bits |= get_bit(scalar->bytes, i);
+ // Select the point to add, in constant time.
+ select_point(bits, 16, g_pre_comp[0], tmp);
+ point_add(nq[0], nq[1], nq[2], nq[0], nq[1], nq[2], 1 /* mixed */, tmp[0],
+ tmp[1], tmp[2]);
+ }
+
+ fe_to_generic(&r->X, nq[0]);
+ fe_to_generic(&r->Y, nq[1]);
+ fe_to_generic(&r->Z, nq[2]);
}
static void ec_GFp_nistp256_point_mul_public(const EC_GROUP *group,
@@ -1066,7 +1048,8 @@
ec_GFp_nistp256_point_get_affine_coordinates;
out->add = ec_GFp_nistp256_add;
out->dbl = ec_GFp_nistp256_dbl;
- out->mul = ec_GFp_nistp256_points_mul;
+ out->mul = ec_GFp_nistp256_point_mul;
+ out->mul_base = ec_GFp_nistp256_point_mul_base;
out->mul_public = ec_GFp_nistp256_point_mul_public;
out->felem_mul = ec_GFp_mont_felem_mul;
out->felem_sqr = ec_GFp_mont_felem_sqr;
diff --git a/src/third_party/fiat/p256_32.h b/src/third_party/fiat/p256_32.h
index faaa0b0..638eb5d 100644
--- a/src/third_party/fiat/p256_32.h
+++ b/src/third_party/fiat/p256_32.h
@@ -77,7 +77,13 @@
static void fiat_p256_cmovznz_u32(uint32_t* out1, fiat_p256_uint1 arg1, uint32_t arg2, uint32_t arg3) {
fiat_p256_uint1 x1 = (!(!arg1));
uint32_t x2 = ((fiat_p256_int1)(0x0 - x1) & UINT32_C(0xffffffff));
- uint32_t x3 = ((x2 & arg3) | ((~x2) & arg2));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint32_t x3 = ((value_barrier_u32(x2) & arg3) | (value_barrier_u32(~x2) & arg2));
*out1 = x3;
}
diff --git a/src/third_party/fiat/p256_64.h b/src/third_party/fiat/p256_64.h
index 8e449c6..7d97e0a 100644
--- a/src/third_party/fiat/p256_64.h
+++ b/src/third_party/fiat/p256_64.h
@@ -79,7 +79,13 @@
static void fiat_p256_cmovznz_u64(uint64_t* out1, fiat_p256_uint1 arg1, uint64_t arg2, uint64_t arg3) {
fiat_p256_uint1 x1 = (!(!arg1));
uint64_t x2 = ((fiat_p256_int1)(0x0 - x1) & UINT64_C(0xffffffffffffffff));
- uint64_t x3 = ((x2 & arg3) | ((~x2) & arg2));
+ // Note this line has been patched from the synthesized code to add value
+ // barriers.
+ //
+ // Clang recognizes this pattern as a select. While it usually transforms it
+ // to a cmov, it sometimes further transforms it into a branch, which we do
+ // not want.
+ uint64_t x3 = ((value_barrier_u64(x2) & arg3) | (value_barrier_u64(~x2) & arg2));
*out1 = x3;
}
diff --git a/src/third_party/sike/LICENSE b/src/third_party/sike/LICENSE
new file mode 100644
index 0000000..5cf7c8d
--- /dev/null
+++ b/src/third_party/sike/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE
diff --git a/src/third_party/sike/asm/fp-armv8.pl b/src/third_party/sike/asm/fp-armv8.pl
new file mode 100644
index 0000000..ce19d80
--- /dev/null
+++ b/src/third_party/sike/asm/fp-armv8.pl
@@ -0,0 +1,915 @@
+#! /usr/bin/env perl
+#
+# April 2019
+#
+# Abstract: field arithmetic in aarch64 assembly for SIDH/p434
+
+$flavour = shift;
+$output = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../../crypto/perlasm/arm-xlate.pl" and -f $xlate) or
+die "can't locate arm-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$PREFIX="sike";
+
+$code.=<<___;
+.section .rodata
+
+# p434 x 2
+.Lp434x2:
+ .quad 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF
+ .quad 0xFB82ECF5C5FFFFFF, 0xF78CB8F062B15D47
+ .quad 0xD9F8BFAD038A40AC, 0x0004683E4E2EE688
+
+# p434 + 1
+.Lp434p1:
+ .quad 0xFDC1767AE3000000, 0x7BC65C783158AEA3
+ .quad 0x6CFC5FD681C52056, 0x0002341F27177344
+
+.text
+___
+
+# Computes C0-C2 = A0 * (B0-B1)
+# Inputs remain intact
+sub mul64x128 {
+ my ($A0,$B0,$B1,$C0,$C1,$C2,$T0,$T1)=@_;
+ my $body=<<___;
+ mul $T1, $A0, $B0
+ umulh $B0, $A0, $B0
+ adds $C0, $C0, $C2
+ adc $C1, $C1, xzr
+
+ mul $T0, $A0, $B1
+ umulh $B1, $A0, $B1
+ adds $C0, $C0, $T1
+ adcs $C1, $C1, $B0
+ adc $C2, xzr, xzr
+
+ adds $C1, $C1, $T0
+ adc $C2, $C2, $B1
+___
+ return $body;
+}
+
+# Computes C0-C4 = A0 * (B0-B3)
+# Inputs remain intact
+sub mul64x256 {
+ my ($A0,$B0,$B1,$B2,$B3,$C0,$C1,$C2,$C3,$C4,$T0,$T1,$T2)=@_;
+ my $body=<<___;
+ mul $C0, $A0, $B0 // C0
+ umulh $T0, $A0, $B0
+
+ mul $C1, $A0, $B1
+ umulh $T1, $A0, $B1
+ adds $C1, $C1, $T0 // C1
+ adc $T0, xzr, xzr
+
+ mul $C2, $A0, $B2
+ umulh $T2, $A0, $B2
+ adds $T1, $T0, $T1
+ adcs $C2, $C2, $T1 // C2
+ adc $T0, xzr, xzr
+
+ mul $C3, $A0, $B3
+ umulh $C4, $A0, $B3
+ adds $T2, $T0, $T2
+ adcs $C3, $C3, $T2 // C3
+ adc $C4, $C4, xzr // C4
+___
+ return $body;
+}
+
+# Computes C0-C4 = (A0-A1) * (B0-B3)
+# Inputs remain intact
+sub mul128x256 {
+ my ($A0,$A1,$B0,$B1,$B2,$B3,$C0,$C1,$C2,$C3,$C4,$C5,$T0,$T1,$T2,$T3)=@_;
+ my $body=<<___;
+ mul $C0, $A0, $B0 // C0
+ umulh $C3, $A0, $B0
+
+ mul $C1, $A0, $B1
+ umulh $C2, $A0, $B1
+
+ mul $T0, $A1, $B0
+ umulh $T1, $A1, $B0
+ adds $C1, $C1, $C3
+ adc $C2, $C2, xzr
+
+ mul $T2, $A0, $B2
+ umulh $T3, $A0, $B2
+ adds $C1, $C1, $T0 // C1
+ adcs $C2, $C2, $T1
+ adc $C3, xzr, xzr
+
+ mul $T0, $A1, $B1
+ umulh $T1, $A1, $B1
+ adds $C2, $C2, $T2
+ adcs $C3, $C3, $T3
+ adc $C4, xzr, xzr
+
+ mul $T2, $A0, $B3
+ umulh $T3, $A0, $B3
+ adds $C2, $C2, $T0 // C2
+ adcs $C3, $C3, $T1
+ adc $C4, $C4, xzr
+
+ mul $T0, $A1, $B2
+ umulh $T1, $A1, $B2
+ adds $C3, $C3, $T2
+ adcs $C4, $C4, $T3
+ adc $C5, xzr, xzr
+
+ mul $T2, $A1, $B3
+ umulh $T3, $A1, $B3
+ adds $C3, $C3, $T0 // C3
+ adcs $C4, $C4, $T1
+ adc $C5, $C5, xzr
+ adds $C4, $C4, $T2 // C4
+ adc $C5, $C5, $T3 // C5
+
+___
+ return $body;
+}
+
+# Computes C0-C5 = (A0-A2) * (B0-B2)
+# Inputs remain intact
+sub mul192 {
+ my ($A0,$A1,$A2,$B0,$B1,$B2,$C0,$C1,$C2,$C3,$C4,$C5,$T0,$T1,$T2,$T3)=@_;
+ my $body=<<___;
+
+ // A0 * B0
+ mul $C0, $A0, $B0 // C0
+ umulh $C3, $A0, $B0
+
+ // A0 * B1
+ mul $C1, $A0, $B1
+ umulh $C2, $A0, $B1
+
+ // A1 * B0
+ mul $T0, $A1, $B0
+ umulh $T1, $A1, $B0
+ adds $C1, $C1, $C3
+ adc $C2, $C2, xzr
+
+ // A0 * B2
+ mul $T2, $A0, $B2
+ umulh $T3, $A0, $B2
+ adds $C1, $C1, $T0 // C1
+ adcs $C2, $C2, $T1
+ adc $C3, xzr, xzr
+
+ // A2 * B0
+ mul $T0, $A2, $B0
+ umulh $C4, $A2, $B0
+ adds $C2, $C2, $T2
+ adcs $C3, $C3, $C4
+ adc $C4, xzr, xzr
+
+ // A1 * B1
+ mul $T2, $A1, $B1
+ umulh $T1, $A1, $B1
+ adds $C2, $C2, $T0
+ adcs $C3, $C3, $T3
+ adc $C4, $C4, xzr
+
+ // A1 * B2
+ mul $T0, $A1, $B2
+ umulh $T3, $A1, $B2
+ adds $C2, $C2, $T2 // C2
+ adcs $C3, $C3, $T1
+ adc $C4, $C4, xzr
+
+ // A2 * B1
+ mul $T2, $A2, $B1
+ umulh $T1, $A2, $B1
+ adds $C3, $C3, $T0
+ adcs $C4, $C4, $T3
+ adc $C5, xzr, xzr
+
+ // A2 * B2
+ mul $T0, $A2, $B2
+ umulh $T3, $A2, $B2
+ adds $C3, $C3, $T2 // C3
+ adcs $C4, $C4, $T1
+ adc $C5, $C5, xzr
+
+ adds $C4, $C4, $T0 // C4
+ adc $C5, $C5, $T3 // C5
+___
+ return $body;
+}
+sub mul256_karatsuba {
+ my ($M,$A0,$A1,$A2,$A3,$B0,$B1,$B2,$B3,$C0,$C1,$C2,$C3,$C4,$C5,$C6,$C7,$T0,$T1)=@_;
+ # (AH+AL) x (BH+BL), low part
+ my $mul_low=&mul64x128($A1, $C6, $T1, $C3, $C4, $C5, $C7, $A0);
+ # AL x BL
+ my $mul_albl=&mul64x128($A1, $B0, $B1, $C1, $T1, $C7, $C6, $A0);
+ # AH x BH
+ my $mul_ahbh=&mul64x128($A3, $B2, $B3, $A1, $C6, $B0, $B1, $A2);
+ my $body=<<___;
+ // A0-A1 <- AH + AL, T0 <- mask
+ adds $A0, $A0, $A2
+ adcs $A1, $A1, $A3
+ adc $T0, xzr, xzr
+
+ // C6, T1 <- BH + BL, C7 <- mask
+ adds $C6, $B0, $B2
+ adcs $T1, $B1, $B3
+ adc $C7, xzr, xzr
+
+ // C0-C1 <- masked (BH + BL)
+ sub $C2, xzr, $T0
+ sub $C3, xzr, $C7
+ and $C0, $C6, $C2
+ and $C1, $T1, $C2
+
+ // C4-C5 <- masked (AH + AL), T0 <- combined carry
+ and $C4, $A0, $C3
+ and $C5, $A1, $C3
+ mul $C2, $A0, $C6
+ mul $C3, $A0, $T1
+ and $T0, $T0, $C7
+
+ // C0-C1, T0 <- (AH+AL) x (BH+BL), part 1
+ adds $C0, $C4, $C0
+ umulh $C4, $A0, $T1
+ adcs $C1, $C5, $C1
+ umulh $C5, $A0, $C6
+ adc $T0, $T0, xzr
+
+ // C2-C5 <- (AH+AL) x (BH+BL), low part
+ $mul_low
+ ldp $A0, $A1, [$M,#0]
+
+ // C2-C5, T0 <- (AH+AL) x (BH+BL), final part
+ adds $C4, $C0, $C4
+ umulh $C7, $A0, $B0
+ umulh $T1, $A0, $B1
+ adcs $C5, $C1, $C5
+ mul $C0, $A0, $B0
+ mul $C1, $A0, $B1
+ adc $T0, $T0, xzr
+
+ // C0-C1, T1, C7 <- AL x BL
+ $mul_albl
+
+ // C2-C5, T0 <- (AH+AL) x (BH+BL) - ALxBL
+ mul $A0, $A2, $B2
+ umulh $B0, $A2, $B2
+ subs $C2, $C2, $C0
+ sbcs $C3, $C3, $C1
+ sbcs $C4, $C4, $T1
+ mul $A1, $A2, $B3
+ umulh $C6, $A2, $B3
+ sbcs $C5, $C5, $C7
+ sbc $T0, $T0, xzr
+
+ // A0, A1, C6, B0 <- AH x BH
+ $mul_ahbh
+
+ // C2-C5, T0 <- (AH+AL) x (BH+BL) - ALxBL - AHxBH
+ subs $C2, $C2, $A0
+ sbcs $C3, $C3, $A1
+ sbcs $C4, $C4, $C6
+ sbcs $C5, $C5, $B0
+ sbc $T0, $T0, xzr
+
+ adds $C2, $C2, $T1
+ adcs $C3, $C3, $C7
+ adcs $C4, $C4, $A0
+ adcs $C5, $C5, $A1
+ adcs $C6, $T0, $C6
+ adc $C7, $B0, xzr
+___
+ return $body;
+}
+
+# 512-bit integer multiplication using Karatsuba (two levels),
+# Comba (lower level).
+# Operation: c [x2] = a [x0] * b [x1]
+sub mul {
+ # (AH+AL) x (BH+BL), low part
+ my $mul_kc_low=&mul256_karatsuba(
+ "x2", # M0
+ "x3","x4","x5","x6", # A0-A3
+ "x10","x11","x12","x13", # B0-B3
+ "x8","x9","x19","x20","x21","x22","x23","x24", # C0-C7
+ "x25","x26"); # TMP
+ # AL x BL
+ my $mul_albl=&mul256_karatsuba(
+ "x0", # M0f
+ "x3","x4","x5","x6", # A0-A3
+ "x10","x11","x12","x13", # B0-B3
+ "x21","x22","x23","x24","x25","x26","x27","x28",# C0-C7
+ "x8","x9"); # TMP
+ # AH x BH
+ my $mul_ahbh=&mul192(
+ "x3","x4","x5", # A0-A2
+ "x10","x11","x12", # B0-B2
+ "x21","x22","x23","x24","x25","x26", # C0-C5
+ "x8","x9","x27","x28"); # TMP
+
+ my $body=<<___;
+ .global ${PREFIX}_mpmul
+ .align 4
+ ${PREFIX}_mpmul:
+ stp x29, x30, [sp,#-96]!
+ add x29, sp, #0
+ stp x19, x20, [sp,#16]
+ stp x21, x22, [sp,#32]
+ stp x23, x24, [sp,#48]
+ stp x25, x26, [sp,#64]
+ stp x27, x28, [sp,#80]
+
+ ldp x3, x4, [x0]
+ ldp x5, x6, [x0,#16]
+ ldp x7, x8, [x0,#32]
+ ldr x9, [x0,#48]
+ ldp x10, x11, [x1,#0]
+ ldp x12, x13, [x1,#16]
+ ldp x14, x15, [x1,#32]
+ ldr x16, [x1,#48]
+
+ // x3-x7 <- AH + AL, x7 <- carry
+ adds x3, x3, x7
+ adcs x4, x4, x8
+ adcs x5, x5, x9
+ adcs x6, x6, xzr
+ adc x7, xzr, xzr
+
+ // x10-x13 <- BH + BL, x8 <- carry
+ adds x10, x10, x14
+ adcs x11, x11, x15
+ adcs x12, x12, x16
+ adcs x13, x13, xzr
+ adc x8, xzr, xzr
+
+ // x9 <- combined carry
+ and x9, x7, x8
+ // x7-x8 <- mask
+ sub x7, xzr, x7
+ sub x8, xzr, x8
+
+ // x15-x19 <- masked (BH + BL)
+ and x14, x10, x7
+ and x15, x11, x7
+ and x16, x12, x7
+ and x17, x13, x7
+
+ // x20-x23 <- masked (AH + AL)
+ and x20, x3, x8
+ and x21, x4, x8
+ and x22, x5, x8
+ and x23, x6, x8
+
+ // x15-x19, x7 <- masked (AH+AL) + masked (BH+BL), step 1
+ adds x14, x14, x20
+ adcs x15, x15, x21
+ adcs x16, x16, x22
+ adcs x17, x17, x23
+ adc x7, x9, xzr
+
+ // x8-x9,x19,x20-x24 <- (AH+AL) x (BH+BL), low part
+ stp x3, x4, [x2,#0]
+ $mul_kc_low
+
+ // x15-x19, x7 <- (AH+AL) x (BH+BL), final step
+ adds x14, x14, x21
+ adcs x15, x15, x22
+ adcs x16, x16, x23
+ adcs x17, x17, x24
+ adc x7, x7, xzr
+
+ // Load AL
+ ldp x3, x4, [x0]
+ ldp x5, x6, [x0,#16]
+ // Load BL
+ ldp x10, x11, [x1,#0]
+ ldp x12, x13, [x1,#16]
+
+ // Temporarily store x8 in x2
+ stp x8, x9, [x2,#0]
+ // x21-x28 <- AL x BL
+ $mul_albl
+ // Restore x8
+ ldp x8, x9, [x2,#0]
+
+ // x8-x10,x20,x15-x17,x19 <- maskd (AH+AL) x (BH+BL) - ALxBL
+ subs x8, x8, x21
+ sbcs x9, x9, x22
+ sbcs x19, x19, x23
+ sbcs x20, x20, x24
+ sbcs x14, x14, x25
+ sbcs x15, x15, x26
+ sbcs x16, x16, x27
+ sbcs x17, x17, x28
+ sbc x7, x7, xzr
+
+ // Store ALxBL, low
+ stp x21, x22, [x2]
+ stp x23, x24, [x2,#16]
+
+ // Load AH
+ ldp x3, x4, [x0,#32]
+ ldr x5, [x0,#48]
+ // Load BH
+ ldp x10, x11, [x1,#32]
+ ldr x12, [x1,#48]
+
+ adds x8, x8, x25
+ adcs x9, x9, x26
+ adcs x19, x19, x27
+ adcs x20, x20, x28
+ adc x1, xzr, xzr
+
+ add x0, x0, #32
+ // Temporarily store x8,x9 in x2
+ stp x8,x9, [x2,#32]
+ // x21-x28 <- AH x BH
+ $mul_ahbh
+ // Restore x8,x9
+ ldp x8,x9, [x2,#32]
+
+ neg x1, x1
+
+ // x8-x9,x19,x20,x14-x17 <- (AH+AL) x (BH+BL) - ALxBL - AHxBH
+ subs x8, x8, x21
+ sbcs x9, x9, x22
+ sbcs x19, x19, x23
+ sbcs x20, x20, x24
+ sbcs x14, x14, x25
+ sbcs x15, x15, x26
+ sbcs x16, x16, xzr
+ sbcs x17, x17, xzr
+ sbc x7, x7, xzr
+
+ // Store (AH+AL) x (BH+BL) - ALxBL - AHxBH, low
+ stp x8, x9, [x2,#32]
+ stp x19, x20, [x2,#48]
+
+ adds x1, x1, #1
+ adcs x14, x14, x21
+ adcs x15, x15, x22
+ adcs x16, x16, x23
+ adcs x17, x17, x24
+ adcs x25, x7, x25
+ adc x26, x26, xzr
+
+ stp x14, x15, [x2,#64]
+ stp x16, x17, [x2,#80]
+ stp x25, x26, [x2,#96]
+
+ ldp x19, x20, [x29,#16]
+ ldp x21, x22, [x29,#32]
+ ldp x23, x24, [x29,#48]
+ ldp x25, x26, [x29,#64]
+ ldp x27, x28, [x29,#80]
+ ldp x29, x30, [sp],#96
+ ret
+___
+ return $body;
+}
+$code.=&mul();
+
+# Montgomery reduction
+# Based on method described in Faz-Hernandez et al. https://eprint.iacr.org/2017/1015
+# Operation: mc [x1] = ma [x0]
+# NOTE: ma=mc is not allowed
+sub rdc {
+ my $mul01=&mul128x256(
+ "x2","x3", # A0-A1
+ "x23","x24","x25","x26", # B0-B3
+ "x4","x5","x6","x7","x8","x9", # C0-C5
+ "x10","x11","x27","x28"); # TMP
+ my $mul23=&mul128x256(
+ "x2","x10", # A0-A1
+ "x23","x24","x25","x26", # B0-B3
+ "x4","x5","x6","x7","x8","x9", # C0-C5
+ "x0","x3","x27","x28"); # TMP
+ my $mul45=&mul128x256(
+ "x11","x12", # A0-A1
+ "x23","x24","x25","x26", # B0-B3
+ "x4","x5","x6","x7","x8","x9", # C0-C5
+ "x10","x3","x27","x28"); # TMP
+ my $mul67=&mul64x256(
+ "x13", # A0
+ "x23","x24","x25","x26", # B0-B3
+ "x4","x5","x6","x7","x8", # C0-C4
+ "x10","x27","x28"); # TMP
+ my $body=<<___;
+ .global ${PREFIX}_fprdc
+ .align 4
+ ${PREFIX}_fprdc:
+ stp x29, x30, [sp, #-96]!
+ add x29, sp, xzr
+ stp x19, x20, [sp,#16]
+ stp x21, x22, [sp,#32]
+ stp x23, x24, [sp,#48]
+ stp x25, x26, [sp,#64]
+ stp x27, x28, [sp,#80]
+
+ ldp x2, x3, [x0,#0] // a[0-1]
+
+ // Load the prime constant
+ adrp x26, :pg_hi21:.Lp434p1
+ add x26, x26, :lo12:.Lp434p1
+ ldp x23, x24, [x26, #0x0]
+ ldp x25, x26, [x26,#0x10]
+
+ // a[0-1] * p434+1
+ $mul01
+
+ ldp x10, x11, [x0, #0x18]
+ ldp x12, x13, [x0, #0x28]
+ ldp x14, x15, [x0, #0x38]
+ ldp x16, x17, [x0, #0x48]
+ ldp x19, x20, [x0, #0x58]
+ ldr x21, [x0, #0x68]
+
+ adds x10, x10, x4
+ adcs x11, x11, x5
+ adcs x12, x12, x6
+ adcs x13, x13, x7
+ adcs x14, x14, x8
+ adcs x15, x15, x9
+ adcs x22, x16, xzr
+ adcs x17, x17, xzr
+ adcs x19, x19, xzr
+ adcs x20, x20, xzr
+ adc x21, x21, xzr
+
+ ldr x2, [x0,#0x10] // a[2]
+ // a[2-3] * p434+1
+ $mul23
+
+ adds x12, x12, x4
+ adcs x13, x13, x5
+ adcs x14, x14, x6
+ adcs x15, x15, x7
+ adcs x16, x22, x8
+ adcs x17, x17, x9
+ adcs x22, x19, xzr
+ adcs x20, x20, xzr
+ adc x21, x21, xzr
+
+ $mul45
+ adds x14, x14, x4
+ adcs x15, x15, x5
+ adcs x16, x16, x6
+ adcs x17, x17, x7
+ adcs x19, x22, x8
+ adcs x20, x20, x9
+ adc x22, x21, xzr
+
+ stp x14, x15, [x1, #0x0] // C0, C1
+
+ $mul67
+ adds x16, x16, x4
+ adcs x17, x17, x5
+ adcs x19, x19, x6
+ adcs x20, x20, x7
+ adc x21, x22, x8
+
+ str x16, [x1, #0x10]
+ stp x17, x19, [x1, #0x18]
+ stp x20, x21, [x1, #0x28]
+
+ ldp x19, x20, [x29,#16]
+ ldp x21, x22, [x29,#32]
+ ldp x23, x24, [x29,#48]
+ ldp x25, x26, [x29,#64]
+ ldp x27, x28, [x29,#80]
+ ldp x29, x30, [sp],#96
+ ret
+___
+}
+$code.=&rdc();
+
+# Field addition
+# Operation: c [x2] = a [x0] + b [x1]
+$code.=<<___;
+ .global ${PREFIX}_fpadd
+ .align 4
+ ${PREFIX}_fpadd:
+ stp x29,x30, [sp,#-16]!
+ add x29, sp, #0
+
+ ldp x3, x4, [x0,#0]
+ ldp x5, x6, [x0,#16]
+ ldp x7, x8, [x0,#32]
+ ldr x9, [x0,#48]
+ ldp x11, x12, [x1,#0]
+ ldp x13, x14, [x1,#16]
+ ldp x15, x16, [x1,#32]
+ ldr x17, [x1,#48]
+
+ // Add a + b
+ adds x3, x3, x11
+ adcs x4, x4, x12
+ adcs x5, x5, x13
+ adcs x6, x6, x14
+ adcs x7, x7, x15
+ adcs x8, x8, x16
+ adc x9, x9, x17
+
+ // Subtract 2xp434
+ adrp x17, :pg_hi21:.Lp434x2
+ add x17, x17, :lo12:.Lp434x2
+ ldp x11, x12, [x17, #0]
+ ldp x13, x14, [x17, #16]
+ ldp x15, x16, [x17, #32]
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x12
+ sbcs x6, x6, x13
+ sbcs x7, x7, x14
+ sbcs x8, x8, x15
+ sbcs x9, x9, x16
+ sbc x0, xzr, xzr // x0 can be reused now
+
+ // Add 2xp434 anded with the mask in x0
+ and x11, x11, x0
+ and x12, x12, x0
+ and x13, x13, x0
+ and x14, x14, x0
+ and x15, x15, x0
+ and x16, x16, x0
+
+ adds x3, x3, x11
+ adcs x4, x4, x12
+ adcs x5, x5, x12
+ adcs x6, x6, x13
+ adcs x7, x7, x14
+ adcs x8, x8, x15
+ adc x9, x9, x16
+
+ stp x3, x4, [x2,#0]
+ stp x5, x6, [x2,#16]
+ stp x7, x8, [x2,#32]
+ str x9, [x2,#48]
+
+ ldp x29, x30, [sp],#16
+ ret
+___
+
+# Field subtraction
+# Operation: c [x2] = a [x0] - b [x1]
+$code.=<<___;
+ .global ${PREFIX}_fpsub
+ .align 4
+ ${PREFIX}_fpsub:
+ stp x29, x30, [sp,#-16]!
+ add x29, sp, #0
+
+ ldp x3, x4, [x0,#0]
+ ldp x5, x6, [x0,#16]
+ ldp x7, x8, [x0,#32]
+ ldr x9, [x0,#48]
+ ldp x11, x12, [x1,#0]
+ ldp x13, x14, [x1,#16]
+ ldp x15, x16, [x1,#32]
+ ldr x17, [x1,#48]
+
+ // Subtract a - b
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ sbcs x7, x7, x15
+ sbcs x8, x8, x16
+ sbcs x9, x9, x17
+ sbc x0, xzr, xzr
+
+ // Add 2xp434 anded with the mask in x0
+ adrp x17, :pg_hi21:.Lp434x2
+ add x17, x17, :lo12:.Lp434x2
+
+ // First half
+ ldp x11, x12, [x17, #0]
+ ldp x13, x14, [x17, #16]
+ ldp x15, x16, [x17, #32]
+
+ // Add 2xp434 anded with the mask in x0
+ and x11, x11, x0
+ and x12, x12, x0
+ and x13, x13, x0
+ and x14, x14, x0
+ and x15, x15, x0
+ and x16, x16, x0
+
+ adds x3, x3, x11
+ adcs x4, x4, x12
+ adcs x5, x5, x12
+ adcs x6, x6, x13
+ adcs x7, x7, x14
+ adcs x8, x8, x15
+ adc x9, x9, x16
+
+ stp x3, x4, [x2,#0]
+ stp x5, x6, [x2,#16]
+ stp x7, x8, [x2,#32]
+ str x9, [x2,#48]
+
+ ldp x29, x30, [sp],#16
+ ret
+___
+
+# 434-bit multiprecision addition
+# Operation: c [x2] = a [x0] + b [x1]
+$code.=<<___;
+ .global ${PREFIX}_mpadd_asm
+ .align 4
+ ${PREFIX}_mpadd_asm:
+ stp x29, x30, [sp,#-16]!
+ add x29, sp, #0
+
+ ldp x3, x4, [x0,#0]
+ ldp x5, x6, [x0,#16]
+ ldp x7, x8, [x0,#32]
+ ldr x9, [x0,#48]
+ ldp x11, x12, [x1,#0]
+ ldp x13, x14, [x1,#16]
+ ldp x15, x16, [x1,#32]
+ ldr x17, [x1,#48]
+
+ adds x3, x3, x11
+ adcs x4, x4, x12
+ adcs x5, x5, x13
+ adcs x6, x6, x14
+ adcs x7, x7, x15
+ adcs x8, x8, x16
+ adc x9, x9, x17
+
+ stp x3, x4, [x2,#0]
+ stp x5, x6, [x2,#16]
+ stp x7, x8, [x2,#32]
+ str x9, [x2,#48]
+
+ ldp x29, x30, [sp],#16
+ ret
+___
+
+# 2x434-bit multiprecision subtraction
+# Operation: c [x2] = a [x0] - b [x1].
+# Returns borrow mask
+$code.=<<___;
+ .global ${PREFIX}_mpsubx2_asm
+ .align 4
+ ${PREFIX}_mpsubx2_asm:
+ stp x29, x30, [sp,#-16]!
+ add x29, sp, #0
+
+ ldp x3, x4, [x0,#0]
+ ldp x5, x6, [x0,#16]
+ ldp x11, x12, [x1,#0]
+ ldp x13, x14, [x1,#16]
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ ldp x7, x8, [x0,#32]
+ ldp x9, x10, [x0,#48]
+ ldp x11, x12, [x1,#32]
+ ldp x13, x14, [x1,#48]
+ sbcs x7, x7, x11
+ sbcs x8, x8, x12
+ sbcs x9, x9, x13
+ sbcs x10, x10, x14
+
+ stp x3, x4, [x2,#0]
+ stp x5, x6, [x2,#16]
+ stp x7, x8, [x2,#32]
+ stp x9, x10, [x2,#48]
+
+ ldp x3, x4, [x0,#64]
+ ldp x5, x6, [x0,#80]
+ ldp x11, x12, [x1,#64]
+ ldp x13, x14, [x1,#80]
+ sbcs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ ldp x7, x8, [x0,#96]
+ ldp x11, x12, [x1,#96]
+ sbcs x7, x7, x11
+ sbcs x8, x8, x12
+ sbc x0, xzr, xzr
+
+ stp x3, x4, [x2,#64]
+ stp x5, x6, [x2,#80]
+ stp x7, x8, [x2,#96]
+
+ ldp x29, x30, [sp],#16
+ ret
+___
+
+
+# Double 2x434-bit multiprecision subtraction
+# Operation: c [x2] = c [x2] - a [x0] - b [x1]
+$code.=<<___;
+ .global ${PREFIX}_mpdblsubx2_asm
+ .align 4
+ ${PREFIX}_mpdblsubx2_asm:
+ stp x29, x30, [sp, #-16]!
+ add x29, sp, #0
+
+ ldp x3, x4, [x2, #0]
+ ldp x5, x6, [x2,#16]
+ ldp x7, x8, [x2,#32]
+
+ ldp x11, x12, [x0, #0]
+ ldp x13, x14, [x0,#16]
+ ldp x15, x16, [x0,#32]
+
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ sbcs x7, x7, x15
+ sbcs x8, x8, x16
+
+ // x9 stores carry
+ adc x9, xzr, xzr
+
+ ldp x11, x12, [x1, #0]
+ ldp x13, x14, [x1,#16]
+ ldp x15, x16, [x1,#32]
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ sbcs x7, x7, x15
+ sbcs x8, x8, x16
+ adc x9, x9, xzr
+
+ stp x3, x4, [x2, #0]
+ stp x5, x6, [x2,#16]
+ stp x7, x8, [x2,#32]
+
+ ldp x3, x4, [x2,#48]
+ ldp x5, x6, [x2,#64]
+ ldp x7, x8, [x2,#80]
+
+ ldp x11, x12, [x0,#48]
+ ldp x13, x14, [x0,#64]
+ ldp x15, x16, [x0,#80]
+
+ // x9 = 2 - x9
+ neg x9, x9
+ add x9, x9, #2
+
+ subs x3, x3, x9
+ sbcs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ sbcs x7, x7, x15
+ sbcs x8, x8, x16
+ adc x9, xzr, xzr
+
+ ldp x11, x12, [x1,#48]
+ ldp x13, x14, [x1,#64]
+ ldp x15, x16, [x1,#80]
+ subs x3, x3, x11
+ sbcs x4, x4, x12
+ sbcs x5, x5, x13
+ sbcs x6, x6, x14
+ sbcs x7, x7, x15
+ sbcs x8, x8, x16
+ adc x9, x9, xzr
+
+ stp x3, x4, [x2,#48]
+ stp x5, x6, [x2,#64]
+ stp x7, x8, [x2,#80]
+
+ ldp x3, x4, [x2,#96]
+ ldp x11, x12, [x0,#96]
+ ldp x13, x14, [x1,#96]
+
+ // x9 = 2 - x9
+ neg x9, x9
+ add x9, x9, #2
+
+ subs x3, x3, x9
+ sbcs x3, x3, x11
+ sbcs x4, x4, x12
+ subs x3, x3, x13
+ sbc x4, x4, x14
+ stp x3, x4, [x2,#96]
+
+ ldp x29, x30, [sp],#16
+ ret
+___
+
+foreach (split("\n",$code)) {
+ s/\`([^\`]*)\`/eval($1)/ge;
+ print $_,"\n";
+}
+
+close STDOUT;
diff --git a/src/third_party/sike/asm/fp-x86_64.pl b/src/third_party/sike/asm/fp-x86_64.pl
new file mode 100755
index 0000000..cffde1a
--- /dev/null
+++ b/src/third_party/sike/asm/fp-x86_64.pl
@@ -0,0 +1,1626 @@
+#! /usr/bin/env perl
+#
+# April 2019
+#
+# Abstract: field arithmetic in x64 assembly for SIDH/p434
+
+$flavour = shift;
+$output = shift;
+if ($flavour =~ /\./) { $output = $flavour; undef $flavour; }
+
+$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
+( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or
+( $xlate="${dir}../../../crypto/perlasm/x86_64-xlate.pl" and -f $xlate) or
+die "can't locate x86_64-xlate.pl";
+
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
+*STDOUT=*OUT;
+
+$PREFIX="sike";
+$bmi2_adx = 1;
+
+$code.=<<___;
+.text
+
+# p434 x 2
+.Lp434x2:
+.quad 0xFFFFFFFFFFFFFFFE
+.quad 0xFFFFFFFFFFFFFFFF
+.quad 0xFB82ECF5C5FFFFFF
+.quad 0xF78CB8F062B15D47
+.quad 0xD9F8BFAD038A40AC
+.quad 0x0004683E4E2EE688
+
+# p434 + 1
+.Lp434p1:
+.quad 0xFDC1767AE3000000
+.quad 0x7BC65C783158AEA3
+.quad 0x6CFC5FD681C52056
+.quad 0x0002341F27177344
+
+.extern OPENSSL_ia32cap_P
+.hidden OPENSSL_ia32cap_P
+___
+
+# Jump to alternative implemenatation provided as an
+# argument in case CPU supports ADOX/ADCX and MULX instructions.
+sub alt_impl {
+ $jmp_func = shift;
+
+ $body=<<___;
+ lea OPENSSL_ia32cap_P(%rip), %rcx
+ mov 8(%rcx), %rcx
+ and \$0x80100, %ecx
+ cmp \$0x80100, %ecx
+ je $jmp_func
+
+___
+ return $body
+}
+
+# Performs schoolbook multiplication of 2 192-bit numbers. Uses
+# MULX instruction. Result is stored in 192 bits pointed by $DST.
+sub mul192 {
+ my ($idxM0,$M0,$idxM1,$M1,$idxDST,$DST,$T0,$T1,$T2,$T3,$T4,$T5,$T6)=@_;
+ my ($ML0,$ML8,$ML16)=map("$idxM0+$_($M0)",(0,8,16));
+ my ($MR0,$MR8,$MR16)=map("$idxM1+$_($M1)",(0,8,16));
+ my ($D0,$D1,$D2,$D3,$D4,$D5)=map("$idxDST+$_($DST)",(0,8,16,24,32,40));
+
+ $body=<<___;
+ mov $ML0, %rdx
+ mulx $MR0, $T1, $T0 # T0:T1 = A0*B0
+ mov $T1, $D0 # DST0
+ mulx $MR8, $T2, $T1 # T1:T2 = A0*B1
+ xor %rax, %rax
+ adox $T2, $T0
+ mulx $MR16,$T3, $T2 # T2:T3 = A0*B2
+ adox $T3, $T1
+
+ mov $ML8, %rdx
+ mulx $MR0, $T4, $T3 # T3:T4 = A1*B0
+ adox %rax, $T2
+ xor %rax, %rax
+
+ mulx $MR8, $T6, $T5 # T6:T7 = A1*B1
+ adox $T0, $T4
+ mov $T4, $D1 # DST1
+ adcx $T6, $T3
+
+ mulx $MR16,$T0, $T6 # T6:T0 = A1*B2
+ adox $T1, $T3
+ adcx $T0, $T5
+ adcx %rax, $T6
+ adox $T2, $T5
+
+ mov $ML16,%rdx
+ mulx $MR0, $T0, $T1 # T1:T0 = A2*B0
+ adox %rax, $T6
+ xor %rax, %rax
+
+ mulx $MR8, $T2, $T4 # T4:T2 = A2*B1
+ adox $T3, $T0
+ mov $T0, $D2 # DST2
+ adcx $T5, $T1
+
+ mulx $MR16,$T3, $T0 # T0:T3 = A2*B2
+ adcx $T6, $T4
+ adcx %rax, $T0
+ adox $T2, $T1
+ adox $T4, $T3
+ adox %rax, $T0
+ mov $T1, $D3 # DST3
+ mov $T3, $D4 # DST4
+ mov $T0, $D5 # DST5
+
+___
+ return $body;
+}
+
+# Performs schoolbook multiplication of 2 256-bit numbers. Uses
+# MULX instruction. Result is stored in 256 bits pointed by $DST.
+sub mul256 {
+ my ($idxM0,$M0,$idxM1,$M1,$idxDST,$DST,$T0,$T1,$T2,$T3,$T4,$T5,$T6,$T7,$T8,$T9)=@_;
+ my ($ML0,$ML8,$ML16,$ML24)=map("$idxM0+$_($M0)",(0,8,16,24));
+ my ($MR0,$MR8,$MR16,$MR24)=map("$idxM1+$_($M1)",(0,8,16,24));
+ my ($D0,$D1,$D2,$D3,$D4,$D5,$D6,$D7)=map("$idxDST+$_($DST)",(0,8,16,24,32,40,48,56));
+
+ $body=<<___;
+ mov $ML0, %rdx
+ mulx $MR0, $T1, $T0 # T0:T1 = A0*B0
+ mov $T1, $D0 # DST0_final
+ mulx $MR8, $T2, $T1 # T1:T2 = A0*B1
+ xor %rax, %rax
+ adox $T2, $T0
+ mulx $MR16,$T3, $T2 # T2:T3 = A0*B2
+ adox $T3, $T1
+ mulx $MR24,$T4, $T3 # T3:T4 = A0*B3
+ adox $T4, $T2
+
+ mov $ML8, %rdx
+ mulx $MR0, $T4, $T5 # T5:T4 = A1*B0
+ adox %rax, $T3
+ xor %rax, %rax
+ mulx $MR8, $T7, $T6 # T6:T7 = A1*B1
+ adox $T0, $T4
+ mov $T4, $D1 # DST1_final
+ adcx $T7, $T5
+ mulx $MR16,$T8, $T7 # T7:T8 = A1*B2
+ adcx $T8, $T6
+ adox $T1, $T5
+ mulx $MR24,$T9, $T8 # T8:T9 = A1*B3
+ adcx $T9, $T7
+ adcx %rax, $T8
+ adox $T2, $T6
+
+ mov $ML16,%rdx
+ mulx $MR0, $T0, $T1 # T1:T0 = A2*B0
+ adox $T3, $T7
+ adox %rax, $T8
+ xor %rax, %rax
+ mulx $MR8, $T3, $T2 # T2:T3 = A2*B1
+ adox $T5, $T0
+ mov $T0, $D2 # DST2_final
+ adcx $T3, $T1
+ mulx $MR16,$T4, $T3 # T3:T4 = A2*B2
+ adcx $T4, $T2
+ adox $T6, $T1
+ mulx $MR24,$T9, $T4 # T3:T4 = A2*B3
+ adcx $T9, $T3
+ adcx %rax, $T4
+
+ adox $T7, $T2
+ adox $T8, $T3
+ adox %rax, $T4
+
+ mov $ML24,%rdx
+ mulx $MR0, $T0, $T5 # T5:T0 = A3*B0
+ xor %rax, %rax
+ mulx $MR8, $T7, $T6 # T6:T7 = A3*B1
+ adcx $T7, $T5
+ adox $T0, $T1
+ mulx $MR16, $T8, $T7 # T7:T8 = A3*B2
+ adcx $T8, $T6
+ adox $T5, $T2
+ mulx $MR24, $T9, $T8 # T8:T9 = A3*B3
+ adcx $T9, $T7
+ adcx %rax, $T8
+ adox $T6, $T3
+ adox $T7, $T4
+ adox %rax, $T8
+ mov $T1, $D3 # DST3_final
+ mov $T2, $D4 # DST4_final
+ mov $T3, $D5 # DST5_final
+ mov $T4, $D6 # DST6_final
+ mov $T8, $D7 # DST7_final
+
+___
+ return $body;
+}
+
+# Performs schoolbook multiplication of 64-bit with 256-bit
+# number.
+sub mul64x256 {
+ my ($idxM0,$M0,$M1,$T0,$T1,$T2,$T3,$T4,$T5)=@_;
+ my $body.=<<___;
+ mov $idxM0($M0), $T5
+
+ xor $T2, $T2
+ mov 0+$M1, %rax
+ mul $T5
+ mov %rax, $T0 # C0
+ mov %rdx, $T1
+
+ xor $T3, $T3
+ mov 8+$M1, %rax
+ mul $T5
+ add %rax, $T1 # C1
+ adc %rdx, $T2
+
+ xor $T4, $T4
+ mov 16+$M1, %rax
+ mul $T5
+ add %rax, $T2 # C2
+ adc %rdx, $T3
+
+ mov 24+$M1, %rax
+ mul $T5
+ add %rax, $T3 # C3
+ adc %rdx, $T4 # C4
+___
+ return $body;
+}
+
+# Performs schoolbook multiplication of 64-bit with 256-bit
+# number. Uses MULX and ADOX instructions.
+sub mulx64x256 {
+ my ($idxM0,$M0,$M1,$T0,$T1,$T2,$T3,$T4,$T5)=@_;
+ my $body.=<<___;
+ xor %rax, %rax
+ mov $idxM0($M0), %rdx
+ mulx 0+$M1, $T0, $T1 # T0 <- C0
+ mulx 8+$M1, $T4, $T2
+ mulx 16+$M1, $T5, $T3
+
+ adox $T4, $T1 # T1 <- C1
+ adox $T5, $T2 # T2 <- C2
+
+ mulx 24+$M1, $T5, $T4
+ adox $T5, $T3 # T3 <- C3
+ adox %rax, $T4 # T4 <- C4
+___
+ return $body;
+}
+
+# Performs schoolbook multiplication of 128-bit with 256-bit
+# number. Destroys RAX and RDX
+sub mul128x256 {
+ my ($idxMA,$MA,$MB,$C0,$C1,$C2,$C3,$C4,$C5,$T0,$T1)=@_;
+ my ($MA0,$MA8)=map("$idxMA+$_($MA)", (0,8));
+ my $body.=<<___;
+ # A0 x B0
+ mov $MA0, $T0
+ mov 0+$MB, %rax
+ mul $T0
+ xor $C2, $C2
+ mov %rax, $C0 # c0
+ mov %rdx, $C1
+
+ # A0 x B1
+ mov 8+$MB, %rax
+ mul $T0
+ xor $C3, $C3
+ add %rax, $C1
+ adc %rdx, $C2
+
+ # A1 x B0
+ mov $MA8, $T1
+ mov 0+$MB, %rax
+ mul $T1
+ add %rax, $C1
+ adc %rdx, $C2
+ adc \$0x0, $C3
+
+ # A0 x B2
+ xor $C4, $C4
+ mov 16+$MB, %rax
+ mul $T0
+ add %rax, $C2
+ adc %rdx, $C3
+ adc \$0x0, $C4
+
+ # A1 x B1
+ mov 8+$MB, %rax
+ mul $T1
+ add %rax, $C2 # c2
+ adc %rdx, $C3
+ adc \$0x0, $C4
+
+ # A0 x B3
+ mov 24+$MB, %rax
+ mul $T0
+ xor $C5, $C5
+ add %rax, $C3
+ adc %rdx, $C4
+ adc \$0x0, $C5
+
+ # A1 x B2
+ mov 16+$MB, %rax
+ mul $T1
+ add %rax, $C3 # c3
+ adc %rdx, $C4
+ adc \$0x0, $C5
+
+ # A1 x B3
+ mov 24+$MB, %rax
+ mul $T1
+ add %rax, $C4
+ adc %rdx, $C5
+
+___
+ return $body;
+}
+
+# Performs schoolbook multiplication of 128-bit with 256-bit
+# number. Uses MULX, ADOX, ADCX instruction.
+sub mulx128x256 {
+ my ($idxM0,$M0,$M1,$T0,$T1,$T2,$T3,$T4,$T5,$T6)=@_;
+ my ($MUL0,$MUL8)=map("$idxM0+$_($M0)", (0,8));
+ my $body.=<<___;
+ xor %rax, %rax
+ mov $MUL0, %rdx
+ mulx 0+$M1, $T0, $T1 # T0 <- C0
+ mulx 8+$M1, $T4, $T2
+ mulx 16+$M1, $T5, $T3
+
+ adox $T4, $T1 # T1: interm1
+ adox $T5, $T2 # T2: interm2
+
+ mulx 24+$M1, $T5, $T4
+ adox $T5, $T3 # T3: interm3
+ adox %rax, $T4 # T4: interm4
+
+ xor %rax, %rax
+ mov $MUL8, %rdx
+ mulx 0+$M1, $T5, $T6
+ adcx $T5, $T1 # T1 <- C1
+ adcx $T6, $T2
+
+ mulx 8+$M1, $T6, $T5
+ adcx $T5, $T3
+ adox $T6, $T2 # T2 <- C2
+
+ mulx 16+$M1, $T6, $T5
+ adcx $T5, $T4
+ adox $T6, $T3 # T3 <- C3
+
+ mulx 24+$M1, $T6, $T5
+ adcx %rax, $T5
+ adox $T6, $T4 # T4 <- C4
+ adox %rax, $T5 # T5 <- C5
+___
+ return $body;
+}
+
+# Compute z = x + y (mod p).
+# Operation: c [rdx] = a [rdi] + b [rsi]
+$code.=<<___;
+.globl ${PREFIX}_fpadd
+.type ${PREFIX}_fpadd,\@function,3
+${PREFIX}_fpadd:
+.cfi_startproc
+ push %r12
+.cfi_adjust_cfa_offset 8
+.cfi_offset r12, -16
+ push %r13
+.cfi_adjust_cfa_offset 8
+.cfi_offset r13, -24
+ push %r14
+.cfi_adjust_cfa_offset 8
+.cfi_offset r14, -32
+
+ xor %rax, %rax
+
+ mov 0x0(%rdi), %r8
+ add 0x0(%rsi), %r8
+ mov 0x8(%rdi), %r9
+ adc 0x8(%rsi), %r9
+ mov 0x10(%rdi), %r10
+ adc 0x10(%rsi), %r10
+ mov 0x18(%rdi), %r11
+ adc 0x18(%rsi), %r11
+ mov 0x20(%rdi), %r12
+ adc 0x20(%rsi), %r12
+ mov 0x28(%rdi), %r13
+ adc 0x28(%rsi), %r13
+ mov 0x30(%rdi), %r14
+ adc 0x30(%rsi), %r14
+
+ mov .Lp434x2(%rip), %rcx
+ sub %rcx, %r8
+ mov 0x8+.Lp434x2(%rip), %rcx
+ sbb %rcx, %r9
+ sbb %rcx, %r10
+ mov 0x10+.Lp434x2(%rip), %rcx
+ sbb %rcx, %r11
+ mov 0x18+.Lp434x2(%rip), %rcx
+ sbb %rcx, %r12
+ mov 0x20+.Lp434x2(%rip), %rcx
+ sbb %rcx, %r13
+ mov 0x28+.Lp434x2(%rip), %rcx
+ sbb %rcx, %r14
+
+ sbb \$0, %rax
+
+ mov .Lp434x2(%rip), %rdi
+ and %rax, %rdi
+ mov 0x8+.Lp434x2(%rip), %rsi
+ and %rax, %rsi
+ mov 0x10+.Lp434x2(%rip), %rcx
+ and %rax, %rcx
+
+ add %rdi, %r8
+ mov %r8, 0x0(%rdx)
+ adc %rsi, %r9
+ mov %r9, 0x8(%rdx)
+ adc %rsi, %r10
+ mov %r10, 0x10(%rdx)
+ adc %rcx, %r11
+ mov %r11, 0x18(%rdx)
+
+ setc %cl
+ mov 0x18+.Lp434x2(%rip), %r8
+ and %rax, %r8
+ mov 0x20+.Lp434x2(%rip), %r9
+ and %rax, %r9
+ mov 0x28+.Lp434x2(%rip), %r10
+ and %rax, %r10
+ bt \$0, %rcx
+
+ adc %r8, %r12
+ mov %r12, 0x20(%rdx)
+ adc %r9, %r13
+ mov %r13, 0x28(%rdx)
+ adc %r10, %r14
+ mov %r14, 0x30(%rdx)
+
+ pop %r14
+.cfi_adjust_cfa_offset -8
+ pop %r13
+.cfi_adjust_cfa_offset -8
+ pop %r12
+.cfi_adjust_cfa_offset -8
+ ret
+.cfi_endproc
+___
+
+# Loads data to XMM0 and XMM1 and
+# conditionaly swaps depending on XMM3
+sub cswap_block16() {
+ my $idx = shift;
+ $idx *= 16;
+ ("
+ movdqu $idx(%rdi), %xmm0
+ movdqu $idx(%rsi), %xmm1
+ movdqa %xmm1, %xmm2
+ pxor %xmm0, %xmm2
+ pand %xmm3, %xmm2
+ pxor %xmm2, %xmm0
+ pxor %xmm2, %xmm1
+ movdqu %xmm0, $idx(%rdi)
+ movdqu %xmm1, $idx(%rsi)
+ ");
+}
+
+# Conditionally swaps bits in x and y in constant time.
+# mask indicates bits to be swapped (set bits are swapped)
+# Operation: [rdi] <-> [rsi] if rdx==1
+sub sike_cswap {
+ # P[0] with Q[0]
+ foreach ( 0.. 6){$BLOCKS.=eval "&cswap_block16($_)";}
+ # P[1] with Q[1]
+ foreach ( 7..13){$BLOCKS.=eval "&cswap_block16($_)";}
+
+ my $body =<<___;
+.globl ${PREFIX}_cswap_asm
+.type ${PREFIX}_cswap_asm,\@function,3
+${PREFIX}_cswap_asm:
+ # Fill XMM3. After this step first half of XMM3 is
+ # just zeros and second half is whatever in RDX
+ mov %rdx, %xmm3
+
+ # Copy lower double word everywhere else. So that
+ # XMM3=RDX|RDX. As RDX has either all bits set
+ # or non result will be that XMM3 has also either
+ # all bits set or non of them. 68 = 01000100b
+ pshufd \$68, %xmm3, %xmm3
+ $BLOCKS
+ ret
+___
+ ($body)
+}
+$code.=&sike_cswap();
+
+
+# Field subtraction
+# Operation: c [rdx] = a [rdi] - b [rsi]
+$code.=<<___;
+.globl ${PREFIX}_fpsub
+.type ${PREFIX}_fpsub,\@function,3
+${PREFIX}_fpsub:
+.cfi_startproc
+ push %r12
+.cfi_adjust_cfa_offset 8
+.cfi_offset r12, -16
+ push %r13
+.cfi_adjust_cfa_offset 8
+.cfi_offset r13, -24
+ push %r14
+.cfi_adjust_cfa_offset 8
+.cfi_offset r14, -32
+
+ xor %rax, %rax
+
+ mov 0x0(%rdi), %r8
+ sub 0x0(%rsi), %r8
+ mov 0x8(%rdi), %r9
+ sbb 0x8(%rsi), %r9
+ mov 0x10(%rdi), %r10
+ sbb 0x10(%rsi), %r10
+ mov 0x18(%rdi), %r11
+ sbb 0x18(%rsi), %r11
+ mov 0x20(%rdi), %r12
+ sbb 0x20(%rsi), %r12
+ mov 0x28(%rdi), %r13
+ sbb 0x28(%rsi), %r13
+ mov 0x30(%rdi), %r14
+ sbb 0x30(%rsi), %r14
+
+ sbb \$0x0, %rax
+
+ mov .Lp434x2(%rip), %rdi
+ and %rax, %rdi
+ mov 0x08+.Lp434x2(%rip), %rsi
+ and %rax, %rsi
+ mov 0x10+.Lp434x2(%rip), %rcx
+ and %rax, %rcx
+
+ add %rdi, %r8
+ mov %r8, 0x0(%rdx)
+ adc %rsi, %r9
+ mov %r9, 0x8(%rdx)
+ adc %rsi, %r10
+ mov %r10, 0x10(%rdx)
+ adc %rcx, %r11
+ mov %r11, 0x18(%rdx)
+
+ setc %cl
+ mov 0x18+.Lp434x2(%rip), %r8
+ and %rax, %r8
+ mov 0x20+.Lp434x2(%rip), %r9
+ and %rax, %r9
+ mov 0x28+.Lp434x2(%rip), %r10
+ and %rax, %r10
+ bt \$0x0, %rcx
+
+ adc %r8, %r12
+ adc %r9, %r13
+ adc %r10, %r14
+ mov %r12, 0x20(%rdx)
+ mov %r13, 0x28(%rdx)
+ mov %r14, 0x30(%rdx)
+
+ pop %r14
+.cfi_adjust_cfa_offset -8
+ pop %r13
+.cfi_adjust_cfa_offset -8
+ pop %r12
+.cfi_adjust_cfa_offset -8
+ ret
+.cfi_endproc
+___
+
+# 434-bit multiprecision addition
+# Operation: c [rdx] = a [rdi] + b [rsi]
+$code.=<<___;
+.globl ${PREFIX}_mpadd_asm
+.type ${PREFIX}_mpadd_asm,\@function,3
+${PREFIX}_mpadd_asm:
+.cfi_startproc
+ mov 0x0(%rdi), %r8;
+ mov 0x8(%rdi), %r9
+ mov 0x10(%rdi), %r10
+ mov 0x18(%rdi), %r11
+ mov 0x20(%rdi), %rcx
+ add 0x0(%rsi), %r8
+ adc 0x8(%rsi), %r9
+ adc 0x10(%rsi), %r10
+ adc 0x18(%rsi), %r11
+ adc 0x20(%rsi), %rcx
+ mov %r8, 0x0(%rdx)
+ mov %r9, 0x8(%rdx)
+ mov %r10, 0x10(%rdx)
+ mov %r11, 0x18(%rdx)
+ mov %rcx, 0x20(%rdx)
+
+ mov 0x28(%rdi), %r8
+ mov 0x30(%rdi), %r9
+ adc 0x28(%rsi), %r8
+ adc 0x30(%rsi), %r9
+ mov %r8, 0x28(%rdx)
+ mov %r9, 0x30(%rdx)
+ ret
+.cfi_endproc
+___
+
+# 2x434-bit multiprecision subtraction
+# Operation: c [rdx] = a [rdi] - b [rsi].
+# Returns borrow mask
+$code.=<<___;
+.globl ${PREFIX}_mpsubx2_asm
+.type ${PREFIX}_mpsubx2_asm,\@function,3
+${PREFIX}_mpsubx2_asm:
+.cfi_startproc
+ xor %rax, %rax
+
+ mov 0x0(%rdi), %r8
+ mov 0x8(%rdi), %r9
+ mov 0x10(%rdi), %r10
+ mov 0x18(%rdi), %r11
+ mov 0x20(%rdi), %rcx
+ sub 0x0(%rsi), %r8
+ sbb 0x8(%rsi), %r9
+ sbb 0x10(%rsi), %r10
+ sbb 0x18(%rsi), %r11
+ sbb 0x20(%rsi), %rcx
+ mov %r8, 0x0(%rdx)
+ mov %r9, 0x8(%rdx)
+ mov %r10, 0x10(%rdx)
+ mov %r11, 0x18(%rdx)
+ mov %rcx, 0x20(%rdx)
+
+ mov 0x28(%rdi), %r8
+ mov 0x30(%rdi), %r9
+ mov 0x38(%rdi), %r10
+ mov 0x40(%rdi), %r11
+ mov 0x48(%rdi), %rcx
+ sbb 0x28(%rsi), %r8
+ sbb 0x30(%rsi), %r9
+ sbb 0x38(%rsi), %r10
+ sbb 0x40(%rsi), %r11
+ sbb 0x48(%rsi), %rcx
+ mov %r8, 0x28(%rdx)
+ mov %r9, 0x30(%rdx)
+ mov %r10, 0x38(%rdx)
+ mov %r11, 0x40(%rdx)
+ mov %rcx, 0x48(%rdx)
+
+ mov 0x50(%rdi), %r8
+ mov 0x58(%rdi), %r9
+ mov 0x60(%rdi), %r10
+ mov 0x68(%rdi), %r11
+ sbb 0x50(%rsi), %r8
+ sbb 0x58(%rsi), %r9
+ sbb 0x60(%rsi), %r10
+ sbb 0x68(%rsi), %r11
+ sbb \$0x0, %rax
+ mov %r8, 0x50(%rdx)
+ mov %r9, 0x58(%rdx)
+ mov %r10, 0x60(%rdx)
+ mov %r11, 0x68(%rdx)
+ ret
+.cfi_endproc
+___
+
+
+# Double 2x434-bit multiprecision subtraction
+# Operation: c [rdx] = c [rdx] - a [rdi] - b [rsi]
+$code.=<<___;
+.globl ${PREFIX}_mpdblsubx2_asm
+.type ${PREFIX}_mpdblsubx2_asm,\@function,3
+${PREFIX}_mpdblsubx2_asm:
+.cfi_startproc
+ push %r12
+.cfi_adjust_cfa_offset 8
+.cfi_offset r12, -16
+ push %r13
+.cfi_adjust_cfa_offset 8
+.cfi_offset r13, -24
+
+ xor %rax, %rax
+
+ # ci:low = c:low - a:low
+ mov 0x0(%rdx), %r8
+ mov 0x8(%rdx), %r9
+ mov 0x10(%rdx), %r10
+ mov 0x18(%rdx), %r11
+ mov 0x20(%rdx), %r12
+ mov 0x28(%rdx), %r13
+ mov 0x30(%rdx), %rcx
+ sub 0x0(%rdi), %r8
+ sbb 0x8(%rdi), %r9
+ sbb 0x10(%rdi), %r10
+ sbb 0x18(%rdi), %r11
+ sbb 0x20(%rdi), %r12
+ sbb 0x28(%rdi), %r13
+ sbb 0x30(%rdi), %rcx
+ adc \$0x0, %rax
+
+ # c:low = ci:low - b:low
+ sub 0x0(%rsi), %r8
+ sbb 0x8(%rsi), %r9
+ sbb 0x10(%rsi), %r10
+ sbb 0x18(%rsi), %r11
+ sbb 0x20(%rsi), %r12
+ sbb 0x28(%rsi), %r13
+ sbb 0x30(%rsi), %rcx
+ adc \$0x0, %rax
+
+ # store c:low
+ mov %r8, 0x0(%rdx)
+ mov %r9, 0x8(%rdx)
+ mov %r10, 0x10(%rdx)
+ mov %r11, 0x18(%rdx)
+ mov %r12, 0x20(%rdx)
+ mov %r13, 0x28(%rdx)
+ mov %rcx, 0x30(%rdx)
+
+ # ci:high = c:high - a:high
+ mov 0x38(%rdx), %r8
+ mov 0x40(%rdx), %r9
+ mov 0x48(%rdx), %r10
+ mov 0x50(%rdx), %r11
+ mov 0x58(%rdx), %r12
+ mov 0x60(%rdx), %r13
+ mov 0x68(%rdx), %rcx
+
+ sub %rax, %r8
+ sbb 0x38(%rdi), %r8
+ sbb 0x40(%rdi), %r9
+ sbb 0x48(%rdi), %r10
+ sbb 0x50(%rdi), %r11
+ sbb 0x58(%rdi), %r12
+ sbb 0x60(%rdi), %r13
+ sbb 0x68(%rdi), %rcx
+
+ # c:high = ci:high - b:high
+ sub 0x38(%rsi), %r8
+ sbb 0x40(%rsi), %r9
+ sbb 0x48(%rsi), %r10
+ sbb 0x50(%rsi), %r11
+ sbb 0x58(%rsi), %r12
+ sbb 0x60(%rsi), %r13
+ sbb 0x68(%rsi), %rcx
+
+ # store c:high
+ mov %r8, 0x38(%rdx)
+ mov %r9, 0x40(%rdx)
+ mov %r10, 0x48(%rdx)
+ mov %r11, 0x50(%rdx)
+ mov %r12, 0x58(%rdx)
+ mov %r13, 0x60(%rdx)
+ mov %rcx, 0x68(%rdx)
+
+ pop %r13
+.cfi_adjust_cfa_offset -8
+ pop %r12
+.cfi_adjust_cfa_offset -8
+ ret
+.cfi_endproc
+
+___
+
+sub redc_common {
+ my ($mul01, $mul23, $mul45, $mul67)=@_;
+ my $body=<<___;
+ $mul01
+ xor %rcx, %rcx
+ add 0x18(%rdi), %r8
+ adc 0x20(%rdi), %r9
+ adc 0x28(%rdi), %r10
+ adc 0x30(%rdi), %r11
+ adc 0x38(%rdi), %r12
+ adc 0x40(%rdi), %r13
+ adc 0x48(%rdi), %rcx
+ mov %r8, 0x18(%rdi)
+ mov %r9, 0x20(%rdi)
+ mov %r10, 0x28(%rdi)
+ mov %r11, 0x30(%rdi)
+ mov %r12, 0x38(%rdi)
+ mov %r13, 0x40(%rdi)
+ mov %rcx, 0x48(%rdi)
+ mov 0x50(%rdi), %r8
+ mov 0x58(%rdi), %r9
+ mov 0x60(%rdi), %r10
+ mov 0x68(%rdi), %r11
+ adc \$0x0, %r8
+ adc \$0x0, %r9
+ adc \$0x0, %r10
+ adc \$0x0, %r11
+ mov %r8, 0x50(%rdi)
+ mov %r9, 0x58(%rdi)
+ mov %r10, 0x60(%rdi)
+ mov %r11, 0x68(%rdi)
+
+ $mul23
+ xor %rcx, %rcx
+ add 0x28(%rdi), %r8
+ adc 0x30(%rdi), %r9
+ adc 0x38(%rdi), %r10
+ adc 0x40(%rdi), %r11
+ adc 0x48(%rdi), %r12
+ adc 0x50(%rdi), %r13
+ adc 0x58(%rdi), %rcx
+ mov %r8, 0x28(%rdi)
+ mov %r9, 0x30(%rdi)
+ mov %r10, 0x38(%rdi)
+ mov %r11, 0x40(%rdi)
+ mov %r12, 0x48(%rdi)
+ mov %r13, 0x50(%rdi)
+ mov %rcx, 0x58(%rdi)
+ mov 0x60(%rdi), %r8
+ mov 0x68(%rdi), %r9
+ adc \$0x0, %r8
+ adc \$0x0, %r9
+ mov %r8, 0x60(%rdi)
+ mov %r9, 0x68(%rdi)
+
+ $mul45
+ xor %rcx, %rcx
+ add 0x38(%rdi), %r8
+ adc 0x40(%rdi), %r9
+ adc 0x48(%rdi), %r10
+ adc 0x50(%rdi), %r11
+ adc 0x58(%rdi), %r12
+ adc 0x60(%rdi), %r13
+ adc 0x68(%rdi), %rcx
+ mov %r8, 0x0(%rsi) # C0
+ mov %r9, 0x8(%rsi) # C1
+ mov %r10, 0x48(%rdi)
+ mov %r11, 0x50(%rdi)
+ mov %r12, 0x58(%rdi)
+ mov %r13, 0x60(%rdi)
+ mov %rcx, 0x68(%rdi)
+
+ $mul67
+ add 0x48(%rdi), %r8
+ adc 0x50(%rdi), %r9
+ adc 0x58(%rdi), %r10
+ adc 0x60(%rdi), %r11
+ adc 0x68(%rdi), %r12
+ mov %r8, 0x10(%rsi) # C2
+ mov %r9, 0x18(%rsi) # C3
+ mov %r10, 0x20(%rsi) # C4
+ mov %r11, 0x28(%rsi) # C5
+ mov %r12, 0x30(%rsi) # C6
+___
+ return $body;
+}
+
+# Optimized Montgomery reduction for CPUs, based on method described
+# in Faz-Hernandez et al. https://eprint.iacr.org/2017/1015.
+# Operation: c [rsi] = a [rdi]
+# NOTE: a=c is not allowed
+sub sike_rdc {
+ my $jump_redc_bdw=&alt_impl(".Lrdc_bdw") if ($bmi2_adx);
+ # a[0-1] x .Lp434p1 --> result: r8:r13
+ my $mulx1=&mulx128x256( 0,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..13)),"%rcx");
+ # a[2-3] x .Lp434p1 --> result: r8:r13
+ my $mulx2=&mulx128x256(16,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..13)),"%rcx");
+ # a[4-5] x .Lp434p1 --> result: r8:r13
+ my $mulx3=&mulx128x256(32,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..13)),"%rcx");
+ # a[6-7] x .Lp434p1 --> result: r8:r13
+ my $mulx4=&mulx64x256( 48,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..13)));
+
+ # a[0-1] x .Lp434p1 --> result: r8:r13
+ my $mul1=&mul128x256( 0,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..14)),"%rcx");
+ # a[2-3] x .Lp434p1 --> result: r8:r13
+ my $mul2=&mul128x256(16,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..14)),"%rcx");
+ # a[4-5] x .Lp434p1 --> result: r8:r13
+ my $mul3=&mul128x256(32,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..14)),"%rcx");
+ # a[6-7] x .Lp434p1 --> result: r8:r13
+ my $mul4=&mul64x256( 48,"%rdi",".Lp434p1(%rip)",map("%r$_",(8..13)));
+
+ my $redc_mul=&redc_common($mul1, $mul2, $mul3, $mul4);
+ my $redc_bdw=&redc_common($mulx1, $mulx2, $mulx3, $mulx4) if ($bmi2_adx);
+
+ # REDC for Broadwell CPUs
+ my $code=<<___;
+ .Lrdc_bdw:
+ .cfi_startproc
+ # sike_fprdc has already pushed r12--15 by this point.
+ .cfi_adjust_cfa_offset 32
+ .cfi_offset r12, -16
+ .cfi_offset r13, -24
+ .cfi_offset r14, -32
+ .cfi_offset r15, -40
+
+ $redc_bdw
+
+ pop %r15
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r15
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r14
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r13
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r12
+ ret
+ .cfi_endproc
+___
+
+ # REDC for CPUs older than Broadwell
+ $code.=<<___;
+ .globl ${PREFIX}_fprdc
+ .type ${PREFIX}_fprdc,\@function,3
+ ${PREFIX}_fprdc:
+ .cfi_startproc
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r12, -16
+ push %r13
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r13, -24
+ push %r14
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r14, -32
+ push %r15
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r15, -40
+
+ # Jump to optimized implementation if
+ # CPU supports ADCX/ADOX/MULX
+ $jump_redc_bdw
+ # Otherwise use generic implementation
+ $redc_mul
+
+ pop %r15
+ .cfi_adjust_cfa_offset -8
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ ret
+ .cfi_endproc
+___
+ return $code;
+}
+$code.=&sike_rdc();
+
+# 434-bit multiplication using Karatsuba (one level),
+# schoolbook (one level). Uses MULX/ADOX/ADCX instructions
+# available on Broadwell micro-architectures and newer.
+sub mul_bdw {
+ # [rsp] <- (AH+AL) x (BH+BL)
+ my $mul256_low=&mul256(0,"%rsp",32,"%rsp",0,"%rsp",map("%r$_",(8..15)),"%rbx","%rbp");
+ # [rcx] <- AL x BL
+ my $mul256_albl=&mul256(0,"%rdi",0,"%rsi",0,"%rcx",map("%r$_",(8..15)),"%rbx","%rbp");
+ # [rcx+64] <- AH x BH
+ my $mul192_ahbh=&mul192(32,"%rdi",32,"%rsi",64,"%rcx",map("%r$_",(8..14)));
+
+ $body=<<___;
+
+ mov %rdx, %rcx
+ xor %rax, %rax
+
+ # r8-r11 <- AH + AL, rax <- mask
+ mov 0x0(%rdi), %r8
+ mov 0x8(%rdi), %r9
+ mov 0x10(%rdi), %r10
+ mov 0x18(%rdi), %r11
+
+ push %rbx
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset rbx, -48
+ push %rbp
+ .cfi_offset rbp, -56
+ .cfi_adjust_cfa_offset 8
+ sub \$96, %rsp
+ .cfi_adjust_cfa_offset 96
+
+ add 0x20(%rdi), %r8
+ adc 0x28(%rdi), %r9
+ adc 0x30(%rdi), %r10
+ adc \$0x0, %r11
+ sbb \$0x0, %rax
+ mov %r8, 0x0(%rsp)
+ mov %r9, 0x8(%rsp)
+ mov %r10, 0x10(%rsp)
+ mov %r11, 0x18(%rsp)
+
+ # r12-r15 <- BH + BL, rbx <- mask
+ xor %rbx, %rbx
+ mov 0x0(%rsi), %r12
+ mov 0x8(%rsi), %r13
+ mov 0x10(%rsi), %r14
+ mov 0x18(%rsi), %r15
+ add 0x20(%rsi), %r12
+ adc 0x28(%rsi), %r13
+ adc 0x30(%rsi), %r14
+ adc \$0x0, %r15
+ sbb \$0x0, %rbx
+ mov %r12, 0x20(%rsp)
+ mov %r13, 0x28(%rsp)
+ mov %r14, 0x30(%rsp)
+ mov %r15, 0x38(%rsp)
+
+ # r12-r15 <- masked (BH + BL)
+ and %rax, %r12
+ and %rax, %r13
+ and %rax, %r14
+ and %rax, %r15
+
+ # r8-r11 <- masked (AH + AL)
+ and %rbx, %r8
+ and %rbx, %r9
+ and %rbx, %r10
+ and %rbx, %r11
+
+ # r8-r11 <- masked (AH + AL) + masked (BH + BL)
+ add %r12, %r8
+ adc %r13, %r9
+ adc %r14, %r10
+ adc %r15, %r11
+ mov %r8, 0x40(%rsp)
+ mov %r9, 0x48(%rsp)
+ mov %r10, 0x50(%rsp)
+ mov %r11, 0x58(%rsp)
+
+ # [rsp] <- CM = (AH+AL) x (BH+BL)
+ $mul256_low
+ # [rcx] <- CL = AL x BL (Result c0-c3)
+ $mul256_albl
+ # [rcx+64] <- CH = AH x BH
+ $mul192_ahbh
+
+ # r8-r11 <- (AH+AL) x (BH+BL), final step
+ mov 0x40(%rsp), %r8
+ mov 0x48(%rsp), %r9
+ mov 0x50(%rsp), %r10
+ mov 0x58(%rsp), %r11
+
+ mov 0x20(%rsp), %rax
+ add %rax, %r8
+ mov 0x28(%rsp), %rax
+ adc %rax, %r9
+ mov 0x30(%rsp), %rax
+ adc %rax, %r10
+ mov 0x38(%rsp), %rax
+ adc %rax, %r11
+
+ # [rsp], x3-x5 <- (AH+AL) x (BH+BL) - ALxBL
+ mov 0x0(%rsp), %r12
+ mov 0x8(%rsp), %r13
+ mov 0x10(%rsp), %r14
+ mov 0x18(%rsp), %r15
+ sub 0x0(%rcx), %r12
+ sbb 0x8(%rcx), %r13
+ sbb 0x10(%rcx), %r14
+ sbb 0x18(%rcx), %r15
+ sbb 0x20(%rcx), %r8
+ sbb 0x28(%rcx), %r9
+ sbb 0x30(%rcx), %r10
+ sbb 0x38(%rcx), %r11
+
+ # r8-r15 <- (AH+AL) x (BH+BL) - ALxBL - AHxBH
+ sub 0x40(%rcx), %r12
+ sbb 0x48(%rcx), %r13
+ sbb 0x50(%rcx), %r14
+ sbb 0x58(%rcx), %r15
+ sbb 0x60(%rcx), %r8
+ sbb 0x68(%rcx), %r9
+ sbb \$0x0, %r10
+ sbb \$0x0, %r11
+
+ add 0x20(%rcx), %r12
+ mov %r12, 0x20(%rcx) # Result C4-C7
+ adc 0x28(%rcx), %r13
+ mov %r13, 0x28(%rcx)
+ adc 0x30(%rcx), %r14
+ mov %r14, 0x30(%rcx)
+ adc 0x38(%rcx), %r15
+ mov %r15, 0x38(%rcx)
+ adc 0x40(%rcx), %r8
+ mov %r8, 0x40(%rcx) # Result C8-C15
+ adc 0x48(%rcx), %r9
+ mov %r9, 0x48(%rcx)
+ adc 0x50(%rcx), %r10
+ mov %r10, 0x50(%rcx)
+ adc 0x58(%rcx), %r11
+ mov %r11, 0x58(%rcx)
+ mov 0x60(%rcx), %r12
+ adc \$0x0, %r12
+ mov %r12, 0x60(%rcx)
+ mov 0x68(%rcx), %r13
+ adc \$0x0, %r13
+ mov %r13, 0x68(%rcx)
+
+ add \$96, %rsp
+ .cfi_adjust_cfa_offset -96
+ pop %rbp
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value rbp
+ pop %rbx
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value rbx
+___
+ return $body;
+}
+
+# 434-bit multiplication using Karatsuba (one level),
+# schoolbook (one level).
+sub mul {
+ my $code=<<___;
+ mov %rdx, %rcx
+
+ sub \$112, %rsp # Allocating space in stack
+ .cfi_adjust_cfa_offset 112
+
+ # rcx[0-3] <- AH+AL
+ xor %rax, %rax
+ mov 0x20(%rdi), %r8
+ mov 0x28(%rdi), %r9
+ mov 0x30(%rdi), %r10
+ xor %r11, %r11
+ add 0x0(%rdi), %r8
+ adc 0x8(%rdi), %r9
+ adc 0x10(%rdi), %r10
+ adc 0x18(%rdi), %r11
+ # store AH+AL mask
+ sbb \$0, %rax
+ mov %rax, 0x40(%rsp)
+ # store AH+AL in 0-0x18(rcx)
+ mov %r8, 0x0(%rcx)
+ mov %r9, 0x8(%rcx)
+ mov %r10, 0x10(%rcx)
+ mov %r11, 0x18(%rcx)
+
+ # r12-r15 <- BH+BL
+ xor %rdx, %rdx
+ mov 0x20(%rsi), %r12
+ mov 0x28(%rsi), %r13
+ mov 0x30(%rsi), %r14
+ xor %r15, %r15
+ add 0x0(%rsi), %r12
+ adc 0x8(%rsi), %r13
+ adc 0x10(%rsi), %r14
+ adc 0x18(%rsi), %r15
+ sbb \$0x0, %rdx
+ # store BH+BL mask
+ mov %rdx, 0x48(%rsp)
+
+ # (rsp[0-0x38]) <- (AH+AL)*(BH+BL)
+ mov (%rcx), %rax
+ mul %r12
+ mov %rax, (%rsp) # c0
+ mov %rdx, %r8
+
+ xor %r9, %r9
+ mov (%rcx), %rax
+ mul %r13
+ add %rax, %r8
+ adc %rdx, %r9
+
+ xor %r10, %r10
+ mov 0x8(%rcx), %rax
+ mul %r12
+ add %rax, %r8
+ mov %r8, 0x8(%rsp) # c1
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ xor %r8, %r8
+ mov (%rcx), %rax
+ mul %r14
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x10(%rcx), %rax
+ mul %r12
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x8(%rcx), %rax
+ mul %r13
+ add %rax, %r9
+ mov %r9, 0x10(%rsp) # c2
+ adc %rdx, %r10
+ adc \$0x0, %r8
+
+ xor %r9, %r9
+ mov (%rcx),%rax
+ mul %r15
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov 0x18(%rcx), %rax
+ mul %r12
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov 0x8(%rcx), %rax
+ mul %r14
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov 0x10(%rcx), %rax
+ mul %r13
+ add %rax, %r10
+ mov %r10, 0x18(%rsp) # c3
+ adc %rdx, %r8
+ adc \$0x0, %r9
+
+ xor %r10, %r10
+ mov 0x8(%rcx), %rax
+ mul %r15
+ add %rax, %r8
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ mov 0x18(%rcx), %rax
+ mul %r13
+ add %rax, %r8
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ mov 0x10(%rcx), %rax
+ mul %r14
+ add %rax, %r8 # c4
+ mov %r8, 0x20(%rsp)
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ xor %r11, %r11
+ mov 0x10(%rcx), %rax
+ mul %r15
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r11
+
+ mov 0x18(%rcx), %rax
+ mul %r14
+ add %rax, %r9 # c5
+ mov %r9, 0x28(%rsp)
+ adc %rdx, %r10
+ adc \$0x0,%r11
+
+ mov 0x18(%rcx), %rax
+ mul %r15
+ add %rax, %r10 # c6
+ mov %r10, 0x30(%rsp)
+ adc %rdx, %r11 # c7
+ mov %r11, 0x38(%rsp)
+
+ # r12-r15 <- masked (BH + BL)
+ mov 0x40(%rsp), %rax
+ and %rax, %r12
+ and %rax, %r13
+ and %rax, %r14
+ and %rax, %r15
+
+ # r8-r11 <- masked (AH + AL)
+ mov 0x48(%rsp),%rax
+ mov 0x00(rcx), %r8
+ and %rax, %r8
+ mov 0x08(rcx), %r9
+ and %rax, %r9
+ mov 0x10(rcx), %r10
+ and %rax, %r10
+ mov 0x18(rcx), %r11
+ and %rax, %r11
+
+ # r12-r15 <- masked (AH + AL) + masked (BH + BL)
+ add %r8, %r12
+ adc %r9, %r13
+ adc %r10, %r14
+ adc %r11, %r15
+
+ # rsp[0x20-0x38] <- (AH+AL) x (BH+BL) high
+ mov 0x20(%rsp), %rax
+ add %rax, %r12
+ mov 0x28(%rsp), %rax
+ adc %rax, %r13
+ mov 0x30(%rsp), %rax
+ adc %rax, %r14
+ mov 0x38(%rsp), %rax
+ adc %rax, %r15
+ mov %r12, 0x50(%rsp)
+ mov %r13, 0x58(%rsp)
+ mov %r14, 0x60(%rsp)
+ mov %r15, 0x68(%rsp)
+
+ # [rcx] <- CL = AL x BL
+ mov (%rdi), %r11
+ mov (%rsi), %rax
+ mul %r11
+ xor %r9, %r9
+ mov %rax, (%rcx) # c0
+ mov %rdx, %r8
+
+ mov 0x10(%rdi), %r14
+ mov 0x8(%rsi), %rax
+ mul %r11
+ xor %r10, %r10
+ add %rax, %r8
+ adc %rdx, %r9
+
+ mov 0x8(%rdi), %r12
+ mov (%rsi), %rax
+ mul %r12
+ add %rax, %r8
+ mov %r8, 0x8(%rcx) # c1
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ xor %r8, %r8
+ mov 0x10(%rsi), %rax
+ mul %r11
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov (%rsi),%r13
+ mov %r14, %rax
+ mul %r13
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x8(%rsi), %rax
+ mul %r12
+ add %rax, %r9
+ mov %r9, 0x10(%rcx) # c2
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ xor %r9, %r9
+ mov 0x18(%rsi), %rax
+ mul %r11
+ mov 0x18(%rdi), %r15
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov %r15, %rax
+ mul %r13
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov 0x10(%rsi), %rax
+ mul %r12
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ mov 0x8(%rsi), %rax
+ mul %r14
+ add %rax, %r10
+ mov %r10, 0x18(%rcx) # c3
+ adc %rdx, %r8
+ adc \$0x0,%r9
+
+ xor %r10, %r10
+ mov 0x18(%rsi), %rax
+ mul %r12
+ add %rax, %r8
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ mov 0x8(%rsi), %rax
+ mul %r15
+ add %rax, %r8
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ mov 0x10(%rsi), %rax
+ mul %r14
+ add %rax, %r8
+ mov %r8, 0x20(%rcx) # c4
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ xor %r8, %r8
+ mov 0x18(%rsi), %rax
+ mul %r14
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x10(%rsi), %rax
+ mul %r15
+ add %rax, %r9
+ mov %r9, 0x28(%rcx) # c5
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x18(%rsi), %rax
+ mul %r15
+ add %rax, %r10
+ mov %r10, 0x30(%rcx) # c6
+ adc %rdx, %r8
+ mov %r8, 0x38(%rcx) # c7
+
+ # rcx[0x40-0x68] <- AH*BH
+ # multiplies 2 192-bit numbers A,B
+ mov 0x20(%rdi), %r11
+ mov 0x20(%rsi), %rax
+ mul %r11
+ xor %r9, %r9
+ mov %rax, 0x40(%rcx) # c0
+ mov %rdx, %r8
+
+ mov 0x30(%rdi), %r14
+ mov 0x28(%rsi), %rax
+ mul %r11
+ xor %r10, %r10
+ add %rax, %r8
+ adc %rdx, %r9
+
+ mov 0x28(%rdi), %r12
+ mov 0x20(%rsi), %rax
+ mul %r12
+ add %rax, %r8
+ mov %r8, 0x48(%rcx) # c1
+ adc %rdx, %r9
+ adc \$0x0,%r10
+
+ xor %r8, %r8
+ mov 0x30(%rsi), %rax
+ mul %r11
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x20(%rsi), %r13
+ mov %r14, %rax
+ mul %r13
+ add %rax, %r9
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x28(%rsi), %rax
+ mul %r12
+ add %rax, %r9
+ mov %r9, 0x50(%rcx) # c2
+ adc %rdx, %r10
+ adc \$0x0,%r8
+
+ mov 0x30(%rsi), %rax
+ mul %r12
+ xor %r12, %r12
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r12
+
+ mov 0x28(%rsi), %rax
+ mul %r14
+ add %rax, %r10
+ adc %rdx, %r8
+ adc \$0x0,%r12
+ mov %r10, 0x58(%rcx) # c3
+
+ mov 0x30(%rsi), %rax
+ mul %r14
+ add %rax, %r8
+ adc \$0x0,%r12
+ mov %r8, 0x60(%rcx) # c4
+
+ add %r12, %rdx # c5
+
+ # [r8-r15] <- (AH+AL)x(BH+BL) - ALxBL
+ mov 0x0(%rsp), %r8
+ sub 0x0(%rcx), %r8
+ mov 0x8(%rsp), %r9
+ sbb 0x8(%rcx), %r9
+ mov 0x10(%rsp), %r10
+ sbb 0x10(%rcx), %r10
+ mov 0x18(%rsp), %r11
+ sbb 0x18(%rcx), %r11
+ mov 0x50(%rsp), %r12
+ sbb 0x20(%rcx), %r12
+ mov 0x58(%rsp), %r13
+ sbb 0x28(%rcx), %r13
+ mov 0x60(%rsp), %r14
+ sbb 0x30(%rcx), %r14
+ mov 0x68(%rsp), %r15
+ sbb 0x38(%rcx), %r15
+
+ # [r8-r15] <- (AH+AL) x (BH+BL) - ALxBL - AHxBH
+ mov 0x40(%rcx), %rax
+ sub %rax, %r8
+ mov 0x48(%rcx), %rax
+ sbb %rax, %r9
+ mov 0x50(%rcx), %rax
+ sbb %rax, %r10
+ mov 0x58(%rcx), %rax
+ sbb %rax, %r11
+ mov 0x60(%rcx), %rax
+ sbb %rax, %r12
+ sbb %rdx, %r13
+ sbb \$0x0,%r14
+ sbb \$0x0,%r15
+
+ # Final result
+ add 0x20(%rcx), %r8
+ mov %r8, 0x20(%rcx) # Result C4-C7
+ adc 0x28(%rcx), %r9
+ mov %r9, 0x28(%rcx)
+ adc 0x30(%rcx), %r10
+ mov %r10, 0x30(%rcx)
+ adc 0x38(%rcx), %r11
+ mov %r11, 0x38(%rcx)
+ adc 0x40(%rcx), %r12
+ mov %r12, 0x40(%rcx) # Result C8-C13
+ adc 0x48(%rcx), %r13
+ mov %r13, 0x48(%rcx)
+ adc 0x50(%rcx), %r14
+ mov %r14, 0x50(%rcx)
+ adc 0x58(%rcx), %r15
+ mov %r15, 0x58(%rcx)
+ mov 0x60(%rcx), %r12
+ adc \$0x0, %r12
+ mov %r12, 0x60(%rcx)
+ adc \$0x0, %rdx
+ mov %rdx, 0x68(%rcx)
+
+ add \$112, %rsp # Restoring space in stack
+ .cfi_adjust_cfa_offset -112
+___
+
+ return $code;
+}
+
+# Integer multiplication based on Karatsuba method
+# Operation: c [rdx] = a [rdi] * b [rsi]
+# NOTE: a=c or b=c are not allowed
+sub sike_mul {
+ my $jump_mul_bdw=&alt_impl(".Lmul_bdw") if ($bmi2_adx);
+ # MUL for Broadwell CPUs
+ my $mul_bdw=&mul_bdw() if ($bmi2_adx);
+ # MUL for CPUs older than Broadwell
+ my $mul=&mul();
+
+ my $body=<<___;
+ .Lmul_bdw:
+ .cfi_startproc
+ # sike_mpmul has already pushed r12--15 by this point.
+ .cfi_adjust_cfa_offset 32
+ .cfi_offset r12, -16
+ .cfi_offset r13, -24
+ .cfi_offset r14, -32
+ .cfi_offset r15, -40
+
+ $mul_bdw
+
+ pop %r15
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r15
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r14
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r13
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ .cfi_same_value r12
+ ret
+ .cfi_endproc
+
+ .globl ${PREFIX}_mpmul
+ .type ${PREFIX}_mpmul,\@function,3
+ ${PREFIX}_mpmul:
+ .cfi_startproc
+ push %r12
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r12, -16
+ push %r13
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r13, -24
+ push %r14
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r14, -32
+ push %r15
+ .cfi_adjust_cfa_offset 8
+ .cfi_offset r15, -40
+
+ # Jump to optimized implementation if
+ # CPU supports ADCX/ADOX/MULX
+ $jump_mul_bdw
+ # Otherwise use generic implementation
+ $mul
+
+ pop %r15
+ .cfi_adjust_cfa_offset -8
+ pop %r14
+ .cfi_adjust_cfa_offset -8
+ pop %r13
+ .cfi_adjust_cfa_offset -8
+ pop %r12
+ .cfi_adjust_cfa_offset -8
+ ret
+ .cfi_endproc
+
+___
+ return $body;
+}
+
+$code.=&sike_mul();
+
+foreach (split("\n",$code)) {
+ s/\`([^\`]*)\`/eval($1)/ge;
+ print $_,"\n";
+}
+
+close STDOUT;
diff --git a/src/third_party/sike/asm/fp_generic.c b/src/third_party/sike/asm/fp_generic.c
new file mode 100644
index 0000000..991ad1e
--- /dev/null
+++ b/src/third_party/sike/asm/fp_generic.c
@@ -0,0 +1,181 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: portable modular arithmetic for P503
+*********************************************************************************************/
+
+#include <openssl/base.h>
+
+#if defined(OPENSSL_NO_ASM) || \
+ (!defined(OPENSSL_X86_64) && !defined(OPENSSL_AARCH64))
+
+#include "../utils.h"
+#include "../fpx.h"
+
+// Global constants
+extern const struct params_t sike_params;
+
+static void digit_x_digit(const crypto_word_t a, const crypto_word_t b, crypto_word_t* c)
+{ // Digit multiplication, digit * digit -> 2-digit result
+ crypto_word_t al, ah, bl, bh, temp;
+ crypto_word_t albl, albh, ahbl, ahbh, res1, res2, res3, carry;
+ crypto_word_t mask_low = (crypto_word_t)(-1) >> (sizeof(crypto_word_t)*4);
+ crypto_word_t mask_high = (crypto_word_t)(-1) << (sizeof(crypto_word_t)*4);
+
+ al = a & mask_low; // Low part
+ ah = a >> (sizeof(crypto_word_t) * 4); // High part
+ bl = b & mask_low;
+ bh = b >> (sizeof(crypto_word_t) * 4);
+
+ albl = al*bl;
+ albh = al*bh;
+ ahbl = ah*bl;
+ ahbh = ah*bh;
+ c[0] = albl & mask_low; // C00
+
+ res1 = albl >> (sizeof(crypto_word_t) * 4);
+ res2 = ahbl & mask_low;
+ res3 = albh & mask_low;
+ temp = res1 + res2 + res3;
+ carry = temp >> (sizeof(crypto_word_t) * 4);
+ c[0] ^= temp << (sizeof(crypto_word_t) * 4); // C01
+
+ res1 = ahbl >> (sizeof(crypto_word_t) * 4);
+ res2 = albh >> (sizeof(crypto_word_t) * 4);
+ res3 = ahbh & mask_low;
+ temp = res1 + res2 + res3 + carry;
+ c[1] = temp & mask_low; // C10
+ carry = temp & mask_high;
+ c[1] ^= (ahbh & mask_high) + carry; // C11
+}
+
+void sike_fpadd(const felm_t a, const felm_t b, felm_t c)
+{ // Modular addition, c = a+b mod p434.
+ // Inputs: a, b in [0, 2*p434-1]
+ // Output: c in [0, 2*p434-1]
+ unsigned int i, carry = 0;
+ crypto_word_t mask;
+
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ ADDC(carry, a[i], b[i], carry, c[i]);
+ }
+
+ carry = 0;
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ SUBC(carry, c[i], sike_params.prime_x2[i], carry, c[i]);
+ }
+ mask = 0 - (crypto_word_t)carry;
+
+ carry = 0;
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ ADDC(carry, c[i], sike_params.prime_x2[i] & mask, carry, c[i]);
+ }
+}
+
+void sike_fpsub(const felm_t a, const felm_t b, felm_t c)
+{ // Modular subtraction, c = a-b mod p434.
+ // Inputs: a, b in [0, 2*p434-1]
+ // Output: c in [0, 2*p434-1]
+ unsigned int i, borrow = 0;
+ crypto_word_t mask;
+
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ SUBC(borrow, a[i], b[i], borrow, c[i]);
+ }
+ mask = 0 - (crypto_word_t)borrow;
+
+ borrow = 0;
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ ADDC(borrow, c[i], sike_params.prime_x2[i] & mask, borrow, c[i]);
+ }
+}
+
+void sike_mpmul(const felm_t a, const felm_t b, dfelm_t c)
+{ // Multiprecision comba multiply, c = a*b, where lng(a) = lng(b) = NWORDS_FIELD.
+ unsigned int i, j;
+ crypto_word_t t = 0, u = 0, v = 0, UV[2];
+ unsigned int carry = 0;
+
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ for (j = 0; j <= i; j++) {
+ MUL(a[j], b[i-j], UV+1, UV[0]);
+ ADDC(0, UV[0], v, carry, v);
+ ADDC(carry, UV[1], u, carry, u);
+ t += carry;
+ }
+ c[i] = v;
+ v = u;
+ u = t;
+ t = 0;
+ }
+
+ for (i = NWORDS_FIELD; i < 2*NWORDS_FIELD-1; i++) {
+ for (j = i-NWORDS_FIELD+1; j < NWORDS_FIELD; j++) {
+ MUL(a[j], b[i-j], UV+1, UV[0]);
+ ADDC(0, UV[0], v, carry, v);
+ ADDC(carry, UV[1], u, carry, u);
+ t += carry;
+ }
+ c[i] = v;
+ v = u;
+ u = t;
+ t = 0;
+ }
+ c[2*NWORDS_FIELD-1] = v;
+}
+
+void sike_fprdc(felm_t ma, felm_t mc)
+{ // Efficient Montgomery reduction using comba and exploiting the special form of the prime p434.
+ // mc = ma*R^-1 mod p434x2, where R = 2^448.
+ // If ma < 2^448*p434, the output mc is in the range [0, 2*p434-1].
+ // ma is assumed to be in Montgomery representation.
+ unsigned int i, j, carry, count = ZERO_WORDS;
+ crypto_word_t UV[2], t = 0, u = 0, v = 0;
+
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ mc[i] = 0;
+ }
+
+ for (i = 0; i < NWORDS_FIELD; i++) {
+ for (j = 0; j < i; j++) {
+ if (j < (i-ZERO_WORDS+1)) {
+ MUL(mc[j], sike_params.prime_p1[i-j], UV+1, UV[0]);
+ ADDC(0, UV[0], v, carry, v);
+ ADDC(carry, UV[1], u, carry, u);
+ t += carry;
+ }
+ }
+ ADDC(0, v, ma[i], carry, v);
+ ADDC(carry, u, 0, carry, u);
+ t += carry;
+ mc[i] = v;
+ v = u;
+ u = t;
+ t = 0;
+ }
+
+ for (i = NWORDS_FIELD; i < 2*NWORDS_FIELD-1; i++) {
+ if (count > 0) {
+ count -= 1;
+ }
+ for (j = i-NWORDS_FIELD+1; j < NWORDS_FIELD; j++) {
+ if (j < (NWORDS_FIELD-count)) {
+ MUL(mc[j], sike_params.prime_p1[i-j], UV+1, UV[0]);
+ ADDC(0, UV[0], v, carry, v);
+ ADDC(carry, UV[1], u, carry, u);
+ t += carry;
+ }
+ }
+ ADDC(0, v, ma[i], carry, v);
+ ADDC(carry, u, 0, carry, u);
+ t += carry;
+ mc[i-NWORDS_FIELD] = v;
+ v = u;
+ u = t;
+ t = 0;
+ }
+ ADDC(0, v, ma[2*NWORDS_FIELD-1], carry, v);
+ mc[NWORDS_FIELD-1] = v;
+}
+
+#endif // NO_ASM || (!X86_64 && !AARCH64)
diff --git a/src/third_party/sike/curve_params.c b/src/third_party/sike/curve_params.c
new file mode 100644
index 0000000..a1fbb3f
--- /dev/null
+++ b/src/third_party/sike/curve_params.c
@@ -0,0 +1,128 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: supersingular isogeny parameters and generation of functions for P434
+*********************************************************************************************/
+
+#include "utils.h"
+
+// Parameters for isogeny system "SIKE"
+const struct params_t sike_params = {
+ .prime = {
+ U64_TO_WORDS(0xFFFFFFFFFFFFFFFF), U64_TO_WORDS(0xFFFFFFFFFFFFFFFF),
+ U64_TO_WORDS(0xFFFFFFFFFFFFFFFF), U64_TO_WORDS(0xFDC1767AE2FFFFFF),
+ U64_TO_WORDS(0x7BC65C783158AEA3), U64_TO_WORDS(0x6CFC5FD681C52056),
+ U64_TO_WORDS(0x0002341F27177344)
+ },
+ .prime_p1 = {
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0xFDC1767AE3000000),
+ U64_TO_WORDS(0x7BC65C783158AEA3), U64_TO_WORDS(0x6CFC5FD681C52056),
+ U64_TO_WORDS(0x0002341F27177344)
+ },
+ .prime_x2 = {
+ U64_TO_WORDS(0xFFFFFFFFFFFFFFFE), U64_TO_WORDS(0xFFFFFFFFFFFFFFFF),
+ U64_TO_WORDS(0xFFFFFFFFFFFFFFFF), U64_TO_WORDS(0xFB82ECF5C5FFFFFF),
+ U64_TO_WORDS(0xF78CB8F062B15D47), U64_TO_WORDS(0xD9F8BFAD038A40AC),
+ U64_TO_WORDS(0x0004683E4E2EE688)
+ },
+ .A_gen = {
+ U64_TO_WORDS(0x05ADF455C5C345BF), U64_TO_WORDS(0x91935C5CC767AC2B),
+ U64_TO_WORDS(0xAFE4E879951F0257), U64_TO_WORDS(0x70E792DC89FA27B1),
+ U64_TO_WORDS(0xF797F526BB48C8CD), U64_TO_WORDS(0x2181DB6131AF621F),
+ U64_TO_WORDS(0x00000A1C08B1ECC4), // XPA0
+ U64_TO_WORDS(0x74840EB87CDA7788), U64_TO_WORDS(0x2971AA0ECF9F9D0B),
+ U64_TO_WORDS(0xCB5732BDF41715D5), U64_TO_WORDS(0x8CD8E51F7AACFFAA),
+ U64_TO_WORDS(0xA7F424730D7E419F), U64_TO_WORDS(0xD671EB919A179E8C),
+ U64_TO_WORDS(0x0000FFA26C5A924A), // XPA1
+ U64_TO_WORDS(0xFEC6E64588B7273B), U64_TO_WORDS(0xD2A626D74CBBF1C6),
+ U64_TO_WORDS(0xF8F58F07A78098C7), U64_TO_WORDS(0xE23941F470841B03),
+ U64_TO_WORDS(0x1B63EDA2045538DD), U64_TO_WORDS(0x735CFEB0FFD49215),
+ U64_TO_WORDS(0x0001C4CB77542876), // XQA0
+ U64_TO_WORDS(0xADB0F733C17FFDD6), U64_TO_WORDS(0x6AFFBD037DA0A050),
+ U64_TO_WORDS(0x680EC43DB144E02F), U64_TO_WORDS(0x1E2E5D5FF524E374),
+ U64_TO_WORDS(0xE2DDA115260E2995), U64_TO_WORDS(0xA6E4B552E2EDE508),
+ U64_TO_WORDS(0x00018ECCDDF4B53E), // XQA1
+ U64_TO_WORDS(0x01BA4DB518CD6C7D), U64_TO_WORDS(0x2CB0251FE3CC0611),
+ U64_TO_WORDS(0x259B0C6949A9121B), U64_TO_WORDS(0x60E17AC16D2F82AD),
+ U64_TO_WORDS(0x3AA41F1CE175D92D), U64_TO_WORDS(0x413FBE6A9B9BC4F3),
+ U64_TO_WORDS(0x00022A81D8D55643), // XRA0
+ U64_TO_WORDS(0xB8ADBC70FC82E54A), U64_TO_WORDS(0xEF9CDDB0D5FADDED),
+ U64_TO_WORDS(0x5820C734C80096A0), U64_TO_WORDS(0x7799994BAA96E0E4),
+ U64_TO_WORDS(0x044961599E379AF8), U64_TO_WORDS(0xDB2B94FBF09F27E2),
+ U64_TO_WORDS(0x0000B87FC716C0C6) // XRA1
+ },
+ .B_gen = {
+ U64_TO_WORDS(0x6E5497556EDD48A3), U64_TO_WORDS(0x2A61B501546F1C05),
+ U64_TO_WORDS(0xEB919446D049887D), U64_TO_WORDS(0x5864A4A69D450C4F),
+ U64_TO_WORDS(0xB883F276A6490D2B), U64_TO_WORDS(0x22CC287022D5F5B9),
+ U64_TO_WORDS(0x0001BED4772E551F), // XPB0
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), // XPB1
+ U64_TO_WORDS(0xFAE2A3F93D8B6B8E), U64_TO_WORDS(0x494871F51700FE1C),
+ U64_TO_WORDS(0xEF1A94228413C27C), U64_TO_WORDS(0x498FF4A4AF60BD62),
+ U64_TO_WORDS(0xB00AD2A708267E8A), U64_TO_WORDS(0xF4328294E017837F),
+ U64_TO_WORDS(0x000034080181D8AE), // XQB0
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), // XQB1
+ U64_TO_WORDS(0x283B34FAFEFDC8E4), U64_TO_WORDS(0x9208F44977C3E647),
+ U64_TO_WORDS(0x7DEAE962816F4E9A), U64_TO_WORDS(0x68A2BA8AA262EC9D),
+ U64_TO_WORDS(0x8176F112EA43F45B), U64_TO_WORDS(0x02106D022634F504),
+ U64_TO_WORDS(0x00007E8A50F02E37), // XRB0
+ U64_TO_WORDS(0xB378B7C1DA22CCB1), U64_TO_WORDS(0x6D089C99AD1D9230),
+ U64_TO_WORDS(0xEBE15711813E2369), U64_TO_WORDS(0x2B35A68239D48A53),
+ U64_TO_WORDS(0x445F6FD138407C93), U64_TO_WORDS(0xBEF93B29A3F6B54B),
+ U64_TO_WORDS(0x000173FA910377D3) // XRB1
+ },
+ .mont_R2 = {
+ U64_TO_WORDS(0x28E55B65DCD69B30), U64_TO_WORDS(0xACEC7367768798C2),
+ U64_TO_WORDS(0xAB27973F8311688D), U64_TO_WORDS(0x175CC6AF8D6C7C0B),
+ U64_TO_WORDS(0xABCD92BF2DDE347E), U64_TO_WORDS(0x69E16A61C7686D9A),
+ U64_TO_WORDS(0x000025A89BCDD12A)
+ },
+ .mont_one = {
+ U64_TO_WORDS(0x000000000000742C), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0xB90FF404FC000000),
+ U64_TO_WORDS(0xD801A4FB559FACD4), U64_TO_WORDS(0xE93254545F77410C),
+ U64_TO_WORDS(0x0000ECEEA7BD2EDA)
+ },
+ .mont_six = {
+ U64_TO_WORDS(0x000000000002B90A), U64_TO_WORDS(0x0000000000000000),
+ U64_TO_WORDS(0x0000000000000000), U64_TO_WORDS(0x5ADCCB2822000000),
+ U64_TO_WORDS(0x187D24F39F0CAFB4), U64_TO_WORDS(0x9D353A4D394145A0),
+ U64_TO_WORDS(0x00012559A0403298)
+ },
+ .A_strat = {
+ 0x30, 0x1C, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x0D, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
+ 0x15, 0x0C, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x03, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x05, 0x03,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01
+ },
+ .B_strat = {
+ 0x42, 0x21, 0x11, 0x09, 0x05, 0x03, 0x02, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x08, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02,
+ 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x10,
+ 0x08, 0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x04, 0x02, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x20, 0x10, 0x08, 0x04, 0x03, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
+ 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02,
+ 0x01, 0x01, 0x02, 0x01, 0x01, 0x10, 0x08, 0x04, 0x02, 0x01,
+ 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
+ 0x01, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04,
+ 0x02, 0x01, 0x01, 0x02, 0x01, 0x01
+ }
+};
diff --git a/src/third_party/sike/fpx.c b/src/third_party/sike/fpx.c
new file mode 100644
index 0000000..9917116
--- /dev/null
+++ b/src/third_party/sike/fpx.c
@@ -0,0 +1,283 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: core functions over GF(p) and GF(p^2)
+*********************************************************************************************/
+#include <openssl/base.h>
+
+#include "utils.h"
+#include "fpx.h"
+
+extern const struct params_t sike_params;
+
+// Multiprecision squaring, c = a^2 mod p.
+static void fpsqr_mont(const felm_t ma, felm_t mc)
+{
+ dfelm_t temp = {0};
+ sike_mpmul(ma, ma, temp);
+ sike_fprdc(temp, mc);
+}
+
+// Chain to compute a^(p-3)/4 using Montgomery arithmetic.
+static void fpinv_chain_mont(felm_t a)
+{
+ unsigned int i, j;
+ felm_t t[31], tt;
+
+ // Precomputed table
+ fpsqr_mont(a, tt);
+ sike_fpmul_mont(a, tt, t[0]);
+ for (i = 0; i <= 29; i++) sike_fpmul_mont(t[i], tt, t[i+1]);
+
+ sike_fpcopy(a, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[5], tt, tt);
+ for (i = 0; i < 10; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[14], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[3], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[23], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[13], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[24], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[7], tt, tt);
+ for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[12], tt, tt);
+ for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[30], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[1], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[30], tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[21], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[2], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[19], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[1], tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[24], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[26], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[16], tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[10], tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[6], tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[0], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[20], tt, tt);
+ for (i = 0; i < 8; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[9], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[25], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[30], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[26], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(a, tt, tt);
+ for (i = 0; i < 7; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[28], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[6], tt, tt);
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[10], tt, tt);
+ for (i = 0; i < 9; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[22], tt, tt);
+ for (j = 0; j < 35; j++) {
+ for (i = 0; i < 6; i++) fpsqr_mont(tt, tt);
+ sike_fpmul_mont(t[30], tt, tt);
+ }
+ sike_fpcopy(tt, a);
+}
+
+// Field inversion using Montgomery arithmetic, a = a^(-1)*R mod p.
+static void fpinv_mont(felm_t a)
+{
+ felm_t tt = {0};
+ sike_fpcopy(a, tt);
+ fpinv_chain_mont(tt);
+ fpsqr_mont(tt, tt);
+ fpsqr_mont(tt, tt);
+ sike_fpmul_mont(a, tt, a);
+}
+
+// Multiprecision addition, c = a+b, where lng(a) = lng(b) = nwords. Returns the carry bit.
+#if defined(OPENSSL_NO_ASM) || (!defined(OPENSSL_X86_64) && !defined(OPENSSL_AARCH64))
+inline static unsigned int mp_add(const felm_t a, const felm_t b, felm_t c, const unsigned int nwords) {
+ uint8_t carry = 0;
+ for (size_t i = 0; i < nwords; i++) {
+ ADDC(carry, a[i], b[i], carry, c[i]);
+ }
+ return carry;
+}
+
+// Multiprecision subtraction, c = a-b, where lng(a) = lng(b) = nwords. Returns the borrow bit.
+inline static unsigned int mp_sub(const felm_t a, const felm_t b, felm_t c, const unsigned int nwords) {
+ uint32_t borrow = 0;
+ for (size_t i = 0; i < nwords; i++) {
+ SUBC(borrow, a[i], b[i], borrow, c[i]);
+ }
+ return borrow;
+}
+#endif
+
+// Multiprecision addition, c = a+b.
+inline static void mp_addfast(const felm_t a, const felm_t b, felm_t c)
+{
+#if defined(OPENSSL_NO_ASM) || (!defined(OPENSSL_X86_64) && !defined(OPENSSL_AARCH64))
+ mp_add(a, b, c, NWORDS_FIELD);
+#else
+ sike_mpadd_asm(a, b, c);
+#endif
+}
+
+// Multiprecision subtraction, c = a-b, where lng(a) = lng(b) = 2*NWORDS_FIELD.
+// If c < 0 then returns mask = 0xFF..F, else mask = 0x00..0
+inline static crypto_word_t mp_subfast(const dfelm_t a, const dfelm_t b, dfelm_t c) {
+#if defined(OPENSSL_NO_ASM) || (!defined(OPENSSL_X86_64) && !defined(OPENSSL_AARCH64))
+ return (0 - (crypto_word_t)mp_sub(a, b, c, 2*NWORDS_FIELD));
+#else
+ return sike_mpsubx2_asm(a, b, c);
+#endif
+}
+
+// Multiprecision subtraction, c = c-a-b, where lng(a) = lng(b) = 2*NWORDS_FIELD.
+// Inputs should be s.t. c > a and c > b
+inline static void mp_dblsubfast(const dfelm_t a, const dfelm_t b, dfelm_t c) {
+#if defined(OPENSSL_NO_ASM) || (!defined(OPENSSL_X86_64) && !defined(OPENSSL_AARCH64))
+ mp_sub(c, a, c, 2*NWORDS_FIELD);
+ mp_sub(c, b, c, 2*NWORDS_FIELD);
+#else
+ sike_mpdblsubx2_asm(a, b, c);
+#endif
+}
+
+// Copy a field element, c = a.
+void sike_fpcopy(const felm_t a, felm_t c) {
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ c[i] = a[i];
+ }
+}
+
+// Field multiplication using Montgomery arithmetic, c = a*b*R^-1 mod prime, where R=2^768
+void sike_fpmul_mont(const felm_t ma, const felm_t mb, felm_t mc)
+{
+ dfelm_t temp = {0};
+ sike_mpmul(ma, mb, temp);
+ sike_fprdc(temp, mc);
+}
+
+// Conversion from Montgomery representation to standard representation,
+// c = ma*R^(-1) mod p = a mod p, where ma in [0, p-1].
+void sike_from_mont(const felm_t ma, felm_t c)
+{
+ felm_t one = {0};
+ one[0] = 1;
+
+ sike_fpmul_mont(ma, one, c);
+ sike_fpcorrection(c);
+}
+
+// GF(p^2) squaring using Montgomery arithmetic, c = a^2 in GF(p^2).
+// Inputs: a = a0+a1*i, where a0, a1 are in [0, 2*p-1]
+// Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
+void sike_fp2sqr_mont(const f2elm_t a, f2elm_t c) {
+ felm_t t1, t2, t3;
+
+ mp_addfast(a->c0, a->c1, t1); // t1 = a0+a1
+ sike_fpsub(a->c0, a->c1, t2); // t2 = a0-a1
+ mp_addfast(a->c0, a->c0, t3); // t3 = 2a0
+ sike_fpmul_mont(t1, t2, c->c0); // c0 = (a0+a1)(a0-a1)
+ sike_fpmul_mont(t3, a->c1, c->c1); // c1 = 2a0*a1
+}
+
+// Modular negation, a = -a mod p503.
+// Input/output: a in [0, 2*p503-1]
+void sike_fpneg(felm_t a) {
+ uint32_t borrow = 0;
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ SUBC(borrow, sike_params.prime_x2[i], a[i], borrow, a[i]);
+ }
+}
+
+// Modular division by two, c = a/2 mod p503.
+// Input : a in [0, 2*p503-1]
+// Output: c in [0, 2*p503-1]
+void sike_fpdiv2(const felm_t a, felm_t c) {
+ uint32_t carry = 0;
+ crypto_word_t mask;
+
+ mask = 0 - (crypto_word_t)(a[0] & 1); // If a is odd compute a+p503
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ ADDC(carry, a[i], sike_params.prime[i] & mask, carry, c[i]);
+ }
+
+ // Multiprecision right shift by one.
+ for (size_t i = 0; i < NWORDS_FIELD-1; i++) {
+ c[i] = (c[i] >> 1) ^ (c[i+1] << (RADIX - 1));
+ }
+ c[NWORDS_FIELD-1] >>= 1;
+}
+
+// Modular correction to reduce field element a in [0, 2*p503-1] to [0, p503-1].
+void sike_fpcorrection(felm_t a) {
+ uint32_t borrow = 0;
+ crypto_word_t mask;
+
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ SUBC(borrow, a[i], sike_params.prime[i], borrow, a[i]);
+ }
+ mask = 0 - (crypto_word_t)borrow;
+
+ borrow = 0;
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ ADDC(borrow, a[i], sike_params.prime[i] & mask, borrow, a[i]);
+ }
+}
+
+// GF(p^2) multiplication using Montgomery arithmetic, c = a*b in GF(p^2).
+// Inputs: a = a0+a1*i and b = b0+b1*i, where a0, a1, b0, b1 are in [0, 2*p-1]
+// Output: c = c0+c1*i, where c0, c1 are in [0, 2*p-1]
+void sike_fp2mul_mont(const f2elm_t a, const f2elm_t b, f2elm_t c) {
+ felm_t t1, t2;
+ dfelm_t tt1, tt2, tt3;
+ crypto_word_t mask;
+
+ mp_addfast(a->c0, a->c1, t1); // t1 = a0+a1
+ mp_addfast(b->c0, b->c1, t2); // t2 = b0+b1
+ sike_mpmul(a->c0, b->c0, tt1); // tt1 = a0*b0
+ sike_mpmul(a->c1, b->c1, tt2); // tt2 = a1*b1
+ sike_mpmul(t1, t2, tt3); // tt3 = (a0+a1)*(b0+b1)
+ mp_dblsubfast(tt1, tt2, tt3); // tt3 = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
+ mask = mp_subfast(tt1, tt2, tt1); // tt1 = a0*b0 - a1*b1. If tt1 < 0 then mask = 0xFF..F, else if tt1 >= 0 then mask = 0x00..0
+
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ t1[i] = sike_params.prime[i] & mask;
+ }
+
+ sike_fprdc(tt3, c->c1); // c[1] = (a0+a1)*(b0+b1) - a0*b0 - a1*b1
+ mp_addfast(&tt1[NWORDS_FIELD], t1, &tt1[NWORDS_FIELD]);
+ sike_fprdc(tt1, c->c0); // c[0] = a0*b0 - a1*b1
+}
+
+// GF(p^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2).
+void sike_fp2inv_mont(f2elm_t a) {
+ f2elm_t t1;
+
+ fpsqr_mont(a->c0, t1->c0); // t10 = a0^2
+ fpsqr_mont(a->c1, t1->c1); // t11 = a1^2
+ sike_fpadd(t1->c0, t1->c1, t1->c0); // t10 = a0^2+a1^2
+ fpinv_mont(t1->c0); // t10 = (a0^2+a1^2)^-1
+ sike_fpneg(a->c1); // a = a0-i*a1
+ sike_fpmul_mont(a->c0, t1->c0, a->c0);
+ sike_fpmul_mont(a->c1, t1->c0, a->c1); // a = (a0-i*a1)*(a0^2+a1^2)^-1
+}
diff --git a/src/third_party/sike/fpx.h b/src/third_party/sike/fpx.h
new file mode 100644
index 0000000..e697688
--- /dev/null
+++ b/src/third_party/sike/fpx.h
@@ -0,0 +1,113 @@
+#ifndef FPX_H_
+#define FPX_H_
+
+#include "utils.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Modular addition, c = a+b mod p.
+void sike_fpadd(const felm_t a, const felm_t b, felm_t c);
+// Modular subtraction, c = a-b mod p.
+void sike_fpsub(const felm_t a, const felm_t b, felm_t c);
+// Modular division by two, c = a/2 mod p.
+void sike_fpdiv2(const felm_t a, felm_t c);
+// Modular correction to reduce field element a in [0, 2*p-1] to [0, p-1].
+void sike_fpcorrection(felm_t a);
+// Multiprecision multiply, c = a*b, where lng(a) = lng(b) = nwords.
+void sike_mpmul(const felm_t a, const felm_t b, dfelm_t c);
+// 443-bit Montgomery reduction, c = a mod p. Buffer 'a' is modified after
+// call returns.
+void sike_fprdc(dfelm_t a, felm_t c);
+// Double 2x443-bit multiprecision subtraction, c = c-a-b
+void sike_mpdblsubx2_asm(const felm_t a, const felm_t b, felm_t c);
+// Multiprecision subtraction, c = a-b
+crypto_word_t sike_mpsubx2_asm(const dfelm_t a, const dfelm_t b, dfelm_t c);
+// 443-bit multiprecision addition, c = a+b
+void sike_mpadd_asm(const felm_t a, const felm_t b, felm_t c);
+// Modular negation, a = -a mod p.
+void sike_fpneg(felm_t a);
+// Copy of a field element, c = a
+void sike_fpcopy(const felm_t a, felm_t c);
+// Copy a field element, c = a.
+void sike_fpzero(felm_t a);
+// If option = 0xFF...FF x=y; y=x, otherwise swap doesn't happen. Constant time.
+void sike_cswap_asm(point_proj_t x, point_proj_t y, const crypto_word_t option);
+// Conversion from Montgomery representation to standard representation,
+// c = ma*R^(-1) mod p = a mod p, where ma in [0, p-1].
+void sike_from_mont(const felm_t ma, felm_t c);
+// Field multiplication using Montgomery arithmetic, c = a*b*R^-1 mod p443, where R=2^768
+void sike_fpmul_mont(const felm_t ma, const felm_t mb, felm_t mc);
+// GF(p443^2) multiplication using Montgomery arithmetic, c = a*b in GF(p443^2)
+void sike_fp2mul_mont(const f2elm_t a, const f2elm_t b, f2elm_t c);
+// GF(p443^2) inversion using Montgomery arithmetic, a = (a0-i*a1)/(a0^2+a1^2)
+void sike_fp2inv_mont(f2elm_t a);
+// GF(p^2) squaring using Montgomery arithmetic, c = a^2 in GF(p^2).
+void sike_fp2sqr_mont(const f2elm_t a, f2elm_t c);
+// Modular correction, a = a in GF(p^2).
+void sike_fp2correction(f2elm_t a);
+
+#if defined(__cplusplus)
+} // extern C
+#endif
+
+// GF(p^2) addition, c = a+b in GF(p^2).
+#define sike_fp2add(a, b, c) \
+do { \
+ sike_fpadd(a->c0, b->c0, c->c0); \
+ sike_fpadd(a->c1, b->c1, c->c1); \
+} while(0)
+
+// GF(p^2) subtraction, c = a-b in GF(p^2).
+#define sike_fp2sub(a,b,c) \
+do { \
+ sike_fpsub(a->c0, b->c0, c->c0); \
+ sike_fpsub(a->c1, b->c1, c->c1); \
+} while(0)
+
+// Copy a GF(p^2) element, c = a.
+#define sike_fp2copy(a, c) \
+do { \
+ sike_fpcopy(a->c0, c->c0); \
+ sike_fpcopy(a->c1, c->c1); \
+} while(0)
+
+// GF(p^2) negation, a = -a in GF(p^2).
+#define sike_fp2neg(a) \
+do { \
+ sike_fpneg(a->c0); \
+ sike_fpneg(a->c1); \
+} while(0)
+
+// GF(p^2) division by two, c = a/2 in GF(p^2).
+#define sike_fp2div2(a, c) \
+do { \
+ sike_fpdiv2(a->c0, c->c0); \
+ sike_fpdiv2(a->c1, c->c1); \
+} while(0)
+
+// Modular correction, a = a in GF(p^2).
+#define sike_fp2correction(a) \
+do { \
+ sike_fpcorrection(a->c0); \
+ sike_fpcorrection(a->c1); \
+} while(0)
+
+// Conversion of a GF(p^2) element to Montgomery representation,
+// mc_i = a_i*R^2*R^(-1) = a_i*R in GF(p^2).
+#define sike_to_fp2mont(a, mc) \
+ do { \
+ sike_fpmul_mont(a->c0, sike_params.mont_R2, mc->c0); \
+ sike_fpmul_mont(a->c1, sike_params.mont_R2, mc->c1); \
+ } while (0)
+
+// Conversion of a GF(p^2) element from Montgomery representation to standard representation,
+// c_i = ma_i*R^(-1) = a_i in GF(p^2).
+#define sike_from_fp2mont(ma, c) \
+do { \
+ sike_from_mont(ma->c0, c->c0); \
+ sike_from_mont(ma->c1, c->c1); \
+} while(0)
+
+#endif // FPX_H_
diff --git a/src/third_party/sike/isogeny.c b/src/third_party/sike/isogeny.c
new file mode 100644
index 0000000..6b910e0
--- /dev/null
+++ b/src/third_party/sike/isogeny.c
@@ -0,0 +1,260 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: elliptic curve and isogeny functions
+*********************************************************************************************/
+#include "utils.h"
+#include "isogeny.h"
+#include "fpx.h"
+
+static void xDBL(const point_proj_t P, point_proj_t Q, const f2elm_t A24plus, const f2elm_t C24)
+{ // Doubling of a Montgomery point in projective coordinates (X:Z).
+ // Input: projective Montgomery x-coordinates P = (X1:Z1), where x1=X1/Z1 and Montgomery curve constants A+2C and 4C.
+ // Output: projective Montgomery x-coordinates Q = 2*P = (X2:Z2).
+ f2elm_t t0, t1;
+
+ sike_fp2sub(P->X, P->Z, t0); // t0 = X1-Z1
+ sike_fp2add(P->X, P->Z, t1); // t1 = X1+Z1
+ sike_fp2sqr_mont(t0, t0); // t0 = (X1-Z1)^2
+ sike_fp2sqr_mont(t1, t1); // t1 = (X1+Z1)^2
+ sike_fp2mul_mont(C24, t0, Q->Z); // Z2 = C24*(X1-Z1)^2
+ sike_fp2mul_mont(t1, Q->Z, Q->X); // X2 = C24*(X1-Z1)^2*(X1+Z1)^2
+ sike_fp2sub(t1, t0, t1); // t1 = (X1+Z1)^2-(X1-Z1)^2
+ sike_fp2mul_mont(A24plus, t1, t0); // t0 = A24plus*[(X1+Z1)^2-(X1-Z1)^2]
+ sike_fp2add(Q->Z, t0, Q->Z); // Z2 = A24plus*[(X1+Z1)^2-(X1-Z1)^2] + C24*(X1-Z1)^2
+ sike_fp2mul_mont(Q->Z, t1, Q->Z); // Z2 = [A24plus*[(X1+Z1)^2-(X1-Z1)^2] + C24*(X1-Z1)^2]*[(X1+Z1)^2-(X1-Z1)^2]
+}
+
+void sike_xDBLe(const point_proj_t P, point_proj_t Q, const f2elm_t A24plus, const f2elm_t C24, size_t e)
+{ // Computes [2^e](X:Z) on Montgomery curve with projective constant via e repeated doublings.
+ // Input: projective Montgomery x-coordinates P = (XP:ZP), such that xP=XP/ZP and Montgomery curve constants A+2C and 4C.
+ // Output: projective Montgomery x-coordinates Q <- (2^e)*P.
+
+ memmove(Q, P, sizeof(*P));
+ for (size_t i = 0; i < e; i++) {
+ xDBL(Q, Q, A24plus, C24);
+ }
+}
+
+void sike_get_4_isog(const point_proj_t P, f2elm_t A24plus, f2elm_t C24, f2elm_t* coeff)
+{ // Computes the corresponding 4-isogeny of a projective Montgomery point (X4:Z4) of order 4.
+ // Input: projective point of order four P = (X4:Z4).
+ // Output: the 4-isogenous Montgomery curve with projective coefficients A+2C/4C and the 3 coefficients
+ // that are used to evaluate the isogeny at a point in eval_4_isog().
+
+ sike_fp2sub(P->X, P->Z, coeff[1]); // coeff[1] = X4-Z4
+ sike_fp2add(P->X, P->Z, coeff[2]); // coeff[2] = X4+Z4
+ sike_fp2sqr_mont(P->Z, coeff[0]); // coeff[0] = Z4^2
+ sike_fp2add(coeff[0], coeff[0], coeff[0]); // coeff[0] = 2*Z4^2
+ sike_fp2sqr_mont(coeff[0], C24); // C24 = 4*Z4^4
+ sike_fp2add(coeff[0], coeff[0], coeff[0]); // coeff[0] = 4*Z4^2
+ sike_fp2sqr_mont(P->X, A24plus); // A24plus = X4^2
+ sike_fp2add(A24plus, A24plus, A24plus); // A24plus = 2*X4^2
+ sike_fp2sqr_mont(A24plus, A24plus); // A24plus = 4*X4^4
+}
+
+void sike_eval_4_isog(point_proj_t P, f2elm_t* coeff)
+{ // Evaluates the isogeny at the point (X:Z) in the domain of the isogeny, given a 4-isogeny phi defined
+ // by the 3 coefficients in coeff (computed in the function get_4_isog()).
+ // Inputs: the coefficients defining the isogeny, and the projective point P = (X:Z).
+ // Output: the projective point P = phi(P) = (X:Z) in the codomain.
+ f2elm_t t0, t1;
+
+ sike_fp2add(P->X, P->Z, t0); // t0 = X+Z
+ sike_fp2sub(P->X, P->Z, t1); // t1 = X-Z
+ sike_fp2mul_mont(t0, coeff[1], P->X); // X = (X+Z)*coeff[1]
+ sike_fp2mul_mont(t1, coeff[2], P->Z); // Z = (X-Z)*coeff[2]
+ sike_fp2mul_mont(t0, t1, t0); // t0 = (X+Z)*(X-Z)
+ sike_fp2mul_mont(t0, coeff[0], t0); // t0 = coeff[0]*(X+Z)*(X-Z)
+ sike_fp2add(P->X, P->Z, t1); // t1 = (X-Z)*coeff[2] + (X+Z)*coeff[1]
+ sike_fp2sub(P->X, P->Z, P->Z); // Z = (X-Z)*coeff[2] - (X+Z)*coeff[1]
+ sike_fp2sqr_mont(t1, t1); // t1 = [(X-Z)*coeff[2] + (X+Z)*coeff[1]]^2
+ sike_fp2sqr_mont(P->Z, P->Z); // Z = [(X-Z)*coeff[2] - (X+Z)*coeff[1]]^2
+ sike_fp2add(t1, t0, P->X); // X = coeff[0]*(X+Z)*(X-Z) + [(X-Z)*coeff[2] + (X+Z)*coeff[1]]^2
+ sike_fp2sub(P->Z, t0, t0); // t0 = [(X-Z)*coeff[2] - (X+Z)*coeff[1]]^2 - coeff[0]*(X+Z)*(X-Z)
+ sike_fp2mul_mont(P->X, t1, P->X); // Xfinal
+ sike_fp2mul_mont(P->Z, t0, P->Z); // Zfinal
+}
+
+
+void sike_xTPL(const point_proj_t P, point_proj_t Q, const f2elm_t A24minus, const f2elm_t A24plus)
+{ // Tripling of a Montgomery point in projective coordinates (X:Z).
+ // Input: projective Montgomery x-coordinates P = (X:Z), where x=X/Z and Montgomery curve constants A24plus = A+2C and A24minus = A-2C.
+ // Output: projective Montgomery x-coordinates Q = 3*P = (X3:Z3).
+ f2elm_t t0, t1, t2, t3, t4, t5, t6;
+
+ sike_fp2sub(P->X, P->Z, t0); // t0 = X-Z
+ sike_fp2sqr_mont(t0, t2); // t2 = (X-Z)^2
+ sike_fp2add(P->X, P->Z, t1); // t1 = X+Z
+ sike_fp2sqr_mont(t1, t3); // t3 = (X+Z)^2
+ sike_fp2add(t0, t1, t4); // t4 = 2*X
+ sike_fp2sub(t1, t0, t0); // t0 = 2*Z
+ sike_fp2sqr_mont(t4, t1); // t1 = 4*X^2
+ sike_fp2sub(t1, t3, t1); // t1 = 4*X^2 - (X+Z)^2
+ sike_fp2sub(t1, t2, t1); // t1 = 4*X^2 - (X+Z)^2 - (X-Z)^2
+ sike_fp2mul_mont(t3, A24plus, t5); // t5 = A24plus*(X+Z)^2
+ sike_fp2mul_mont(t3, t5, t3); // t3 = A24plus*(X+Z)^3
+ sike_fp2mul_mont(A24minus, t2, t6); // t6 = A24minus*(X-Z)^2
+ sike_fp2mul_mont(t2, t6, t2); // t2 = A24minus*(X-Z)^3
+ sike_fp2sub(t2, t3, t3); // t3 = A24minus*(X-Z)^3 - coeff*(X+Z)^3
+ sike_fp2sub(t5, t6, t2); // t2 = A24plus*(X+Z)^2 - A24minus*(X-Z)^2
+ sike_fp2mul_mont(t1, t2, t1); // t1 = [4*X^2 - (X+Z)^2 - (X-Z)^2]*[A24plus*(X+Z)^2 - A24minus*(X-Z)^2]
+ sike_fp2add(t3, t1, t2); // t2 = [4*X^2 - (X+Z)^2 - (X-Z)^2]*[A24plus*(X+Z)^2 - A24minus*(X-Z)^2] + A24minus*(X-Z)^3 - coeff*(X+Z)^3
+ sike_fp2sqr_mont(t2, t2); // t2 = t2^2
+ sike_fp2mul_mont(t4, t2, Q->X); // X3 = 2*X*t2
+ sike_fp2sub(t3, t1, t1); // t1 = A24minus*(X-Z)^3 - A24plus*(X+Z)^3 - [4*X^2 - (X+Z)^2 - (X-Z)^2]*[A24plus*(X+Z)^2 - A24minus*(X-Z)^2]
+ sike_fp2sqr_mont(t1, t1); // t1 = t1^2
+ sike_fp2mul_mont(t0, t1, Q->Z); // Z3 = 2*Z*t1
+}
+
+void sike_xTPLe(const point_proj_t P, point_proj_t Q, const f2elm_t A24minus, const f2elm_t A24plus, size_t e)
+{ // Computes [3^e](X:Z) on Montgomery curve with projective constant via e repeated triplings.
+ // Input: projective Montgomery x-coordinates P = (XP:ZP), such that xP=XP/ZP and Montgomery curve constants A24plus = A+2C and A24minus = A-2C.
+ // Output: projective Montgomery x-coordinates Q <- (3^e)*P.
+ memmove(Q, P, sizeof(*P));
+ for (size_t i = 0; i < e; i++) {
+ sike_xTPL(Q, Q, A24minus, A24plus);
+ }
+}
+
+void sike_get_3_isog(const point_proj_t P, f2elm_t A24minus, f2elm_t A24plus, f2elm_t* coeff)
+{ // Computes the corresponding 3-isogeny of a projective Montgomery point (X3:Z3) of order 3.
+ // Input: projective point of order three P = (X3:Z3).
+ // Output: the 3-isogenous Montgomery curve with projective coefficient A/C.
+ f2elm_t t0, t1, t2, t3, t4;
+
+ sike_fp2sub(P->X, P->Z, coeff[0]); // coeff0 = X-Z
+ sike_fp2sqr_mont(coeff[0], t0); // t0 = (X-Z)^2
+ sike_fp2add(P->X, P->Z, coeff[1]); // coeff1 = X+Z
+ sike_fp2sqr_mont(coeff[1], t1); // t1 = (X+Z)^2
+ sike_fp2add(t0, t1, t2); // t2 = (X+Z)^2 + (X-Z)^2
+ sike_fp2add(coeff[0], coeff[1], t3); // t3 = 2*X
+ sike_fp2sqr_mont(t3, t3); // t3 = 4*X^2
+ sike_fp2sub(t3, t2, t3); // t3 = 4*X^2 - (X+Z)^2 - (X-Z)^2
+ sike_fp2add(t1, t3, t2); // t2 = 4*X^2 - (X-Z)^2
+ sike_fp2add(t3, t0, t3); // t3 = 4*X^2 - (X+Z)^2
+ sike_fp2add(t0, t3, t4); // t4 = 4*X^2 - (X+Z)^2 + (X-Z)^2
+ sike_fp2add(t4, t4, t4); // t4 = 2(4*X^2 - (X+Z)^2 + (X-Z)^2)
+ sike_fp2add(t1, t4, t4); // t4 = 8*X^2 - (X+Z)^2 + 2*(X-Z)^2
+ sike_fp2mul_mont(t2, t4, A24minus); // A24minus = [4*X^2 - (X-Z)^2]*[8*X^2 - (X+Z)^2 + 2*(X-Z)^2]
+ sike_fp2add(t1, t2, t4); // t4 = 4*X^2 + (X+Z)^2 - (X-Z)^2
+ sike_fp2add(t4, t4, t4); // t4 = 2(4*X^2 + (X+Z)^2 - (X-Z)^2)
+ sike_fp2add(t0, t4, t4); // t4 = 8*X^2 + 2*(X+Z)^2 - (X-Z)^2
+ sike_fp2mul_mont(t3, t4, t4); // t4 = [4*X^2 - (X+Z)^2]*[8*X^2 + 2*(X+Z)^2 - (X-Z)^2]
+ sike_fp2sub(t4, A24minus, t0); // t0 = [4*X^2 - (X+Z)^2]*[8*X^2 + 2*(X+Z)^2 - (X-Z)^2] - [4*X^2 - (X-Z)^2]*[8*X^2 - (X+Z)^2 + 2*(X-Z)^2]
+ sike_fp2add(A24minus, t0, A24plus); // A24plus = 8*X^2 - (X+Z)^2 + 2*(X-Z)^2
+}
+
+
+void sike_eval_3_isog(point_proj_t Q, f2elm_t* coeff)
+{ // Computes the 3-isogeny R=phi(X:Z), given projective point (X3:Z3) of order 3 on a Montgomery curve and
+ // a point P with 2 coefficients in coeff (computed in the function get_3_isog()).
+ // Inputs: projective points P = (X3:Z3) and Q = (X:Z).
+ // Output: the projective point Q <- phi(Q) = (X3:Z3).
+ f2elm_t t0, t1, t2;
+
+ sike_fp2add(Q->X, Q->Z, t0); // t0 = X+Z
+ sike_fp2sub(Q->X, Q->Z, t1); // t1 = X-Z
+ sike_fp2mul_mont(t0, coeff[0], t0); // t0 = coeff0*(X+Z)
+ sike_fp2mul_mont(t1, coeff[1], t1); // t1 = coeff1*(X-Z)
+ sike_fp2add(t0, t1, t2); // t2 = coeff0*(X+Z) + coeff1*(X-Z)
+ sike_fp2sub(t1, t0, t0); // t0 = coeff1*(X-Z) - coeff0*(X+Z)
+ sike_fp2sqr_mont(t2, t2); // t2 = [coeff0*(X+Z) + coeff1*(X-Z)]^2
+ sike_fp2sqr_mont(t0, t0); // t0 = [coeff1*(X-Z) - coeff0*(X+Z)]^2
+ sike_fp2mul_mont(Q->X, t2, Q->X); // X3final = X*[coeff0*(X+Z) + coeff1*(X-Z)]^2
+ sike_fp2mul_mont(Q->Z, t0, Q->Z); // Z3final = Z*[coeff1*(X-Z) - coeff0*(X+Z)]^2
+}
+
+
+void sike_inv_3_way(f2elm_t z1, f2elm_t z2, f2elm_t z3)
+{ // 3-way simultaneous inversion
+ // Input: z1,z2,z3
+ // Output: 1/z1,1/z2,1/z3 (override inputs).
+ f2elm_t t0, t1, t2, t3;
+
+ sike_fp2mul_mont(z1, z2, t0); // t0 = z1*z2
+ sike_fp2mul_mont(z3, t0, t1); // t1 = z1*z2*z3
+ sike_fp2inv_mont(t1); // t1 = 1/(z1*z2*z3)
+ sike_fp2mul_mont(z3, t1, t2); // t2 = 1/(z1*z2)
+ sike_fp2mul_mont(t2, z2, t3); // t3 = 1/z1
+ sike_fp2mul_mont(t2, z1, z2); // z2 = 1/z2
+ sike_fp2mul_mont(t0, t1, z3); // z3 = 1/z3
+ sike_fp2copy(t3, z1); // z1 = 1/z1
+}
+
+
+void sike_get_A(const f2elm_t xP, const f2elm_t xQ, const f2elm_t xR, f2elm_t A)
+{ // Given the x-coordinates of P, Q, and R, returns the value A corresponding to the Montgomery curve E_A: y^2=x^3+A*x^2+x such that R=Q-P on E_A.
+ // Input: the x-coordinates xP, xQ, and xR of the points P, Q and R.
+ // Output: the coefficient A corresponding to the curve E_A: y^2=x^3+A*x^2+x.
+ f2elm_t t0, t1, one = F2ELM_INIT;
+
+ extern const struct params_t sike_params;
+ sike_fpcopy(sike_params.mont_one, one->c0);
+ sike_fp2add(xP, xQ, t1); // t1 = xP+xQ
+ sike_fp2mul_mont(xP, xQ, t0); // t0 = xP*xQ
+ sike_fp2mul_mont(xR, t1, A); // A = xR*t1
+ sike_fp2add(t0, A, A); // A = A+t0
+ sike_fp2mul_mont(t0, xR, t0); // t0 = t0*xR
+ sike_fp2sub(A, one, A); // A = A-1
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2add(t1, xR, t1); // t1 = t1+xR
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2sqr_mont(A, A); // A = A^2
+ sike_fp2inv_mont(t0); // t0 = 1/t0
+ sike_fp2mul_mont(A, t0, A); // A = A*t0
+ sike_fp2sub(A, t1, A); // Afinal = A-t1
+}
+
+
+void sike_j_inv(const f2elm_t A, const f2elm_t C, f2elm_t jinv)
+{ // Computes the j-invariant of a Montgomery curve with projective constant.
+ // Input: A,C in GF(p^2).
+ // Output: j=256*(A^2-3*C^2)^3/(C^4*(A^2-4*C^2)), which is the j-invariant of the Montgomery curve B*y^2=x^3+(A/C)*x^2+x or (equivalently) j-invariant of B'*y^2=C*x^3+A*x^2+C*x.
+ f2elm_t t0, t1;
+
+ sike_fp2sqr_mont(A, jinv); // jinv = A^2
+ sike_fp2sqr_mont(C, t1); // t1 = C^2
+ sike_fp2add(t1, t1, t0); // t0 = t1+t1
+ sike_fp2sub(jinv, t0, t0); // t0 = jinv-t0
+ sike_fp2sub(t0, t1, t0); // t0 = t0-t1
+ sike_fp2sub(t0, t1, jinv); // jinv = t0-t1
+ sike_fp2sqr_mont(t1, t1); // t1 = t1^2
+ sike_fp2mul_mont(jinv, t1, jinv); // jinv = jinv*t1
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2sqr_mont(t0, t1); // t1 = t0^2
+ sike_fp2mul_mont(t0, t1, t0); // t0 = t0*t1
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2add(t0, t0, t0); // t0 = t0+t0
+ sike_fp2inv_mont(jinv); // jinv = 1/jinv
+ sike_fp2mul_mont(jinv, t0, jinv); // jinv = t0*jinv
+}
+
+
+void sike_xDBLADD(point_proj_t P, point_proj_t Q, const f2elm_t xPQ, const f2elm_t A24)
+{ // Simultaneous doubling and differential addition.
+ // Input: projective Montgomery points P=(XP:ZP) and Q=(XQ:ZQ) such that xP=XP/ZP and xQ=XQ/ZQ, affine difference xPQ=x(P-Q) and Montgomery curve constant A24=(A+2)/4.
+ // Output: projective Montgomery points P <- 2*P = (X2P:Z2P) such that x(2P)=X2P/Z2P, and Q <- P+Q = (XQP:ZQP) such that = x(Q+P)=XQP/ZQP.
+ f2elm_t t0, t1, t2;
+
+ sike_fp2add(P->X, P->Z, t0); // t0 = XP+ZP
+ sike_fp2sub(P->X, P->Z, t1); // t1 = XP-ZP
+ sike_fp2sqr_mont(t0, P->X); // XP = (XP+ZP)^2
+ sike_fp2sub(Q->X, Q->Z, t2); // t2 = XQ-ZQ
+ sike_fp2correction(t2);
+ sike_fp2add(Q->X, Q->Z, Q->X); // XQ = XQ+ZQ
+ sike_fp2mul_mont(t0, t2, t0); // t0 = (XP+ZP)*(XQ-ZQ)
+ sike_fp2sqr_mont(t1, P->Z); // ZP = (XP-ZP)^2
+ sike_fp2mul_mont(t1, Q->X, t1); // t1 = (XP-ZP)*(XQ+ZQ)
+ sike_fp2sub(P->X, P->Z, t2); // t2 = (XP+ZP)^2-(XP-ZP)^2
+ sike_fp2mul_mont(P->X, P->Z, P->X); // XP = (XP+ZP)^2*(XP-ZP)^2
+ sike_fp2mul_mont(t2, A24, Q->X); // XQ = A24*[(XP+ZP)^2-(XP-ZP)^2]
+ sike_fp2sub(t0, t1, Q->Z); // ZQ = (XP+ZP)*(XQ-ZQ)-(XP-ZP)*(XQ+ZQ)
+ sike_fp2add(Q->X, P->Z, P->Z); // ZP = A24*[(XP+ZP)^2-(XP-ZP)^2]+(XP-ZP)^2
+ sike_fp2add(t0, t1, Q->X); // XQ = (XP+ZP)*(XQ-ZQ)+(XP-ZP)*(XQ+ZQ)
+ sike_fp2mul_mont(P->Z, t2, P->Z); // ZP = [A24*[(XP+ZP)^2-(XP-ZP)^2]+(XP-ZP)^2]*[(XP+ZP)^2-(XP-ZP)^2]
+ sike_fp2sqr_mont(Q->Z, Q->Z); // ZQ = [(XP+ZP)*(XQ-ZQ)-(XP-ZP)*(XQ+ZQ)]^2
+ sike_fp2sqr_mont(Q->X, Q->X); // XQ = [(XP+ZP)*(XQ-ZQ)+(XP-ZP)*(XQ+ZQ)]^2
+ sike_fp2mul_mont(Q->Z, xPQ, Q->Z); // ZQ = xPQ*[(XP+ZP)*(XQ-ZQ)-(XP-ZP)*(XQ+ZQ)]^2
+}
diff --git a/src/third_party/sike/isogeny.h b/src/third_party/sike/isogeny.h
new file mode 100644
index 0000000..18337dd
--- /dev/null
+++ b/src/third_party/sike/isogeny.h
@@ -0,0 +1,49 @@
+#ifndef ISOGENY_H_
+#define ISOGENY_H_
+
+// Computes [2^e](X:Z) on Montgomery curve with projective
+// constant via e repeated doublings.
+void sike_xDBLe(
+ const point_proj_t P, point_proj_t Q, const f2elm_t A24plus,
+ const f2elm_t C24, size_t e);
+// Simultaneous doubling and differential addition.
+void sike_xDBLADD(
+ point_proj_t P, point_proj_t Q, const f2elm_t xPQ,
+ const f2elm_t A24);
+// Tripling of a Montgomery point in projective coordinates (X:Z).
+void sike_xTPL(
+ const point_proj_t P, point_proj_t Q, const f2elm_t A24minus,
+ const f2elm_t A24plus);
+// Computes [3^e](X:Z) on Montgomery curve with projective constant
+// via e repeated triplings.
+void sike_xTPLe(
+ const point_proj_t P, point_proj_t Q, const f2elm_t A24minus,
+ const f2elm_t A24plus, size_t e);
+// Given the x-coordinates of P, Q, and R, returns the value A
+// corresponding to the Montgomery curve E_A: y^2=x^3+A*x^2+x such that R=Q-P on E_A.
+void sike_get_A(
+ const f2elm_t xP, const f2elm_t xQ, const f2elm_t xR, f2elm_t A);
+// Computes the j-invariant of a Montgomery curve with projective constant.
+void sike_j_inv(
+ const f2elm_t A, const f2elm_t C, f2elm_t jinv);
+// Computes the corresponding 4-isogeny of a projective Montgomery
+// point (X4:Z4) of order 4.
+void sike_get_4_isog(
+ const point_proj_t P, f2elm_t A24plus, f2elm_t C24, f2elm_t* coeff);
+// Computes the corresponding 3-isogeny of a projective Montgomery
+// point (X3:Z3) of order 3.
+void sike_get_3_isog(
+ const point_proj_t P, f2elm_t A24minus, f2elm_t A24plus,
+ f2elm_t* coeff);
+// Computes the 3-isogeny R=phi(X:Z), given projective point (X3:Z3)
+// of order 3 on a Montgomery curve and a point P with coefficients given in coeff.
+void sike_eval_3_isog(
+ point_proj_t Q, f2elm_t* coeff);
+// Evaluates the isogeny at the point (X:Z) in the domain of the isogeny.
+void sike_eval_4_isog(
+ point_proj_t P, f2elm_t* coeff);
+// 3-way simultaneous inversion
+void sike_inv_3_way(
+ f2elm_t z1, f2elm_t z2, f2elm_t z3);
+
+#endif // ISOGENY_H_
diff --git a/src/third_party/sike/sike.c b/src/third_party/sike/sike.c
new file mode 100644
index 0000000..87b7417
--- /dev/null
+++ b/src/third_party/sike/sike.c
@@ -0,0 +1,531 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: supersingular isogeny key encapsulation (SIKE) protocol
+*********************************************************************************************/
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <openssl/bn.h>
+#include <openssl/base.h>
+#include <openssl/rand.h>
+#include <openssl/mem.h>
+#include <openssl/sha.h>
+
+#include "utils.h"
+#include "isogeny.h"
+#include "fpx.h"
+
+extern const struct params_t sike_params;
+
+// SIDH_JINV_BYTESZ is a number of bytes used for encoding j-invariant.
+#define SIDH_JINV_BYTESZ 110U
+// SIDH_PRV_A_BITSZ is a number of bits of SIDH private key (2-isogeny)
+#define SIDH_PRV_A_BITSZ 216U
+// SIDH_PRV_A_BITSZ is a number of bits of SIDH private key (3-isogeny)
+#define SIDH_PRV_B_BITSZ 217U
+// MAX_INT_POINTS_ALICE is a number of points used in 2-isogeny tree computation
+#define MAX_INT_POINTS_ALICE 7U
+// MAX_INT_POINTS_ALICE is a number of points used in 3-isogeny tree computation
+#define MAX_INT_POINTS_BOB 8U
+
+// Swap points.
+// If option = 0 then P <- P and Q <- Q, else if option = 0xFF...FF then P <- Q and Q <- P
+#if !defined(OPENSSL_X86_64) || defined(OPENSSL_NO_ASM)
+static void sike_cswap(point_proj_t P, point_proj_t Q, const crypto_word_t option)
+{
+ crypto_word_t temp;
+ for (size_t i = 0; i < NWORDS_FIELD; i++) {
+ temp = option & (P->X->c0[i] ^ Q->X->c0[i]);
+ P->X->c0[i] = temp ^ P->X->c0[i];
+ Q->X->c0[i] = temp ^ Q->X->c0[i];
+ temp = option & (P->Z->c0[i] ^ Q->Z->c0[i]);
+ P->Z->c0[i] = temp ^ P->Z->c0[i];
+ Q->Z->c0[i] = temp ^ Q->Z->c0[i];
+ temp = option & (P->X->c1[i] ^ Q->X->c1[i]);
+ P->X->c1[i] = temp ^ P->X->c1[i];
+ Q->X->c1[i] = temp ^ Q->X->c1[i];
+ temp = option & (P->Z->c1[i] ^ Q->Z->c1[i]);
+ P->Z->c1[i] = temp ^ P->Z->c1[i];
+ Q->Z->c1[i] = temp ^ Q->Z->c1[i];
+ }
+}
+#endif
+
+// Swap points.
+// If option = 0 then P <- P and Q <- Q, else if option = 0xFF...FF then P <- Q and Q <- P
+static inline void sike_fp2cswap(point_proj_t P, point_proj_t Q, const crypto_word_t option)
+{
+#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)
+ sike_cswap_asm(P, Q, option);
+#else
+ sike_cswap(P, Q, option);
+#endif
+}
+
+static void ladder3Pt(
+ const f2elm_t xP, const f2elm_t xQ, const f2elm_t xPQ, const uint8_t* m,
+ int is_A, point_proj_t R, const f2elm_t A) {
+ point_proj_t R0 = POINT_PROJ_INIT, R2 = POINT_PROJ_INIT;
+ f2elm_t A24 = F2ELM_INIT;
+ crypto_word_t mask;
+ int bit, swap, prevbit = 0;
+
+ const size_t nbits = is_A?SIDH_PRV_A_BITSZ:SIDH_PRV_B_BITSZ;
+
+ // Initializing constant
+ sike_fpcopy(sike_params.mont_one, A24[0].c0);
+ sike_fp2add(A24, A24, A24);
+ sike_fp2add(A, A24, A24);
+ sike_fp2div2(A24, A24);
+ sike_fp2div2(A24, A24); // A24 = (A+2)/4
+
+ // Initializing points
+ sike_fp2copy(xQ, R0->X);
+ sike_fpcopy(sike_params.mont_one, R0->Z[0].c0);
+ sike_fp2copy(xPQ, R2->X);
+ sike_fpcopy(sike_params.mont_one, R2->Z[0].c0);
+ sike_fp2copy(xP, R->X);
+ sike_fpcopy(sike_params.mont_one, R->Z[0].c0);
+ memset(R->Z->c1, 0, sizeof(R->Z->c1));
+
+ // Main loop
+ for (size_t i = 0; i < nbits; i++) {
+ bit = (m[i >> 3] >> (i & 7)) & 1;
+ swap = bit ^ prevbit;
+ prevbit = bit;
+ mask = 0 - (crypto_word_t)swap;
+
+ sike_fp2cswap(R, R2, mask);
+ sike_xDBLADD(R0, R2, R->X, A24);
+ sike_fp2mul_mont(R2->X, R->Z, R2->X);
+ }
+
+ mask = 0 - (crypto_word_t)prevbit;
+ sike_fp2cswap(R, R2, mask);
+}
+
+// Initialization of basis points
+static inline void sike_init_basis(const crypto_word_t *gen, f2elm_t XP, f2elm_t XQ, f2elm_t XR) {
+ sike_fpcopy(gen, XP->c0);
+ sike_fpcopy(gen + NWORDS_FIELD, XP->c1);
+ sike_fpcopy(gen + 2*NWORDS_FIELD, XQ->c0);
+ sike_fpcopy(gen + 3*NWORDS_FIELD, XQ->c1);
+ sike_fpcopy(gen + 4*NWORDS_FIELD, XR->c0);
+ sike_fpcopy(gen + 5*NWORDS_FIELD, XR->c1);
+}
+
+// Conversion of GF(p^2) element from Montgomery to standard representation.
+static inline void sike_fp2_encode(const f2elm_t x, uint8_t *enc) {
+ f2elm_t t;
+ sike_from_fp2mont(x, t);
+
+ // convert to bytes in little endian form
+ for (size_t i=0; i<FIELD_BYTESZ; i++) {
+ enc[i+ 0] = (t[0].c0[i/LSZ] >> (8*(i%LSZ))) & 0xFF;
+ enc[i+FIELD_BYTESZ] = (t[0].c1[i/LSZ] >> (8*(i%LSZ))) & 0xFF;
+ }
+}
+
+// Parse byte sequence back into GF(p^2) element, and conversion to Montgomery representation.
+// Elements over GF(p503) are encoded in 63 octets in little endian format
+// (i.e., the least significant octet is located in the lowest memory address).
+static inline void fp2_decode(const uint8_t *enc, f2elm_t t) {
+ memset(t[0].c0, 0, sizeof(t[0].c0));
+ memset(t[0].c1, 0, sizeof(t[0].c1));
+ // convert bytes in little endian form to f2elm_t
+ for (size_t i = 0; i < FIELD_BYTESZ; i++) {
+ t[0].c0[i/LSZ] |= ((crypto_word_t)enc[i+ 0]) << (8*(i%LSZ));
+ t[0].c1[i/LSZ] |= ((crypto_word_t)enc[i+FIELD_BYTESZ]) << (8*(i%LSZ));
+ }
+ sike_to_fp2mont(t, t);
+}
+
+// Alice's ephemeral public key generation
+// Input: a private key prA in the range [0, 2^250 - 1], stored in 32 bytes.
+// Output: the public key pkA consisting of 3 GF(p503^2) elements encoded in 378 bytes.
+static void gen_iso_A(const uint8_t* skA, uint8_t* pkA)
+{
+ point_proj_t R, pts[MAX_INT_POINTS_ALICE];
+ point_proj_t phiP = POINT_PROJ_INIT;
+ point_proj_t phiQ = POINT_PROJ_INIT;
+ point_proj_t phiR = POINT_PROJ_INIT;
+ f2elm_t XPA, XQA, XRA, coeff[3];
+ f2elm_t A24plus = F2ELM_INIT;
+ f2elm_t C24 = F2ELM_INIT;
+ f2elm_t A = F2ELM_INIT;
+ unsigned int m, index = 0, pts_index[MAX_INT_POINTS_ALICE], npts = 0, ii = 0;
+
+ // Initialize basis points
+ sike_init_basis(sike_params.A_gen, XPA, XQA, XRA);
+ sike_init_basis(sike_params.B_gen, phiP->X, phiQ->X, phiR->X);
+ sike_fpcopy(sike_params.mont_one, (phiP->Z)->c0);
+ sike_fpcopy(sike_params.mont_one, (phiQ->Z)->c0);
+ sike_fpcopy(sike_params.mont_one, (phiR->Z)->c0);
+
+ // Initialize constants: A24plus = A+2C, C24 = 4C, where A=6, C=1
+ sike_fpcopy(sike_params.mont_one, A24plus->c0);
+ sike_fp2add(A24plus, A24plus, A24plus);
+ sike_fp2add(A24plus, A24plus, C24);
+ sike_fp2add(A24plus, C24, A);
+ sike_fp2add(C24, C24, A24plus);
+
+ // Retrieve kernel point
+ ladder3Pt(XPA, XQA, XRA, skA, 1, R, A);
+
+ // Traverse tree
+ index = 0;
+ for (size_t row = 1; row < A_max; row++) {
+ while (index < A_max-row) {
+ sike_fp2copy(R->X, pts[npts]->X);
+ sike_fp2copy(R->Z, pts[npts]->Z);
+ pts_index[npts++] = index;
+ m = sike_params.A_strat[ii++];
+ sike_xDBLe(R, R, A24plus, C24, (2*m));
+ index += m;
+ }
+ sike_get_4_isog(R, A24plus, C24, coeff);
+
+ for (size_t i = 0; i < npts; i++) {
+ sike_eval_4_isog(pts[i], coeff);
+ }
+ sike_eval_4_isog(phiP, coeff);
+ sike_eval_4_isog(phiQ, coeff);
+ sike_eval_4_isog(phiR, coeff);
+
+ sike_fp2copy(pts[npts-1]->X, R->X);
+ sike_fp2copy(pts[npts-1]->Z, R->Z);
+ index = pts_index[npts-1];
+ npts -= 1;
+ }
+
+ sike_get_4_isog(R, A24plus, C24, coeff);
+ sike_eval_4_isog(phiP, coeff);
+ sike_eval_4_isog(phiQ, coeff);
+ sike_eval_4_isog(phiR, coeff);
+
+ sike_inv_3_way(phiP->Z, phiQ->Z, phiR->Z);
+ sike_fp2mul_mont(phiP->X, phiP->Z, phiP->X);
+ sike_fp2mul_mont(phiQ->X, phiQ->Z, phiQ->X);
+ sike_fp2mul_mont(phiR->X, phiR->Z, phiR->X);
+
+ // Format public key
+ sike_fp2_encode(phiP->X, pkA);
+ sike_fp2_encode(phiQ->X, pkA + SIDH_JINV_BYTESZ);
+ sike_fp2_encode(phiR->X, pkA + 2*SIDH_JINV_BYTESZ);
+}
+
+// Bob's ephemeral key-pair generation
+// It produces a private key skB and computes the public key pkB.
+// The private key is an integer in the range [0, 2^Floor(Log(2,3^159)) - 1], stored in 32 bytes.
+// The public key consists of 3 GF(p503^2) elements encoded in 378 bytes.
+static void gen_iso_B(const uint8_t* skB, uint8_t* pkB)
+{
+ point_proj_t R, pts[MAX_INT_POINTS_BOB];
+ point_proj_t phiP = POINT_PROJ_INIT;
+ point_proj_t phiQ = POINT_PROJ_INIT;
+ point_proj_t phiR = POINT_PROJ_INIT;
+ f2elm_t XPB, XQB, XRB, coeff[3];
+ f2elm_t A24plus = F2ELM_INIT;
+ f2elm_t A24minus = F2ELM_INIT;
+ f2elm_t A = F2ELM_INIT;
+ unsigned int m, index = 0, pts_index[MAX_INT_POINTS_BOB], npts = 0, ii = 0;
+
+ // Initialize basis points
+ sike_init_basis(sike_params.B_gen, XPB, XQB, XRB);
+ sike_init_basis(sike_params.A_gen, phiP->X, phiQ->X, phiR->X);
+ sike_fpcopy(sike_params.mont_one, (phiP->Z)->c0);
+ sike_fpcopy(sike_params.mont_one, (phiQ->Z)->c0);
+ sike_fpcopy(sike_params.mont_one, (phiR->Z)->c0);
+
+ // Initialize constants: A24minus = A-2C, A24plus = A+2C, where A=6, C=1
+ sike_fpcopy(sike_params.mont_one, A24plus->c0);
+ sike_fp2add(A24plus, A24plus, A24plus);
+ sike_fp2add(A24plus, A24plus, A24minus);
+ sike_fp2add(A24plus, A24minus, A);
+ sike_fp2add(A24minus, A24minus, A24plus);
+
+ // Retrieve kernel point
+ ladder3Pt(XPB, XQB, XRB, skB, 0, R, A);
+
+ // Traverse tree
+ index = 0;
+ for (size_t row = 1; row < B_max; row++) {
+ while (index < B_max-row) {
+ sike_fp2copy(R->X, pts[npts]->X);
+ sike_fp2copy(R->Z, pts[npts]->Z);
+ pts_index[npts++] = index;
+ m = sike_params.B_strat[ii++];
+ sike_xTPLe(R, R, A24minus, A24plus, m);
+ index += m;
+ }
+ sike_get_3_isog(R, A24minus, A24plus, coeff);
+
+ for (size_t i = 0; i < npts; i++) {
+ sike_eval_3_isog(pts[i], coeff);
+ }
+ sike_eval_3_isog(phiP, coeff);
+ sike_eval_3_isog(phiQ, coeff);
+ sike_eval_3_isog(phiR, coeff);
+
+ sike_fp2copy(pts[npts-1]->X, R->X);
+ sike_fp2copy(pts[npts-1]->Z, R->Z);
+ index = pts_index[npts-1];
+ npts -= 1;
+ }
+
+ sike_get_3_isog(R, A24minus, A24plus, coeff);
+ sike_eval_3_isog(phiP, coeff);
+ sike_eval_3_isog(phiQ, coeff);
+ sike_eval_3_isog(phiR, coeff);
+
+ sike_inv_3_way(phiP->Z, phiQ->Z, phiR->Z);
+ sike_fp2mul_mont(phiP->X, phiP->Z, phiP->X);
+ sike_fp2mul_mont(phiQ->X, phiQ->Z, phiQ->X);
+ sike_fp2mul_mont(phiR->X, phiR->Z, phiR->X);
+
+ // Format public key
+ sike_fp2_encode(phiP->X, pkB);
+ sike_fp2_encode(phiQ->X, pkB + SIDH_JINV_BYTESZ);
+ sike_fp2_encode(phiR->X, pkB + 2*SIDH_JINV_BYTESZ);
+}
+
+// Alice's ephemeral shared secret computation
+// It produces a shared secret key ssA using her secret key skA and Bob's public key pkB
+// Inputs: Alice's skA is an integer in the range [0, 2^250 - 1], stored in 32 bytes.
+// Bob's pkB consists of 3 GF(p503^2) elements encoded in 378 bytes.
+// Output: a shared secret ssA that consists of one element in GF(p503^2) encoded in 126 bytes.
+static void ex_iso_A(const uint8_t* skA, const uint8_t* pkB, uint8_t* ssA)
+{
+ point_proj_t R, pts[MAX_INT_POINTS_ALICE];
+ f2elm_t coeff[3], PKB[3], jinv;
+ f2elm_t A24plus = F2ELM_INIT;
+ f2elm_t C24 = F2ELM_INIT;
+ f2elm_t A = F2ELM_INIT;
+ unsigned int m, index = 0, pts_index[MAX_INT_POINTS_ALICE], npts = 0, ii = 0;
+
+ // Initialize images of Bob's basis
+ fp2_decode(pkB, PKB[0]);
+ fp2_decode(pkB + SIDH_JINV_BYTESZ, PKB[1]);
+ fp2_decode(pkB + 2*SIDH_JINV_BYTESZ, PKB[2]);
+
+ // Initialize constants
+ sike_get_A(PKB[0], PKB[1], PKB[2], A);
+ sike_fpadd(sike_params.mont_one, sike_params.mont_one, C24->c0);
+ sike_fp2add(A, C24, A24plus);
+ sike_fpadd(C24->c0, C24->c0, C24->c0);
+
+ // Retrieve kernel point
+ ladder3Pt(PKB[0], PKB[1], PKB[2], skA, 1, R, A);
+
+ // Traverse tree
+ index = 0;
+ for (size_t row = 1; row < A_max; row++) {
+ while (index < A_max-row) {
+ sike_fp2copy(R->X, pts[npts]->X);
+ sike_fp2copy(R->Z, pts[npts]->Z);
+ pts_index[npts++] = index;
+ m = sike_params.A_strat[ii++];
+ sike_xDBLe(R, R, A24plus, C24, (2*m));
+ index += m;
+ }
+ sike_get_4_isog(R, A24plus, C24, coeff);
+
+ for (size_t i = 0; i < npts; i++) {
+ sike_eval_4_isog(pts[i], coeff);
+ }
+
+ sike_fp2copy(pts[npts-1]->X, R->X);
+ sike_fp2copy(pts[npts-1]->Z, R->Z);
+ index = pts_index[npts-1];
+ npts -= 1;
+ }
+
+ sike_get_4_isog(R, A24plus, C24, coeff);
+ sike_fp2add(A24plus, A24plus, A24plus);
+ sike_fp2sub(A24plus, C24, A24plus);
+ sike_fp2add(A24plus, A24plus, A24plus);
+ sike_j_inv(A24plus, C24, jinv);
+ sike_fp2_encode(jinv, ssA);
+}
+
+// Bob's ephemeral shared secret computation
+// It produces a shared secret key ssB using his secret key skB and Alice's public key pkA
+// Inputs: Bob's skB is an integer in the range [0, 2^Floor(Log(2,3^159)) - 1], stored in 32 bytes.
+// Alice's pkA consists of 3 GF(p503^2) elements encoded in 378 bytes.
+// Output: a shared secret ssB that consists of one element in GF(p503^2) encoded in 126 bytes.
+static void ex_iso_B(const uint8_t* skB, const uint8_t* pkA, uint8_t* ssB)
+{
+ point_proj_t R, pts[MAX_INT_POINTS_BOB];
+ f2elm_t coeff[3], PKB[3], jinv;
+ f2elm_t A24plus = F2ELM_INIT;
+ f2elm_t A24minus = F2ELM_INIT;
+ f2elm_t A = F2ELM_INIT;
+ unsigned int m, index = 0, pts_index[MAX_INT_POINTS_BOB], npts = 0, ii = 0;
+
+ // Initialize images of Alice's basis
+ fp2_decode(pkA, PKB[0]);
+ fp2_decode(pkA + SIDH_JINV_BYTESZ, PKB[1]);
+ fp2_decode(pkA + 2*SIDH_JINV_BYTESZ, PKB[2]);
+
+ // Initialize constants
+ sike_get_A(PKB[0], PKB[1], PKB[2], A);
+ sike_fpadd(sike_params.mont_one, sike_params.mont_one, A24minus->c0);
+ sike_fp2add(A, A24minus, A24plus);
+ sike_fp2sub(A, A24minus, A24minus);
+
+ // Retrieve kernel point
+ ladder3Pt(PKB[0], PKB[1], PKB[2], skB, 0, R, A);
+
+ // Traverse tree
+ index = 0;
+ for (size_t row = 1; row < B_max; row++) {
+ while (index < B_max-row) {
+ sike_fp2copy(R->X, pts[npts]->X);
+ sike_fp2copy(R->Z, pts[npts]->Z);
+ pts_index[npts++] = index;
+ m = sike_params.B_strat[ii++];
+ sike_xTPLe(R, R, A24minus, A24plus, m);
+ index += m;
+ }
+ sike_get_3_isog(R, A24minus, A24plus, coeff);
+
+ for (size_t i = 0; i < npts; i++) {
+ sike_eval_3_isog(pts[i], coeff);
+ }
+
+ sike_fp2copy(pts[npts-1]->X, R->X);
+ sike_fp2copy(pts[npts-1]->Z, R->Z);
+ index = pts_index[npts-1];
+ npts -= 1;
+ }
+
+ sike_get_3_isog(R, A24minus, A24plus, coeff);
+ sike_fp2add(A24plus, A24minus, A);
+ sike_fp2add(A, A, A);
+ sike_fp2sub(A24plus, A24minus, A24plus);
+ sike_j_inv(A, A24plus, jinv);
+ sike_fp2_encode(jinv, ssB);
+}
+
+int SIKE_keypair(uint8_t out_priv[SIKE_PRV_BYTESZ],
+ uint8_t out_pub[SIKE_PUB_BYTESZ]) {
+ int ret = 0;
+
+ // Calculate private key for Alice. Needs to be in range [0, 2^0xFA - 1] and <
+ // 253 bits
+ BIGNUM *bn_sidh_prv = BN_new();
+ if (!bn_sidh_prv ||
+ !BN_rand(bn_sidh_prv, SIDH_PRV_B_BITSZ, BN_RAND_TOP_ONE,
+ BN_RAND_BOTTOM_ANY) ||
+ !BN_bn2le_padded(out_priv, BITS_TO_BYTES(SIDH_PRV_B_BITSZ),
+ bn_sidh_prv)) {
+ goto end;
+ }
+
+ gen_iso_B(out_priv, out_pub);
+ ret = 1;
+
+end:
+ BN_free(bn_sidh_prv);
+ return ret;
+}
+
+void SIKE_encaps(uint8_t out_shared_key[SIKE_SS_BYTESZ],
+ uint8_t out_ciphertext[SIKE_CT_BYTESZ],
+ const uint8_t pub_key[SIKE_PUB_BYTESZ]) {
+ // Secret buffer is reused by the function to store some ephemeral
+ // secret data. It's size must be maximum of SHA256_CBLOCK,
+ // SIKE_MSG_BYTESZ and SIDH_PRV_A_BITSZ in bytes.
+ uint8_t secret[SHA256_CBLOCK];
+ uint8_t j[SIDH_JINV_BYTESZ];
+ uint8_t temp[SIKE_MSG_BYTESZ + SIKE_CT_BYTESZ];
+ SHA256_CTX ctx;
+
+ // Generate secret key for A
+ // secret key A = SHA256({0,1}^n || pub_key)) mod SIDH_PRV_A_BITSZ
+ RAND_bytes(temp, SIKE_MSG_BYTESZ);
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
+ SHA256_Update(&ctx, pub_key, SIKE_PUB_BYTESZ);
+ SHA256_Final(secret, &ctx);
+
+ // Generate public key for A - first part of the ciphertext
+ gen_iso_A(secret, out_ciphertext);
+
+ // Generate c1:
+ // h = SHA256(j-invariant)
+ // c1 = h ^ m
+ ex_iso_A(secret, pub_key, j);
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, j, sizeof(j));
+ SHA256_Final(secret, &ctx);
+
+ // c1 = h ^ m
+ uint8_t *c1 = &out_ciphertext[SIKE_PUB_BYTESZ];
+ for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
+ c1[i] = temp[i] ^ secret[i];
+ }
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
+ SHA256_Update(&ctx, out_ciphertext, SIKE_CT_BYTESZ);
+ SHA256_Final(secret, &ctx);
+ // Generate shared secret out_shared_key = SHA256(m||out_ciphertext)
+ memcpy(out_shared_key, secret, SIKE_SS_BYTESZ);
+}
+
+void SIKE_decaps(uint8_t out_shared_key[SIKE_SS_BYTESZ],
+ const uint8_t ciphertext[SIKE_CT_BYTESZ],
+ const uint8_t pub_key[SIKE_PUB_BYTESZ],
+ const uint8_t priv_key[SIKE_PRV_BYTESZ]) {
+ // Secret buffer is reused by the function to store some ephemeral
+ // secret data. It's size must be maximum of SHA256_CBLOCK,
+ // SIKE_MSG_BYTESZ and SIDH_PRV_A_BITSZ in bytes.
+ uint8_t secret[SHA256_CBLOCK];
+ uint8_t j[SIDH_JINV_BYTESZ];
+ uint8_t c0[SIKE_PUB_BYTESZ];
+ uint8_t temp[SIKE_MSG_BYTESZ];
+ uint8_t shared_nok[SIKE_MSG_BYTESZ];
+ SHA256_CTX ctx;
+
+ // This is OK as we are only using ephemeral keys in BoringSSL
+ RAND_bytes(shared_nok, SIKE_MSG_BYTESZ);
+
+ // Recover m
+ // Let ciphertext = c0 || c1 - both have fixed sizes
+ // m = F(j-invariant(c0, priv_key)) ^ c1
+ ex_iso_B(priv_key, ciphertext, j);
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, j, sizeof(j));
+ SHA256_Final(secret, &ctx);
+
+ const uint8_t *c1 = &ciphertext[sizeof(c0)];
+ for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
+ temp[i] = c1[i] ^ secret[i];
+ }
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
+ SHA256_Update(&ctx, pub_key, SIKE_PUB_BYTESZ);
+ SHA256_Final(secret, &ctx);
+
+ // Recover c0 = public key A
+ gen_iso_A(secret, c0);
+ crypto_word_t ok = constant_time_is_zero_w(
+ CRYPTO_memcmp(c0, ciphertext, SIKE_PUB_BYTESZ));
+ for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
+ temp[i] = constant_time_select_8(ok, temp[i], shared_nok[i]);
+ }
+
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
+ SHA256_Update(&ctx, ciphertext, SIKE_CT_BYTESZ);
+ SHA256_Final(secret, &ctx);
+
+ // Generate shared secret out_shared_key = SHA256(m||ciphertext)
+ memcpy(out_shared_key, secret, SIKE_SS_BYTESZ);
+}
diff --git a/src/third_party/sike/sike.h b/src/third_party/sike/sike.h
new file mode 100644
index 0000000..5819ebf
--- /dev/null
+++ b/src/third_party/sike/sike.h
@@ -0,0 +1,64 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: API header file for SIKE
+*********************************************************************************************/
+
+#ifndef SIKE_H_
+#define SIKE_H_
+
+#include <stdint.h>
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* SIKE
+ *
+ * SIKE is a isogeny based post-quantum key encapsulation mechanism. Description of the
+ * algorithm is provided in [SIKE]. This implementation uses 434-bit field size. The code
+ * is based on "Additional_Implementations" from PQC NIST submission package which can
+ * be found here:
+ * https://csrc.nist.gov/CSRC/media/Projects/Post-Quantum-Cryptography/documents/round-1/submissions/SIKE.zip
+ *
+ * [SIKE] https://sike.org/files/SIDH-spec.pdf
+ */
+
+// SIKE_PUB_BYTESZ is the number of bytes in a public key.
+#define SIKE_PUB_BYTESZ 330
+// SIKE_PRV_BYTESZ is the number of bytes in a private key.
+#define SIKE_PRV_BYTESZ 28
+// SIKE_SS_BYTESZ is the number of bytes in a shared key.
+#define SIKE_SS_BYTESZ 16
+// SIKE_MSG_BYTESZ is the number of bytes in a random bit string concatenated
+// with the public key (see 1.4 of SIKE).
+#define SIKE_MSG_BYTESZ 16
+// SIKE_SS_BYTESZ is the number of bytes in a ciphertext.
+#define SIKE_CT_BYTESZ (SIKE_PUB_BYTESZ + SIKE_MSG_BYTESZ)
+
+// SIKE_keypair outputs a public and secret key. Internally it uses BN_rand() as
+// an entropy source. In case of success function returns 1, otherwise 0.
+OPENSSL_EXPORT int SIKE_keypair(
+ uint8_t out_priv[SIKE_PRV_BYTESZ],
+ uint8_t out_pub[SIKE_PUB_BYTESZ]);
+
+// SIKE_encaps generates and encrypts a random session key, writing those values to
+// |out_shared_key| and |out_ciphertext|, respectively.
+OPENSSL_EXPORT void SIKE_encaps(
+ uint8_t out_shared_key[SIKE_SS_BYTESZ],
+ uint8_t out_ciphertext[SIKE_CT_BYTESZ],
+ const uint8_t pub_key[SIKE_PUB_BYTESZ]);
+
+// SIKE_decaps outputs a random session key, writing it to |out_shared_key|.
+OPENSSL_EXPORT void SIKE_decaps(
+ uint8_t out_shared_key[SIKE_SS_BYTESZ],
+ const uint8_t ciphertext[SIKE_CT_BYTESZ],
+ const uint8_t pub_key[SIKE_PUB_BYTESZ],
+ const uint8_t priv_key[SIKE_PRV_BYTESZ]);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/src/third_party/sike/sike_test.cc b/src/third_party/sike/sike_test.cc
new file mode 100644
index 0000000..2180a52
--- /dev/null
+++ b/src/third_party/sike/sike_test.cc
@@ -0,0 +1,251 @@
+/* Copyright (c) 2018, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <gtest/gtest.h>
+#include <stdint.h>
+
+#include "sike.h"
+#include "fpx.h"
+#include "../../crypto/test/abi_test.h"
+
+TEST(SIKE, RoundTrip) {
+ uint8_t sk[SIKE_PRV_BYTESZ] = {0};
+ uint8_t pk[SIKE_PUB_BYTESZ] = {0};
+ uint8_t ct[SIKE_CT_BYTESZ] = {0};
+ uint8_t ss_enc[SIKE_SS_BYTESZ] = {0};
+ uint8_t ss_dec[SIKE_SS_BYTESZ] = {0};
+
+ for (size_t i = 0; i < 30; i++) {
+ EXPECT_EQ(SIKE_keypair(sk, pk), 1);
+ SIKE_encaps(ss_enc, ct, pk);
+ SIKE_decaps(ss_dec, ct, pk, sk);
+
+ EXPECT_EQ(memcmp(ss_enc, ss_dec, SIKE_SS_BYTESZ), 0);
+ }
+}
+
+TEST(SIKE, Decapsulation) {
+ const uint8_t sk[SIKE_PRV_BYTESZ] = {
+ 0xB1, 0xFD, 0x34, 0x42, 0xDB, 0x02, 0xBC, 0x9D, 0x4C, 0xD0,
+ 0x72, 0x34, 0x4D, 0xBD, 0x06, 0xDF, 0x1C, 0x7D, 0x0A, 0x88,
+ 0xB2, 0x50, 0xC4, 0xF6, 0xAE, 0xE8, 0x25, 0x01};
+
+ const uint8_t pk[SIKE_PUB_BYTESZ] = {
+ 0x6D, 0x8D, 0xF5, 0x7B, 0xCD, 0x47, 0xCA, 0xCB, 0x7A, 0x38, 0xB7, 0xA6,
+ 0x90, 0xB7, 0x37, 0x03, 0xD4, 0x6F, 0x27, 0x73, 0x74, 0x17, 0x5A, 0xA4,
+ 0x0D, 0xC6, 0x81, 0xAD, 0xDB, 0xF7, 0x18, 0xB2, 0x3C, 0x30, 0xCF, 0xAA,
+ 0x08, 0x11, 0x91, 0xCC, 0x27, 0x4E, 0xF1, 0xA6, 0xB7, 0xDA, 0xD2, 0xCF,
+ 0x99, 0x7F, 0xF7, 0xE1, 0xD0, 0xCE, 0x00, 0xD2, 0x4B, 0xA4, 0x33, 0xB4,
+ 0x87, 0x01, 0x3F, 0x02, 0xF7, 0xF9, 0xDE, 0xC3, 0x60, 0x62, 0xDA, 0x3F,
+ 0x74, 0xA9, 0x44, 0xBE, 0x19, 0xD5, 0x03, 0x2A, 0x79, 0x8C, 0xA7, 0xFF,
+ 0xEA, 0xB3, 0xBB, 0xB5, 0xD4, 0x1D, 0x8F, 0x92, 0xCE, 0x62, 0x6E, 0x99,
+ 0x24, 0xD7, 0x57, 0xFA, 0xCD, 0xB6, 0xE2, 0x8E, 0xFD, 0x22, 0x0E, 0x31,
+ 0x21, 0x01, 0x8D, 0x79, 0xF8, 0x3E, 0x27, 0xEC, 0x43, 0x40, 0xDB, 0x82,
+ 0xE5, 0xEB, 0x6C, 0x97, 0x66, 0x29, 0x15, 0x68, 0xB7, 0x4D, 0x84, 0xD1,
+ 0x8A, 0x0B, 0x12, 0x36, 0x2C, 0x0C, 0x0A, 0x6E, 0x4E, 0xDE, 0xA5, 0x8A,
+ 0xDE, 0x77, 0xDD, 0x70, 0x49, 0x73, 0xAC, 0x27, 0x6D, 0x8D, 0x25, 0x9A,
+ 0xE4, 0x25, 0xE8, 0x95, 0x8F, 0xFE, 0x90, 0x3B, 0x00, 0x69, 0x20, 0xE8,
+ 0x7C, 0xA5, 0xF5, 0x79, 0xC0, 0x61, 0x51, 0x91, 0x35, 0x25, 0x3F, 0x17,
+ 0x2F, 0x70, 0x73, 0xF0, 0x89, 0xB5, 0xC8, 0x25, 0xB8, 0xE5, 0x7E, 0x34,
+ 0xDD, 0x11, 0xE5, 0xD6, 0xC3, 0xD5, 0x29, 0x89, 0xC6, 0x2C, 0x99, 0x53,
+ 0x1D, 0x2C, 0x77, 0xB0, 0xB6, 0xA1, 0xBD, 0x79, 0xFB, 0x4A, 0xC2, 0x48,
+ 0x4C, 0x62, 0x51, 0x00, 0xE3, 0x91, 0x2A, 0xCB, 0x84, 0x03, 0x5D, 0x2D,
+ 0xC8, 0x33, 0xE9, 0x14, 0xBF, 0x74, 0x21, 0xBC, 0xF4, 0x76, 0xE5, 0x42,
+ 0xB8, 0xBD, 0xE2, 0xE7, 0x20, 0x95, 0x54, 0xF2, 0xED, 0xC0, 0x79, 0x38,
+ 0x1E, 0xD2, 0xEA, 0x1A, 0x63, 0x85, 0xE7, 0x3A, 0xDA, 0xAD, 0xAB, 0x1B,
+ 0x1E, 0x19, 0x9E, 0x73, 0xD0, 0x10, 0x2E, 0x38, 0xAC, 0x8B, 0x00, 0x6A,
+ 0x30, 0x2C, 0x3D, 0x70, 0x8E, 0x39, 0x6D, 0xC0, 0x12, 0x61, 0x7D, 0x2A,
+ 0x0A, 0x04, 0x95, 0x8E, 0x09, 0x3C, 0x7B, 0xEC, 0x2E, 0xBC, 0xE8, 0xE8,
+ 0xE8, 0x37, 0x29, 0xC4, 0x7E, 0x76, 0x48, 0xB9, 0x3B, 0x72, 0xE5, 0x99,
+ 0x9B, 0xF9, 0xE3, 0x99, 0x72, 0x3F, 0x35, 0x29, 0x85, 0xE0, 0xC8, 0xBF,
+ 0xB1, 0x6B, 0xB1, 0x6E, 0x72, 0x00};
+
+ const uint8_t ct[SIKE_CT_BYTESZ] = {
+ 0xFF, 0xEB, 0xEF, 0x4A, 0xC0, 0x57, 0x0F, 0x26, 0xAC, 0x76, 0xA8, 0xB0,
+ 0xA3, 0x5D, 0x9C, 0xD9, 0x25, 0xD1, 0x7F, 0x92, 0x5D, 0xF4, 0x23, 0x34,
+ 0xC3, 0x03, 0x10, 0xE1, 0xB0, 0x24, 0x9B, 0x44, 0x58, 0x26, 0x13, 0x56,
+ 0x83, 0x43, 0x72, 0x69, 0x28, 0x0D, 0x55, 0x07, 0x1F, 0xDB, 0xC0, 0x23,
+ 0x34, 0x83, 0x1A, 0x09, 0x9B, 0x80, 0x00, 0x64, 0x56, 0xDC, 0x79, 0x7A,
+ 0xD2, 0xCE, 0x23, 0xC9, 0x72, 0x27, 0xFC, 0x8D, 0xAB, 0xBF, 0xD3, 0x17,
+ 0xF6, 0x91, 0x7B, 0x15, 0x93, 0x83, 0x8A, 0x4F, 0x6C, 0xCA, 0x4A, 0x94,
+ 0xDA, 0xC7, 0x9D, 0xB6, 0xD6, 0xBA, 0xBD, 0x81, 0x9A, 0x78, 0xE5, 0xE5,
+ 0xBE, 0x17, 0xBC, 0xCB, 0xC8, 0x23, 0x80, 0x5F, 0x75, 0xF8, 0xDB, 0x51,
+ 0x55, 0x00, 0x25, 0x33, 0x52, 0x64, 0xB2, 0xD6, 0xD8, 0x9A, 0x2A, 0x9E,
+ 0x29, 0x99, 0x13, 0x33, 0xE2, 0xA7, 0x98, 0xAC, 0xD7, 0x79, 0x5C, 0x2F,
+ 0xBA, 0x07, 0xC3, 0x03, 0x37, 0xD6, 0xE6, 0xB5, 0xA1, 0xF5, 0x29, 0xB6,
+ 0xF6, 0xC0, 0x5C, 0x44, 0x68, 0x2B, 0x0B, 0xF5, 0x00, 0x01, 0x44, 0xD5,
+ 0xCC, 0x23, 0xB5, 0x27, 0x4F, 0xCA, 0xB4, 0x05, 0x01, 0xF9, 0xD4, 0x41,
+ 0xE0, 0xE1, 0x1E, 0xCF, 0xA9, 0xBC, 0x79, 0xD7, 0xD5, 0xF5, 0x3C, 0xE6,
+ 0x93, 0xF4, 0x6C, 0x84, 0x5A, 0x2C, 0x4B, 0xE4, 0x91, 0xB2, 0xB2, 0xB8,
+ 0xAD, 0x74, 0x9A, 0x69, 0x79, 0x4C, 0x84, 0xB7, 0xBF, 0xF1, 0x68, 0x4B,
+ 0xAE, 0x0F, 0x7F, 0x45, 0x3B, 0x18, 0x3F, 0xFA, 0x00, 0x48, 0xE0, 0x3A,
+ 0xE2, 0xC0, 0xAE, 0x00, 0xCE, 0x90, 0x28, 0xA4, 0x1B, 0xBE, 0xCA, 0x0C,
+ 0x21, 0x29, 0x64, 0x30, 0x5E, 0x35, 0xAD, 0xFD, 0x83, 0x47, 0x40, 0x6D,
+ 0x15, 0x56, 0xFC, 0xF8, 0x5F, 0xAB, 0x81, 0xFE, 0x6B, 0xE9, 0x6B, 0xED,
+ 0x27, 0x35, 0x7C, 0xD8, 0x2C, 0xD4, 0xF2, 0x11, 0xE6, 0xAF, 0xDF, 0xB8,
+ 0x91, 0x96, 0xEB, 0xF7, 0x4C, 0x8D, 0x70, 0x77, 0x90, 0x81, 0x00, 0x09,
+ 0x19, 0x27, 0x8A, 0x9E, 0xB6, 0x1A, 0xE9, 0xAC, 0x6C, 0xC9, 0xF8, 0xEA,
+ 0xA2, 0x34, 0xB8, 0xAC, 0xB3, 0xB3, 0x68, 0xA1, 0xB7, 0x29, 0x55, 0xCA,
+ 0x40, 0x23, 0x92, 0x5C, 0x0C, 0x79, 0x6B, 0xD6, 0x9F, 0x5B, 0xD2, 0xE6,
+ 0xAE, 0x04, 0xCB, 0xEC, 0xC7, 0x88, 0x18, 0xDB, 0x7A, 0xE6, 0xD6, 0xC9,
+ 0x39, 0xFD, 0x93, 0x9B, 0xC8, 0x01, 0x6F, 0x3E, 0x6C, 0x90, 0x3E, 0x73,
+ 0x76, 0x99, 0x7C, 0x48, 0xDA, 0x68, 0x48, 0x80, 0x2B, 0x63};
+
+ const uint8_t ss_exp[SIKE_SS_BYTESZ] = {0xA1, 0xF9, 0x5A, 0x67, 0xB9, 0x3D,
+ 0x1E, 0x72, 0xE8, 0xC5, 0x71, 0xF1,
+ 0x4C, 0xB2, 0xAA, 0x6D};
+
+ uint8_t ss_dec[SIKE_SS_BYTESZ] = {0};
+ SIKE_decaps(ss_dec, ct, pk, sk);
+ EXPECT_EQ(memcmp(ss_dec, ss_exp, sizeof(ss_exp)), 0);
+}
+
+// SIKE_encaps and SIKE_keypair doesn't return zeros.
+TEST(SIKE, NonZero) {
+ uint8_t sk[SIKE_PRV_BYTESZ] = {0};
+ uint8_t pk[SIKE_PUB_BYTESZ] = {0};
+ uint8_t ct[SIKE_CT_BYTESZ] = {0};
+ uint8_t ss[SIKE_SS_BYTESZ] = {0};
+
+ // Check secret and public key returned by SIKE_keypair
+ EXPECT_EQ(SIKE_keypair(sk, pk), 1);
+ uint8_t tmp = 0;
+ for (size_t i = 0; i < sizeof(sk); i++) {
+ tmp |= sk[i];
+ }
+ EXPECT_NE(tmp, 0);
+
+ tmp = 0;
+ for (size_t i = 0; i < sizeof(pk); i++) {
+ tmp |= pk[i];
+ }
+ EXPECT_NE(tmp, 0);
+
+ // Check shared secret and ciphertext returned by SIKE_encaps
+ SIKE_encaps(ss, ct, pk);
+ tmp = 0;
+ for (size_t i = 0; i < sizeof(ct); i++) {
+ tmp |= ct[i];
+ }
+ EXPECT_NE(tmp, 0);
+
+ tmp = 0;
+ for (size_t i = 0; i < sizeof(ss); i++) {
+ tmp |= ss[i];
+ }
+ EXPECT_NE(tmp, 0);
+}
+
+TEST(SIKE, Negative) {
+ uint8_t sk[SIKE_PRV_BYTESZ] = {0};
+ uint8_t pk[SIKE_PUB_BYTESZ] = {0};
+ uint8_t ct[SIKE_CT_BYTESZ] = {0};
+ uint8_t ss_enc[SIKE_SS_BYTESZ] = {0};
+ uint8_t ss_dec[SIKE_SS_BYTESZ] = {0};
+
+ EXPECT_EQ(SIKE_keypair(sk, pk), 1);
+ SIKE_encaps(ss_enc, ct, pk);
+
+ // Change cipertext
+ uint8_t ct_tmp[SIKE_CT_BYTESZ] = {0};
+ memcpy(ct_tmp, ct, sizeof(ct));
+ ct_tmp[0] = ~ct_tmp[0];
+ SIKE_decaps(ss_dec, ct_tmp, pk, sk);
+ EXPECT_NE(memcmp(ss_enc, ss_dec, SIKE_SS_BYTESZ), 0);
+
+ // Change secret key
+ uint8_t sk_tmp[SIKE_PRV_BYTESZ] = {0};
+ memcpy(sk_tmp, sk, sizeof(sk));
+ sk_tmp[0] = ~sk_tmp[0];
+ SIKE_decaps(ss_dec, ct, pk, sk_tmp);
+ EXPECT_NE(memcmp(ss_enc, ss_dec, SIKE_SS_BYTESZ), 0);
+
+ // Change public key
+ uint8_t pk_tmp[SIKE_PUB_BYTESZ] = {0};
+ memcpy(pk_tmp, pk, sizeof(pk));
+ pk_tmp[0] = ~pk_tmp[0];
+ SIKE_decaps(ss_dec, ct, pk_tmp, sk);
+ EXPECT_NE(memcmp(ss_enc, ss_dec, SIKE_SS_BYTESZ), 0);
+}
+
+TEST(SIKE, Unaligned) {
+ alignas(4) uint8_t priv[SIKE_PRV_BYTESZ + 1];
+ alignas(4) uint8_t pub[SIKE_PUB_BYTESZ + 1];
+ alignas(4) uint8_t shared_key1[SIKE_SS_BYTESZ + 1];
+ alignas(4) uint8_t ciphertext[SIKE_CT_BYTESZ + 1];
+ alignas(4) uint8_t shared_key2[SIKE_SS_BYTESZ + 1];
+
+ ASSERT_TRUE(SIKE_keypair(priv + 1, pub + 1));
+ SIKE_encaps(shared_key1 + 1, ciphertext + 1, pub + 1);
+ SIKE_decaps(shared_key2 + 1, ciphertext + 1, pub + 1, priv + 1);
+
+ EXPECT_EQ(memcmp(shared_key1 + 1, shared_key2 + 1, SIKE_SS_BYTESZ), 0);
+}
+
+#if defined(SUPPORTS_ABI_TEST) && \
+ (defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64))
+TEST(SIKE, ABI) {
+ felm_t a, b, c;
+ dfelm_t d, e, f;
+ CHECK_ABI(sike_fpadd, a, b, c);
+ CHECK_ABI(sike_fpsub, a, b, c);
+ CHECK_ABI(sike_mpmul, a, b, d);
+ CHECK_ABI(sike_fprdc, d, a);
+ CHECK_ABI(sike_mpadd_asm, a, b, c);
+ CHECK_ABI(sike_mpsubx2_asm, d, e, f);
+ CHECK_ABI(sike_mpdblsubx2_asm, d, e, f);
+}
+
+// Additional tests for checking if assembly implementation
+// of MUL and REDC handles carry chains correctly.
+TEST(SIKE, CarryChains) {
+ // Expected results
+ const dfelm_t exp_mul = {
+ 0x0000000000000001, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
+ 0x0000000000000000, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ };
+
+ const felm_t exp_redc = {
+ 0x93AA0C8C2D3235BE, 0xA8CD35DDDE399B46, 0xB9BBA5469509CA65,
+ 0x6B2FB3A5A2FB86E4, 0x585591BA6DBE862C, 0xD92D3FF5FE0938F2,
+ 0x0001E1F0EE75A1E1
+ };
+
+ // Input
+ dfelm_t in14 = {
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
+ };
+
+ felm_t in7 = {
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
+ 0xFFFFFFFFFFFFFFFF
+ };
+
+ dfelm_t res;
+ sike_mpmul(in7, in7, res);
+ EXPECT_EQ(memcmp(exp_mul, res, sizeof(exp_mul)), 0);
+
+ // modifies in14 and in7
+ sike_fprdc(in14, in7);
+ EXPECT_EQ(memcmp(exp_redc, in7, sizeof(exp_redc)), 0);
+}
+#endif // SUPPORTS_ABI_TEST && (X86_64 || AARCH64)
diff --git a/src/third_party/sike/utils.h b/src/third_party/sike/utils.h
new file mode 100644
index 0000000..cbc8329
--- /dev/null
+++ b/src/third_party/sike/utils.h
@@ -0,0 +1,145 @@
+/********************************************************************************************
+* SIDH: an efficient supersingular isogeny cryptography library
+*
+* Abstract: internal header file for P434
+*********************************************************************************************/
+
+#ifndef UTILS_H_
+#define UTILS_H_
+
+#include <openssl/base.h>
+
+#include "../crypto/internal.h"
+#include "sike.h"
+
+// Conversion macro from number of bits to number of bytes
+#define BITS_TO_BYTES(nbits) (((nbits)+7)/8)
+
+// Bit size of the field
+#define BITS_FIELD 434
+// Byte size of the field
+#define FIELD_BYTESZ BITS_TO_BYTES(BITS_FIELD)
+// Number of 64-bit words of a 224-bit element
+#define NBITS_ORDER 224
+#define NWORDS64_ORDER ((NBITS_ORDER+63)/64)
+// Number of elements in Alice's strategy
+#define A_max 108
+// Number of elements in Bob's strategy
+#define B_max 137
+// Word size size
+#define RADIX sizeof(crypto_word_t)*8
+// Byte size of a limb
+#define LSZ sizeof(crypto_word_t)
+
+#if defined(OPENSSL_64_BIT)
+ // Number of words of a 434-bit field element
+ #define NWORDS_FIELD 7
+ // Number of "0" digits in the least significant part of p434 + 1
+ #define ZERO_WORDS 3
+ // U64_TO_WORDS expands |x| for a |crypto_word_t| array literal.
+ #define U64_TO_WORDS(x) UINT64_C(x)
+#else
+ // Number of words of a 434-bit field element
+ #define NWORDS_FIELD 14
+ // Number of "0" digits in the least significant part of p434 + 1
+ #define ZERO_WORDS 6
+ // U64_TO_WORDS expands |x| for a |crypto_word_t| array literal.
+ #define U64_TO_WORDS(x) \
+ (uint32_t)(UINT64_C(x) & 0xffffffff), (uint32_t)(UINT64_C(x) >> 32)
+#endif
+
+// Extended datatype support
+#if !defined(BORINGSSL_HAS_UINT128)
+ typedef uint64_t uint128_t[2];
+#endif
+
+// The following functions return 1 (TRUE) if condition is true, 0 (FALSE) otherwise
+// Digit multiplication
+#define MUL(multiplier, multiplicand, hi, lo) digit_x_digit((multiplier), (multiplicand), &(lo));
+
+// If mask |x|==0xff.ff set |x| to 1, otherwise 0
+#define M2B(x) ((x)>>(RADIX-1))
+
+// Digit addition with carry
+#define ADDC(carryIn, addend1, addend2, carryOut, sumOut) \
+do { \
+ crypto_word_t tempReg = (addend1) + (crypto_word_t)(carryIn); \
+ (sumOut) = (addend2) + tempReg; \
+ (carryOut) = M2B(constant_time_lt_w(tempReg, (crypto_word_t)(carryIn)) | \
+ constant_time_lt_w((sumOut), tempReg)); \
+} while(0)
+
+// Digit subtraction with borrow
+#define SUBC(borrowIn, minuend, subtrahend, borrowOut, differenceOut) \
+do { \
+ crypto_word_t tempReg = (minuend) - (subtrahend); \
+ crypto_word_t borrowReg = M2B(constant_time_lt_w((minuend), (subtrahend))); \
+ borrowReg |= ((borrowIn) & constant_time_is_zero_w(tempReg)); \
+ (differenceOut) = tempReg - (crypto_word_t)(borrowIn); \
+ (borrowOut) = borrowReg; \
+} while(0)
+
+/* Old GCC 4.9 (jessie) doesn't implement {0} initialization properly,
+ which violates C11 as described in 6.7.9, 21 (similarily C99, 6.7.8).
+ Defines below are used to work around the bug, and provide a way
+ to initialize f2elem_t and point_proj_t structs.
+ Bug has been fixed in GCC6 (debian stretch).
+*/
+#define F2ELM_INIT {{ {0}, {0} }}
+#define POINT_PROJ_INIT {{ F2ELM_INIT, F2ELM_INIT }}
+
+// Datatype for representing 434-bit field elements (448-bit max.)
+// Elements over GF(p434) are encoded in 63 octets in little endian format
+// (i.e., the least significant octet is located in the lowest memory address).
+typedef crypto_word_t felm_t[NWORDS_FIELD];
+
+// An element in F_{p^2}, is composed of two coefficients from F_p, * i.e.
+// Fp2 element = c0 + c1*i in F_{p^2}
+// Datatype for representing double-precision 2x434-bit field elements (448-bit max.)
+// Elements (a+b*i) over GF(p434^2), where a and b are defined over GF(p434), are
+// encoded as {a, b}, with a in the lowest memory portion.
+typedef struct {
+ felm_t c0;
+ felm_t c1;
+} fp2;
+
+// Our F_{p^2} element type is a pointer to the struct.
+typedef fp2 f2elm_t[1];
+
+// Datatype for representing double-precision 2x434-bit
+// field elements in contiguous memory.
+typedef crypto_word_t dfelm_t[2*NWORDS_FIELD];
+
+// Constants used during SIKE computation.
+struct params_t {
+ // Stores a prime
+ const crypto_word_t prime[NWORDS_FIELD];
+ // Stores prime + 1
+ const crypto_word_t prime_p1[NWORDS_FIELD];
+ // Stores prime * 2
+ const crypto_word_t prime_x2[NWORDS_FIELD];
+ // Alice's generator values {XPA0 + XPA1*i, XQA0 + XQA1*i, XRA0 + XRA1*i}
+ // in GF(prime^2), expressed in Montgomery representation
+ const crypto_word_t A_gen[6*NWORDS_FIELD];
+ // Bob's generator values {XPB0 + XPB1*i, XQB0 + XQB1*i, XRB0 + XRB1*i}
+ // in GF(prime^2), expressed in Montgomery representation
+ const crypto_word_t B_gen[6*NWORDS_FIELD];
+ // Montgomery constant mont_R2 = (2^448)^2 mod prime
+ const crypto_word_t mont_R2[NWORDS_FIELD];
+ // Value 'one' in Montgomery representation
+ const crypto_word_t mont_one[NWORDS_FIELD];
+ // Value '6' in Montgomery representation
+ const crypto_word_t mont_six[NWORDS_FIELD];
+ // Fixed parameters for isogeny tree computation
+ const unsigned int A_strat[A_max-1];
+ const unsigned int B_strat[B_max-1];
+};
+
+// Point representation in projective XZ Montgomery coordinates.
+typedef struct {
+ f2elm_t X;
+ f2elm_t Z;
+} point_proj;
+typedef point_proj point_proj_t[1];
+
+#endif // UTILS_H_
diff --git a/src/third_party/wycheproof_testvectors/kwp_test.txt b/src/third_party/wycheproof_testvectors/kwp_test.txt
new file mode 100644
index 0000000..ef48491
--- /dev/null
+++ b/src/third_party/wycheproof_testvectors/kwp_test.txt
@@ -0,0 +1,1562 @@
+# Imported from Wycheproof's third_party/wycheproof_testvectors/kwp_test.json.
+# This file is generated by convert_wycheproof.go. Do not edit by hand.
+#
+# Algorithm: KWP
+# Generator version: 0.4.12
+
+[keySize = 128]
+
+# tcId = 1
+ct = 8cd63fa6788aa5edfa753fc87d645a672b14107c3b4519e7
+key = 6f67486d1e914419cb43c28509c7c1ea
+msg = 8dc0632d92ee0be4f740028410b08270
+result = valid
+
+# tcId = 2
+ct = e8bac475d1429034b32f9bdeec09a37f9b3704028f1e0270
+key = a0b17172bb296db7f5c869e9a36b5ce3
+msg = 615dd022d607c910f20178cbdf42060f
+result = valid
+
+# tcId = 3
+ct = 4c8bcd601b508ef399f71b841294497a4493c4a0014c0103
+key = 0e49d571c19b5250effd41d94bde39d6
+msg = f25e4de8caca363fd5f29442eb147b55
+result = valid
+
+# tcId = 4
+# wrapped key is longer than wrapping key
+ct = 9e4510cc84c4bd7abab0a8a5d7f1e6ff3e6777ca2dff9be7e223652239fe57d8
+key = e0e12959109103e30ae8b5684a22e662
+msg = dbb0f2bb2be912a20430972d9842ce3fd3b928e573e1ac8e
+result = acceptable
+
+# tcId = 5
+# wrapped key is longer than wrapping key
+ct = 8fbf39ae583bd4efa7a3e8f7b86870b34766ae7d8923a8e97b0cd289ad98cacb
+key = dd583d9f1059861430ec8b5d8a180e9b
+msg = f2e34f356362a31b51d6e02bcd333c9e6170494ca5ff5487
+result = acceptable
+
+# tcId = 6
+# wrapped key is longer than wrapping key
+ct = df2fbe5fa86418edc7b5b04a4aea724aca17e88cedc84ca8b0b0f048e64590cb
+key = faf5ccfae42b43cee2c5f0f3177a7c5d
+msg = 4e02084833660c463830483b36dab866c64c8cf7429cac3d
+result = acceptable
+
+# tcId = 7
+# wrapped key is longer than wrapping key
+ct = 67f8edf57f84ea0a35b35511d67d3f299c9984b2c07d3809c3d7f5f45091f1a8fbb937ed447677f6
+key = c2b9d23f2831ddcdeb456853d4014db9
+msg = f4cfea98e58b939cc859554385cf3a6c7f8217f728efb431c964786de8274907
+result = acceptable
+
+# tcId = 8
+# wrapped key is longer than wrapping key
+ct = 60d55a22ba7dbd7d8f317388e01e6be561d15d29f85c566f1259aa7e7dc3d5d30e0ef5f4c6267553
+key = 620a08f320cdedbf7ae551add348d95e
+msg = cec34eaf8e67e1ce619ddfc309531c42f16033a7e2cbc4f5eb3a548164e9b291
+result = acceptable
+
+# tcId = 9
+# wrapped key is longer than wrapping key
+ct = d78a8291108f0f2d8be0ec10ec08240bf4d3021f0a5ed7faba0748db73762f34a0504bd373212df2
+key = ed089ac274f8c7cea2415671a94b5e53
+msg = 6065e41df14daeeefacac5daeb7674cdc9c1f686013b797153e80ef215893299
+result = acceptable
+
+# tcId = 10
+# Round counter overflows 256
+ct = 9341221aca1c647e2afc2bdd9cf4ed6e60058eb0a84cb3fc2daf3a87d9fad0a1f8268b27aaf7201d705e72f7e2240309ad98742094e3f1c99b7faa9ae181b441f5004b8bc93cdd4160d403d0884749a3c379d47c112a45788c05c2106c98f59758d393e04c880691b0e8683a12df7f876e1e1f68b4acbae9cc8310b34d59ccf4617cee72e845df1e0e32e5b4938f2923d55f1bb5156dd8c787401e6ef241ea4073d0a59ddfcd7a53db5d89b480b030cfb9084ea8479b964f090bb612d5251eee9ef8870a45f1e76fd24abdd9b350fe148b15a4cfeb032d57b5743b3548a7ce9eec8e21a31ce832530edfd1cffd9bb37369e6463c6b373ab60d80b0a2677e92e658f7daf2a5234b7312bf2d967cd0bc809e9be2f706ae63bd632fd611f161e48ee19677f3243aa0e91f6651a1cef62feff7a72eedf830bae1dc6d89e55ccb5e6f97889c6266f7d3f2eb0aea6c8c42200febccc5916825368adc87e04e835de06fd7bc2805c219e7f0b6252563f29969b1f30cfa1a8da4b90ae7534fb849d068a7e77de7360f8af173
+key = b6121acad51038e11873aaa7e6c7be06
+msg = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+
+# tcId = 11
+# wrapping small key
+ct = a65959a600000000
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg =
+result = acceptable
+
+# tcId = 12
+# wrapping small key
+ct = 09bcbab50b8dd45ad83412e2919030d3
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 4c
+result = acceptable
+
+# tcId = 13
+# wrapping small key
+ct = 0cbe852cdce4f0b5333366f446b2b1c5
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = be52
+result = acceptable
+
+# tcId = 14
+# wrapping small key
+ct = a9dc66e03435ab3d4f97ff66f2c911a3
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 2d5244
+result = acceptable
+
+# tcId = 15
+# wrapping small key
+ct = 1b970c8ecb4187447e60e6083da03086
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 6c3d3b4c
+result = acceptable
+
+# tcId = 16
+# wrapping small key
+ct = 0344f7b34ab8ef28aaa843f276b0b3d5
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 0412ab3ec6
+result = acceptable
+
+# tcId = 17
+# wrapping small key
+ct = 17356c7148334ca1a24aab7e82a66e18
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 8ae08938929c
+result = acceptable
+
+# tcId = 18
+# wrapping small key
+ct = 1db7510a55591a455d9f8167e6db3c88
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 7c8dfbb68d72af
+result = acceptable
+
+# tcId = 19
+# wrapping small key
+ct = 936fe58b629ea6ec158145218f2361c7
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 536f8f83b64771c1
+result = acceptable
+
+# tcId = 20
+# wrapping small key
+ct = 6787816804b3127d0ca4073f1dba5c4d3db1ec9c227e6556
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 8571f282b18b64ec5e
+result = acceptable
+
+# tcId = 21
+# wrapping small key
+ct = 34131c3bfcc48af15eea8672e52927b462f81d5ba0e6260f
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 8ada889862813e364c4d
+result = acceptable
+
+# tcId = 22
+# wrapping small key
+ct = 4d1ec9287cd4dd378b9aefee79d4ed35bcb98ad9fa9fe529
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = f9c56e8058758a5c7c2baa
+result = acceptable
+
+# tcId = 23
+# wrapping small key
+ct = 7209f5b6bd5d4916f4995d280e9aa89edd5e96e3c9283ad2
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 7c7dbc83fa62206a521ed4ad
+result = acceptable
+
+# tcId = 24
+# wrapping small key
+ct = d85a1efc6ab3a40948f723d9810a5deb019b3ce0208a0d94
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = a6614daf00df6d14f50388bad5
+result = acceptable
+
+# tcId = 25
+# wrapping small key
+ct = 43509b5df3688b6e44c1a994592f4c03da34712f886e63d5
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 450580a47d7008321496bfb82f48
+result = acceptable
+
+# tcId = 26
+# wrapping small key
+ct = 16e369351c40f220d3fb1197f35da652a3a40ca3b1e99bfb
+key = 1abf4b7fa2bb62a78f09ddab04625dca
+msg = 9efd21e13855eea8907afdcd8935f4
+result = acceptable
+
+# tcId = 27
+# Modified IV
+ct = 82af032f5389caa503147d2825336eab84816fb6f8ae6df4
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 28
+# Modified IV
+ct = 4e00a9eeef87eb6d7be4ec46204d94006c216d5177d2a83c
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 29
+# Modified IV
+ct = d3dc6c3b4707a08039d621879caf419b9895482fff7bdcd0
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 30
+# Modified IV
+ct = 09d3bfc3c9c5af2b2951b06406f7ea4d84e9c37402637e2c
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 31
+# Modified IV
+ct = 3396679a4d87caf7ce7eb4707ba1c6526728f5a973191713
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 32
+# Modified IV
+ct = ec637d90d945e92929c1c873d9aa9c47bc7b172237319d15
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 33
+# Modified IV
+ct = 748f373d48d8590e2216b294b9ef94860dbb6b0b0ab625c5
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 34
+# Modified IV
+ct = 61d7c584197f257caf2583e444896f1d3ba12509b1ef725b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 35
+# Modified IV
+ct = 7f8cda973fe58b484b120fc710b520c5636057629795f89a
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 36
+# Modified IV
+ct = ccea198029edb9d848d6ca76667b666b1dbebd1e4b1faa8d
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 37
+# Modified IV
+ct = ee08cb9d20a98b88b2d8f0e39acf34219d105dc14afbe364
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 38
+# Modified IV
+ct = 6782992bf8cff068cf41341dd2ca04adedea92e846f74411
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 39
+# Modified IV
+ct = 7ed35d0c08042dd56bb5df78056ecd21b8c797d36f57aaec
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 40
+# Modified IV
+ct = 37e3b4cefee648766a8efe73d6af12812eded603ab7141bb
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 41
+# Modified IV
+ct = fe73777d8992e07eef0d053ad5ec0bf8243fc7e0bc2b405b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 42
+# Modified IV
+ct = 39292c91b6b826d47d502043c3ba4f41e2ce32960a0291b5
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 43
+# Modified IV
+ct = 36ef8fc13d0f1f5745e3939877b62b8ecba2f5f0b19f9e90
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 44
+# Modified IV
+ct = 7255c4eacb4105a68095e9e5b5a4bd8f9623a0da5c6fc230
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 45
+# Modified IV
+ct = ea26eec89a46ff1a628834c7247a8e4e45d8a8d3229e26cc
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 46
+# Modified IV
+ct = 508593fa85a8effd27c8a225981978fcec6e992eb488c9c2
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 47
+# Modified IV
+ct = b8a4cb22f15529864d4ced8e8abae69752a9045a084dfc3f
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 48
+# Modified IV
+ct = a0a6bf5e47e89706932b1057b680c3c81dc4d9d0b4f9153b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 49
+# Modified IV
+ct = 11f3af4ed30e77520517c880f1d0c272a89a968dc697cb5a
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 50
+# Modified IV
+ct = 6fc912a0bda73bacfa93db4002f18f349fa30f22f7a95ab9
+key = 4f710eb6b5e28703becfc3dc52fa8bc1
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 51
+# RFC 3349 padding
+ct = 3731038571c35f7dcc55e48892de353e54c079b89774bbfd
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 000102030405060708090a0b0c0d0e0f
+result = invalid
+
+# tcId = 52
+# Invalid encryption
+ct = d85c6bfd092df1aeae5a548e47aa7681
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0001020304050607
+result = invalid
+
+# tcId = 53
+# padding too long
+ct = 7a92427387f5587ee825d1ffa011c40286844ecdadce31cd9678338694ea2682
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 54
+# padding too long
+ct = a437d354606ae752894feb62c8def7d17046d8e47f9aed755fba48b3a3009e3ff67d34e26a779064
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0000000000000000000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 55
+# incorrectly encoded length
+ct = e8d240d64f16d1522ae2ded42ced257dfec158ff2fe1467d
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 56
+# length = 2**32-1
+ct = 6d1bfda356b7b954e7aaccc6df953322f75be95947b02b30
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 57
+# length = 2**32-1
+ct = 17dbf878ef4076cfcaba5f81d7b123d7
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0000000000000000
+result = invalid
+
+# tcId = 58
+# length = 2**31-1
+ct = 75c23e253478037802fae0f86af9c78d4e4d9be0c3bff89f
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 59
+# length = 2**31 + 16
+ct = 55717658c6a35e15ee36c66cce91083b63091f51525c0b51
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 60
+# data is incorrectly padded
+ct = 8ede88a52ccb8a6d617456955a9f04c94d87696125ded87eebe3e97e185496d9
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = ffffffffffffffffffffffffffffffffffffffffffffffff
+result = invalid
+
+# tcId = 61
+# data is incorrectly padded
+ct = 5b4a8f1abffa51676ac8b5ddf9366c12
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0001020304050607
+result = invalid
+
+# tcId = 62
+# length = 0
+ct = 205cc6dd9592da0ebff6b4b48a0c450eeaeb11a60d33f387
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 63
+# RFC 3349 padding with incorrect size
+ct = 908a68b0d2054e199220d37c34a2e136
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0001020304050607
+result = invalid
+
+# tcId = 64
+# length = 9
+ct = f84bdb15045cee3a8a0f3ed2f07c1771
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0000000000000000
+result = invalid
+
+# tcId = 65
+# length = 16
+ct = 7592b1ee6ee92c9467db366adcfa65bb
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0000000000000000
+result = invalid
+
+# tcId = 66
+# length = 2**31 + 8
+ct = db93a1db3b5babc80a304d527682c1ef
+key = 48a53c11ef2d727db7eb9a834b134ea9
+msg = 0000000000000000
+result = invalid
+
+[keySize = 192]
+
+# tcId = 67
+ct = 5c117a678223cfe5ee691503061e7ab1e5f720e005171b32
+key = f75a2f49a630c7dc91626b00ce029f0bd2981d7c74a93ebe
+msg = 9adbc00c710b1101bdf6a4ed65b32d72
+result = valid
+
+# tcId = 68
+ct = 6a7f9e03b6f379c56da3a56d8f32eba515454a91fd417449
+key = b713f6b7814f98894d7b153974684359f1460213eb74be68
+msg = 78585f0c49922e82caf17ebc3721b4db
+result = valid
+
+# tcId = 69
+ct = 764097f5ee8236bc0d93bbcea139a652f4b211cc33a61ac9
+key = 13ecf423211caa334ba6db37259a535c20de8ad10fc8c432
+msg = 4fc75d0f221e22408a37e11265d49a05
+result = valid
+
+# tcId = 70
+ct = 04b83ec803a75bbcb2f87fc6f488a4ccc1827b412483070eed195b6f0048ccbe
+key = 4417fbbea51bdd91818d74051957dd70e135c5cf3732bdf1
+msg = f5357da9f8fd4a1190f36e9fa09a90fcf14d87d62332f1a5
+result = valid
+
+# tcId = 71
+ct = 46ab71f032cb1ccbcc7447a5183574268c0167a26a93fe8422bf284417aa93ea
+key = b3f26d8a22fdd61f709841231fbde695b3f28dddced6d41e
+msg = 0d0af955d2e3829cc3d643219b301e64e0510dfbc428119a
+result = valid
+
+# tcId = 72
+ct = 47ca298ee47b1b755a499129347e11e7a25754ccb6c2689e8eff270e98c81d18
+key = f70cfb262c729a18206c8afd74356ec7e049d10b44a6e000
+msg = 241cedfa64c4e7bec541a2eb4c368269e0f0ddebc58267ea
+result = valid
+
+# tcId = 73
+# wrapped key is longer than wrapping key
+ct = ecac4c91758e1ae7bb010c34f4c5f99a3d728b9fa92cb778d3fe80d777a20d3de85ef46e7a0c6a6a
+key = 1639f9f81e53e2eeb677a249e5eced3af108971301601a7b
+msg = ec3c6a1f1a9585327fe658490c74635e5300876da5846a629398984fb551d691
+result = acceptable
+
+# tcId = 74
+# wrapped key is longer than wrapping key
+ct = 39b7326a44eaed08bffbd4aeaf3e2c3f899c1fd049384ed7b3eb92b788c6449acd6385f0bb18cf28
+key = 1f22d5658aa685b8ba8659dc342880d5b2399e6a815005b0
+msg = 50be4c1b2f29a63f44d7fc63737f600f0194ea3fb36e173d2ddd19f218656380
+result = acceptable
+
+# tcId = 75
+# wrapped key is longer than wrapping key
+ct = 3d2e9f39c7b13e9585227c4344fbe596f92b002456616f137deacc6a8c941649ce294bb2695c1807
+key = 3a2f4aa50441954bba5a1836294ce071f9296b23dbed6771
+msg = 65da02ff21b483a1e39575490b4319e84ae0299f1f00b3859fbe2e74b3ec2aaf
+result = acceptable
+
+# tcId = 76
+# Round counter overflows 256
+ct = d6aacfb52c26baae78c2f54259a4e4168f817064344e2ba8fbfa7fae9f1fd69bd5bc5c1e20a6101b4a7119cbce028e25a9e93d29ee260c4e609baedee788411c2afe60218ce1b0d28b9c29b941251fdcbac3009d59040a0337b8b4a3a020c6d8f310cba63db046d8f36b64c9092e75cee463fc7692ef56bed395c4579da0ecb02129e45ad8a7f116aac6170204888e40693f017a6a0a7dd3962004e60db3a9b6c8b7614a467ccb799bce1ba83f5c0921f1e52bb3909bc0486ec0eaea736498f3ba520a519c3ddf491307958620b737613417b15b438b80b43189baa455031f5771502002ea170c767b33d247feebce62e606f2262537f85f18d1951cc75cedef291c6a501cb1778586249b58156eb8d7283a3f508ee8bcc1206d77bbd6892fe74b865bfc02a8f07223087a6c1e50a41b7cf5f6ee04bd07766b2e5b34c4a7666b0ce06f670e6434a59fb74e0df36c91d94e5e8b721e53e09b6f6504c5d515492a373fcc348a63122cc6e4716e0e1a543d038c6f7731199f691780a8a655cca6718e3dc56e815b3669
+key = b6121acad51038e11873aaa7e6c7be06f93826b74fec0ea1
+msg = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+
+# tcId = 77
+# wrapping small key
+ct = a65959a600000000
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg =
+result = acceptable
+
+# tcId = 78
+# wrapping small key
+ct = 52c7f388d0d4237afaa29f2b94723475
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = a3
+result = acceptable
+
+# tcId = 79
+# wrapping small key
+ct = 833431ce8799be69b36aafe3f38d9dac
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 594b
+result = acceptable
+
+# tcId = 80
+# wrapping small key
+ct = 31674f46b989f6ead582c70dedc8c6b9
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 72ab34
+result = acceptable
+
+# tcId = 81
+# wrapping small key
+ct = 80535172d2a498aa31601d70fdca9dea
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = d4d9460f
+result = acceptable
+
+# tcId = 82
+# wrapping small key
+ct = 56232300dd7b2a71d2328b6df47af8e3
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 643972e552
+result = acceptable
+
+# tcId = 83
+# wrapping small key
+ct = e27e08efe39adbbad8d300b87be2c258
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = f3cdb73d2561
+result = acceptable
+
+# tcId = 84
+# wrapping small key
+ct = 8f90942cdab33e58b24a23ad7efb7538
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 7b0b53b6429e14
+result = acceptable
+
+# tcId = 85
+# wrapping small key
+ct = 0ebaf23c858015d3bda5b8d908db6049
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 6b2393773e6d1378
+result = acceptable
+
+# tcId = 86
+# wrapping small key
+ct = d56f89977b8eff511158edad6b993007189e5a4b8c0e2faf
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 2c52d6639e769960e8
+result = acceptable
+
+# tcId = 87
+# wrapping small key
+ct = dd889475a76733849f59bed49a15d4315bdb5ba00dc63470
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 707c9356216d69c69048
+result = acceptable
+
+# tcId = 88
+# wrapping small key
+ct = 1a9b3369239b0f40a8dc5bd8d965caf7431445799337b99b
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 615f6fa79e1847e7359a8a
+result = acceptable
+
+# tcId = 89
+# wrapping small key
+ct = 5232f8f6679a17d3303b0bd72b06b56b5089e80372dc295b
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 7f5e999168ec60624426cbb1
+result = acceptable
+
+# tcId = 90
+# wrapping small key
+ct = e5544361c60980f3d38f2d8820a150f48f49ef3f9184b29f
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 3f93aaf4463775baf6c0c975ae
+result = acceptable
+
+# tcId = 91
+# wrapping small key
+ct = 55396065905915ec914b8d1efbf471e37d283fc2c1496b49
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = fefcf10c976309b2beb085771e50
+result = acceptable
+
+# tcId = 92
+# wrapping small key
+ct = d90376be302a24c541bd6d96094f0025e3d73888391b4306
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b
+msg = 6854354d0099f7eff740b0587140b3
+result = acceptable
+
+# tcId = 93
+# Modified IV
+ct = eee27510be39cc88379459420f3773642a423ac1ff0cfb84
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 94
+# Modified IV
+ct = 765df3fa1aca6f13268ba79f8659807049a313a0308b643e
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 95
+# Modified IV
+ct = 71346c17a2718cb7c357e3af2b2d0c3e29b7e02317926746
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 96
+# Modified IV
+ct = 55fd49ba081fdf72896068c5a968e2b3c4a473786a2e12c2
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 97
+# Modified IV
+ct = 133c66fcbf0e9d5139eff3fcb494b672d72bb622d7015c4b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 98
+# Modified IV
+ct = 8439244f27470e5f1f294cfa22ef5412675d7fbbd92ff016
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 99
+# Modified IV
+ct = 4265bdb7d8ea30d9a51e5f48b7ac5487e0c95f154ea8baeb
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 100
+# Modified IV
+ct = 31afcca8ff2b8806408c3460181ee5a96bbaf51d133211be
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 101
+# Modified IV
+ct = 196f2a6eccb5368fe6a3f2fa0874d8fc9b3b52484e2d6351
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 102
+# Modified IV
+ct = 8dc73d363fdb32f6e0ff830c2a48db5815f66d0922694c74
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 103
+# Modified IV
+ct = e90022b9da998b4a30c91c1bd1a1f8ca05a52432867e5e78
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 104
+# Modified IV
+ct = c9898a1b70bd718df45f1f3eca82eab1eaddb8ed7f2380dc
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 105
+# Modified IV
+ct = cc3f2cd6476eddbbfdc801b61174301688554f3db54c2903
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 106
+# Modified IV
+ct = 23e15705e7b00d82bd052f0e0135ab7ac0dcce471ff2f1a7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 107
+# Modified IV
+ct = b8e2862c0f9eae4f44ad99496e3ed62b3b9c4ce7ab5afb74
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 108
+# Modified IV
+ct = 66d8a7769d81421efda456992f6c26cb17665fe080b0160e
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 109
+# Modified IV
+ct = a6a28bceb91551a395369ff09370658cc92b092855f417aa
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 110
+# Modified IV
+ct = 03ff601cf12b432078a2185590fb5d01e3441cf084bcb04a
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 111
+# Modified IV
+ct = e250d358d16d9fd20ad80a99656509229dca391aad3798f0
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 112
+# Modified IV
+ct = 643a17860b116ec74089bc574685a6328a3d7a07cd18b520
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 113
+# Modified IV
+ct = 3e86e8128904f753c0f3fe3401ba36672966567725c4726c
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 114
+# Modified IV
+ct = d4b8cc849176b8344b0849490143d3512915171bd7d5759e
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 115
+# Modified IV
+ct = f84e0e6ff64e0b27b8b59b5b77c223023f0fea95433864ec
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 116
+# Modified IV
+ct = 4030b4b0e9c1b1ce8e52f6bdb48088e65b05844307989c8b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 117
+# Modified IV
+ct = ccb3b36c26b2d901b7f0765362d992b2d5089c2a7559b195becbe173780352fa
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 118
+# Modified IV
+ct = 4e5fc8dccaeec9b1c8a606a2bd7d7201eede62b9c2e939a5aba663a6a040e361
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 119
+# Modified IV
+ct = af21f5e7f15a63c8ea6001cf024f281e7f44aedd68954564fc2bd146e96d793a
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 120
+# Modified IV
+ct = 3a4f571ffbf761d3f7d413172ee1e4ae2862baacfd5ab66dc685b9af8b70b538
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 121
+# Modified IV
+ct = 273de386d5fef497f9487afd54c1c0fae8aacabf2af465caf352e2300d29266b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 122
+# Modified IV
+ct = 16511743dc44199cee1dbf5045141b075f01ee13326c9faf2c74b7c99791830f
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 123
+# Modified IV
+ct = 370f92db00f7fc8a0e654318a5b3ff89a604034f421339201d79e0ec4d6088de
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 124
+# Modified IV
+ct = e3edd0e84832f3615f6deefb444de3b9ec527741686029db91de0bb9b2a5c05d
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 125
+# RFC 3349 padding
+ct = 36ee480138edf11e144efcddd24d2c121749da6e4eab17fe
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 000102030405060708090a0b0c0d0e0f
+result = invalid
+
+# tcId = 126
+# Invalid encryption
+ct = 166beb49e97a4a9cc7b0ccf441ec15b5
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0001020304050607
+result = invalid
+
+# tcId = 127
+# padding too long
+ct = 74ff3070a0a08471c001febb95a890f35159a9fe263719e40c2332ce5c58fada
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 128
+# padding too long
+ct = 4f0b38eb328d1227b1e17c103a44a373ff67cee953c59eea26117947b5d3ef8932c8858b4f9fb47c
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0000000000000000000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 129
+# incorrectly encoded length
+ct = 775dcabab9e4be8fd9963a4dc7a1447ef82888403882bdb6
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 130
+# length = 2**32-1
+ct = 669803237fa10eabb4d2c6ad85bd9f7df5f4a33340eb0ce9
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 131
+# length = 2**32-1
+ct = c788504d786f5c21b6671bf190657301
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0000000000000000
+result = invalid
+
+# tcId = 132
+# length = 2**31-1
+ct = d079f60d3258f5e695d1a73db008ef38516b713eca2c0eaf
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 133
+# length = 2**31 + 16
+ct = f1ae4b8865013b0fc63b463e664cec3c6031f61f2de82f43
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 134
+# data is incorrectly padded
+ct = 8874e1b6e15e3ef6c461411a5f5ad0c8b05368cd5b3ee39b2b413d18a4eebfc9
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = ffffffffffffffffffffffffffffffffffffffffffffffff
+result = invalid
+
+# tcId = 135
+# data is incorrectly padded
+ct = 890a3dab8439bb73b14c6e99c34f0b0e
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0001020304050607
+result = invalid
+
+# tcId = 136
+# length = 0
+ct = d4f633aedeb89e349a98738b00ee42c90d583b16e986e49f
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 137
+# RFC 3349 padding with incorrect size
+ct = b8b2a5b1d3280dcb4daeeed43f36509b
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0001020304050607
+result = invalid
+
+# tcId = 138
+# length = 9
+ct = 4429cf64251d8a54a9d1389c01c30900
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0000000000000000
+result = invalid
+
+# tcId = 139
+# length = 16
+ct = e5634eca10372c867c7f91ee813ec3f3
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0000000000000000
+result = invalid
+
+# tcId = 140
+# length = 2**31 + 8
+ct = 9e517d4d0142e1544ba1e7419a696c21
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702e
+msg = 0000000000000000
+result = invalid
+
+# tcId = 141
+# RFC 3394
+ct = 138bdeaa9b8fa7fc61f97742e72248ee5ae6ae5360d1ae6a5f54f373fa543b6a
+key = 5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8
+msg = c37b7e6492584340bed12207808941155068f738
+result = valid
+
+# tcId = 142
+# RFC 3394
+ct = afbeb0f07dfbf5419200f2ccb50bb24f
+key = 5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8
+msg = 466f7250617369
+result = valid
+
+[keySize = 256]
+
+# tcId = 143
+ct = e3eab96d9a2fda12f9e252053aff15e753e5ea6f5172c92b
+key = fce0429c610658ef8e7cfb0154c51de2239a8a317f5af5b6714f985fb5c4d75c
+msg = 287326b5ed0078e7ca0164d748f667e7
+result = valid
+
+# tcId = 144
+ct = 9d2b42fb2fdb92c89fb0c3bcd9e1600d3334b4e35e791369
+key = 0dda6da5123e2c37c6fa16ba0d334cd01acd652f8994211751dfab4faac2fc22
+msg = b40b6828729b456322a8d065abc0d081
+result = valid
+
+# tcId = 145
+ct = 5291e05abd55f5886850855e3f9f2f576b101acc222d6766
+key = d6925914cd06308f81ad91e23073593d99d4e50351b20eb2a8d1a1ac4ced6588
+msg = 037b27b3dc95b19d15bd4091e320bfe1
+result = valid
+
+# tcId = 146
+ct = 4b1220525c537aec30ebcd562b694b4e9e2ccd819de22ef608b5d8090779d9de
+key = 07518a82cbc8da1dcec55f3763a206d277487abd03cedd0b8bef9ee2fb157121
+msg = faa4664d79fce3c7d2fdd462f6c1c423c2f8e6b69be2e071
+result = valid
+
+# tcId = 147
+ct = 67b2cbd68f6a208d647bdc5af7d0bccf6711a9e8fd0d9434363006addd4b9696
+key = ea46991d4e71f53dd624e7fe7fde11944a7c5942d232369b8065d42b8cd2dde1
+msg = dffc5cf1dd5411d015d84601fa38df5effe885c7f26a4825
+result = valid
+
+# tcId = 148
+ct = cfdbbd95f187508a488fe017c5e5d5a5975b68441d520e0e931922388e28784c
+key = fdcfa902c6f222f527af84da533b14b52e2615da3a89d1d35708b0cd49f60d87
+msg = 966b07047354966a703e79607b556032f4f596b7f9206f05
+result = valid
+
+# tcId = 149
+ct = b63b7e0fec7e315816233db6758fd3e744b9f6a40862bdf866487e53bcb950d8b2649269e51b4475
+key = 38e1b1d075d9d852b9a6c01c8ff6965af01bac457a4e339ae3e1d7b2ffacc0cd
+msg = 80ad6820f1c90981e2ca42b817a345c1179d0a11d8e23a8adc0505e13d87295a
+result = valid
+
+# tcId = 150
+ct = 837cfc316b49299edaf427e0988020ee876204b29d847669daab72c8660b0d860e9de3bd851198ff
+key = c641f1689d81caa8ba37d895272240664054ed974cfffc40e6c5c0cad1b916c7
+msg = 3fd0ba19955e46749f54d88e99d080b7339d588fe612ec0f4021ca3ca2104270
+result = valid
+
+# tcId = 151
+ct = 0e9e2e9aa34bbf973d67bc534ac86fc5b5a5f9da5f026866177894ec6077a5c84501510e1bf4afb3
+key = aa0ab9d68ed4a04e723f81b44c0c88d0bcde7a80cfd476eb4b8836d9aa01ec4c
+msg = 57faa8766f6d6a0aa1cf643f857c150df5b31303b50af480e21c4b5e8c8a15d5
+result = valid
+
+# tcId = 152
+# Round counter overflows 256
+ct = 1c6b7e4003384f071bf29baea9098ad81da8e9862909329f52793b35d592c10dba15aa89400ea6403df8dcaffd0dbf5606303f109f79ad700ed5d5ad4e59950ce9ce5296c9d186a0df441973d1835f9ac000ad1a6797875c3a03161e9e3f5ea464032e407854eadca5a9e7a386bb0d29253e3804adefd8c0402cc8c40ac7f9041429cc0bb77a405b284baa2dae764ea09c654c0a82f2c5724221ba44e341503d3103dbc393c7702182f8cc2762ddbc873b7f84197709886a4b5df5b04ff9d21b79b50904af3c32128dfb9cde94fe1254d981e6ce3acfda82db1fa2badbccd2d29052a04a69ce1f5652f30496ea57edc7e3e885dd4a35ca15aba602bb4c888a8064da94c2ac5c12c11f608810af46fbb49c3e8f8771ff661f8d8dccd163d0c4a401b8b9aa74e68a56011cf78d21dc7541a974f9dad5ae27f8a26d1b0e76be2f86c6a21e9d1c2b5df3c8878a8bcae143b3af1f082afc52616eeadd2232926597b245d394931e02e493b0bc27a92d013e111694cac2c5a2a46e008a8498b5c31bb5ec35a4e9957e365d
+key = b6121acad51038e11873aaa7e6c7be06f93826b74fec0ea1c02f9981ed49d16a
+msg = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+
+# tcId = 153
+# wrapping small key
+ct = a65959a600000000
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg =
+result = acceptable
+
+# tcId = 154
+# wrapping small key
+ct = 06c1e65ac0f385b4e8c400d229f39422
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = ae
+result = acceptable
+
+# tcId = 155
+# wrapping small key
+ct = c98da5936a1313eba1a6773b8060ea5e
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = c548
+result = acceptable
+
+# tcId = 156
+# wrapping small key
+ct = b2a77d9b837e87cdb7391e1df7cdaf14
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = f713b9
+result = acceptable
+
+# tcId = 157
+# wrapping small key
+ct = d8ecf20191f75aa36686298bfa5022ab
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = f375cbf7
+result = acceptable
+
+# tcId = 158
+# wrapping small key
+ct = 077362f50356fc7c54c70f9cb4306f7d
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = d9445094b1
+result = acceptable
+
+# tcId = 159
+# wrapping small key
+ct = a4bd6a116ad88a52aae3f0c0cb893f9b
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = fab43e91ae15
+result = acceptable
+
+# tcId = 160
+# wrapping small key
+ct = 68a52de00ec0f1ebbedc38fee6be0c23
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = 90735025797bd2
+result = acceptable
+
+# tcId = 161
+# wrapping small key
+ct = 3a6746052a1744cfe7e2f36dafc4042d
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = e43f5e4e123a03c4
+result = acceptable
+
+# tcId = 162
+# wrapping small key
+ct = db7e73da22219e1baac0f4e955c3db2b900b5d3078f94b59
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = 1723eb9d000916996a
+result = acceptable
+
+# tcId = 163
+# wrapping small key
+ct = f77ec14a010777f1f1071808f285c1c00b4e9420f0e8bf48
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = 8b18daecde14b8472ffd
+result = acceptable
+
+# tcId = 164
+# wrapping small key
+ct = 6b40d4f0863581a7d0365ad477568bfad94f8bf134984838
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = e5bd6fbacbf3ef0d40c884
+result = acceptable
+
+# tcId = 165
+# wrapping small key
+ct = 660f645b02405a18f7225b68c0a09a949b2b5ba784922cfe
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = b3be5e5397df5f46b099e821
+result = acceptable
+
+# tcId = 166
+# wrapping small key
+ct = 6bea6bf57601bf063873f47ec3572cfb9cfb595d8bdb5e97
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = 4cdd960cabcf8aaf69c37da1d3
+result = acceptable
+
+# tcId = 167
+# wrapping small key
+ct = b631292536aaf02d829cc6d3c39e5a5cd76240889e9d51d0
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = da29e0889cf98742612e0326300b
+result = acceptable
+
+# tcId = 168
+# wrapping small key
+ct = de497acf18a177a3a9b3d8da46d74dfa58dcc537a3a95323
+key = 1abf4b7fa2bb62a78f09ddab04625dcacdd9e551d1a69b6b162baa53d2700093
+msg = 72aaee126a822184806c7d22eed66b
+result = acceptable
+
+# tcId = 169
+# Modified IV
+ct = aef4d2357a8fc5c3b4a80a15ed49781d3a82c98eb78c9180
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 170
+# Modified IV
+ct = 6eaefd5193f0725fea545077a430860663901979f0b6f4a3
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 171
+# Modified IV
+ct = f9ded536c1ae9c680f7d9c4b91a566a07b1628e9b9f4fccd
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 172
+# Modified IV
+ct = 443526477c779a329ded0b230307afa64fdc10dfc86414dd
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 173
+# Modified IV
+ct = 21ba79f3b423a66e7baad86fe49786e07a33dfdf227687e9
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 174
+# Modified IV
+ct = 3e65dbacaae556fa18bd192035cd55958adeac30e5ca7b3b
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 175
+# Modified IV
+ct = de2054883b00f81ff68e42b7ff1c05ef5faaf75b2bb14004
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 176
+# Modified IV
+ct = 2aa3c6ba891d1211677d59f886cc6d05698243d10dc189f7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae37
+result = invalid
+
+# tcId = 177
+# Modified IV
+ct = 9b1e7d6caf42bb3a15530f2387ed7329310ba76e1852566a
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 178
+# Modified IV
+ct = daf6a9f5e4b4985fcd4815bf6298a3039bcb32327b0876ff
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 179
+# Modified IV
+ct = 14c4079399721142fd5fce26e9417064c7e0201fb7b5255c
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 180
+# Modified IV
+ct = f48a30b8691a2a80dd79c355c281addf779bfed8971e3ce4
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 181
+# Modified IV
+ct = 248f867430ffc954b494c936a3ef815b1754009928aaf0c4
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 182
+# Modified IV
+ct = dcaa88dad9b03e59a3ac8350239824368004e2ca616c15d7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 183
+# Modified IV
+ct = a6cc8470192687ec9a31258ddb73084005784475f3442705
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 184
+# Modified IV
+ct = 0527ab5408b4f1484b27f98641511143ab88783688256815
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070
+result = invalid
+
+# tcId = 185
+# Modified IV
+ct = 7fd3ad3aee0545da1ed3a54d5a198a2c76cf8290c011c042
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 186
+# Modified IV
+ct = a24e94c12b2e6b776c8febe9179521beae0cfbd507d358b4
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 187
+# Modified IV
+ct = 9395b071fa3d9908b2e1b349bf7cd6a1cfc86b979c8c73cd
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 188
+# Modified IV
+ct = 1eb452770bc0f26a3576b604bf5ac72f714fc468c357eba7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 189
+# Modified IV
+ct = b42bcb4161f40b30f3d2f740f43e441d3c9a39613914f1c6
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 190
+# Modified IV
+ct = f3d76dd320e5f1b3f85b8f73a9ebcfabfb8346daafaf36e6
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 191
+# Modified IV
+ct = b8e26164496942f44f16751096fb47952ec478bb288e72a1
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 192
+# Modified IV
+ct = fa783b3aca0ec1e677378f23ebe937776fa590ecc6b01392
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5
+result = invalid
+
+# tcId = 193
+# Modified IV
+ct = 8b011408049eab81cc185796b9636982c1ad28e940e5c35ab1219434c23e8c59
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 194
+# Modified IV
+ct = 08db2f06aa2400d4cc1113b1c9e3ba1b39e3e26a84918f9266796c426c166428
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 195
+# Modified IV
+ct = 3114404be000ee167b65dd3cfae3b10c50dffe1df864b5e52a2805f0c80021c0
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 196
+# Modified IV
+ct = 405ae5bdeff8b05d28ea55900b8e81dc789d532ec3fc457730819e762172f751
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 197
+# Modified IV
+ct = 7c19e66d21c0f1409ee6f03a36ab6ba532349e2567200b95d7f5012b2b7e5d33
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 198
+# Modified IV
+ct = 955ac67d6e496b9b93a4dda8f6e65e668f1326b256ee146a7647ba18deee7986
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 199
+# Modified IV
+ct = c8600aa18be27279493fd68c84130c8bc328b0f6821e01e892b6c2dc1c005270
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 200
+# Modified IV
+ct = 492566e0dc539e234b08b95fb23594a6d14f59fa4367799495c2e7f2993135ec
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1
+result = invalid
+
+# tcId = 201
+# Modified IV
+ct = 8c5c2ea18125a03d15d2a624c9bfcccdf53709a89ae03d5728c98943b13df72c6f02fc8e1cfcdfa7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 202
+# Modified IV
+ct = 8836c5cb2eec2ca2541b18c1259933ebd601bd6763d9f7cebf06ed6abbe37d455aca13a2db87d111
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 203
+# Modified IV
+ct = 2554e0faf721d77f7dfadaaa90b70c2f242f93bdc4f876cd058a86ccfff33f8fd88736997f505d98
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 204
+# Modified IV
+ct = 53ee4c8f03212b389f5bc2b26bc898deb91a457f258a22028a688919e12c4da23090c26b5c9ff692
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 205
+# Modified IV
+ct = 3a63b0283ec071a4d4c32b0f30b384eccb3cd8d7fb12de6806e12fef5da82a7a39aad8128c3e5915
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 206
+# Modified IV
+ct = d1cfaaa9adc25f948c0c4720967b01488e06d3dfc5622b5de38a722798d4a3a44fa6194a92c5ede7
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 207
+# Modified IV
+ct = 251a71511a4e73d1469a051fd88fa78cae96547fd8ca8e323b05d8717cdcd239292c7bbe0708fae5
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 208
+# Modified IV
+ct = 14a62f7284124d795826cc89852e97dbe6b8a30ac56df07173878cf0136dbe386ec46327d6fc65f1
+key = 4f710eb6b5e28703becfc3dc52fa8bc1dd44a4a6d38a84b4f94e89ac32d987e7
+msg = a828cbda9b5ff0ae374f84fa01d070a5f0a17a0c462be4f1acce3497352690
+result = invalid
+
+# tcId = 209
+# RFC 3349 padding
+ct = ac1a774a5de27e4f9c356e4f62deaf8b7eeee6bcafafd895
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 000102030405060708090a0b0c0d0e0f
+result = invalid
+
+# tcId = 210
+# Invalid encryption
+ct = b3941437f55e7cbc3f88050aff703967
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0001020304050607
+result = invalid
+
+# tcId = 211
+# padding too long
+ct = 86175acf19ad0b7ac60d1fe4bb7850635e7ec6f8a314f85b6dd3d8f9349ea38d
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 212
+# padding too long
+ct = 791f088847a76731e0d56b9b2dcb28bf9f091a9725790e0a64fc8e7cb3ad50f380297a98e3b1c33e
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0000000000000000000000000000000000000000000000000000000000000000
+result = invalid
+
+# tcId = 213
+# incorrectly encoded length
+ct = 868c34495bd3d7b4e2c1861e7fcbbdb372099488dd96c9ea
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 214
+# length = 2**32-1
+ct = 4a8b4aeaa713469bfd9bf88d4072379fc858e40b24b0bebe
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 215
+# length = 2**32-1
+ct = c210aa3b5fbf5eac97e68d98d7727f38
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0000000000000000
+result = invalid
+
+# tcId = 216
+# length = 2**31-1
+ct = e0ebd376e050cc9027b76dfc38ee2c6ae2808cecf480a560
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 217
+# length = 2**31 + 16
+ct = 23a693e211c08ab9b222c2ede2db18f437e22917fdff8032
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 218
+# data is incorrectly padded
+ct = 003f2916fea6827e01199028d3dc4e03889113f97b1860cc242e5a0f28a0f159
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = ffffffffffffffffffffffffffffffffffffffffffffffff
+result = invalid
+
+# tcId = 219
+# data is incorrectly padded
+ct = 5c25a170d5225a6d66e117c691b37383
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0001020304050607
+result = invalid
+
+# tcId = 220
+# length = 0
+ct = df9ef924eb59634be5b27cabd33d72bd6be6e01e4672ab05
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 00000000000000000000000000000000
+result = invalid
+
+# tcId = 221
+# RFC 3349 padding with incorrect size
+ct = e6e66fad359a7b63a977788acd297121
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0001020304050607
+result = invalid
+
+# tcId = 222
+# length = 9
+ct = 76b88ecda760b1af80703036185fc476
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0000000000000000
+result = invalid
+
+# tcId = 223
+# length = 16
+ct = fd101943f4ab7c38ec68c75d4b3193dc
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0000000000000000
+result = invalid
+
+# tcId = 224
+# length = 2**31 + 8
+ct = 1793a3a9bd146726edbcb9589f20e849
+key = 48a53c11ef2d727db7eb9a834b134ea9602273aca929702eb2c31d96a58c9be2
+msg = 0000000000000000
+result = invalid
+
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index a0fc905..3929cf6 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -51,6 +51,8 @@
#include "../crypto/internal.h"
#include "internal.h"
+#include "../third_party/sike/sike.h"
+
// TimeResults represents the results of benchmarking a function.
struct TimeResults {
@@ -294,6 +296,64 @@
return true;
}
+static bool SpeedSIKEP434(const std::string &selected) {
+ if (!selected.empty() && selected.find("SIKE") == std::string::npos) {
+ return true;
+ }
+ // speed generation
+ uint8_t public_SIKE[SIKE_PUB_BYTESZ];
+ uint8_t private_SIKE[SIKE_PRV_BYTESZ];
+ uint8_t ct[SIKE_CT_BYTESZ];
+ bool res;
+
+ {
+ TimeResults results;
+ res = TimeFunction(&results,
+ [&private_SIKE, &public_SIKE]() -> bool {
+ return (SIKE_keypair(private_SIKE, public_SIKE) == 1);
+ });
+ results.Print("SIKE/P434 generate");
+ }
+
+ if (!res) {
+ fprintf(stderr, "Failed to time SIKE_keypair.\n");
+ return false;
+ }
+
+ {
+ TimeResults results;
+ TimeFunction(&results,
+ [&ct, &public_SIKE]() -> bool {
+ uint8_t ss[SIKE_SS_BYTESZ];
+ SIKE_encaps(ss, ct, public_SIKE);
+ return true;
+ });
+ results.Print("SIKE/P434 encap");
+ }
+
+ if (!res) {
+ fprintf(stderr, "Failed to time SIKE_encaps.\n");
+ return false;
+ }
+
+ {
+ TimeResults results;
+ TimeFunction(&results,
+ [&ct, &public_SIKE, &private_SIKE]() -> bool {
+ uint8_t ss[SIKE_SS_BYTESZ];
+ SIKE_decaps(ss, ct, public_SIKE, private_SIKE);
+ return true;
+ });
+ results.Print("SIKE/P434 decap");
+ }
+
+ if (!res) {
+ fprintf(stderr, "Failed to time SIKE_decaps.\n");
+ return false;
+ }
+ return true;
+}
+
static uint8_t *align(uint8_t *in, unsigned alignment) {
return reinterpret_cast<uint8_t *>(
(reinterpret_cast<uintptr_t>(in) + alignment) &
@@ -435,7 +495,7 @@
static bool SpeedHashChunk(const EVP_MD *md, std::string name,
size_t chunk_len) {
bssl::ScopedEVP_MD_CTX ctx;
- uint8_t scratch[8192];
+ uint8_t scratch[16384];
if (chunk_len > sizeof(scratch)) {
return false;
@@ -476,7 +536,7 @@
}
static bool SpeedRandomChunk(std::string name, size_t chunk_len) {
- uint8_t scratch[8192];
+ uint8_t scratch[16384];
if (chunk_len > sizeof(scratch)) {
return false;
@@ -938,6 +998,7 @@
!SpeedECDH(selected) ||
!SpeedECDSA(selected) ||
!Speed25519(selected) ||
+ !SpeedSIKEP434(selected) ||
!SpeedSPAKE2(selected) ||
!SpeedScrypt(selected) ||
!SpeedRSAKeyGen(selected) ||
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index 1eb563d..760f2e4 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -19,6 +19,8 @@
'checkout_sde': False,
'checkout_nasm': False,
'checkout_libcxx': False,
+
+ 'android_sdk_platform-tools_version': 'MSnxgXN7IurL-MQs1RrTkSFSb8Xd1UtZjLArI8Ty1FgC',
}
deps = {
@@ -27,23 +29,29 @@
'condition': 'checkout_android',
},
- 'boringssl/util/bot/android_tools': {
- 'url': Var('chromium_git') + '/android_tools.git' + '@' + '347a7c8078a009e98995985b7ab6ec6b35696dea',
- 'condition': 'checkout_android',
+ 'boringssl/util/bot/android_sdk/public': {
+ 'packages': [
+ {
+ 'package': 'chromium/third_party/android_sdk/public/platform-tools',
+ 'version': Var('android_sdk_platform-tools_version'),
+ },
+ ],
+ 'condition': 'checkout_android',
+ 'dep_type': 'cipd',
},
'boringssl/util/bot/gyp':
Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
'boringssl/util/bot/libFuzzer': {
- 'url': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' +'@' + 'e847d8a9b47158695593d5693b0f69250472b229',
+ 'url': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' +'@' + 'e9b95bcfe2f5472fac2e516a0040aedf2140dd62',
'condition': 'checkout_fuzzer',
},
# Update the following revisions from
# https://chromium.googlesource.com/chromium/src/+/master/buildtools/DEPS
'boringssl/util/bot/libcxx': {
- 'url': Var('chromium_git') + '/chromium/llvm-project/libcxx.git' + '@' + '955113db37563c8632e31ddcff2047845553d7ff',
+ 'url': Var('chromium_git') + '/chromium/llvm-project/libcxx.git' + '@' + '5938e0582bac570a41edb3d6a2217c299adc1bc6',
'condition': 'checkout_libcxx',
},
'boringssl/util/bot/libcxxabi': {
diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py
index d295748..1e97ecf 100755
--- a/src/util/bot/go/bootstrap.py
+++ b/src/util/bot/go/bootstrap.py
@@ -45,7 +45,7 @@
EXE_SFX = '.exe' if sys.platform == 'win32' else ''
# Pinned version of Go toolset to download.
-TOOLSET_VERSION = 'go1.12.1'
+TOOLSET_VERSION = 'go1.12.6'
# Platform dependent portion of a download URL. See http://golang.org/dl/.
TOOLSET_VARIANTS = {
diff --git a/src/util/bot/update_clang.py b/src/util/bot/update_clang.py
index 5151b13..2965f22 100644
--- a/src/util/bot/update_clang.py
+++ b/src/util/bot/update_clang.py
@@ -19,10 +19,12 @@
# CLANG_REVISION and CLANG_SUB_REVISION determine the build of clang
# to use. These should be synced with tools/clang/scripts/update.py in
# Chromium.
-CLANG_REVISION = '357569'
+CLANG_REVISION = '80fee25776c2fb61e74c1ecb1a523375c2500b69'
+CLANG_SVN_REVISION = '362913'
CLANG_SUB_REVISION = 1
-PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
+PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8],
+ CLANG_SUB_REVISION)
# Path constants. (All of these should be absolute paths.)
THIS_DIR = os.path.abspath(os.path.dirname(__file__))
diff --git a/src/util/convert_wycheproof.go b/src/util/convert_wycheproof.go
index 7846067..e6eab95 100644
--- a/src/util/convert_wycheproof.go
+++ b/src/util/convert_wycheproof.go
@@ -212,6 +212,7 @@
"ecdsa_secp521r1_sha512_test.json",
"eddsa_test.json",
"kw_test.json",
+ "kwp_test.json",
"rsa_pss_2048_sha1_mgf1_20_test.json",
"rsa_pss_2048_sha256_mgf1_0_test.json",
"rsa_pss_2048_sha256_mgf1_32_test.json",
diff --git a/src/util/doc.config b/src/util/doc.config
index 5bee1b5..d96e0fa 100644
--- a/src/util/doc.config
+++ b/src/util/doc.config
@@ -39,7 +39,8 @@
"include/openssl/md5.h",
"include/openssl/rc4.h",
"include/openssl/rsa.h",
- "include/openssl/sha.h"
+ "include/openssl/sha.h",
+ "include/openssl/siphash.h"
]
},{
"Name": "Crypto interfaces",
diff --git a/src/util/fipstools/acvp/ACVP.md b/src/util/fipstools/acvp/ACVP.md
new file mode 100644
index 0000000..ddae4ed
--- /dev/null
+++ b/src/util/fipstools/acvp/ACVP.md
@@ -0,0 +1,159 @@
+ACVP Client
+===========
+
+[ACVP](https://github.com/usnistgov/ACVP) is the next version of NIST's [CAVP](https://github.com/usnistgov/ACVP)—a program for running cryptographic implementations against a set of test vectors. CAVP involved emailing around zip files of somewhat-INI-like test vectors where no two files had quite the same format. ACVP is supposed to replace that with a) TLS connections rather than email and b) JSON rather than bespoke formats.
+
+The tool in this directory can speak to ACVP servers and run the resulting test vectors through a candidate FIPS module by lowering the tests to a much simpler protocol. It also provides an interface for manipulating the ACVP database which includes lists of modules, vendors, contacts, operating environments etc.
+
+## Configuration
+
+Configuration is done via a `config.json` file in the current working directory. Here's a template:
+
+```
+{
+ "ACVPServer": "https://demo.acvts.nist.gov/",
+ "CertPEMFile": "certificate_from_nist.pem",
+ "PrivateKeyDERFile": "your_private_key.key",
+ "TOTPSecret": "<base64 from NIST goes here>",
+ "SessionTokensCache": "~/.cache/acvp-session-tokens",
+ "LogFile": "log"
+}
+```
+
+NIST's ACVP servers use both TLS client certificates and TOTP for authentication. When registering with NIST, they'll sign a CSR and return a certificate in PEM format, which is pointed to be `CertPEMFile`. The corresponding PKCS#1, DER-encoded private key is expected in `PrivateKeyDERFile`. Lastly, NIST will provide a file that contains the base64-encoded TOTP seed, which must be pasted in as the value of `TOTPSecret`.
+
+NIST's ACVP server provides special access tokens for each test session and test sessions can _only_ be accessed via those tokens. The reasoning behind this is unclear but this client can, optionally, keep records of these access tokens in the directory named by `SessionTokensCache`. If that directory name begins with `~/` then that prefix will be replaced with the value of `$HOME`.
+
+Lastly, a log of all HTTP traffic will be written to the file named by `LogFile`, if provided. This is useful for debugging.
+
+## Interactive Use
+
+ACVP provides a fairly complex interface to a database of several types of objects. A rough UI is provided for this which is triggered when the client is invoked with no command-line arguments.
+
+The simplest objects in ACVP are request objects. These record the status of requested changes to the database and, in practice, changes to the NIST demo database never succeed. The set of pending requests for the current user can be enumerated just by evaluating the `requests` object:
+
+```
+> requests
+[
+ {
+ "url": "/acvp/v1/requests/374",
+ "status": "processing"
+ },
+ {
+ "url": "/acvp/v1/requests/218",
+ "status": "processing"
+ }
+]
+```
+
+A specific request can be evaluated by using indexing syntax:
+
+```
+> requests[374]
+{
+ "url": "/acvp/v1/requests/374",
+ "status": "processing"
+}
+```
+
+The list of vendors provides a more complex example. Since there are large number of duplicates in NIST's database, there are more than 10 000 vendor objects and enumerating them all takes a long time. Thus evaluating the `vendors` object doesn't do that:
+
+```
+> vendors
+[object set vendors]
+```
+
+It is still possible to use indexing syntax to read a specific vendor object if you know the ID:
+
+```
+> vendors[1234]
+{
+ "url": "/acvp/v1/vendors/1234",
+ "name": "Apple Inc.",
+ "website": "www.apple.com",
+ "contactsUrl": "/acvp/v1/vendors/1234/contacts",
+ "addresses": [
+ {
+ "url": "/acvp/v1/vendors/1234/addresses/1234",
+ "street1": "1 Infinite Loop",
+ "locality": "Cupertino",
+ "region": "CA",
+ "country": "USA",
+ "postalCode": "95014"
+ }
+ ]
+}
+```
+
+Finding a vendor when the ID is not known requires searching and the ACVP spec [documents](http://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.8.1), for each object type, what values and what relations can be searched on. This is reflected in a variant of the indexing syntax:
+
+```
+> vendors[where name contains "Google LLC"]
+[
+ {
+ "url": "/acvp/v1/vendors/11136",
+ "name": "Google LLC",
+ "website": "www.google.com",
+ "contactsUrl": "/acvp/v1/vendors/11136/contacts",
+ "addresses": [
+ {
+ "url": "/acvp/v1/vendors/11136/addresses/11136",
+ "street1": "1600 Amphitheatre Parkway",
+ "locality": "Mountain View",
+ "region": "CA",
+ "country": "USA",
+ "postalCode": "94043"
+ }
+ ]
+ },
+ {
+ "url": "/acvp/v1/vendors/11137",
+ "name": "Google LLC",
+ "website": "www.google.com",
+ "contactsUrl": "/acvp/v1/vendors/11137/contacts",
+ "addresses": [
+ {
+ "url": "/acvp/v1/vendors/11137/addresses/11137",
+ "street1": "1600 Amphitheatre Parkway",
+ "locality": "Mountain View",
+ "region": "CA",
+ "country": "USA",
+ "postalCode": "94043"
+ }
+ ]
+ }
+]
+```
+
+In general, `&&` and `||` can be used as in C and the relationships are `==`, `!=`, `contains`, `startsWith`, and `endsWith`. Only values and relations listed in the ACVP spec for a given object can be used.
+
+More complex interaction remains to be fleshed out. However, it is generally possible to create new objects by evaluating, for example, `vendors.new()`. That will invoke `$EDITOR` to edit the JSON to be submitted. (For now, however, no helpful templates are provided.)
+
+The current list of objects is:
+
+* `requests`
+* `vendors`
+* `persons`
+* `modules`
+* `oes` (operating environments)
+* `deps`
+* `algos`
+* `sessions`
+
+## Running test sessions
+
+Handling of test sessions (in non-interactive mode) is split into a “front” part, which talks to the ACVP server, and a “middle” part, which runs the actual test vectors. The middle part receives the raw JSON of the vector sets and returns the response. It also knows the set of algorithms that it supports and their respective parameters. For the moment, the only middle part provided is called `subprocess` which lowers the ACVP tests to simple binary protocol and talks to a FIPS module in a separate process to run the cryptographic algorithms.
+
+For development purposes, this code can be exercised by passing, say, `-run SHA2-256` to the client.
+
+### The subprocess protocol
+
+The lowering of ACVP to a simpler protocol might be useful for other projects so the protocol is described here. The C++ implementation for BoringSSL is in the `modulewrapper` directory.
+
+The protocol follows a strict request–response model over stdin/stdout: the subprocess only speaks in response to a request and there is exactly one response for every request. Conceptually requests consist of one or more byte strings and responses consist of zero or more byte strings.
+
+On the wire, a request involves sending the number of byte strings, then the length of each byte string in order, then the contents of each byte string. All numbers are little-endian and 32-bit. The first byte string is mandatory and is the name of the command to perform. A response has the same format except that there may be zero byte strings and the first byte string has no special semantics.
+
+All implementations must support the `getConfig` command which takes no arguments and returns a single byte string which is a JSON blob of ACVP algorithm configuration. This blob describes all the algorithms and capabilities that the module supports and is an array of JSON objects suitable for including as the `algorithms` value when [creating an ACVP vector set](http://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.15.2.1).
+
+Each supported algorithm will have its own commands that the module must implement. So far, only hash functions are supported and the commands take a byte string to hash and return a single byte string of the resulting digest. The commands are named after the ACVP algorithm names, i.e. `SHA-1`, `SHA2-224`, `SHA2-256`, `SHA2-384`, and `SHA2-512`.
diff --git a/src/util/fipstools/acvp/acvptool/acvp.go b/src/util/fipstools/acvp/acvptool/acvp.go
new file mode 100644
index 0000000..ed1a84f
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/acvp.go
@@ -0,0 +1,430 @@
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/base64"
+ "encoding/binary"
+ "encoding/json"
+ "encoding/pem"
+ "errors"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+ neturl "net/url"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/acvp"
+ "boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/subprocess"
+)
+
+var (
+ configFilename = flag.String("config", "config.json", "Location of the configuration JSON file")
+ runFlag = flag.String("run", "", "Name of primitive to run tests for")
+ wrapperPath = flag.String("wrapper", "../../../../build/util/fipstools/acvp/modulewrapper/modulewrapper", "Path to the wrapper binary")
+)
+
+type Config struct {
+ CertPEMFile string
+ PrivateKeyDERFile string
+ TOTPSecret string
+ ACVPServer string
+ SessionTokensCache string
+ LogFile string
+}
+
+func isCommentLine(line []byte) bool {
+ var foundCommentStart bool
+ for _, b := range line {
+ if !foundCommentStart {
+ if b == ' ' || b == '\t' {
+ continue
+ }
+ if b != '/' {
+ return false
+ }
+ foundCommentStart = true
+ } else {
+ return b == '/'
+ }
+ }
+ return false
+}
+
+func jsonFromFile(out interface{}, filename string) error {
+ in, err := os.Open(filename)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ scanner := bufio.NewScanner(in)
+ var commentsRemoved bytes.Buffer
+ for scanner.Scan() {
+ if isCommentLine(scanner.Bytes()) {
+ continue
+ }
+ commentsRemoved.Write(scanner.Bytes())
+ commentsRemoved.WriteString("\n")
+ }
+ if err := scanner.Err(); err != nil {
+ return err
+ }
+
+ decoder := json.NewDecoder(&commentsRemoved)
+ decoder.DisallowUnknownFields()
+ if err := decoder.Decode(out); err != nil {
+ return err
+ }
+ if decoder.More() {
+ return errors.New("trailing garbage found")
+ }
+ return nil
+}
+
+// TOTP implements the time-based one-time password algorithm with the suggested
+// granularity of 30 seconds. See https://tools.ietf.org/html/rfc6238 and then
+// https://tools.ietf.org/html/rfc4226#section-5.3
+func TOTP(secret []byte) string {
+ const timeStep = 30
+ now := uint64(time.Now().Unix()) / 30
+ var nowBuf [8]byte
+ binary.BigEndian.PutUint64(nowBuf[:], now)
+ mac := hmac.New(sha256.New, secret)
+ mac.Write(nowBuf[:])
+ digest := mac.Sum(nil)
+ value := binary.BigEndian.Uint32(digest[digest[31]&15:])
+ value &= 0x7fffffff
+ value %= 100000000
+ return fmt.Sprintf("%08d", value)
+}
+
+type Middle interface {
+ Close()
+ Config() ([]byte, error)
+ Process(algorithm string, vectorSet []byte) ([]byte, error)
+}
+
+func loadCachedSessionTokens(server *acvp.Server, cachePath string) error {
+ cacheDir, err := os.Open(cachePath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ if err := os.Mkdir(cachePath, 0700); err != nil {
+ return fmt.Errorf("Failed to create session token cache directory %q: %s", cachePath, err)
+ }
+ return nil
+ }
+ return fmt.Errorf("Failed to open session token cache directory %q: %s", cachePath, err)
+ }
+ defer cacheDir.Close()
+ names, err := cacheDir.Readdirnames(0)
+ if err != nil {
+ return fmt.Errorf("Failed to list session token cache directory %q: %s", cachePath, err)
+ }
+
+ loaded := 0
+ for _, name := range names {
+ if !strings.HasSuffix(name, ".token") {
+ continue
+ }
+ path := filepath.Join(cachePath, name)
+ contents, err := ioutil.ReadFile(path)
+ if err != nil {
+ return fmt.Errorf("Failed to read session token cache entry %q: %s", path, err)
+ }
+ urlPath, err := neturl.PathUnescape(name[:len(name)-6])
+ if err != nil {
+ return fmt.Errorf("Failed to unescape token filename %q: %s", name, err)
+ }
+ server.PrefixTokens[urlPath] = string(contents)
+ loaded++
+ }
+
+ log.Printf("Loaded %d cached tokens", loaded)
+ return nil
+}
+
+func trimLeadingSlash(s string) string {
+ if strings.HasPrefix(s, "/") {
+ return s[1:]
+ }
+ return s
+}
+
+func main() {
+ flag.Parse()
+
+ var config Config
+ if err := jsonFromFile(&config, *configFilename); err != nil {
+ log.Fatalf("Failed to load config file: %s", err)
+ }
+
+ if len(config.TOTPSecret) == 0 {
+ log.Fatal("Config file missing TOTPSecret")
+ }
+ totpSecret, err := base64.StdEncoding.DecodeString(config.TOTPSecret)
+ if err != nil {
+ log.Fatalf("Failed to decode TOTP secret from config file: %s", err)
+ }
+
+ if len(config.CertPEMFile) == 0 {
+ log.Fatal("Config file missing CertPEMFile")
+ }
+ certPEM, err := ioutil.ReadFile(config.CertPEMFile)
+ if err != nil {
+ log.Fatalf("failed to read certificate from %q: %s", config.CertPEMFile, err)
+ }
+ block, _ := pem.Decode(certPEM)
+ certDER := block.Bytes
+
+ if len(config.PrivateKeyDERFile) == 0 {
+ log.Fatal("Config file missing PrivateKeyDERFile")
+ }
+ keyDER, err := ioutil.ReadFile(config.PrivateKeyDERFile)
+ if err != nil {
+ log.Fatalf("failed to read private key from %q: %s", config.PrivateKeyDERFile, err)
+ }
+
+ certKey, err := x509.ParsePKCS1PrivateKey(keyDER)
+ if err != nil {
+ log.Fatalf("failed to parse private key from %q: %s", config.PrivateKeyDERFile, err)
+ }
+
+ var middle Middle
+ middle, err = subprocess.New(*wrapperPath)
+ if err != nil {
+ log.Fatalf("failed to initialise middle: %s", err)
+ }
+ defer middle.Close()
+
+ configBytes, err := middle.Config()
+ if err != nil {
+ log.Fatalf("failed to get config from middle: %s", err)
+ }
+
+ var supportedAlgos []map[string]interface{}
+ if err := json.Unmarshal(configBytes, &supportedAlgos); err != nil {
+ log.Fatalf("failed to parse configuration from Middle: %s", err)
+ }
+
+ runAlgos := make(map[string]bool)
+ if len(*runFlag) > 0 {
+ for _, substr := range strings.Split(*runFlag, ",") {
+ runAlgos[substr] = false
+ }
+ }
+
+ var algorithms []map[string]interface{}
+ for _, supportedAlgo := range supportedAlgos {
+ algoInterface, ok := supportedAlgo["algorithm"]
+ if !ok {
+ continue
+ }
+
+ algo, ok := algoInterface.(string)
+ if !ok {
+ continue
+ }
+
+ if _, ok := runAlgos[algo]; ok {
+ algorithms = append(algorithms, supportedAlgo)
+ runAlgos[algo] = true
+ }
+ }
+
+ for algo, recognised := range runAlgos {
+ if !recognised {
+ log.Fatalf("requested algorithm %q was not recognised", algo)
+ }
+ }
+
+ if len(config.ACVPServer) == 0 {
+ config.ACVPServer = "https://demo.acvts.nist.gov/"
+ }
+ server := acvp.NewServer(config.ACVPServer, config.LogFile, [][]byte{certDER}, certKey, func() string {
+ return TOTP(totpSecret[:])
+ })
+
+ var sessionTokensCacheDir string
+ if len(config.SessionTokensCache) > 0 {
+ sessionTokensCacheDir = config.SessionTokensCache
+ if strings.HasPrefix(sessionTokensCacheDir, "~/") {
+ home := os.Getenv("HOME")
+ if len(home) == 0 {
+ log.Fatal("~ used in config file but $HOME not set")
+ }
+ sessionTokensCacheDir = filepath.Join(home, sessionTokensCacheDir[2:])
+ }
+
+ if err := loadCachedSessionTokens(server, sessionTokensCacheDir); err != nil {
+ log.Fatal(err)
+ }
+ }
+
+ if err := server.Login(); err != nil {
+ log.Fatalf("failed to login: %s", err)
+ }
+
+ if len(*runFlag) == 0 {
+ runInteractive(server, config)
+ return
+ }
+
+ requestBytes, err := json.Marshal(acvp.TestSession{
+ IsSample: true,
+ Publishable: false,
+ Algorithms: algorithms,
+ })
+ if err != nil {
+ log.Fatalf("Failed to serialise JSON: %s", err)
+ }
+
+ var result acvp.TestSession
+ if err := server.Post(&result, "acvp/v1/testSessions", requestBytes); err != nil {
+ log.Fatalf("Request to create test session failed: %s", err)
+ }
+
+ url := trimLeadingSlash(result.URL)
+ log.Printf("Created test session %q", url)
+ if token := result.AccessToken; len(token) > 0 {
+ server.PrefixTokens[url] = token
+ if len(sessionTokensCacheDir) > 0 {
+ ioutil.WriteFile(filepath.Join(sessionTokensCacheDir, neturl.PathEscape(url))+".token", []byte(token), 0600)
+ }
+ }
+
+ log.Printf("Have vector sets %v", result.VectorSetURLs)
+
+ for _, setURL := range result.VectorSetURLs {
+ firstTime := true
+ for {
+ if firstTime {
+ log.Printf("Fetching test vectors %q", setURL)
+ firstTime = false
+ }
+
+ vectorsBytes, err := server.GetBytes(trimLeadingSlash(setURL))
+ if err != nil {
+ log.Fatalf("Failed to fetch vector set %q: %s", setURL, err)
+ }
+
+ var vectors acvp.Vectors
+ if err := json.Unmarshal(vectorsBytes, &vectors); err != nil {
+ log.Fatalf("Failed to parse vector set from %q: %s", setURL, err)
+ }
+
+ if retry := vectors.Retry; retry > 0 {
+ log.Printf("Server requested %d seconds delay", retry)
+ if retry > 10 {
+ retry = 10
+ }
+ time.Sleep(time.Duration(retry) * time.Second)
+ continue
+ }
+
+ replyGroups, err := middle.Process(vectors.Algo, vectorsBytes)
+ if err != nil {
+ log.Printf("Failed: %s", err)
+ log.Printf("Deleting test set")
+ server.Delete(url)
+ os.Exit(1)
+ }
+
+ headerBytes, err := json.Marshal(acvp.Vectors{
+ ID: vectors.ID,
+ Algo: vectors.Algo,
+ })
+ if err != nil {
+ log.Printf("Failed to marshal result: %s", err)
+ log.Printf("Deleting test set")
+ server.Delete(url)
+ os.Exit(1)
+ }
+
+ var resultBuf bytes.Buffer
+ resultBuf.Write(headerBytes[:len(headerBytes)-1])
+ resultBuf.WriteString(`,"testGroups":`)
+ resultBuf.Write(replyGroups)
+ resultBuf.WriteString("}")
+
+ resultData := resultBuf.Bytes()
+ resultSize := uint64(len(resultData)) + 32 /* for framing overhead */
+ if resultSize >= server.SizeLimit {
+ log.Printf("Result is %d bytes, too much given server limit of %d bytes. Using large-upload process.", resultSize, server.SizeLimit)
+ largeRequestBytes, err := json.Marshal(acvp.LargeUploadRequest{
+ Size: resultSize,
+ URL: setURL,
+ })
+ if err != nil {
+ log.Printf("Failed to marshal large-upload request: %s", err)
+ log.Printf("Deleting test set")
+ server.Delete(url)
+ os.Exit(1)
+ }
+
+ var largeResponse acvp.LargeUploadResponse
+ if err := server.Post(&largeResponse, "/large", largeRequestBytes); err != nil {
+ log.Fatalf("Failed to request large-upload endpoint: %s", err)
+ }
+
+ log.Printf("Directed to large-upload endpoint at %q", largeResponse.URL)
+ client := &http.Client{}
+ req, err := http.NewRequest("POST", largeResponse.URL, bytes.NewBuffer(resultData))
+ if err != nil {
+ log.Fatalf("Failed to create POST request: %s", err)
+ }
+ token := largeResponse.AccessToken
+ if len(token) == 0 {
+ token = server.AccessToken
+ }
+ req.Header.Add("Authorization", "Bearer "+token)
+ req.Header.Add("Content-Type", "application/json")
+ resp, err := client.Do(req)
+ if err != nil {
+ log.Fatalf("Failed writing large upload: %s", err)
+ }
+ resp.Body.Close()
+ if resp.StatusCode != 200 {
+ log.Fatalf("Large upload resulted in status code %d", resp.StatusCode)
+ }
+ } else {
+ log.Printf("Result size %d bytes", resultSize)
+ if err := server.Post(nil, trimLeadingSlash(setURL)+"/results", resultData); err != nil {
+ log.Fatalf("Failed to upload results: %s\n", err)
+ }
+ }
+
+ break
+ }
+ }
+
+FetchResults:
+ for {
+ var results acvp.SessionResults
+ if err := server.Get(&results, trimLeadingSlash(url)+"/results"); err != nil {
+ log.Fatalf("Failed to fetch session results: %s", err)
+ }
+
+ if results.Passed {
+ break
+ }
+
+ for _, result := range results.Results {
+ if result.Status == "incomplete" {
+ log.Print("Server hasn't finished processing results. Waiting 10 seconds.")
+ time.Sleep(10 * time.Second)
+ continue FetchResults
+ }
+ }
+
+ log.Fatalf("Server did not accept results: %#v", results)
+ }
+}
diff --git a/src/util/fipstools/acvp/acvptool/acvp/acvp.go b/src/util/fipstools/acvp/acvptool/acvp/acvp.go
new file mode 100644
index 0000000..2f5d363
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/acvp/acvp.go
@@ -0,0 +1,646 @@
+package acvp
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/tls"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "reflect"
+ "strings"
+ "time"
+)
+
+// Server represents an ACVP server.
+type Server struct {
+ // PrefixTokens are access tokens that apply to URLs under a certain prefix.
+ // The keys of this map are strings like "acvp/v1/testSessions/1234" and the
+ // values are JWT access tokens.
+ PrefixTokens map[string]string
+ // SizeLimit is the maximum number of bytes that the server can accept as an
+ // upload before the large endpoint support must be used.
+ SizeLimit uint64
+ // AccessToken is the top-level access token for the current session.
+ AccessToken string
+
+ client *http.Client
+ prefix string
+ totpFunc func() string
+}
+
+// NewServer returns a fresh Server instance representing the ACVP server at
+// prefix (e.g. "https://acvp.example.com/"). A copy of all bytes exchanged
+// will be written to logFile, if not empty.
+func NewServer(prefix string, logFile string, derCertificates [][]byte, privateKey crypto.PrivateKey, totp func() string) *Server {
+ if !strings.HasSuffix(prefix, "/") {
+ prefix = prefix + "/"
+ }
+
+ tlsConfig := &tls.Config{
+ Certificates: []tls.Certificate{
+ tls.Certificate{
+ Certificate: derCertificates,
+ PrivateKey: privateKey,
+ },
+ },
+ Renegotiation: tls.RenegotiateOnceAsClient,
+ }
+
+ client := &http.Client{
+ Transport: &http.Transport{
+ Dial: func(network, addr string) (net.Conn, error) {
+ panic("HTTP connection requested")
+ },
+ DialTLS: func(network, addr string) (net.Conn, error) {
+ conn, err := tls.Dial(network, addr, tlsConfig)
+ if err != nil {
+ return nil, err
+ }
+ if len(logFile) > 0 {
+ logFile, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
+ if err != nil {
+ return nil, err
+ }
+ return &logger{Conn: conn, log: logFile}, nil
+ }
+ return conn, err
+ },
+ },
+ Timeout: 10 * time.Second,
+ }
+
+ return &Server{client: client, prefix: prefix, totpFunc: totp, PrefixTokens: make(map[string]string)}
+}
+
+type logger struct {
+ *tls.Conn
+ log *os.File
+ lastDirection int
+}
+
+var newLine = []byte{'\n'}
+
+func (l *logger) Read(buf []byte) (int, error) {
+ if l.lastDirection != 1 {
+ l.log.Write(newLine)
+ }
+ l.lastDirection = 1
+
+ n, err := l.Conn.Read(buf)
+ if err == nil {
+ l.log.Write(buf[:n])
+ }
+ return n, err
+}
+
+func (l *logger) Write(buf []byte) (int, error) {
+ if l.lastDirection != 2 {
+ l.log.Write(newLine)
+ }
+ l.lastDirection = 2
+
+ n, err := l.Conn.Write(buf)
+ if err == nil {
+ l.log.Write(buf[:n])
+ }
+ return n, err
+}
+
+const requestPrefix = `[{"acvVersion":"1.0"},`
+const requestSuffix = "]"
+
+// parseHeaderElement parses the first JSON object that's always returned by
+// ACVP servers. If successful, it returns a JSON Decoder positioned just
+// before the second element.
+func parseHeaderElement(in io.Reader) (*json.Decoder, error) {
+ decoder := json.NewDecoder(in)
+ arrayStart, err := decoder.Token()
+ if err != nil {
+ return nil, errors.New("failed to read from server reply: " + err.Error())
+ }
+ if delim, ok := arrayStart.(json.Delim); !ok || delim != '[' {
+ return nil, fmt.Errorf("found %#v when expecting initial array from server", arrayStart)
+ }
+
+ var version struct {
+ Version string `json:"acvVersion"`
+ }
+ if err := decoder.Decode(&version); err != nil {
+ return nil, errors.New("parse error while decoding version element: " + err.Error())
+ }
+ if !strings.HasPrefix(version.Version, "1.") {
+ return nil, fmt.Errorf("expected version 1.* from server but found %q", version.Version)
+ }
+
+ return decoder, nil
+}
+
+// parseReplyToBytes reads the contents of an ACVP reply after removing the
+// header element.
+func parseReplyToBytes(in io.Reader) ([]byte, error) {
+ decoder, err := parseHeaderElement(in)
+ if err != nil {
+ return nil, err
+ }
+
+ buf, err := ioutil.ReadAll(decoder.Buffered())
+ if err != nil {
+ return nil, err
+ }
+
+ rest, err := ioutil.ReadAll(in)
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, rest...)
+
+ buf = bytes.TrimSpace(buf)
+ if len(buf) == 0 || buf[0] != ',' {
+ return nil, errors.New("didn't find initial ','")
+ }
+ buf = buf[1:]
+
+ if len(buf) == 0 || buf[len(buf)-1] != ']' {
+ return nil, errors.New("didn't find trailing ']'")
+ }
+ buf = buf[:len(buf)-1]
+
+ return buf, nil
+}
+
+// parseReply parses the contents of an ACVP reply (after removing the header
+// element) into out. See the documentation of the encoding/json package for
+// details of the parsing.
+func parseReply(out interface{}, in io.Reader) error {
+ if out == nil {
+ // No reply expected.
+ return nil
+ }
+
+ decoder, err := parseHeaderElement(in)
+ if err != nil {
+ return err
+ }
+
+ if err := decoder.Decode(out); err != nil {
+ return errors.New("error while decoding reply body: " + err.Error())
+ }
+
+ arrayEnd, err := decoder.Token()
+ if err != nil {
+ return errors.New("failed to read end of reply from server: " + err.Error())
+ }
+ if delim, ok := arrayEnd.(json.Delim); !ok || delim != ']' {
+ return fmt.Errorf("found %#v when expecting end of array from server", arrayEnd)
+ }
+ if decoder.More() {
+ return errors.New("unexpected trailing data from server")
+ }
+
+ return nil
+}
+
+// expired returns true if the given JWT token has expired.
+func expired(tokenStr string) bool {
+ parts := strings.Split(tokenStr, ".")
+ if len(parts) != 3 {
+ return false
+ }
+ jsonBytes, err := base64.RawURLEncoding.DecodeString(parts[1])
+ if err != nil {
+ return false
+ }
+ var token struct {
+ Expiry uint64 `json:"exp"`
+ }
+ if json.Unmarshal(jsonBytes, &token) != nil {
+ return false
+ }
+ return token.Expiry > 0 && token.Expiry < uint64(time.Now().Unix())
+}
+
+func (server *Server) getToken(endPoint string) (string, error) {
+ for path, token := range server.PrefixTokens {
+ if endPoint != path && !strings.HasPrefix(endPoint, path+"/") {
+ continue
+ }
+
+ if !expired(token) {
+ return token, nil
+ }
+
+ var reply struct {
+ AccessToken string `json:"accessToken"`
+ }
+ if err := server.postMessage(&reply, "acvp/v1/login", map[string]string{
+ "password": server.totpFunc(),
+ "accessToken": token,
+ }); err != nil {
+ return "", err
+ }
+ server.PrefixTokens[path] = reply.AccessToken
+ return reply.AccessToken, nil
+ }
+ return server.AccessToken, nil
+}
+
+// Login sends a login request and stores the returned access tokens for use
+// with future requests. The login process isn't specifically documented in
+// draft-fussell-acvp-spec and the best reference is
+// https://github.com/usnistgov/ACVP/wiki#credentials-for-accessing-the-demo-server
+func (server *Server) Login() error {
+ var reply struct {
+ AccessToken string `json:"accessToken"`
+ LargeEndpointRequired bool `json:"largeEndpointRequired"`
+ SizeLimit uint64 `json:"sizeConstraint"`
+ }
+
+ if err := server.postMessage(&reply, "acvp/v1/login", map[string]string{"password": server.totpFunc()}); err != nil {
+ return err
+ }
+
+ if len(reply.AccessToken) == 0 {
+ return errors.New("login reply didn't contain access token")
+ }
+ server.AccessToken = reply.AccessToken
+
+ if reply.LargeEndpointRequired {
+ if reply.SizeLimit == 0 {
+ return errors.New("login indicated largeEndpointRequired but didn't provide a sizeConstraint")
+ }
+ server.SizeLimit = reply.SizeLimit
+ }
+
+ return nil
+}
+
+type Relation int
+
+const (
+ Equals Relation = iota
+ NotEquals Relation = iota
+ GreaterThan Relation = iota
+ GreaterThanEqual Relation = iota
+ LessThan Relation = iota
+ LessThanEqual Relation = iota
+ Contains Relation = iota
+ StartsWith Relation = iota
+ EndsWith Relation = iota
+)
+
+func (rel Relation) String() string {
+ switch rel {
+ case Equals:
+ return "eq"
+ case NotEquals:
+ return "ne"
+ case GreaterThan:
+ return "gt"
+ case GreaterThanEqual:
+ return "ge"
+ case LessThan:
+ return "lt"
+ case LessThanEqual:
+ return "le"
+ case Contains:
+ return "contains"
+ case StartsWith:
+ return "start"
+ case EndsWith:
+ return "end"
+ default:
+ panic("unknown relation")
+ }
+}
+
+type Condition struct {
+ Param string
+ Relation Relation
+ Value string
+}
+
+type Conjunction []Condition
+
+type Query []Conjunction
+
+func (query Query) toURLParams() string {
+ var ret string
+
+ for i, conj := range query {
+ for _, cond := range conj {
+ if len(ret) > 0 {
+ ret += "&"
+ }
+ ret += fmt.Sprintf("%s[%d]=%s:%s", url.QueryEscape(cond.Param), i, cond.Relation.String(), url.QueryEscape(cond.Value))
+ }
+ }
+
+ return ret
+}
+
+var NotFound = errors.New("acvp: HTTP code 404")
+
+func (server *Server) newRequestWithToken(method, endpoint string, body io.Reader) (*http.Request, error) {
+ token, err := server.getToken(endpoint)
+ if err != nil {
+ return nil, err
+ }
+ req, err := http.NewRequest(method, server.prefix+endpoint, body)
+ if err != nil {
+ return nil, err
+ }
+ if len(token) != 0 {
+ req.Header.Add("Authorization", "Bearer "+token)
+ }
+ return req, nil
+}
+
+func (server *Server) Get(out interface{}, endPoint string) error {
+ req, err := server.newRequestWithToken("GET", endPoint, nil)
+ if err != nil {
+ return err
+ }
+ resp, err := server.client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error while fetching chunk for %q: %s", endPoint, err)
+ }
+
+ defer resp.Body.Close()
+ if resp.StatusCode == 404 {
+ return NotFound
+ } else if resp.StatusCode != 200 {
+ return fmt.Errorf("acvp: HTTP error %d", resp.StatusCode)
+ }
+ return parseReply(out, resp.Body)
+}
+
+func (server *Server) GetBytes(endPoint string) ([]byte, error) {
+ req, err := server.newRequestWithToken("GET", endPoint, nil)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := server.client.Do(req)
+ if err != nil {
+ return nil, fmt.Errorf("error while fetching chunk for %q: %s", endPoint, err)
+ }
+
+ defer resp.Body.Close()
+ if resp.StatusCode == 404 {
+ return nil, NotFound
+ } else if resp.StatusCode != 200 {
+ return nil, fmt.Errorf("acvp: HTTP error %d", resp.StatusCode)
+ }
+ return parseReplyToBytes(resp.Body)
+}
+
+func (server *Server) write(method string, reply interface{}, endPoint string, contents []byte) error {
+ var buf bytes.Buffer
+ buf.WriteString(requestPrefix)
+ buf.Write(contents)
+ buf.WriteString(requestSuffix)
+
+ req, err := server.newRequestWithToken("POST", endPoint, &buf)
+ if err != nil {
+ return err
+ }
+ req.Header.Add("Content-Type", "application/json")
+ resp, err := server.client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error while writing to %q: %s", endPoint, err)
+ }
+
+ defer resp.Body.Close()
+ if resp.StatusCode == 404 {
+ return NotFound
+ } else if resp.StatusCode != 200 {
+ return fmt.Errorf("acvp: HTTP error %d", resp.StatusCode)
+ }
+ return parseReply(reply, resp.Body)
+}
+
+func (server *Server) postMessage(reply interface{}, endPoint string, request interface{}) error {
+ contents, err := json.Marshal(request)
+ if err != nil {
+ return err
+ }
+ return server.write("POST", reply, endPoint, contents)
+}
+
+func (server *Server) Post(out interface{}, endPoint string, contents []byte) error {
+ return server.write("POST", out, endPoint, contents)
+}
+
+func (server *Server) Put(out interface{}, endPoint string, contents []byte) error {
+ return server.write("PUT", out, endPoint, contents)
+}
+
+func (server *Server) Delete(endPoint string) error {
+ req, err := server.newRequestWithToken("DELETE", endPoint, nil)
+ resp, err := server.client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error while writing to %q: %s", endPoint, err)
+ }
+
+ defer resp.Body.Close()
+ if resp.StatusCode != 200 {
+ return fmt.Errorf("acvp: HTTP error %d", resp.StatusCode)
+ }
+ fmt.Printf("DELETE %q %d\n", server.prefix+endPoint, resp.StatusCode)
+ return nil
+}
+
+var (
+ uint64Type = reflect.TypeOf(uint64(0))
+ boolType = reflect.TypeOf(false)
+ stringType = reflect.TypeOf("")
+)
+
+// GetPaged returns an array of records of some type using one or more requests to the server. See
+// https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#paging_response
+func (server *Server) GetPaged(out interface{}, endPoint string, condition Query) error {
+ output := reflect.ValueOf(out)
+ if output.Kind() != reflect.Ptr {
+ panic(fmt.Sprintf("GetPaged output parameter of non-pointer type %T", out))
+ }
+
+ token, err := server.getToken(endPoint)
+ if err != nil {
+ return err
+ }
+
+ outputSlice := output.Elem()
+
+ replyType := reflect.StructOf([]reflect.StructField{
+ {Name: "TotalCount", Type: uint64Type, Tag: `json:"totalCount"`},
+ {Name: "Incomplete", Type: boolType, Tag: `json:"incomplete"`},
+ {Name: "Data", Type: output.Elem().Type(), Tag: `json:"data"`},
+ {Name: "Links", Type: reflect.StructOf([]reflect.StructField{
+ {Name: "Next", Type: stringType, Tag: `json:"next"`},
+ }), Tag: `json:"links"`},
+ })
+ nextURL := server.prefix + endPoint
+ conditionParams := condition.toURLParams()
+ if len(conditionParams) > 0 {
+ nextURL += "?" + conditionParams
+ }
+
+ isFirstRequest := true
+ for {
+ req, err := http.NewRequest("GET", nextURL, nil)
+ if err != nil {
+ return err
+ }
+ if len(token) != 0 {
+ req.Header.Add("Authorization", "Bearer "+token)
+ }
+ resp, err := server.client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error while fetching chunk for %q: %s", endPoint, err)
+ }
+ if resp.StatusCode == 404 && isFirstRequest {
+ resp.Body.Close()
+ return nil
+ } else if resp.StatusCode != 200 {
+ resp.Body.Close()
+ return fmt.Errorf("acvp: HTTP error %d", resp.StatusCode)
+ }
+ isFirstRequest = false
+
+ reply := reflect.New(replyType)
+ err = parseReply(reply.Interface(), resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ return err
+ }
+
+ data := reply.Elem().FieldByName("Data")
+ for i := 0; i < data.Len(); i++ {
+ outputSlice.Set(reflect.Append(outputSlice, data.Index(i)))
+ }
+
+ if uint64(outputSlice.Len()) == reply.Elem().FieldByName("TotalCount").Uint() ||
+ reply.Elem().FieldByName("Links").FieldByName("Next").String() == "" {
+ break
+ }
+
+ nextURL = server.prefix + endPoint + fmt.Sprintf("?offset=%d", outputSlice.Len())
+ if len(conditionParams) > 0 {
+ nextURL += "&" + conditionParams
+ }
+ }
+
+ return nil
+}
+
+// https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.8.3.1
+type Vendor struct {
+ URL string `json:"url,omitempty"`
+ Name string `json:"name,omitempty"`
+ ParentURL string `json:"parentUrl,omitempty"`
+ Website string `json:"website,omitempty"`
+ Emails []string `json:"emails,omitempty"`
+ ContactsURL string `json:"contactsUrl,omitempty"`
+ Addresses []Address `json:"addresses,omitempty"`
+}
+
+// https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.9
+type Address struct {
+ URL string `json:"url,omitempty"`
+ Street1 string `json:"street1,omitempty"`
+ Street2 string `json:"street2,omitempty"`
+ Street3 string `json:"street3,omitempty"`
+ Locality string `json:"locality,omitempty"`
+ Region string `json:"region,omitempty"`
+ Country string `json:"country,omitempty"`
+ PostalCode string `json:"postalCode,omitempty"`
+}
+
+// https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.10
+type Person struct {
+ URL string `json:"url,omitempty"`
+ FullName string `json:"fullName,omitempty"`
+ VendorURL string `json:"vendorUrl,omitempty"`
+ Emails []string `json:"emails,omitempty"`
+ PhoneNumbers []struct {
+ Number string `json:"number,omitempty"`
+ Type string `json:"type,omitempty"`
+ } `json:"phoneNumbers,omitempty"`
+}
+
+// https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.11
+type Module struct {
+ URL string `json:"url,omitempty"`
+ Name string `json:"name,omitempty"`
+ Version string `json:"version,omitempty"`
+ Type string `json:"type,omitempty"`
+ Website string `json:"website,omitempty"`
+ VendorURL string `json:"vendorUrl,omitempty"`
+ AddressURL string `json:"addressUrl,omitempty"`
+ ContactURLs []string `json:"contactUrls,omitempty"`
+ Description string `json:"description,omitempty"`
+}
+
+type RequestStatus struct {
+ URL string `json:"url,omitempty"`
+ Status string `json:"status,omitempty"`
+ Message string `json:"message,omitempty"`
+ ApprovedURL string `json:"approvedUrl,omitempty"`
+}
+
+type OperationalEnvironment struct {
+ URL string `json:"url,omitempty"`
+ Name string `json:"name,omitempty"`
+ DependencyUrls []string `json:"dependencyUrls,omitempty"`
+ Dependencies []Dependency `json:"dependencies,omitempty"`
+}
+
+type Dependency map[string]interface{}
+
+type Algorithm map[string]interface{}
+
+type TestSession struct {
+ URL string `json:"url,omitempty"`
+ ACVPVersion string `json:"acvpVersion,omitempty"`
+ Created string `json:"createdOn,omitempty"`
+ Expires string `json:"expiresOn,omitempty"`
+ VectorSetURLs []string `json:"vectorSetUrls,omitempty"`
+ AccessToken string `json:"accessToken,omitempty"`
+ Algorithms []map[string]interface{} `json:"algorithms,omitempty"`
+ EncryptAtRest bool `json:"encryptAtRest,omitempty"`
+ IsSample bool `json:"isSample,omitempty"`
+ Publishable bool `json:"publishable,omitempty"`
+ Passed bool `json:"passed,omitempty"`
+}
+
+type Vectors struct {
+ Retry uint64 `json:"retry,omitempty"`
+ ID uint64 `json:"vsId"`
+ Algo string `json:"algorithm,omitempty"`
+ Revision string `json:"revision,omitempty"`
+}
+
+type LargeUploadRequest struct {
+ Size uint64 `json:"submissionSize,omitempty"`
+ URL string `json:"vectorSetUrl,omitempty"`
+}
+
+type LargeUploadResponse struct {
+ URL string `json:"url"`
+ AccessToken string `json:"accessToken"`
+}
+
+type SessionResults struct {
+ Passed bool `json:"passed"`
+ Results []struct {
+ URL string `json:"vectorSetUrl,omitempty"`
+ Status string `json:"status"`
+ } `json:"results"`
+}
diff --git a/src/util/fipstools/acvp/acvptool/interactive.go b/src/util/fipstools/acvp/acvptool/interactive.go
new file mode 100644
index 0000000..8de57ff
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/interactive.go
@@ -0,0 +1,707 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ neturl "net/url"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "reflect"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "boringssl.googlesource.com/boringssl/util/fipstools/acvp/acvptool/acvp"
+ "golang.org/x/crypto/ssh/terminal"
+)
+
+func updateTerminalSize(term *terminal.Terminal) {
+ width, height, err := terminal.GetSize(0)
+ if err != nil {
+ return
+ }
+ term.SetSize(width, height)
+}
+
+func skipWS(node *node32) *node32 {
+ for ; node != nil && node.pegRule == ruleWS; node = node.next {
+ }
+ return node
+}
+
+func assertNodeType(node *node32, rule pegRule) {
+ if node.pegRule != rule {
+ panic(fmt.Sprintf("expected %q, found %q", rul3s[rule], rul3s[node.pegRule]))
+ }
+}
+
+type Object interface {
+ String() (string, error)
+ Index(string) (Object, error)
+ Search(acvp.Query) (Object, error)
+ Action(action string, args []string) error
+}
+
+type ServerObjectSet struct {
+ env *Env
+ name string
+ searchKeys map[string][]acvp.Relation
+ resultType reflect.Type
+ subObjects map[string]func(*Env, string) (Object, error)
+ canEnumerate bool
+}
+
+func (set ServerObjectSet) String() (string, error) {
+ if !set.canEnumerate {
+ return "[object set " + set.name + "]", nil
+ }
+
+ data := reflect.New(reflect.SliceOf(set.resultType)).Interface()
+ if err := set.env.server.GetPaged(data, "acvp/v1/"+set.name, nil); err != nil {
+ return "", err
+ }
+ ret, err := json.MarshalIndent(data, "", " ")
+ return string(ret), err
+}
+
+func (set ServerObjectSet) Index(indexStr string) (Object, error) {
+ index, err := strconv.ParseUint(indexStr, 0, 64)
+ if err != nil {
+ return nil, fmt.Errorf("object set indexes must be unsigned integers, trying to parse %q failed: %s", indexStr, err)
+ }
+ return ServerObject{&set, index}, nil
+}
+
+func (set ServerObjectSet) Search(condition acvp.Query) (Object, error) {
+ if set.searchKeys == nil {
+ return nil, errors.New("this object set cannot be searched")
+ }
+
+ for _, conj := range condition {
+ NextCondition:
+ for _, cond := range conj {
+ allowed, ok := set.searchKeys[cond.Param]
+ if !ok {
+ return nil, fmt.Errorf("search key %q not valid for this object set", cond.Param)
+ }
+
+ for _, rel := range allowed {
+ if rel == cond.Relation {
+ continue NextCondition
+ }
+ }
+
+ return nil, fmt.Errorf("search key %q cannot be used with relation %q", cond.Param, cond.Relation.String())
+ }
+ }
+
+ return Search{ServerObjectSet: set, query: condition}, nil
+}
+
+func (set ServerObjectSet) Action(action string, args []string) error {
+ switch action {
+ default:
+ return fmt.Errorf("unknown action %q", action)
+
+ case "new":
+ if len(args) != 0 {
+ return fmt.Errorf("found %d arguments but %q takes none", len(args), action)
+ }
+
+ newContents, err := edit("")
+ if err != nil {
+ return err
+ }
+
+ if strings.TrimSpace(string(newContents)) == "" {
+ io.WriteString(set.env.term, "Resulting file was empty. Ignoring.\n")
+ return nil
+ }
+
+ var result map[string]interface{}
+ if err := set.env.server.Post(&result, "acvp/v1/"+set.name, newContents); err != nil {
+ return err
+ }
+
+ // In case it's a testSession that was just created, poke any access token
+ // into the server's lookup table and the cache.
+ if urlInterface, ok := result["url"]; ok {
+ if url, ok := urlInterface.(string); ok {
+ if tokenInterface, ok := result["accessToken"]; ok {
+ if token, ok := tokenInterface.(string); ok {
+ for strings.HasPrefix(url, "/") {
+ url = url[1:]
+ }
+ set.env.server.PrefixTokens[url] = token
+ if len(set.env.config.SessionTokensCache) > 0 {
+ ioutil.WriteFile(filepath.Join(set.env.config.SessionTokensCache, neturl.PathEscape(url))+".token", []byte(token), 0600)
+ }
+ }
+ }
+ }
+ }
+
+ ret, err := json.MarshalIndent(result, "", " ")
+ if err != nil {
+ return err
+ }
+ set.env.term.Write(ret)
+ return nil
+ }
+}
+
+type ServerObject struct {
+ set *ServerObjectSet
+ index uint64
+}
+
+func (obj ServerObject) String() (string, error) {
+ data := reflect.New(obj.set.resultType).Interface()
+ if err := obj.set.env.server.Get(data, "acvp/v1/"+obj.set.name+"/"+strconv.FormatUint(obj.index, 10)); err != nil {
+ return "", err
+ }
+ ret, err := json.MarshalIndent(data, "", " ")
+ return string(ret), err
+}
+
+func (obj ServerObject) Index(index string) (Object, error) {
+ if obj.set.subObjects == nil {
+ return nil, errors.New("cannot index " + obj.set.name + " objects")
+ }
+ constr, ok := obj.set.subObjects[index]
+ if !ok {
+ return nil, fmt.Errorf("no such subobject %q", index)
+ }
+ return constr(obj.set.env, fmt.Sprintf("%s/%d", obj.set.name, obj.index))
+}
+
+func (ServerObject) Search(condition acvp.Query) (Object, error) {
+ return nil, errors.New("cannot search individual object")
+}
+
+func edit(initialContents string) ([]byte, error) {
+ tmp, err := ioutil.TempFile("", "acvp*.json")
+ if err != nil {
+ return nil, err
+ }
+ path := tmp.Name()
+ defer os.Remove(path)
+
+ _, err = io.WriteString(tmp, initialContents)
+ tmp.Close()
+ if err != nil {
+ return nil, err
+ }
+
+ editor := os.Getenv("EDITOR")
+ if len(editor) == 0 {
+ editor = "vim"
+ }
+
+ cmd := exec.Command(editor, path)
+ cmd.Stdout = os.Stdout
+ cmd.Stdin = os.Stdin
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ return nil, err
+ }
+
+ return ioutil.ReadFile(path)
+}
+
+func (obj ServerObject) Action(action string, args []string) error {
+ switch action {
+ default:
+ return fmt.Errorf("unknown action %q", action)
+
+ case "edit":
+ if len(args) != 0 {
+ return fmt.Errorf("found %d arguments but %q takes none", len(args), action)
+ }
+
+ contents, err := obj.String()
+ if err != nil {
+ return err
+ }
+
+ newContents, err := edit(contents)
+ if err != nil {
+ return err
+ }
+
+ if trimmed := strings.TrimSpace(string(newContents)); len(trimmed) == 0 || trimmed == strings.TrimSpace(contents) {
+ io.WriteString(obj.set.env.term, "Resulting file was equal or empty. Not updating.\n")
+ return nil
+ }
+
+ var status acvp.RequestStatus
+ if err := obj.set.env.server.Put(&status, "acvp/v1/"+obj.set.name+"/"+strconv.FormatUint(obj.index, 10), newContents); err != nil {
+ return err
+ }
+
+ fmt.Fprintf(obj.set.env.term, "%#v\n", status)
+ return nil
+
+ case "delete":
+ if len(args) != 0 {
+ return fmt.Errorf("found %d arguments but %q takes none", len(args), action)
+ }
+ return obj.set.env.server.Delete("acvp/v1/" + obj.set.name + "/" + strconv.FormatUint(obj.index, 10))
+ }
+}
+
+type Search struct {
+ ServerObjectSet
+ query acvp.Query
+}
+
+func (search Search) String() (string, error) {
+ data := reflect.New(reflect.SliceOf(search.resultType)).Interface()
+ fmt.Printf("Searching for %#v\n", search.query)
+ if err := search.env.server.GetPaged(data, "acvp/v1/"+search.name, search.query); err != nil {
+ return "", err
+ }
+ ret, err := json.MarshalIndent(data, "", " ")
+ return string(ret), err
+}
+
+func (search Search) Index(_ string) (Object, error) {
+ return nil, errors.New("indexing of search results not supported")
+}
+
+func (search Search) Search(condition acvp.Query) (Object, error) {
+ search.query = append(search.query, condition...)
+ return search, nil
+}
+
+func (Search) Action(_ string, _ []string) error {
+ return errors.New("no actions supported on search objects")
+}
+
+type Algorithms struct {
+ ServerObjectSet
+}
+
+func (algos Algorithms) String() (string, error) {
+ var result struct {
+ Algorithms []map[string]interface{} `json:"algorithms"`
+ }
+ if err := algos.env.server.Get(&result, "acvp/v1/algorithms"); err != nil {
+ return "", err
+ }
+ ret, err := json.MarshalIndent(result.Algorithms, "", " ")
+ return string(ret), err
+}
+
+type Env struct {
+ line string
+ variables map[string]Object
+ server *acvp.Server
+ term *terminal.Terminal
+ config Config
+}
+
+func (e *Env) bytes(node *node32) []byte {
+ return []byte(e.line[node.begin:node.end])
+}
+
+func (e *Env) contents(node *node32) string {
+ return e.line[node.begin:node.end]
+}
+
+type stringLiteral struct {
+ env *Env
+ contents string
+}
+
+func (s stringLiteral) String() (string, error) {
+ return s.contents, nil
+}
+
+func (stringLiteral) Index(_ string) (Object, error) {
+ return nil, errors.New("cannot index strings")
+}
+
+func (stringLiteral) Search(_ acvp.Query) (Object, error) {
+ return nil, errors.New("cannot search strings")
+}
+
+func (s stringLiteral) Action(action string, args []string) error {
+ switch action {
+ default:
+ return fmt.Errorf("action %q not supported on string literals", action)
+
+ case "GET":
+ if len(args) != 0 {
+ return fmt.Errorf("found %d arguments but %q takes none", len(args), action)
+ }
+
+ var results map[string]interface{}
+ if err := s.env.server.Get(&results, s.contents); err != nil {
+ return err
+ }
+ ret, err := json.MarshalIndent(results, "", " ")
+ if err != nil {
+ return err
+ }
+ s.env.term.Write(ret)
+ return nil
+ }
+}
+
+type results struct {
+ env *Env
+ prefix string
+}
+
+func (r results) String() (string, error) {
+ var results map[string]interface{}
+ if err := r.env.server.Get(&results, "acvp/v1/"+r.prefix+"/results"); err != nil {
+ return "", err
+ }
+ ret, err := json.MarshalIndent(results, "", " ")
+ return string(ret), err
+}
+
+func (results) Index(_ string) (Object, error) {
+ return nil, errors.New("cannot index results objects")
+}
+
+func (results) Search(_ acvp.Query) (Object, error) {
+ return nil, errors.New("cannot search results objects")
+}
+
+func (results) Action(_ string, _ []string) error {
+ return errors.New("no actions supported on results objects")
+}
+
+func (e *Env) parseStringLiteral(node *node32) string {
+ assertNodeType(node, ruleStringLiteral)
+ in := e.bytes(node)
+ var buf bytes.Buffer
+ for i := 1; i < len(in)-1; i++ {
+ if in[i] == '\\' {
+ switch in[i+1] {
+ case '\\':
+ buf.WriteByte('\\')
+ case 'n':
+ buf.WriteByte('\n')
+ case '"':
+ buf.WriteByte('"')
+ default:
+ panic("unknown escape")
+ }
+ i++
+ continue
+ }
+ buf.WriteByte(in[i])
+ }
+
+ return buf.String()
+}
+
+func (e *Env) evalExpression(node *node32) (obj Object, err error) {
+ switch node.pegRule {
+ case ruleStringLiteral:
+ return stringLiteral{e, e.parseStringLiteral(node)}, nil
+
+ case ruleVariable:
+ varName := e.contents(node)
+ obj, ok := e.variables[varName]
+ if !ok {
+ return nil, fmt.Errorf("unknown variable %q", varName)
+ }
+ return obj, nil
+
+ case ruleIndexing:
+ node = node.up
+ assertNodeType(node, ruleVariable)
+ varName := e.contents(node)
+ obj, ok := e.variables[varName]
+ if !ok {
+ return nil, fmt.Errorf("unknown variable %q", varName)
+ }
+
+ node = node.next
+ for node != nil {
+ assertNodeType(node, ruleIndex)
+ indexStr := e.contents(node)
+ if obj, err = obj.Index(indexStr); err != nil {
+ return nil, err
+ }
+ node = node.next
+ }
+
+ return obj, nil
+
+ case ruleSearch:
+ node = node.up
+ assertNodeType(node, ruleVariable)
+ varName := e.contents(node)
+ obj, ok := e.variables[varName]
+ if !ok {
+ return nil, fmt.Errorf("unknown variable %q", varName)
+ }
+
+ node = skipWS(node.next)
+ assertNodeType(node, ruleQuery)
+ node = node.up
+
+ var query acvp.Query
+ for node != nil {
+ assertNodeType(node, ruleConjunctions)
+ query = append(query, e.parseConjunction(node.up))
+ node = skipWS(node.next)
+ }
+
+ if len(query) == 0 {
+ return nil, errors.New("cannot have empty query")
+ }
+
+ return obj.Search(query)
+ }
+
+ panic("unhandled")
+}
+
+func (e *Env) evalAction(node *node32) error {
+ assertNodeType(node, ruleExpression)
+ obj, err := e.evalExpression(node.up)
+ if err != nil {
+ return err
+ }
+
+ node = node.next
+ assertNodeType(node, ruleCommand)
+ node = node.up
+ assertNodeType(node, ruleFunction)
+ function := e.contents(node)
+ node = node.next
+
+ var args []string
+ for node != nil {
+ assertNodeType(node, ruleArgs)
+ node = node.up
+ args = append(args, e.parseStringLiteral(node))
+
+ node = skipWS(node.next)
+ }
+
+ return obj.Action(function, args)
+}
+
+func (e *Env) parseConjunction(node *node32) (ret acvp.Conjunction) {
+ for node != nil {
+ assertNodeType(node, ruleConjunction)
+ ret = append(ret, e.parseCondition(node.up))
+
+ node = skipWS(node.next)
+ if node != nil {
+ assertNodeType(node, ruleConjunctions)
+ node = node.up
+ }
+ }
+ return ret
+}
+
+func (e *Env) parseCondition(node *node32) (ret acvp.Condition) {
+ assertNodeType(node, ruleField)
+ ret.Param = e.contents(node)
+ node = skipWS(node.next)
+
+ assertNodeType(node, ruleRelation)
+ switch e.contents(node) {
+ case "==":
+ ret.Relation = acvp.Equals
+ case "!=":
+ ret.Relation = acvp.NotEquals
+ case "contains":
+ ret.Relation = acvp.Contains
+ case "startsWith":
+ ret.Relation = acvp.StartsWith
+ case "endsWith":
+ ret.Relation = acvp.EndsWith
+ default:
+ panic("relation not handled: " + e.contents(node))
+ }
+ node = skipWS(node.next)
+
+ ret.Value = e.parseStringLiteral(node)
+
+ return ret
+}
+
+func runInteractive(server *acvp.Server, config Config) {
+ oldState, err := terminal.MakeRaw(0)
+ if err != nil {
+ panic(err)
+ }
+ defer terminal.Restore(0, oldState)
+ term := terminal.NewTerminal(os.Stdin, "> ")
+
+ resizeChan := make(chan os.Signal)
+ go func() {
+ for _ = range resizeChan {
+ updateTerminalSize(term)
+ }
+ }()
+ signal.Notify(resizeChan, syscall.SIGWINCH)
+
+ env := &Env{variables: make(map[string]Object), server: server, term: term, config: config}
+ env.variables["requests"] = ServerObjectSet{
+ env: env,
+ name: "requests",
+ resultType: reflect.TypeOf(&acvp.RequestStatus{}),
+ canEnumerate: true,
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.8
+ env.variables["vendors"] = ServerObjectSet{
+ env: env,
+ name: "vendors",
+ searchKeys: map[string][]acvp.Relation{
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.8.1
+ "name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "website": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "email": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "phoneNumber": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ },
+ subObjects: map[string]func(*Env, string) (Object, error){
+ "contacts": func(env *Env, prefix string) (Object, error) {
+ return ServerObjectSet{
+ env: env,
+ name: prefix + "/contacts",
+ resultType: reflect.TypeOf(&acvp.Person{}),
+ canEnumerate: true,
+ }, nil
+ },
+ "addresses": func(env *Env, prefix string) (Object, error) {
+ return ServerObjectSet{
+ env: env,
+ name: prefix + "/addresses",
+ resultType: reflect.TypeOf(&acvp.Address{}),
+ canEnumerate: true,
+ }, nil
+ },
+ },
+ resultType: reflect.TypeOf(&acvp.Vendor{}),
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.9
+ env.variables["persons"] = ServerObjectSet{
+ env: env,
+ name: "persons",
+ searchKeys: map[string][]acvp.Relation{
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.10.1
+ "fullName": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "email": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "phoneNumber": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "vendorId": []acvp.Relation{acvp.Equals, acvp.NotEquals, acvp.LessThan, acvp.LessThanEqual, acvp.GreaterThan, acvp.GreaterThanEqual},
+ },
+ resultType: reflect.TypeOf(&acvp.Person{}),
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.11
+ env.variables["modules"] = ServerObjectSet{
+ env: env,
+ name: "modules",
+ searchKeys: map[string][]acvp.Relation{
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.10.1
+ "name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "version": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "website": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "description": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "type": []acvp.Relation{acvp.Equals, acvp.NotEquals},
+ "vendorId": []acvp.Relation{acvp.Equals, acvp.NotEquals, acvp.LessThan, acvp.LessThanEqual, acvp.GreaterThan, acvp.GreaterThanEqual},
+ },
+ resultType: reflect.TypeOf(&acvp.Module{}),
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.12
+ env.variables["oes"] = ServerObjectSet{
+ env: env,
+ name: "oes",
+ searchKeys: map[string][]acvp.Relation{
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.12.1
+ "name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ },
+ resultType: reflect.TypeOf(&acvp.OperationalEnvironment{}),
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.13
+ env.variables["deps"] = ServerObjectSet{
+ env: env,
+ name: "dependencies",
+ searchKeys: map[string][]acvp.Relation{
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.12.1
+ "name": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "type": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ "description": []acvp.Relation{acvp.Equals, acvp.StartsWith, acvp.EndsWith, acvp.Contains},
+ },
+ resultType: reflect.TypeOf(&acvp.Dependency{}),
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.14
+ env.variables["algos"] = Algorithms{
+ ServerObjectSet{
+ env: env,
+ name: "algorithms",
+ resultType: reflect.TypeOf(&acvp.Algorithm{}),
+ canEnumerate: true,
+ },
+ }
+ // https://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.15
+ env.variables["sessions"] = ServerObjectSet{
+ env: env,
+ name: "testSessions",
+ resultType: reflect.TypeOf(&acvp.TestSession{}),
+ canEnumerate: true,
+ subObjects: map[string]func(env *Env, prefix string) (Object, error){
+ "results": func(env *Env, prefix string) (Object, error) {
+ return results{env: env, prefix: prefix}, nil
+ },
+ },
+ }
+
+ for {
+ if env.line, err = term.ReadLine(); err != nil {
+ return
+ }
+ if len(env.line) == 0 {
+ continue
+ }
+
+ stmt := Statement{Buffer: env.line, Pretty: true}
+ stmt.Init()
+ if err := stmt.Parse(); err != nil {
+ io.WriteString(term, err.Error())
+ continue
+ }
+
+ node := skipWS(stmt.AST().up)
+ switch node.pegRule {
+ case ruleExpression:
+ obj, err := env.evalExpression(node.up)
+ var repr string
+ if err == nil {
+ repr, err = obj.String()
+ }
+
+ if err != nil {
+ fmt.Fprintf(term, "error while evaluating expression: %s\n", err)
+ } else {
+ io.WriteString(term, repr)
+ io.WriteString(term, "\n")
+ }
+
+ case ruleAction:
+ if err := env.evalAction(node.up); err != nil {
+ io.WriteString(term, err.Error())
+ io.WriteString(term, "\n")
+ }
+
+ default:
+ fmt.Fprintf(term, "internal error parsing input.\n")
+ }
+ }
+}
diff --git a/src/util/fipstools/acvp/acvptool/parser.peg b/src/util/fipstools/acvp/acvptool/parser.peg
new file mode 100644
index 0000000..ca759c8
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/parser.peg
@@ -0,0 +1,25 @@
+package main
+
+type Statement Peg {}
+
+Statement <- WS? (Assignment / Action / Expression) WS? !.
+Assignment <- Variable WS? '=' WS? Expression
+Variable <- [a-zA-Z_][a-zA-Z0-9_]*
+Expression <- (StringLiteral / Indexing / Search / Variable)
+StringLiteral <- '"' QuotedText '"'
+QuotedText <- (EscapedChar / [^\\"])*
+EscapedChar <- '\\' [\\n"]
+Indexing <- Variable ('[' Index ']')+
+Index <- [0-9a-z]+
+Search <- Variable '[' WS? 'where' WS Query ']'
+Action <- Expression '.' Command
+Command <- Function '(' Args? ')'
+Function <- [a-zA-Z]+
+Args <- StringLiteral (WS? ',' WS? Args)
+Query <- Conjunctions (WS? '||' WS? Conjunctions)?
+Conjunctions <- Conjunction (WS? '&&' WS? Conjunctions)?
+Conjunction <- Field WS? Relation WS? StringLiteral
+Field <- [a-z][a-zA-Z0-9]*
+Relation <- ('==' / '!=' / 'contains' / 'startsWith' / 'endsWith')
+
+WS <- [ \t]+
diff --git a/src/util/fipstools/acvp/acvptool/parser.peg.go b/src/util/fipstools/acvp/acvptool/parser.peg.go
new file mode 100644
index 0000000..c80a7dc
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/parser.peg.go
@@ -0,0 +1,1334 @@
+package main
+
+import (
+ "fmt"
+ "math"
+ "sort"
+ "strconv"
+)
+
+const endSymbol rune = 1114112
+
+/* The rule types inferred from the grammar are below. */
+type pegRule uint8
+
+const (
+ ruleUnknown pegRule = iota
+ ruleStatement
+ ruleAssignment
+ ruleVariable
+ ruleExpression
+ ruleStringLiteral
+ ruleQuotedText
+ ruleEscapedChar
+ ruleIndexing
+ ruleIndex
+ ruleSearch
+ ruleAction
+ ruleCommand
+ ruleFunction
+ ruleArgs
+ ruleQuery
+ ruleConjunctions
+ ruleConjunction
+ ruleField
+ ruleRelation
+ ruleWS
+)
+
+var rul3s = [...]string{
+ "Unknown",
+ "Statement",
+ "Assignment",
+ "Variable",
+ "Expression",
+ "StringLiteral",
+ "QuotedText",
+ "EscapedChar",
+ "Indexing",
+ "Index",
+ "Search",
+ "Action",
+ "Command",
+ "Function",
+ "Args",
+ "Query",
+ "Conjunctions",
+ "Conjunction",
+ "Field",
+ "Relation",
+ "WS",
+}
+
+type token32 struct {
+ pegRule
+ begin, end uint32
+}
+
+func (t *token32) String() string {
+ return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end)
+}
+
+type node32 struct {
+ token32
+ up, next *node32
+}
+
+func (node *node32) print(pretty bool, buffer string) {
+ var print func(node *node32, depth int)
+ print = func(node *node32, depth int) {
+ for node != nil {
+ for c := 0; c < depth; c++ {
+ fmt.Printf(" ")
+ }
+ rule := rul3s[node.pegRule]
+ quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end])))
+ if !pretty {
+ fmt.Printf("%v %v\n", rule, quote)
+ } else {
+ fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote)
+ }
+ if node.up != nil {
+ print(node.up, depth+1)
+ }
+ node = node.next
+ }
+ }
+ print(node, 0)
+}
+
+func (node *node32) Print(buffer string) {
+ node.print(false, buffer)
+}
+
+func (node *node32) PrettyPrint(buffer string) {
+ node.print(true, buffer)
+}
+
+type tokens32 struct {
+ tree []token32
+}
+
+func (t *tokens32) Trim(length uint32) {
+ t.tree = t.tree[:length]
+}
+
+func (t *tokens32) Print() {
+ for _, token := range t.tree {
+ fmt.Println(token.String())
+ }
+}
+
+func (t *tokens32) AST() *node32 {
+ type element struct {
+ node *node32
+ down *element
+ }
+ tokens := t.Tokens()
+ var stack *element
+ for _, token := range tokens {
+ if token.begin == token.end {
+ continue
+ }
+ node := &node32{token32: token}
+ for stack != nil && stack.node.begin >= token.begin && stack.node.end <= token.end {
+ stack.node.next = node.up
+ node.up = stack.node
+ stack = stack.down
+ }
+ stack = &element{node: node, down: stack}
+ }
+ if stack != nil {
+ return stack.node
+ }
+ return nil
+}
+
+func (t *tokens32) PrintSyntaxTree(buffer string) {
+ t.AST().Print(buffer)
+}
+
+func (t *tokens32) PrettyPrintSyntaxTree(buffer string) {
+ t.AST().PrettyPrint(buffer)
+}
+
+func (t *tokens32) Add(rule pegRule, begin, end, index uint32) {
+ if tree := t.tree; int(index) >= len(tree) {
+ expanded := make([]token32, 2*len(tree))
+ copy(expanded, tree)
+ t.tree = expanded
+ }
+ t.tree[index] = token32{
+ pegRule: rule,
+ begin: begin,
+ end: end,
+ }
+}
+
+func (t *tokens32) Tokens() []token32 {
+ return t.tree
+}
+
+type Statement struct {
+ Buffer string
+ buffer []rune
+ rules [21]func() bool
+ parse func(rule ...int) error
+ reset func()
+ Pretty bool
+ tokens32
+}
+
+func (p *Statement) Parse(rule ...int) error {
+ return p.parse(rule...)
+}
+
+func (p *Statement) Reset() {
+ p.reset()
+}
+
+type textPosition struct {
+ line, symbol int
+}
+
+type textPositionMap map[int]textPosition
+
+func translatePositions(buffer []rune, positions []int) textPositionMap {
+ length, translations, j, line, symbol := len(positions), make(textPositionMap, len(positions)), 0, 1, 0
+ sort.Ints(positions)
+
+search:
+ for i, c := range buffer {
+ if c == '\n' {
+ line, symbol = line+1, 0
+ } else {
+ symbol++
+ }
+ if i == positions[j] {
+ translations[positions[j]] = textPosition{line, symbol}
+ for j++; j < length; j++ {
+ if i != positions[j] {
+ continue search
+ }
+ }
+ break search
+ }
+ }
+
+ return translations
+}
+
+type parseError struct {
+ p *Statement
+ max token32
+}
+
+func (e *parseError) Error() string {
+ tokens, error := []token32{e.max}, "\n"
+ positions, p := make([]int, 2*len(tokens)), 0
+ for _, token := range tokens {
+ positions[p], p = int(token.begin), p+1
+ positions[p], p = int(token.end), p+1
+ }
+ translations := translatePositions(e.p.buffer, positions)
+ format := "parse error near %v (line %v symbol %v - line %v symbol %v):\n%v\n"
+ if e.p.Pretty {
+ format = "parse error near \x1B[34m%v\x1B[m (line %v symbol %v - line %v symbol %v):\n%v\n"
+ }
+ for _, token := range tokens {
+ begin, end := int(token.begin), int(token.end)
+ error += fmt.Sprintf(format,
+ rul3s[token.pegRule],
+ translations[begin].line, translations[begin].symbol,
+ translations[end].line, translations[end].symbol,
+ strconv.Quote(string(e.p.buffer[begin:end])))
+ }
+
+ return error
+}
+
+func (p *Statement) PrintSyntaxTree() {
+ if p.Pretty {
+ p.tokens32.PrettyPrintSyntaxTree(p.Buffer)
+ } else {
+ p.tokens32.PrintSyntaxTree(p.Buffer)
+ }
+}
+
+func (p *Statement) Init() {
+ var (
+ max token32
+ position, tokenIndex uint32
+ buffer []rune
+ )
+ p.reset = func() {
+ max = token32{}
+ position, tokenIndex = 0, 0
+
+ p.buffer = []rune(p.Buffer)
+ if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol {
+ p.buffer = append(p.buffer, endSymbol)
+ }
+ buffer = p.buffer
+ }
+ p.reset()
+
+ _rules := p.rules
+ tree := tokens32{tree: make([]token32, math.MaxInt16)}
+ p.parse = func(rule ...int) error {
+ r := 1
+ if len(rule) > 0 {
+ r = rule[0]
+ }
+ matches := p.rules[r]()
+ p.tokens32 = tree
+ if matches {
+ p.Trim(tokenIndex)
+ return nil
+ }
+ return &parseError{p, max}
+ }
+
+ add := func(rule pegRule, begin uint32) {
+ tree.Add(rule, begin, position, tokenIndex)
+ tokenIndex++
+ if begin != position && position > max.end {
+ max = token32{rule, begin, position}
+ }
+ }
+
+ matchDot := func() bool {
+ if buffer[position] != endSymbol {
+ position++
+ return true
+ }
+ return false
+ }
+
+ /*matchChar := func(c byte) bool {
+ if buffer[position] == c {
+ position++
+ return true
+ }
+ return false
+ }*/
+
+ /*matchRange := func(lower byte, upper byte) bool {
+ if c := buffer[position]; c >= lower && c <= upper {
+ position++
+ return true
+ }
+ return false
+ }*/
+
+ _rules = [...]func() bool{
+ nil,
+ /* 0 Statement <- <(WS? (Assignment / Action / Expression) WS? !.)> */
+ func() bool {
+ position0, tokenIndex0 := position, tokenIndex
+ {
+ position1 := position
+ {
+ position2, tokenIndex2 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l2
+ }
+ goto l3
+ l2:
+ position, tokenIndex = position2, tokenIndex2
+ }
+ l3:
+ {
+ position4, tokenIndex4 := position, tokenIndex
+ if !_rules[ruleAssignment]() {
+ goto l5
+ }
+ goto l4
+ l5:
+ position, tokenIndex = position4, tokenIndex4
+ if !_rules[ruleAction]() {
+ goto l6
+ }
+ goto l4
+ l6:
+ position, tokenIndex = position4, tokenIndex4
+ if !_rules[ruleExpression]() {
+ goto l0
+ }
+ }
+ l4:
+ {
+ position7, tokenIndex7 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l7
+ }
+ goto l8
+ l7:
+ position, tokenIndex = position7, tokenIndex7
+ }
+ l8:
+ {
+ position9, tokenIndex9 := position, tokenIndex
+ if !matchDot() {
+ goto l9
+ }
+ goto l0
+ l9:
+ position, tokenIndex = position9, tokenIndex9
+ }
+ add(ruleStatement, position1)
+ }
+ return true
+ l0:
+ position, tokenIndex = position0, tokenIndex0
+ return false
+ },
+ /* 1 Assignment <- <(Variable WS? '=' WS? Expression)> */
+ func() bool {
+ position10, tokenIndex10 := position, tokenIndex
+ {
+ position11 := position
+ if !_rules[ruleVariable]() {
+ goto l10
+ }
+ {
+ position12, tokenIndex12 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l12
+ }
+ goto l13
+ l12:
+ position, tokenIndex = position12, tokenIndex12
+ }
+ l13:
+ if buffer[position] != rune('=') {
+ goto l10
+ }
+ position++
+ {
+ position14, tokenIndex14 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l14
+ }
+ goto l15
+ l14:
+ position, tokenIndex = position14, tokenIndex14
+ }
+ l15:
+ if !_rules[ruleExpression]() {
+ goto l10
+ }
+ add(ruleAssignment, position11)
+ }
+ return true
+ l10:
+ position, tokenIndex = position10, tokenIndex10
+ return false
+ },
+ /* 2 Variable <- <(([a-z] / [A-Z] / '_') ([a-z] / [A-Z] / [0-9] / '_')*)> */
+ func() bool {
+ position16, tokenIndex16 := position, tokenIndex
+ {
+ position17 := position
+ {
+ position18, tokenIndex18 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l19
+ }
+ position++
+ goto l18
+ l19:
+ position, tokenIndex = position18, tokenIndex18
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l20
+ }
+ position++
+ goto l18
+ l20:
+ position, tokenIndex = position18, tokenIndex18
+ if buffer[position] != rune('_') {
+ goto l16
+ }
+ position++
+ }
+ l18:
+ l21:
+ {
+ position22, tokenIndex22 := position, tokenIndex
+ {
+ position23, tokenIndex23 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l24
+ }
+ position++
+ goto l23
+ l24:
+ position, tokenIndex = position23, tokenIndex23
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l25
+ }
+ position++
+ goto l23
+ l25:
+ position, tokenIndex = position23, tokenIndex23
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l26
+ }
+ position++
+ goto l23
+ l26:
+ position, tokenIndex = position23, tokenIndex23
+ if buffer[position] != rune('_') {
+ goto l22
+ }
+ position++
+ }
+ l23:
+ goto l21
+ l22:
+ position, tokenIndex = position22, tokenIndex22
+ }
+ add(ruleVariable, position17)
+ }
+ return true
+ l16:
+ position, tokenIndex = position16, tokenIndex16
+ return false
+ },
+ /* 3 Expression <- <(StringLiteral / Indexing / Search / Variable)> */
+ func() bool {
+ position27, tokenIndex27 := position, tokenIndex
+ {
+ position28 := position
+ {
+ position29, tokenIndex29 := position, tokenIndex
+ if !_rules[ruleStringLiteral]() {
+ goto l30
+ }
+ goto l29
+ l30:
+ position, tokenIndex = position29, tokenIndex29
+ if !_rules[ruleIndexing]() {
+ goto l31
+ }
+ goto l29
+ l31:
+ position, tokenIndex = position29, tokenIndex29
+ if !_rules[ruleSearch]() {
+ goto l32
+ }
+ goto l29
+ l32:
+ position, tokenIndex = position29, tokenIndex29
+ if !_rules[ruleVariable]() {
+ goto l27
+ }
+ }
+ l29:
+ add(ruleExpression, position28)
+ }
+ return true
+ l27:
+ position, tokenIndex = position27, tokenIndex27
+ return false
+ },
+ /* 4 StringLiteral <- <('"' QuotedText '"')> */
+ func() bool {
+ position33, tokenIndex33 := position, tokenIndex
+ {
+ position34 := position
+ if buffer[position] != rune('"') {
+ goto l33
+ }
+ position++
+ if !_rules[ruleQuotedText]() {
+ goto l33
+ }
+ if buffer[position] != rune('"') {
+ goto l33
+ }
+ position++
+ add(ruleStringLiteral, position34)
+ }
+ return true
+ l33:
+ position, tokenIndex = position33, tokenIndex33
+ return false
+ },
+ /* 5 QuotedText <- <(EscapedChar / (!('\\' / '"') .))*> */
+ func() bool {
+ {
+ position36 := position
+ l37:
+ {
+ position38, tokenIndex38 := position, tokenIndex
+ {
+ position39, tokenIndex39 := position, tokenIndex
+ if !_rules[ruleEscapedChar]() {
+ goto l40
+ }
+ goto l39
+ l40:
+ position, tokenIndex = position39, tokenIndex39
+ {
+ position41, tokenIndex41 := position, tokenIndex
+ {
+ position42, tokenIndex42 := position, tokenIndex
+ if buffer[position] != rune('\\') {
+ goto l43
+ }
+ position++
+ goto l42
+ l43:
+ position, tokenIndex = position42, tokenIndex42
+ if buffer[position] != rune('"') {
+ goto l41
+ }
+ position++
+ }
+ l42:
+ goto l38
+ l41:
+ position, tokenIndex = position41, tokenIndex41
+ }
+ if !matchDot() {
+ goto l38
+ }
+ }
+ l39:
+ goto l37
+ l38:
+ position, tokenIndex = position38, tokenIndex38
+ }
+ add(ruleQuotedText, position36)
+ }
+ return true
+ },
+ /* 6 EscapedChar <- <('\\' ('\\' / 'n' / '"'))> */
+ func() bool {
+ position44, tokenIndex44 := position, tokenIndex
+ {
+ position45 := position
+ if buffer[position] != rune('\\') {
+ goto l44
+ }
+ position++
+ {
+ position46, tokenIndex46 := position, tokenIndex
+ if buffer[position] != rune('\\') {
+ goto l47
+ }
+ position++
+ goto l46
+ l47:
+ position, tokenIndex = position46, tokenIndex46
+ if buffer[position] != rune('n') {
+ goto l48
+ }
+ position++
+ goto l46
+ l48:
+ position, tokenIndex = position46, tokenIndex46
+ if buffer[position] != rune('"') {
+ goto l44
+ }
+ position++
+ }
+ l46:
+ add(ruleEscapedChar, position45)
+ }
+ return true
+ l44:
+ position, tokenIndex = position44, tokenIndex44
+ return false
+ },
+ /* 7 Indexing <- <(Variable ('[' Index ']')+)> */
+ func() bool {
+ position49, tokenIndex49 := position, tokenIndex
+ {
+ position50 := position
+ if !_rules[ruleVariable]() {
+ goto l49
+ }
+ if buffer[position] != rune('[') {
+ goto l49
+ }
+ position++
+ if !_rules[ruleIndex]() {
+ goto l49
+ }
+ if buffer[position] != rune(']') {
+ goto l49
+ }
+ position++
+ l51:
+ {
+ position52, tokenIndex52 := position, tokenIndex
+ if buffer[position] != rune('[') {
+ goto l52
+ }
+ position++
+ if !_rules[ruleIndex]() {
+ goto l52
+ }
+ if buffer[position] != rune(']') {
+ goto l52
+ }
+ position++
+ goto l51
+ l52:
+ position, tokenIndex = position52, tokenIndex52
+ }
+ add(ruleIndexing, position50)
+ }
+ return true
+ l49:
+ position, tokenIndex = position49, tokenIndex49
+ return false
+ },
+ /* 8 Index <- <([0-9] / [a-z])+> */
+ func() bool {
+ position53, tokenIndex53 := position, tokenIndex
+ {
+ position54 := position
+ {
+ position57, tokenIndex57 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l58
+ }
+ position++
+ goto l57
+ l58:
+ position, tokenIndex = position57, tokenIndex57
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l53
+ }
+ position++
+ }
+ l57:
+ l55:
+ {
+ position56, tokenIndex56 := position, tokenIndex
+ {
+ position59, tokenIndex59 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l60
+ }
+ position++
+ goto l59
+ l60:
+ position, tokenIndex = position59, tokenIndex59
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l56
+ }
+ position++
+ }
+ l59:
+ goto l55
+ l56:
+ position, tokenIndex = position56, tokenIndex56
+ }
+ add(ruleIndex, position54)
+ }
+ return true
+ l53:
+ position, tokenIndex = position53, tokenIndex53
+ return false
+ },
+ /* 9 Search <- <(Variable '[' WS? ('w' 'h' 'e' 'r' 'e') WS Query ']')> */
+ func() bool {
+ position61, tokenIndex61 := position, tokenIndex
+ {
+ position62 := position
+ if !_rules[ruleVariable]() {
+ goto l61
+ }
+ if buffer[position] != rune('[') {
+ goto l61
+ }
+ position++
+ {
+ position63, tokenIndex63 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l63
+ }
+ goto l64
+ l63:
+ position, tokenIndex = position63, tokenIndex63
+ }
+ l64:
+ if buffer[position] != rune('w') {
+ goto l61
+ }
+ position++
+ if buffer[position] != rune('h') {
+ goto l61
+ }
+ position++
+ if buffer[position] != rune('e') {
+ goto l61
+ }
+ position++
+ if buffer[position] != rune('r') {
+ goto l61
+ }
+ position++
+ if buffer[position] != rune('e') {
+ goto l61
+ }
+ position++
+ if !_rules[ruleWS]() {
+ goto l61
+ }
+ if !_rules[ruleQuery]() {
+ goto l61
+ }
+ if buffer[position] != rune(']') {
+ goto l61
+ }
+ position++
+ add(ruleSearch, position62)
+ }
+ return true
+ l61:
+ position, tokenIndex = position61, tokenIndex61
+ return false
+ },
+ /* 10 Action <- <(Expression '.' Command)> */
+ func() bool {
+ position65, tokenIndex65 := position, tokenIndex
+ {
+ position66 := position
+ if !_rules[ruleExpression]() {
+ goto l65
+ }
+ if buffer[position] != rune('.') {
+ goto l65
+ }
+ position++
+ if !_rules[ruleCommand]() {
+ goto l65
+ }
+ add(ruleAction, position66)
+ }
+ return true
+ l65:
+ position, tokenIndex = position65, tokenIndex65
+ return false
+ },
+ /* 11 Command <- <(Function '(' Args? ')')> */
+ func() bool {
+ position67, tokenIndex67 := position, tokenIndex
+ {
+ position68 := position
+ if !_rules[ruleFunction]() {
+ goto l67
+ }
+ if buffer[position] != rune('(') {
+ goto l67
+ }
+ position++
+ {
+ position69, tokenIndex69 := position, tokenIndex
+ if !_rules[ruleArgs]() {
+ goto l69
+ }
+ goto l70
+ l69:
+ position, tokenIndex = position69, tokenIndex69
+ }
+ l70:
+ if buffer[position] != rune(')') {
+ goto l67
+ }
+ position++
+ add(ruleCommand, position68)
+ }
+ return true
+ l67:
+ position, tokenIndex = position67, tokenIndex67
+ return false
+ },
+ /* 12 Function <- <([a-z] / [A-Z])+> */
+ func() bool {
+ position71, tokenIndex71 := position, tokenIndex
+ {
+ position72 := position
+ {
+ position75, tokenIndex75 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l76
+ }
+ position++
+ goto l75
+ l76:
+ position, tokenIndex = position75, tokenIndex75
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l71
+ }
+ position++
+ }
+ l75:
+ l73:
+ {
+ position74, tokenIndex74 := position, tokenIndex
+ {
+ position77, tokenIndex77 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l78
+ }
+ position++
+ goto l77
+ l78:
+ position, tokenIndex = position77, tokenIndex77
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l74
+ }
+ position++
+ }
+ l77:
+ goto l73
+ l74:
+ position, tokenIndex = position74, tokenIndex74
+ }
+ add(ruleFunction, position72)
+ }
+ return true
+ l71:
+ position, tokenIndex = position71, tokenIndex71
+ return false
+ },
+ /* 13 Args <- <(StringLiteral (WS? ',' WS? Args))> */
+ func() bool {
+ position79, tokenIndex79 := position, tokenIndex
+ {
+ position80 := position
+ if !_rules[ruleStringLiteral]() {
+ goto l79
+ }
+ {
+ position81, tokenIndex81 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l81
+ }
+ goto l82
+ l81:
+ position, tokenIndex = position81, tokenIndex81
+ }
+ l82:
+ if buffer[position] != rune(',') {
+ goto l79
+ }
+ position++
+ {
+ position83, tokenIndex83 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l83
+ }
+ goto l84
+ l83:
+ position, tokenIndex = position83, tokenIndex83
+ }
+ l84:
+ if !_rules[ruleArgs]() {
+ goto l79
+ }
+ add(ruleArgs, position80)
+ }
+ return true
+ l79:
+ position, tokenIndex = position79, tokenIndex79
+ return false
+ },
+ /* 14 Query <- <(Conjunctions (WS? ('|' '|') WS? Conjunctions)?)> */
+ func() bool {
+ position85, tokenIndex85 := position, tokenIndex
+ {
+ position86 := position
+ if !_rules[ruleConjunctions]() {
+ goto l85
+ }
+ {
+ position87, tokenIndex87 := position, tokenIndex
+ {
+ position89, tokenIndex89 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l89
+ }
+ goto l90
+ l89:
+ position, tokenIndex = position89, tokenIndex89
+ }
+ l90:
+ if buffer[position] != rune('|') {
+ goto l87
+ }
+ position++
+ if buffer[position] != rune('|') {
+ goto l87
+ }
+ position++
+ {
+ position91, tokenIndex91 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l91
+ }
+ goto l92
+ l91:
+ position, tokenIndex = position91, tokenIndex91
+ }
+ l92:
+ if !_rules[ruleConjunctions]() {
+ goto l87
+ }
+ goto l88
+ l87:
+ position, tokenIndex = position87, tokenIndex87
+ }
+ l88:
+ add(ruleQuery, position86)
+ }
+ return true
+ l85:
+ position, tokenIndex = position85, tokenIndex85
+ return false
+ },
+ /* 15 Conjunctions <- <(Conjunction (WS? ('&' '&') WS? Conjunctions)?)> */
+ func() bool {
+ position93, tokenIndex93 := position, tokenIndex
+ {
+ position94 := position
+ if !_rules[ruleConjunction]() {
+ goto l93
+ }
+ {
+ position95, tokenIndex95 := position, tokenIndex
+ {
+ position97, tokenIndex97 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l97
+ }
+ goto l98
+ l97:
+ position, tokenIndex = position97, tokenIndex97
+ }
+ l98:
+ if buffer[position] != rune('&') {
+ goto l95
+ }
+ position++
+ if buffer[position] != rune('&') {
+ goto l95
+ }
+ position++
+ {
+ position99, tokenIndex99 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l99
+ }
+ goto l100
+ l99:
+ position, tokenIndex = position99, tokenIndex99
+ }
+ l100:
+ if !_rules[ruleConjunctions]() {
+ goto l95
+ }
+ goto l96
+ l95:
+ position, tokenIndex = position95, tokenIndex95
+ }
+ l96:
+ add(ruleConjunctions, position94)
+ }
+ return true
+ l93:
+ position, tokenIndex = position93, tokenIndex93
+ return false
+ },
+ /* 16 Conjunction <- <(Field WS? Relation WS? StringLiteral)> */
+ func() bool {
+ position101, tokenIndex101 := position, tokenIndex
+ {
+ position102 := position
+ if !_rules[ruleField]() {
+ goto l101
+ }
+ {
+ position103, tokenIndex103 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l103
+ }
+ goto l104
+ l103:
+ position, tokenIndex = position103, tokenIndex103
+ }
+ l104:
+ if !_rules[ruleRelation]() {
+ goto l101
+ }
+ {
+ position105, tokenIndex105 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l105
+ }
+ goto l106
+ l105:
+ position, tokenIndex = position105, tokenIndex105
+ }
+ l106:
+ if !_rules[ruleStringLiteral]() {
+ goto l101
+ }
+ add(ruleConjunction, position102)
+ }
+ return true
+ l101:
+ position, tokenIndex = position101, tokenIndex101
+ return false
+ },
+ /* 17 Field <- <([a-z] ([a-z] / [A-Z] / [0-9])*)> */
+ func() bool {
+ position107, tokenIndex107 := position, tokenIndex
+ {
+ position108 := position
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l107
+ }
+ position++
+ l109:
+ {
+ position110, tokenIndex110 := position, tokenIndex
+ {
+ position111, tokenIndex111 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l112
+ }
+ position++
+ goto l111
+ l112:
+ position, tokenIndex = position111, tokenIndex111
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l113
+ }
+ position++
+ goto l111
+ l113:
+ position, tokenIndex = position111, tokenIndex111
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l110
+ }
+ position++
+ }
+ l111:
+ goto l109
+ l110:
+ position, tokenIndex = position110, tokenIndex110
+ }
+ add(ruleField, position108)
+ }
+ return true
+ l107:
+ position, tokenIndex = position107, tokenIndex107
+ return false
+ },
+ /* 18 Relation <- <(('=' '=') / ('!' '=') / ('c' 'o' 'n' 't' 'a' 'i' 'n' 's') / ('s' 't' 'a' 'r' 't' 's' 'W' 'i' 't' 'h') / ('e' 'n' 'd' 's' 'W' 'i' 't' 'h'))> */
+ func() bool {
+ position114, tokenIndex114 := position, tokenIndex
+ {
+ position115 := position
+ {
+ position116, tokenIndex116 := position, tokenIndex
+ if buffer[position] != rune('=') {
+ goto l117
+ }
+ position++
+ if buffer[position] != rune('=') {
+ goto l117
+ }
+ position++
+ goto l116
+ l117:
+ position, tokenIndex = position116, tokenIndex116
+ if buffer[position] != rune('!') {
+ goto l118
+ }
+ position++
+ if buffer[position] != rune('=') {
+ goto l118
+ }
+ position++
+ goto l116
+ l118:
+ position, tokenIndex = position116, tokenIndex116
+ if buffer[position] != rune('c') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('o') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('n') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('a') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('i') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('n') {
+ goto l119
+ }
+ position++
+ if buffer[position] != rune('s') {
+ goto l119
+ }
+ position++
+ goto l116
+ l119:
+ position, tokenIndex = position116, tokenIndex116
+ if buffer[position] != rune('s') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('a') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('r') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('s') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('W') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('i') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l120
+ }
+ position++
+ if buffer[position] != rune('h') {
+ goto l120
+ }
+ position++
+ goto l116
+ l120:
+ position, tokenIndex = position116, tokenIndex116
+ if buffer[position] != rune('e') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('n') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('d') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('s') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('W') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('i') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l114
+ }
+ position++
+ if buffer[position] != rune('h') {
+ goto l114
+ }
+ position++
+ }
+ l116:
+ add(ruleRelation, position115)
+ }
+ return true
+ l114:
+ position, tokenIndex = position114, tokenIndex114
+ return false
+ },
+ /* 19 WS <- <(' ' / '\t')+> */
+ func() bool {
+ position121, tokenIndex121 := position, tokenIndex
+ {
+ position122 := position
+ {
+ position125, tokenIndex125 := position, tokenIndex
+ if buffer[position] != rune(' ') {
+ goto l126
+ }
+ position++
+ goto l125
+ l126:
+ position, tokenIndex = position125, tokenIndex125
+ if buffer[position] != rune('\t') {
+ goto l121
+ }
+ position++
+ }
+ l125:
+ l123:
+ {
+ position124, tokenIndex124 := position, tokenIndex
+ {
+ position127, tokenIndex127 := position, tokenIndex
+ if buffer[position] != rune(' ') {
+ goto l128
+ }
+ position++
+ goto l127
+ l128:
+ position, tokenIndex = position127, tokenIndex127
+ if buffer[position] != rune('\t') {
+ goto l124
+ }
+ position++
+ }
+ l127:
+ goto l123
+ l124:
+ position, tokenIndex = position124, tokenIndex124
+ }
+ add(ruleWS, position122)
+ }
+ return true
+ l121:
+ position, tokenIndex = position121, tokenIndex121
+ return false
+ },
+ }
+ p.rules = _rules
+}
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/block.go b/src/util/fipstools/acvp/acvptool/subprocess/block.go
new file mode 100644
index 0000000..a5ed2cb
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/subprocess/block.go
@@ -0,0 +1,252 @@
+package subprocess
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+)
+
+// blockCipher implements an ACVP algorithm by making requests to the subprocess
+// to encrypt and decrypt with a block cipher.
+type blockCipher struct {
+ algo string
+ blockSize int
+ hasIV bool
+ m *Subprocess
+}
+
+type blockCipherVectorSet struct {
+ Groups []blockCipherTestGroup `json:"testGroups"`
+}
+
+type blockCipherTestGroup struct {
+ ID uint64 `json:"tgId"`
+ Type string `json:"testType"`
+ Direction string `json:"direction"`
+ KeyBits int `json:"keylen"`
+ Tests []struct {
+ ID uint64 `json:"tcId"`
+ PlaintextHex string `json:"pt"`
+ CiphertextHex string `json:"ct"`
+ IVHex string `json:"iv"`
+ KeyHex string `json:"key"`
+ } `json:"tests"`
+}
+
+type blockCipherTestGroupResponse struct {
+ ID uint64 `json:"tgId"`
+ Tests []blockCipherTestResponse `json:"tests"`
+}
+
+type blockCipherTestResponse struct {
+ ID uint64 `json:"tcId"`
+ CiphertextHex string `json:"ct,omitempty"`
+ PlaintextHex string `json:"pt,omitempty"`
+ MCTResults []blockCipherMCTResult `json:"resultsArray,omitempty"`
+}
+
+type blockCipherMCTResult struct {
+ KeyHex string `json:"key"`
+ PlaintextHex string `json:"pt"`
+ CiphertextHex string `json:"ct"`
+ IVHex string `json:"iv,omitempty"`
+}
+
+func (b *blockCipher) Process(vectorSet []byte) (interface{}, error) {
+ var parsed blockCipherVectorSet
+ if err := json.Unmarshal(vectorSet, &parsed); err != nil {
+ return nil, err
+ }
+
+ var ret []blockCipherTestGroupResponse
+ // See
+ // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-block-ciph-00.html#rfc.section.5.2
+ // for details about the tests.
+ for _, group := range parsed.Groups {
+ response := blockCipherTestGroupResponse{
+ ID: group.ID,
+ }
+
+ var encrypt bool
+ switch group.Direction {
+ case "encrypt":
+ encrypt = true
+ case "decrypt":
+ encrypt = false
+ default:
+ return nil, fmt.Errorf("test group %d has unknown direction %q", group.ID, group.Direction)
+ }
+
+ op := b.algo + "/encrypt"
+ if !encrypt {
+ op = b.algo + "/decrypt"
+ }
+
+ var mct bool
+ switch group.Type {
+ case "AFT":
+ mct = false
+ case "MCT":
+ mct = true
+ default:
+ return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
+ }
+
+ if group.KeyBits%8 != 0 {
+ return nil, fmt.Errorf("test group %d contains non-byte-multiple key length %d", group.ID, group.KeyBits)
+ }
+ keyBytes := group.KeyBits / 8
+
+ for _, test := range group.Tests {
+ if len(test.KeyHex) != keyBytes*2 {
+ return nil, fmt.Errorf("test case %d/%d contains key %q of length %d, but expected %d-bit key", group.ID, test.ID, test.KeyHex, len(test.KeyHex), group.KeyBits)
+ }
+
+ key, err := hex.DecodeString(test.KeyHex)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+
+ var inputHex string
+ if encrypt {
+ inputHex = test.PlaintextHex
+ } else {
+ inputHex = test.CiphertextHex
+ }
+
+ input, err := hex.DecodeString(inputHex)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+
+ if len(input)%b.blockSize != 0 {
+ return nil, fmt.Errorf("test case %d/%d has input of length %d, but expected multiple of %d", group.ID, test.ID, len(input), b.blockSize)
+ }
+
+ var iv []byte
+ if b.hasIV {
+ if iv, err = hex.DecodeString(test.IVHex); err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+ if len(iv) != b.blockSize {
+ return nil, fmt.Errorf("test case %d/%d has IV of length %d, but expected %d", group.ID, test.ID, len(iv), b.blockSize)
+ }
+ }
+
+ testResp := blockCipherTestResponse{ID: test.ID}
+ if !mct {
+ var result [][]byte
+ var err error
+
+ if b.hasIV {
+ result, err = b.m.transact(op, 1, key, input, iv)
+ } else {
+ result, err = b.m.transact(op, 1, key, input)
+ }
+ if err != nil {
+ panic("block operation failed: " + err.Error())
+ }
+
+ if encrypt {
+ testResp.CiphertextHex = hex.EncodeToString(result[0])
+ } else {
+ testResp.PlaintextHex = hex.EncodeToString(result[0])
+ }
+ } else {
+ for i := 0; i < 100; i++ {
+ var iteration blockCipherMCTResult
+ iteration.KeyHex = hex.EncodeToString(key)
+ if encrypt {
+ iteration.PlaintextHex = hex.EncodeToString(input)
+ } else {
+ iteration.CiphertextHex = hex.EncodeToString(input)
+ }
+
+ var result, prevResult []byte
+ if !b.hasIV {
+ for j := 0; j < 1000; j++ {
+ prevResult = input
+ result, err := b.m.transact(op, 1, key, input)
+ if err != nil {
+ panic("block operation failed")
+ }
+ input = result[0]
+ }
+ result = input
+ } else {
+ iteration.IVHex = hex.EncodeToString(iv)
+
+ var prevInput []byte
+ for j := 0; j < 1000; j++ {
+ prevResult = result
+ if j > 0 {
+ if encrypt {
+ iv = result
+ } else {
+ iv = prevInput
+ }
+ }
+
+ results, err := b.m.transact(op, 1, key, input, iv)
+ if err != nil {
+ panic("block operation failed")
+ }
+ result = results[0]
+
+ prevInput = input
+ if j == 0 {
+ input = iv
+ } else {
+ input = prevResult
+ }
+ }
+ }
+
+ if encrypt {
+ iteration.CiphertextHex = hex.EncodeToString(result)
+ } else {
+ iteration.PlaintextHex = hex.EncodeToString(result)
+ }
+
+ switch keyBytes {
+ case 16:
+ for i := range key {
+ key[i] ^= result[i]
+ }
+ case 24:
+ for i := 0; i < 8; i++ {
+ key[i] ^= prevResult[i+8]
+ }
+ for i := range result {
+ key[i+8] ^= result[i]
+ }
+ case 32:
+ for i, b := range prevResult {
+ key[i] ^= b
+ }
+ for i, b := range result {
+ key[i+16] ^= b
+ }
+ default:
+ panic("unhandled key length")
+ }
+
+ if !b.hasIV {
+ input = result
+ } else {
+ iv = result
+ input = prevResult
+ }
+
+ testResp.MCTResults = append(testResp.MCTResults, iteration)
+ }
+ }
+
+ response.Tests = append(response.Tests, testResp)
+ }
+
+ ret = append(ret, response)
+ }
+
+ return ret, nil
+}
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/hash.go b/src/util/fipstools/acvp/acvptool/subprocess/hash.go
new file mode 100644
index 0000000..7be3162
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/subprocess/hash.go
@@ -0,0 +1,127 @@
+package subprocess
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+)
+
+// The following structures reflect the JSON of ACVP hash tests. See
+// https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#test_vectors
+
+type hashTestVectorSet struct {
+ Groups []hashTestGroup `json:"testGroups"`
+}
+
+type hashTestGroup struct {
+ ID uint64 `json:"tgId"`
+ Type string `json:"testType"`
+ Tests []struct {
+ ID uint64 `json:"tcId"`
+ BitLength uint64 `json:"len"`
+ MsgHex string `json:"msg"`
+ } `json:"tests"`
+}
+
+type hashTestGroupResponse struct {
+ ID uint64 `json:"tgId"`
+ Tests []hashTestResponse `json:"tests"`
+}
+
+type hashTestResponse struct {
+ ID uint64 `json:"tcId"`
+ DigestHex string `json:"md,omitempty"`
+ MCTResults []hashMCTResult `json:"resultsArray,omitempty"`
+}
+
+type hashMCTResult struct {
+ DigestHex string `json:"md"`
+}
+
+// hashPrimitive implements an ACVP algorithm by making requests to the
+// subprocess to hash strings.
+type hashPrimitive struct {
+ // algo is the ACVP name for this algorithm and also the command name
+ // given to the subprocess to hash with this hash function.
+ algo string
+ // size is the number of bytes of digest that the hash produces.
+ size int
+ m *Subprocess
+}
+
+// hash uses the subprocess to hash msg and returns the digest.
+func (h *hashPrimitive) hash(msg []byte) []byte {
+ result, err := h.m.transact(h.algo, 1, msg)
+ if err != nil {
+ panic("hash operation failed: " + err.Error())
+ }
+ return result[0]
+}
+
+func (h *hashPrimitive) Process(vectorSet []byte) (interface{}, error) {
+ var parsed hashTestVectorSet
+ if err := json.Unmarshal(vectorSet, &parsed); err != nil {
+ return nil, err
+ }
+
+ var ret []hashTestGroupResponse
+ // See
+ // https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
+ // for details about the tests.
+ for _, group := range parsed.Groups {
+ response := hashTestGroupResponse{
+ ID: group.ID,
+ }
+
+ for _, test := range group.Tests {
+ if uint64(len(test.MsgHex))*4 != test.BitLength {
+ return nil, fmt.Errorf("test case %d/%d contains hex message of length %d but specifies a bit length of %d", group.ID, test.ID, len(test.MsgHex), test.BitLength)
+ }
+ msg, err := hex.DecodeString(test.MsgHex)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
+ }
+
+ // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
+ switch group.Type {
+ case "AFT":
+ response.Tests = append(response.Tests, hashTestResponse{
+ ID: test.ID,
+ DigestHex: hex.EncodeToString(h.hash(msg)),
+ })
+
+ case "MCT":
+ if len(msg) != h.size {
+ return nil, fmt.Errorf("MCT test case %d/%d contains message of length %d but the digest length is %d", group.ID, test.ID, len(msg), h.size)
+ }
+
+ testResponse := hashTestResponse{ID: test.ID}
+
+ buf := make([]byte, 3*h.size)
+ var digest []byte
+ for i := 0; i < 100; i++ {
+ copy(buf, msg)
+ copy(buf[h.size:], msg)
+ copy(buf[2*h.size:], msg)
+ for j := 0; j < 1000; j++ {
+ digest = h.hash(buf)
+ copy(buf, buf[h.size:])
+ copy(buf[2*h.size:], digest)
+ }
+
+ testResponse.MCTResults = append(testResponse.MCTResults, hashMCTResult{hex.EncodeToString(digest)})
+ msg = digest
+ }
+
+ response.Tests = append(response.Tests, testResponse)
+
+ default:
+ return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
+ }
+ }
+
+ ret = append(ret, response)
+ }
+
+ return ret, nil
+}
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go
new file mode 100644
index 0000000..404569f
--- /dev/null
+++ b/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -0,0 +1,164 @@
+package subprocess
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+)
+
+// Subprocess is a "middle" layer that interacts with a FIPS module via running
+// a command and speaking a simple protocol over stdin/stdout.
+type Subprocess struct {
+ cmd *exec.Cmd
+ stdin io.WriteCloser
+ stdout io.ReadCloser
+ primitives map[string]primitive
+}
+
+// New returns a new Subprocess middle layer that runs the given binary.
+func New(path string) (*Subprocess, error) {
+ cmd := exec.Command(path)
+ cmd.Stderr = os.Stderr
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+
+ m := &Subprocess{
+ cmd: cmd,
+ stdin: stdin,
+ stdout: stdout,
+ }
+
+ m.primitives = map[string]primitive{
+ "SHA-1": &hashPrimitive{"SHA-1", 20, m},
+ "SHA2-224": &hashPrimitive{"SHA2-224", 28, m},
+ "SHA2-256": &hashPrimitive{"SHA2-256", 32, m},
+ "SHA2-384": &hashPrimitive{"SHA2-384", 48, m},
+ "SHA2-512": &hashPrimitive{"SHA2-512", 64, m},
+ "ACVP-AES-ECB": &blockCipher{"AES", 16, false, m},
+ "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, true, m},
+ }
+
+ return m, nil
+}
+
+// Close signals the child process to exit and waits for it to complete.
+func (m *Subprocess) Close() {
+ m.stdout.Close()
+ m.stdin.Close()
+ m.cmd.Wait()
+}
+
+// transact performs a single request--response pair with the subprocess.
+func (m *Subprocess) transact(cmd string, expectedResults int, args ...[]byte) ([][]byte, error) {
+ argLength := len(cmd)
+ for _, arg := range args {
+ argLength += len(arg)
+ }
+
+ buf := make([]byte, 4*(2+len(args)), 4*(2+len(args))+argLength)
+ binary.LittleEndian.PutUint32(buf, uint32(1+len(args)))
+ binary.LittleEndian.PutUint32(buf[4:], uint32(len(cmd)))
+ for i, arg := range args {
+ binary.LittleEndian.PutUint32(buf[4*(i+2):], uint32(len(arg)))
+ }
+ buf = append(buf, []byte(cmd)...)
+ for _, arg := range args {
+ buf = append(buf, arg...)
+ }
+
+ if _, err := m.stdin.Write(buf); err != nil {
+ return nil, err
+ }
+
+ buf = buf[:4]
+ if _, err := io.ReadFull(m.stdout, buf); err != nil {
+ return nil, err
+ }
+
+ numResults := binary.LittleEndian.Uint32(buf)
+ if int(numResults) != expectedResults {
+ return nil, fmt.Errorf("expected %d results from %q but got %d", expectedResults, cmd, numResults)
+ }
+
+ buf = make([]byte, 4*numResults)
+ if _, err := io.ReadFull(m.stdout, buf); err != nil {
+ return nil, err
+ }
+
+ var resultsLength uint64
+ for i := uint32(0); i < numResults; i++ {
+ resultsLength += uint64(binary.LittleEndian.Uint32(buf[4*i:]))
+ }
+
+ if resultsLength > (1 << 30) {
+ return nil, fmt.Errorf("results too large (%d bytes)", resultsLength)
+ }
+
+ results := make([]byte, resultsLength)
+ if _, err := io.ReadFull(m.stdout, results); err != nil {
+ return nil, err
+ }
+
+ ret := make([][]byte, 0, numResults)
+ var offset int
+ for i := uint32(0); i < numResults; i++ {
+ length := binary.LittleEndian.Uint32(buf[4*i:])
+ ret = append(ret, results[offset:offset+int(length)])
+ offset += int(length)
+ }
+
+ return ret, nil
+}
+
+// Config returns a JSON blob that describes the supported primitives. The
+// format of the blob is defined by ACVP. See
+// http://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.15.2.1
+func (m *Subprocess) Config() ([]byte, error) {
+ results, err := m.transact("getConfig", 1)
+ if err != nil {
+ return nil, err
+ }
+ var config []struct {
+ Algorithm string `json:"algorithm"`
+ }
+ if err := json.Unmarshal(results[0], &config); err != nil {
+ return nil, errors.New("failed to parse config response from wrapper: " + err.Error())
+ }
+ for _, algo := range config {
+ if _, ok := m.primitives[algo.Algorithm]; !ok {
+ return nil, fmt.Errorf("wrapper config advertises support for unknown algorithm %q", algo.Algorithm)
+ }
+ }
+ return results[0], nil
+}
+
+// Process runs a set of test vectors and returns the result.
+func (m *Subprocess) Process(algorithm string, vectorSet []byte) ([]byte, error) {
+ prim, ok := m.primitives[algorithm]
+ if !ok {
+ return nil, fmt.Errorf("unknown algorithm %q", algorithm)
+ }
+ ret, err := prim.Process(vectorSet)
+ if err != nil {
+ return nil, err
+ }
+ return json.Marshal(ret)
+}
+
+type primitive interface {
+ Process(vectorSet []byte) (interface{}, error)
+}
diff --git a/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt b/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt
new file mode 100644
index 0000000..8bee5cd
--- /dev/null
+++ b/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt
@@ -0,0 +1,13 @@
+include_directories(../../../../include)
+
+if(FIPS)
+ add_executable(
+ modulewrapper
+
+ modulewrapper.cc
+ )
+
+ add_dependencies(modulewrapper global_target)
+
+ target_link_libraries(modulewrapper crypto)
+endif()
diff --git a/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
new file mode 100644
index 0000000..f877c75
--- /dev/null
+++ b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -0,0 +1,331 @@
+/* Copyright (c) 2019, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <vector>
+
+#include <assert.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <cstdarg>
+
+#include <openssl/aes.h>
+#include <openssl/sha.h>
+#include <openssl/span.h>
+
+static constexpr size_t kMaxArgs = 8;
+static constexpr size_t kMaxArgLength = (1 << 20);
+static constexpr size_t kMaxNameLength = 30;
+
+static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
+ "Argument limits permit excessive messages");
+
+using namespace bssl;
+
+static bool ReadAll(int fd, void *in_data, size_t data_len) {
+ uint8_t *data = reinterpret_cast<uint8_t *>(in_data);
+ size_t done = 0;
+
+ while (done < data_len) {
+ ssize_t r;
+ do {
+ r = read(fd, &data[done], data_len - done);
+ } while (r == -1 && errno == EINTR);
+
+ if (r <= 0) {
+ return false;
+ }
+
+ done += r;
+ }
+
+ return true;
+}
+
+template <typename... Args>
+static bool WriteReply(int fd, Args... args) {
+ std::vector<Span<const uint8_t>> spans = {args...};
+ if (spans.empty() || spans.size() > kMaxArgs) {
+ abort();
+ }
+
+ uint32_t nums[1 + kMaxArgs];
+ iovec iovs[kMaxArgs + 1];
+ nums[0] = spans.size();
+ iovs[0].iov_base = nums;
+ iovs[0].iov_len = sizeof(uint32_t) * (1 + spans.size());
+
+ for (size_t i = 0; i < spans.size(); i++) {
+ const auto &span = spans[i];
+ nums[i + 1] = span.size();
+ iovs[i + 1].iov_base = const_cast<uint8_t *>(span.data());
+ iovs[i + 1].iov_len = span.size();
+ }
+
+ const size_t num_iov = spans.size() + 1;
+ size_t iov_done = 0;
+ while (iov_done < num_iov) {
+ ssize_t r;
+ do {
+ r = writev(fd, &iovs[iov_done], num_iov - iov_done);
+ } while (r == -1 && errno == EINTR);
+
+ if (r <= 0) {
+ return false;
+ }
+
+ size_t written = r;
+ for (size_t i = iov_done; written > 0 && i < num_iov; i++) {
+ iovec &iov = iovs[i];
+
+ size_t done = written;
+ if (done > iov.iov_len) {
+ done = iov.iov_len;
+ }
+
+ iov.iov_base = reinterpret_cast<uint8_t *>(iov.iov_base) + done;
+ iov.iov_len -= done;
+ written -= done;
+
+ if (iov.iov_len == 0) {
+ iov_done++;
+ }
+ }
+
+ assert(written == 0);
+ }
+
+ return true;
+}
+
+static bool GetConfig(const Span<const uint8_t> args[]) {
+ static constexpr char kConfig[] =
+ "["
+ "{"
+ " \"algorithm\": \"SHA2-224\","
+ " \"revision\": \"1.0\","
+ " \"messageLength\": [{"
+ " \"min\": 0, \"max\": 65528, \"increment\": 8"
+ " }]"
+ "},"
+ "{"
+ " \"algorithm\": \"SHA2-256\","
+ " \"revision\": \"1.0\","
+ " \"messageLength\": [{"
+ " \"min\": 0, \"max\": 65528, \"increment\": 8"
+ " }]"
+ "},"
+ "{"
+ " \"algorithm\": \"SHA2-384\","
+ " \"revision\": \"1.0\","
+ " \"messageLength\": [{"
+ " \"min\": 0, \"max\": 65528, \"increment\": 8"
+ " }]"
+ "},"
+ "{"
+ " \"algorithm\": \"SHA2-512\","
+ " \"revision\": \"1.0\","
+ " \"messageLength\": [{"
+ " \"min\": 0, \"max\": 65528, \"increment\": 8"
+ " }]"
+ "},"
+ "{"
+ " \"algorithm\": \"SHA-1\","
+ " \"revision\": \"1.0\","
+ " \"messageLength\": [{"
+ " \"min\": 0, \"max\": 65528, \"increment\": 8"
+ " }]"
+ "},"
+ "{"
+ " \"algorithm\": \"ACVP-AES-ECB\","
+ " \"revision\": \"1.0\","
+ " \"direction\": [\"encrypt\", \"decrypt\"],"
+ " \"keyLen\": [128, 192, 256]"
+ "},"
+ "{"
+ " \"algorithm\": \"ACVP-AES-CBC\","
+ " \"revision\": \"1.0\","
+ " \"direction\": [\"encrypt\", \"decrypt\"],"
+ " \"keyLen\": [128, 192, 256]"
+ "}"
+ "]";
+ return WriteReply(
+ STDOUT_FILENO,
+ Span<const uint8_t>(reinterpret_cast<const uint8_t *>(kConfig),
+ sizeof(kConfig) - 1));
+}
+
+template <uint8_t *(*OneShotHash)(const uint8_t *, size_t, uint8_t *),
+ size_t DigestLength>
+static bool Hash(const Span<const uint8_t> args[]) {
+ uint8_t digest[DigestLength];
+ OneShotHash(args[0].data(), args[0].size(), digest);
+ return WriteReply(STDOUT_FILENO, Span<const uint8_t>(digest));
+}
+
+template <int (*SetKey)(const uint8_t *key, unsigned bits, AES_KEY *out),
+ void (*Block)(const uint8_t *in, uint8_t *out, const AES_KEY *key)>
+static bool AES(const Span<const uint8_t> args[]) {
+ AES_KEY key;
+ if (SetKey(args[0].data(), args[0].size() * 8, &key) != 0) {
+ return false;
+ }
+ if (args[1].size() % AES_BLOCK_SIZE != 0) {
+ return false;
+ }
+
+ std::vector<uint8_t> out;
+ out.resize(args[1].size());
+ for (size_t i = 0; i < args[1].size(); i += AES_BLOCK_SIZE) {
+ Block(args[1].data() + i, &out[i], &key);
+ }
+ return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+}
+
+template <int (*SetKey)(const uint8_t *key, unsigned bits, AES_KEY *out),
+ int Direction>
+static bool AES_CBC(const Span<const uint8_t> args[]) {
+ AES_KEY key;
+ if (SetKey(args[0].data(), args[0].size() * 8, &key) != 0) {
+ return false;
+ }
+ if (args[1].size() % AES_BLOCK_SIZE != 0 ||
+ args[2].size() != AES_BLOCK_SIZE) {
+ return false;
+ }
+ uint8_t iv[AES_BLOCK_SIZE];
+ memcpy(iv, args[2].data(), AES_BLOCK_SIZE);
+
+ std::vector<uint8_t> out;
+ out.resize(args[1].size());
+ AES_cbc_encrypt(args[1].data(), out.data(), args[1].size(), &key, iv,
+ Direction);
+ return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+}
+
+static constexpr struct {
+ const char name[kMaxNameLength + 1];
+ uint8_t expected_args;
+ bool (*handler)(const Span<const uint8_t>[]);
+} kFunctions[] = {
+ {"getConfig", 0, GetConfig},
+ {"SHA-1", 1, Hash<SHA1, SHA_DIGEST_LENGTH>},
+ {"SHA2-224", 1, Hash<SHA224, SHA224_DIGEST_LENGTH>},
+ {"SHA2-256", 1, Hash<SHA256, SHA256_DIGEST_LENGTH>},
+ {"SHA2-384", 1, Hash<SHA384, SHA256_DIGEST_LENGTH>},
+ {"SHA2-512", 1, Hash<SHA512, SHA512_DIGEST_LENGTH>},
+ {"AES/encrypt", 2, AES<AES_set_encrypt_key, AES_encrypt>},
+ {"AES/decrypt", 2, AES<AES_set_decrypt_key, AES_decrypt>},
+ {"AES-CBC/encrypt", 3, AES_CBC<AES_set_encrypt_key, AES_ENCRYPT>},
+ {"AES-CBC/decrypt", 3, AES_CBC<AES_set_decrypt_key, AES_DECRYPT>},
+};
+
+int main() {
+ uint32_t nums[1 + kMaxArgs];
+ uint8_t *buf = nullptr;
+ size_t buf_len = 0;
+ Span<const uint8_t> args[kMaxArgs];
+
+ for (;;) {
+ if (!ReadAll(STDIN_FILENO, nums, sizeof(uint32_t) * 2)) {
+ return 1;
+ }
+
+ const size_t num_args = nums[0];
+ if (num_args == 0) {
+ fprintf(stderr, "Invalid, zero-argument operation requested.\n");
+ return 2;
+ } else if (num_args > kMaxArgs) {
+ fprintf(stderr,
+ "Operation requested with %zu args, but %zu is the limit.\n",
+ num_args, kMaxArgs);
+ return 2;
+ }
+
+ if (num_args > 1 &&
+ !ReadAll(STDIN_FILENO, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
+ return 1;
+ }
+
+ size_t need = 0;
+ for (size_t i = 0; i < num_args; i++) {
+ const size_t arg_length = nums[i + 1];
+ if (i == 0 && arg_length > kMaxNameLength) {
+ fprintf(stderr,
+ "Operation with name of length %zu exceeded limit of %zu.\n",
+ arg_length, kMaxNameLength);
+ return 2;
+ } else if (arg_length > kMaxArgLength) {
+ fprintf(
+ stderr,
+ "Operation with argument of length %zu exceeded limit of %zu.\n",
+ arg_length, kMaxArgLength);
+ return 2;
+ }
+
+ // static_assert around kMaxArgs etc enforces that this doesn't overflow.
+ need += arg_length;
+ }
+
+ if (need > buf_len) {
+ free(buf);
+ size_t alloced = need + (need >> 1);
+ if (alloced < need) {
+ abort();
+ }
+ buf = reinterpret_cast<uint8_t *>(malloc(alloced));
+ if (buf == nullptr) {
+ abort();
+ }
+ buf_len = alloced;
+ }
+
+ if (!ReadAll(STDIN_FILENO, buf, need)) {
+ return 1;
+ }
+
+ size_t offset = 0;
+ for (size_t i = 0; i < num_args; i++) {
+ args[i] = Span<const uint8_t>(&buf[offset], nums[i + 1]);
+ offset += nums[i + 1];
+ }
+
+ bool found = true;
+ for (const auto &func : kFunctions) {
+ if (args[0].size() == strlen(func.name) &&
+ memcmp(args[0].data(), func.name, args[0].size()) == 0) {
+ if (num_args - 1 != func.expected_args) {
+ fprintf(stderr,
+ "\'%s\' operation received %zu arguments but expected %u.\n",
+ func.name, num_args - 1, func.expected_args);
+ return 2;
+ }
+
+ if (!func.handler(&args[1])) {
+ return 4;
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ const std::string name(reinterpret_cast<const char *>(args[0].data()),
+ args[0].size());
+ fprintf(stderr, "Unknown operation: %s\n", name.c_str());
+ return 3;
+ }
+ }
+}
diff --git a/src/util/fipstools/break-tests.sh b/src/util/fipstools/break-tests.sh
index 5b1cd21..719461e 100644
--- a/src/util/fipstools/break-tests.sh
+++ b/src/util/fipstools/break-tests.sh
@@ -40,7 +40,7 @@
for test in $TESTS; do
pushd build-$test
printf "\n\n\\x1b[1m$test\\x1b[0m\n"
- ./fipstools/test_fips
+ ./util/fipstools/test_fips
echo "Waiting for keypress..."
read
popd
@@ -48,6 +48,6 @@
pushd build-NONE
printf "\\x1b[1mIntegrity\\x1b[0m\n"
-go run ../util/fipstools/break-hash.go ./fipstools/test_fips ./fipstools/test_fips_broken
-./fipstools/test_fips_broken
+go run ../util/fipstools/break-hash.go ./util/fipstools/test_fips ./util/fipstools/test_fips_broken
+./util/fipstools/test_fips_broken
popd
diff --git a/src/fipstools/CMakeLists.txt b/src/util/fipstools/cavp/CMakeLists.txt
similarity index 94%
rename from src/fipstools/CMakeLists.txt
rename to src/util/fipstools/cavp/CMakeLists.txt
index 58c9a60..a50c9ab 100644
--- a/src/fipstools/CMakeLists.txt
+++ b/src/util/fipstools/cavp/CMakeLists.txt
@@ -1,4 +1,4 @@
-include_directories(../include)
+include_directories(../../../include)
if(FIPS)
add_executable(
diff --git a/src/fipstools/cavp_aes_gcm_test.cc b/src/util/fipstools/cavp/cavp_aes_gcm_test.cc
similarity index 100%
rename from src/fipstools/cavp_aes_gcm_test.cc
rename to src/util/fipstools/cavp/cavp_aes_gcm_test.cc
diff --git a/src/fipstools/cavp_aes_test.cc b/src/util/fipstools/cavp/cavp_aes_test.cc
similarity index 100%
rename from src/fipstools/cavp_aes_test.cc
rename to src/util/fipstools/cavp/cavp_aes_test.cc
diff --git a/src/fipstools/cavp_ctr_drbg_test.cc b/src/util/fipstools/cavp/cavp_ctr_drbg_test.cc
similarity index 100%
rename from src/fipstools/cavp_ctr_drbg_test.cc
rename to src/util/fipstools/cavp/cavp_ctr_drbg_test.cc
diff --git a/src/fipstools/cavp_ecdsa2_keypair_test.cc b/src/util/fipstools/cavp/cavp_ecdsa2_keypair_test.cc
similarity index 100%
rename from src/fipstools/cavp_ecdsa2_keypair_test.cc
rename to src/util/fipstools/cavp/cavp_ecdsa2_keypair_test.cc
diff --git a/src/fipstools/cavp_ecdsa2_pkv_test.cc b/src/util/fipstools/cavp/cavp_ecdsa2_pkv_test.cc
similarity index 100%
rename from src/fipstools/cavp_ecdsa2_pkv_test.cc
rename to src/util/fipstools/cavp/cavp_ecdsa2_pkv_test.cc
diff --git a/src/fipstools/cavp_ecdsa2_siggen_test.cc b/src/util/fipstools/cavp/cavp_ecdsa2_siggen_test.cc
similarity index 100%
rename from src/fipstools/cavp_ecdsa2_siggen_test.cc
rename to src/util/fipstools/cavp/cavp_ecdsa2_siggen_test.cc
diff --git a/src/fipstools/cavp_ecdsa2_sigver_test.cc b/src/util/fipstools/cavp/cavp_ecdsa2_sigver_test.cc
similarity index 100%
rename from src/fipstools/cavp_ecdsa2_sigver_test.cc
rename to src/util/fipstools/cavp/cavp_ecdsa2_sigver_test.cc
diff --git a/src/fipstools/cavp_hmac_test.cc b/src/util/fipstools/cavp/cavp_hmac_test.cc
similarity index 100%
rename from src/fipstools/cavp_hmac_test.cc
rename to src/util/fipstools/cavp/cavp_hmac_test.cc
diff --git a/src/fipstools/cavp_kas_test.cc b/src/util/fipstools/cavp/cavp_kas_test.cc
similarity index 100%
rename from src/fipstools/cavp_kas_test.cc
rename to src/util/fipstools/cavp/cavp_kas_test.cc
diff --git a/src/fipstools/cavp_keywrap_test.cc b/src/util/fipstools/cavp/cavp_keywrap_test.cc
similarity index 67%
rename from src/fipstools/cavp_keywrap_test.cc
rename to src/util/fipstools/cavp/cavp_keywrap_test.cc
index c7270df..a4abccd 100644
--- a/src/fipstools/cavp_keywrap_test.cc
+++ b/src/util/fipstools/cavp/cavp_keywrap_test.cc
@@ -28,15 +28,16 @@
struct TestCtx {
bool encrypt;
+ bool padding;
};
-}
+} // namespace
static bool AESKeyWrap(std::vector<uint8_t> *out, bool encrypt,
const std::vector<uint8_t> &key,
const std::vector<uint8_t> &in) {
size_t key_bits = key.size() * 8;
- if (key_bits != 128 && key_bits != 256) {
+ if (key_bits != 128 && key_bits != 192 && key_bits != 256) {
return false;
}
AES_KEY aes_key;
@@ -60,6 +61,36 @@
return true;
}
+static bool AESKeyWrapWithPadding(std::vector<uint8_t> *out, bool encrypt,
+ const std::vector<uint8_t> &key,
+ const std::vector<uint8_t> &in) {
+ const size_t key_bits = key.size() * 8;
+ if (key_bits != 128 && key_bits != 192 && key_bits != 256) {
+ return false;
+ }
+ AES_KEY aes_key;
+
+ size_t out_len;
+ if (encrypt) {
+ out->resize(in.size() + 15);
+ if (AES_set_encrypt_key(key.data(), key_bits, &aes_key) ||
+ !AES_wrap_key_padded(&aes_key, out->data(), &out_len, out->size(),
+ in.data(), in.size())) {
+ return false;
+ }
+ } else {
+ out->resize(in.size());
+ if (AES_set_decrypt_key(key.data(), key_bits, &aes_key) ||
+ !AES_unwrap_key_padded(&aes_key, out->data(), &out_len, out->size(),
+ in.data(), in.size())) {
+ return false;
+ }
+ }
+
+ out->resize(out_len);
+ return true;
+}
+
static bool TestCipher(FileTest *t, void *arg) {
TestCtx *ctx = reinterpret_cast<TestCtx *>(arg);
@@ -75,8 +106,13 @@
}
// clang-format on
+ auto wrap_function = AESKeyWrap;
+ if (ctx->padding) {
+ wrap_function = AESKeyWrapWithPadding;
+ }
+
printf("%s", t->CurrentTestToString().c_str());
- if (!AESKeyWrap(&result, ctx->encrypt, key, in)) {
+ if (!wrap_function(&result, ctx->encrypt, key, in)) {
if (ctx->encrypt) {
return false;
} else {
@@ -93,7 +129,7 @@
static int usage(char *arg) {
fprintf(
stderr,
- "usage: %s (enc|dec) (128|256) <test file>\n",
+ "usage: %s (enc|dec|enc-pad|dec-pad) (128|192|256) <test file>\n",
arg);
return 1;
}
@@ -104,16 +140,21 @@
}
const std::string op(argv[1]);
- bool encrypt;
+ bool encrypt = false;
+ bool padding = false;
if (op == "enc") {
encrypt = true;
} else if (op == "dec") {
- encrypt = false;
+ } else if (op == "enc-pad") {
+ encrypt = true;
+ padding = true;
+ } else if (op == "dec-pad") {
+ padding = true;
} else {
return usage(argv[0]);
}
- TestCtx ctx = {encrypt};
+ TestCtx ctx = {encrypt, padding};
FileTest::Options opts;
opts.path = argv[3];
diff --git a/src/fipstools/cavp_main.cc b/src/util/fipstools/cavp/cavp_main.cc
similarity index 100%
rename from src/fipstools/cavp_main.cc
rename to src/util/fipstools/cavp/cavp_main.cc
diff --git a/src/fipstools/cavp_rsa2_keygen_test.cc b/src/util/fipstools/cavp/cavp_rsa2_keygen_test.cc
similarity index 100%
rename from src/fipstools/cavp_rsa2_keygen_test.cc
rename to src/util/fipstools/cavp/cavp_rsa2_keygen_test.cc
diff --git a/src/fipstools/cavp_rsa2_siggen_test.cc b/src/util/fipstools/cavp/cavp_rsa2_siggen_test.cc
similarity index 100%
rename from src/fipstools/cavp_rsa2_siggen_test.cc
rename to src/util/fipstools/cavp/cavp_rsa2_siggen_test.cc
diff --git a/src/fipstools/cavp_rsa2_sigver_test.cc b/src/util/fipstools/cavp/cavp_rsa2_sigver_test.cc
similarity index 100%
rename from src/fipstools/cavp_rsa2_sigver_test.cc
rename to src/util/fipstools/cavp/cavp_rsa2_sigver_test.cc
diff --git a/src/fipstools/cavp_sha_monte_test.cc b/src/util/fipstools/cavp/cavp_sha_monte_test.cc
similarity index 100%
rename from src/fipstools/cavp_sha_monte_test.cc
rename to src/util/fipstools/cavp/cavp_sha_monte_test.cc
diff --git a/src/fipstools/cavp_sha_test.cc b/src/util/fipstools/cavp/cavp_sha_test.cc
similarity index 100%
rename from src/fipstools/cavp_sha_test.cc
rename to src/util/fipstools/cavp/cavp_sha_test.cc
diff --git a/src/fipstools/cavp_tdes_test.cc b/src/util/fipstools/cavp/cavp_tdes_test.cc
similarity index 100%
rename from src/fipstools/cavp_tdes_test.cc
rename to src/util/fipstools/cavp/cavp_tdes_test.cc
diff --git a/src/fipstools/cavp_test_util.cc b/src/util/fipstools/cavp/cavp_test_util.cc
similarity index 100%
rename from src/fipstools/cavp_test_util.cc
rename to src/util/fipstools/cavp/cavp_test_util.cc
diff --git a/src/fipstools/cavp_test_util.h b/src/util/fipstools/cavp/cavp_test_util.h
similarity index 100%
rename from src/fipstools/cavp_test_util.h
rename to src/util/fipstools/cavp/cavp_test_util.h
diff --git a/src/fipstools/cavp_tlskdf_test.cc b/src/util/fipstools/cavp/cavp_tlskdf_test.cc
similarity index 100%
rename from src/fipstools/cavp_tlskdf_test.cc
rename to src/util/fipstools/cavp/cavp_tlskdf_test.cc
diff --git a/src/fipstools/run_cavp.go b/src/util/fipstools/cavp/run_cavp.go
similarity index 97%
rename from src/fipstools/run_cavp.go
rename to src/util/fipstools/cavp/run_cavp.go
index 438bb78..b37f453 100644
--- a/src/fipstools/run_cavp.go
+++ b/src/util/fipstools/cavp/run_cavp.go
@@ -283,9 +283,17 @@
nil,
[]test{
{"KW_AD_128", []string{"dec", "128"}, false},
+ {"KW_AD_192", []string{"dec", "192"}, false},
{"KW_AD_256", []string{"dec", "256"}, false},
{"KW_AE_128", []string{"enc", "128"}, false},
+ {"KW_AE_192", []string{"enc", "192"}, false},
{"KW_AE_256", []string{"enc", "256"}, false},
+ {"KWP_AD_128", []string{"dec-pad", "128"}, false},
+ {"KWP_AD_192", []string{"dec-pad", "192"}, false},
+ {"KWP_AD_256", []string{"dec-pad", "256"}, false},
+ {"KWP_AE_128", []string{"enc-pad", "128"}, false},
+ {"KWP_AE_192", []string{"enc-pad", "192"}, false},
+ {"KWP_AE_256", []string{"enc-pad", "256"}, false},
},
}
diff --git a/src/fipstools/test_fips.c b/src/util/fipstools/cavp/test_fips.c
similarity index 100%
rename from src/fipstools/test_fips.c
rename to src/util/fipstools/cavp/test_fips.c
diff --git a/src/util/fipstools/delocate/delocate.go b/src/util/fipstools/delocate/delocate.go
index 593abec..a43b428 100644
--- a/src/util/fipstools/delocate/delocate.go
+++ b/src/util/fipstools/delocate/delocate.go
@@ -1269,6 +1269,10 @@
// maxObservedFileNumber contains the largest seen file number in a
// .file directive. Zero is not a valid number.
maxObservedFileNumber := 0
+ // fileDirectivesContainMD5 is true if the compiler is outputting MD5
+ // checksums in .file directives. If it does so, then this script needs
+ // to match that behaviour otherwise warnings result.
+ fileDirectivesContainMD5 := false
// OPENSSL_ia32cap_get will be synthesized by this script.
symbols["OPENSSL_ia32cap_get"] = struct{}{}
@@ -1328,6 +1332,12 @@
if fileNo > maxObservedFileNumber {
maxObservedFileNumber = fileNo
}
+
+ for _, token := range parts[2:] {
+ if token == "md5" {
+ fileDirectivesContainMD5 = true
+ }
+ }
}, ruleStatement, ruleLocationDirective)
}
@@ -1348,7 +1358,11 @@
}
w.WriteString(".text\n")
- w.WriteString(fmt.Sprintf(".file %d \"inserted_by_delocate.c\"\n", maxObservedFileNumber+1))
+ var fileTrailing string
+ if fileDirectivesContainMD5 {
+ fileTrailing = " md5 0x00000000000000000000000000000000"
+ }
+ w.WriteString(fmt.Sprintf(".file %d \"inserted_by_delocate.c\"%s\n", maxObservedFileNumber+1, fileTrailing))
w.WriteString(fmt.Sprintf(".loc %d 1 0\n", maxObservedFileNumber+1))
w.WriteString("BORINGSSL_bcm_text_start:\n")
diff --git a/src/util/fipstools/delocate/delocate.peg b/src/util/fipstools/delocate/delocate.peg
index 82a3cb5..b24a47a 100644
--- a/src/util/fipstools/delocate/delocate.peg
+++ b/src/util/fipstools/delocate/delocate.peg
@@ -36,7 +36,7 @@
QuotedArg <- '"' QuotedText '"'
QuotedText <- (EscapedChar / [^"])*
LabelContainingDirective <- LabelContainingDirectiveName WS SymbolArgs
-LabelContainingDirectiveName <- ".long" / ".set" / ".8byte" / ".4byte" / ".quad" / ".tc" / ".localentry" / ".size" / ".type"
+LabelContainingDirectiveName <- ".long" / ".set" / ".8byte" / ".4byte" / ".quad" / ".tc" / ".localentry" / ".size" / ".type" / ".uleb128" / ".sleb128"
SymbolArgs <- SymbolArg ((WS? ',' WS?) SymbolArg)*
SymbolArg <- Offset /
SymbolType /
diff --git a/src/util/fipstools/delocate/delocate.peg.go b/src/util/fipstools/delocate/delocate.peg.go
index e147943..298f55c 100644
--- a/src/util/fipstools/delocate/delocate.peg.go
+++ b/src/util/fipstools/delocate/delocate.peg.go
@@ -1260,7 +1260,7 @@
position, tokenIndex = position131, tokenIndex131
return false
},
- /* 11 LabelContainingDirectiveName <- <(('.' ('l' / 'L') ('o' / 'O') ('n' / 'N') ('g' / 'G')) / ('.' ('s' / 'S') ('e' / 'E') ('t' / 'T')) / ('.' '8' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' '4' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' ('q' / 'Q') ('u' / 'U') ('a' / 'A') ('d' / 'D')) / ('.' ('t' / 'T') ('c' / 'C')) / ('.' ('l' / 'L') ('o' / 'O') ('c' / 'C') ('a' / 'A') ('l' / 'L') ('e' / 'E') ('n' / 'N') ('t' / 'T') ('r' / 'R') ('y' / 'Y')) / ('.' ('s' / 'S') ('i' / 'I') ('z' / 'Z') ('e' / 'E')) / ('.' ('t' / 'T') ('y' / 'Y') ('p' / 'P') ('e' / 'E')))> */
+ /* 11 LabelContainingDirectiveName <- <(('.' ('l' / 'L') ('o' / 'O') ('n' / 'N') ('g' / 'G')) / ('.' ('s' / 'S') ('e' / 'E') ('t' / 'T')) / ('.' '8' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' '4' ('b' / 'B') ('y' / 'Y') ('t' / 'T') ('e' / 'E')) / ('.' ('q' / 'Q') ('u' / 'U') ('a' / 'A') ('d' / 'D')) / ('.' ('t' / 'T') ('c' / 'C')) / ('.' ('l' / 'L') ('o' / 'O') ('c' / 'C') ('a' / 'A') ('l' / 'L') ('e' / 'E') ('n' / 'N') ('t' / 'T') ('r' / 'R') ('y' / 'Y')) / ('.' ('s' / 'S') ('i' / 'I') ('z' / 'Z') ('e' / 'E')) / ('.' ('t' / 'T') ('y' / 'Y') ('p' / 'P') ('e' / 'E')) / ('.' ('u' / 'U') ('l' / 'L') ('e' / 'E') ('b' / 'B') '1' '2' '8') / ('.' ('s' / 'S') ('l' / 'L') ('e' / 'E') ('b' / 'B') '1' '2' '8'))> */
func() bool {
position133, tokenIndex133 := position, tokenIndex
{
@@ -1857,69 +1857,227 @@
l205:
position, tokenIndex = position135, tokenIndex135
if buffer[position] != rune('.') {
+ goto l214
+ }
+ position++
+ {
+ position215, tokenIndex215 := position, tokenIndex
+ if buffer[position] != rune('t') {
+ goto l216
+ }
+ position++
+ goto l215
+ l216:
+ position, tokenIndex = position215, tokenIndex215
+ if buffer[position] != rune('T') {
+ goto l214
+ }
+ position++
+ }
+ l215:
+ {
+ position217, tokenIndex217 := position, tokenIndex
+ if buffer[position] != rune('y') {
+ goto l218
+ }
+ position++
+ goto l217
+ l218:
+ position, tokenIndex = position217, tokenIndex217
+ if buffer[position] != rune('Y') {
+ goto l214
+ }
+ position++
+ }
+ l217:
+ {
+ position219, tokenIndex219 := position, tokenIndex
+ if buffer[position] != rune('p') {
+ goto l220
+ }
+ position++
+ goto l219
+ l220:
+ position, tokenIndex = position219, tokenIndex219
+ if buffer[position] != rune('P') {
+ goto l214
+ }
+ position++
+ }
+ l219:
+ {
+ position221, tokenIndex221 := position, tokenIndex
+ if buffer[position] != rune('e') {
+ goto l222
+ }
+ position++
+ goto l221
+ l222:
+ position, tokenIndex = position221, tokenIndex221
+ if buffer[position] != rune('E') {
+ goto l214
+ }
+ position++
+ }
+ l221:
+ goto l135
+ l214:
+ position, tokenIndex = position135, tokenIndex135
+ if buffer[position] != rune('.') {
+ goto l223
+ }
+ position++
+ {
+ position224, tokenIndex224 := position, tokenIndex
+ if buffer[position] != rune('u') {
+ goto l225
+ }
+ position++
+ goto l224
+ l225:
+ position, tokenIndex = position224, tokenIndex224
+ if buffer[position] != rune('U') {
+ goto l223
+ }
+ position++
+ }
+ l224:
+ {
+ position226, tokenIndex226 := position, tokenIndex
+ if buffer[position] != rune('l') {
+ goto l227
+ }
+ position++
+ goto l226
+ l227:
+ position, tokenIndex = position226, tokenIndex226
+ if buffer[position] != rune('L') {
+ goto l223
+ }
+ position++
+ }
+ l226:
+ {
+ position228, tokenIndex228 := position, tokenIndex
+ if buffer[position] != rune('e') {
+ goto l229
+ }
+ position++
+ goto l228
+ l229:
+ position, tokenIndex = position228, tokenIndex228
+ if buffer[position] != rune('E') {
+ goto l223
+ }
+ position++
+ }
+ l228:
+ {
+ position230, tokenIndex230 := position, tokenIndex
+ if buffer[position] != rune('b') {
+ goto l231
+ }
+ position++
+ goto l230
+ l231:
+ position, tokenIndex = position230, tokenIndex230
+ if buffer[position] != rune('B') {
+ goto l223
+ }
+ position++
+ }
+ l230:
+ if buffer[position] != rune('1') {
+ goto l223
+ }
+ position++
+ if buffer[position] != rune('2') {
+ goto l223
+ }
+ position++
+ if buffer[position] != rune('8') {
+ goto l223
+ }
+ position++
+ goto l135
+ l223:
+ position, tokenIndex = position135, tokenIndex135
+ if buffer[position] != rune('.') {
goto l133
}
position++
{
- position214, tokenIndex214 := position, tokenIndex
- if buffer[position] != rune('t') {
- goto l215
+ position232, tokenIndex232 := position, tokenIndex
+ if buffer[position] != rune('s') {
+ goto l233
}
position++
- goto l214
- l215:
- position, tokenIndex = position214, tokenIndex214
- if buffer[position] != rune('T') {
+ goto l232
+ l233:
+ position, tokenIndex = position232, tokenIndex232
+ if buffer[position] != rune('S') {
goto l133
}
position++
}
- l214:
+ l232:
{
- position216, tokenIndex216 := position, tokenIndex
- if buffer[position] != rune('y') {
- goto l217
+ position234, tokenIndex234 := position, tokenIndex
+ if buffer[position] != rune('l') {
+ goto l235
}
position++
- goto l216
- l217:
- position, tokenIndex = position216, tokenIndex216
- if buffer[position] != rune('Y') {
+ goto l234
+ l235:
+ position, tokenIndex = position234, tokenIndex234
+ if buffer[position] != rune('L') {
goto l133
}
position++
}
- l216:
+ l234:
{
- position218, tokenIndex218 := position, tokenIndex
- if buffer[position] != rune('p') {
- goto l219
- }
- position++
- goto l218
- l219:
- position, tokenIndex = position218, tokenIndex218
- if buffer[position] != rune('P') {
- goto l133
- }
- position++
- }
- l218:
- {
- position220, tokenIndex220 := position, tokenIndex
+ position236, tokenIndex236 := position, tokenIndex
if buffer[position] != rune('e') {
- goto l221
+ goto l237
}
position++
- goto l220
- l221:
- position, tokenIndex = position220, tokenIndex220
+ goto l236
+ l237:
+ position, tokenIndex = position236, tokenIndex236
if buffer[position] != rune('E') {
goto l133
}
position++
}
- l220:
+ l236:
+ {
+ position238, tokenIndex238 := position, tokenIndex
+ if buffer[position] != rune('b') {
+ goto l239
+ }
+ position++
+ goto l238
+ l239:
+ position, tokenIndex = position238, tokenIndex238
+ if buffer[position] != rune('B') {
+ goto l133
+ }
+ position++
+ }
+ l238:
+ if buffer[position] != rune('1') {
+ goto l133
+ }
+ position++
+ if buffer[position] != rune('2') {
+ goto l133
+ }
+ position++
+ if buffer[position] != rune('8') {
+ goto l133
+ }
+ position++
}
l135:
add(ruleLabelContainingDirectiveName, position134)
@@ -1931,570 +2089,460 @@
},
/* 12 SymbolArgs <- <(SymbolArg (WS? ',' WS? SymbolArg)*)> */
func() bool {
- position222, tokenIndex222 := position, tokenIndex
+ position240, tokenIndex240 := position, tokenIndex
{
- position223 := position
+ position241 := position
if !_rules[ruleSymbolArg]() {
- goto l222
+ goto l240
}
- l224:
+ l242:
{
- position225, tokenIndex225 := position, tokenIndex
+ position243, tokenIndex243 := position, tokenIndex
{
- position226, tokenIndex226 := position, tokenIndex
+ position244, tokenIndex244 := position, tokenIndex
if !_rules[ruleWS]() {
- goto l226
+ goto l244
}
- goto l227
- l226:
- position, tokenIndex = position226, tokenIndex226
+ goto l245
+ l244:
+ position, tokenIndex = position244, tokenIndex244
}
- l227:
+ l245:
if buffer[position] != rune(',') {
- goto l225
+ goto l243
}
position++
{
- position228, tokenIndex228 := position, tokenIndex
+ position246, tokenIndex246 := position, tokenIndex
if !_rules[ruleWS]() {
- goto l228
+ goto l246
}
- goto l229
- l228:
- position, tokenIndex = position228, tokenIndex228
+ goto l247
+ l246:
+ position, tokenIndex = position246, tokenIndex246
}
- l229:
+ l247:
if !_rules[ruleSymbolArg]() {
- goto l225
+ goto l243
}
- goto l224
- l225:
- position, tokenIndex = position225, tokenIndex225
+ goto l242
+ l243:
+ position, tokenIndex = position243, tokenIndex243
}
- add(ruleSymbolArgs, position223)
+ add(ruleSymbolArgs, position241)
}
return true
- l222:
- position, tokenIndex = position222, tokenIndex222
+ l240:
+ position, tokenIndex = position240, tokenIndex240
return false
},
/* 13 SymbolArg <- <(Offset / SymbolType / ((Offset / LocalSymbol / SymbolName / Dot) WS? Operator WS? (Offset / LocalSymbol / SymbolName)) / (LocalSymbol TCMarker?) / (SymbolName Offset) / (SymbolName TCMarker?))> */
func() bool {
- position230, tokenIndex230 := position, tokenIndex
+ position248, tokenIndex248 := position, tokenIndex
{
- position231 := position
+ position249 := position
{
- position232, tokenIndex232 := position, tokenIndex
+ position250, tokenIndex250 := position, tokenIndex
if !_rules[ruleOffset]() {
- goto l233
+ goto l251
}
- goto l232
- l233:
- position, tokenIndex = position232, tokenIndex232
+ goto l250
+ l251:
+ position, tokenIndex = position250, tokenIndex250
if !_rules[ruleSymbolType]() {
- goto l234
+ goto l252
}
- goto l232
- l234:
- position, tokenIndex = position232, tokenIndex232
+ goto l250
+ l252:
+ position, tokenIndex = position250, tokenIndex250
{
- position236, tokenIndex236 := position, tokenIndex
+ position254, tokenIndex254 := position, tokenIndex
if !_rules[ruleOffset]() {
- goto l237
+ goto l255
}
- goto l236
- l237:
- position, tokenIndex = position236, tokenIndex236
+ goto l254
+ l255:
+ position, tokenIndex = position254, tokenIndex254
if !_rules[ruleLocalSymbol]() {
- goto l238
+ goto l256
}
- goto l236
- l238:
- position, tokenIndex = position236, tokenIndex236
+ goto l254
+ l256:
+ position, tokenIndex = position254, tokenIndex254
if !_rules[ruleSymbolName]() {
- goto l239
+ goto l257
}
- goto l236
- l239:
- position, tokenIndex = position236, tokenIndex236
+ goto l254
+ l257:
+ position, tokenIndex = position254, tokenIndex254
if !_rules[ruleDot]() {
- goto l235
+ goto l253
}
}
- l236:
+ l254:
{
- position240, tokenIndex240 := position, tokenIndex
+ position258, tokenIndex258 := position, tokenIndex
if !_rules[ruleWS]() {
- goto l240
+ goto l258
}
- goto l241
- l240:
- position, tokenIndex = position240, tokenIndex240
+ goto l259
+ l258:
+ position, tokenIndex = position258, tokenIndex258
}
- l241:
+ l259:
if !_rules[ruleOperator]() {
- goto l235
+ goto l253
}
{
- position242, tokenIndex242 := position, tokenIndex
+ position260, tokenIndex260 := position, tokenIndex
if !_rules[ruleWS]() {
- goto l242
+ goto l260
}
- goto l243
- l242:
- position, tokenIndex = position242, tokenIndex242
+ goto l261
+ l260:
+ position, tokenIndex = position260, tokenIndex260
}
- l243:
+ l261:
{
- position244, tokenIndex244 := position, tokenIndex
+ position262, tokenIndex262 := position, tokenIndex
if !_rules[ruleOffset]() {
- goto l245
+ goto l263
}
- goto l244
- l245:
- position, tokenIndex = position244, tokenIndex244
+ goto l262
+ l263:
+ position, tokenIndex = position262, tokenIndex262
if !_rules[ruleLocalSymbol]() {
- goto l246
+ goto l264
}
- goto l244
- l246:
- position, tokenIndex = position244, tokenIndex244
+ goto l262
+ l264:
+ position, tokenIndex = position262, tokenIndex262
if !_rules[ruleSymbolName]() {
- goto l235
+ goto l253
}
}
- l244:
- goto l232
- l235:
- position, tokenIndex = position232, tokenIndex232
+ l262:
+ goto l250
+ l253:
+ position, tokenIndex = position250, tokenIndex250
if !_rules[ruleLocalSymbol]() {
- goto l247
+ goto l265
}
{
- position248, tokenIndex248 := position, tokenIndex
+ position266, tokenIndex266 := position, tokenIndex
if !_rules[ruleTCMarker]() {
- goto l248
+ goto l266
}
- goto l249
- l248:
- position, tokenIndex = position248, tokenIndex248
+ goto l267
+ l266:
+ position, tokenIndex = position266, tokenIndex266
}
- l249:
- goto l232
- l247:
- position, tokenIndex = position232, tokenIndex232
+ l267:
+ goto l250
+ l265:
+ position, tokenIndex = position250, tokenIndex250
if !_rules[ruleSymbolName]() {
- goto l250
+ goto l268
}
if !_rules[ruleOffset]() {
- goto l250
+ goto l268
}
- goto l232
- l250:
- position, tokenIndex = position232, tokenIndex232
+ goto l250
+ l268:
+ position, tokenIndex = position250, tokenIndex250
if !_rules[ruleSymbolName]() {
- goto l230
+ goto l248
}
{
- position251, tokenIndex251 := position, tokenIndex
+ position269, tokenIndex269 := position, tokenIndex
if !_rules[ruleTCMarker]() {
- goto l251
+ goto l269
}
- goto l252
- l251:
- position, tokenIndex = position251, tokenIndex251
+ goto l270
+ l269:
+ position, tokenIndex = position269, tokenIndex269
}
- l252:
+ l270:
}
- l232:
- add(ruleSymbolArg, position231)
+ l250:
+ add(ruleSymbolArg, position249)
}
return true
- l230:
- position, tokenIndex = position230, tokenIndex230
+ l248:
+ position, tokenIndex = position248, tokenIndex248
return false
},
/* 14 SymbolType <- <(('@' 'f' 'u' 'n' 'c' 't' 'i' 'o' 'n') / ('@' 'o' 'b' 'j' 'e' 'c' 't'))> */
func() bool {
- position253, tokenIndex253 := position, tokenIndex
- {
- position254 := position
- {
- position255, tokenIndex255 := position, tokenIndex
- if buffer[position] != rune('@') {
- goto l256
- }
- position++
- if buffer[position] != rune('f') {
- goto l256
- }
- position++
- if buffer[position] != rune('u') {
- goto l256
- }
- position++
- if buffer[position] != rune('n') {
- goto l256
- }
- position++
- if buffer[position] != rune('c') {
- goto l256
- }
- position++
- if buffer[position] != rune('t') {
- goto l256
- }
- position++
- if buffer[position] != rune('i') {
- goto l256
- }
- position++
- if buffer[position] != rune('o') {
- goto l256
- }
- position++
- if buffer[position] != rune('n') {
- goto l256
- }
- position++
- goto l255
- l256:
- position, tokenIndex = position255, tokenIndex255
- if buffer[position] != rune('@') {
- goto l253
- }
- position++
- if buffer[position] != rune('o') {
- goto l253
- }
- position++
- if buffer[position] != rune('b') {
- goto l253
- }
- position++
- if buffer[position] != rune('j') {
- goto l253
- }
- position++
- if buffer[position] != rune('e') {
- goto l253
- }
- position++
- if buffer[position] != rune('c') {
- goto l253
- }
- position++
- if buffer[position] != rune('t') {
- goto l253
- }
- position++
- }
- l255:
- add(ruleSymbolType, position254)
- }
- return true
- l253:
- position, tokenIndex = position253, tokenIndex253
- return false
- },
- /* 15 Dot <- <'.'> */
- func() bool {
- position257, tokenIndex257 := position, tokenIndex
- {
- position258 := position
- if buffer[position] != rune('.') {
- goto l257
- }
- position++
- add(ruleDot, position258)
- }
- return true
- l257:
- position, tokenIndex = position257, tokenIndex257
- return false
- },
- /* 16 TCMarker <- <('[' 'T' 'C' ']')> */
- func() bool {
- position259, tokenIndex259 := position, tokenIndex
- {
- position260 := position
- if buffer[position] != rune('[') {
- goto l259
- }
- position++
- if buffer[position] != rune('T') {
- goto l259
- }
- position++
- if buffer[position] != rune('C') {
- goto l259
- }
- position++
- if buffer[position] != rune(']') {
- goto l259
- }
- position++
- add(ruleTCMarker, position260)
- }
- return true
- l259:
- position, tokenIndex = position259, tokenIndex259
- return false
- },
- /* 17 EscapedChar <- <('\\' .)> */
- func() bool {
- position261, tokenIndex261 := position, tokenIndex
- {
- position262 := position
- if buffer[position] != rune('\\') {
- goto l261
- }
- position++
- if !matchDot() {
- goto l261
- }
- add(ruleEscapedChar, position262)
- }
- return true
- l261:
- position, tokenIndex = position261, tokenIndex261
- return false
- },
- /* 18 WS <- <(' ' / '\t')+> */
- func() bool {
- position263, tokenIndex263 := position, tokenIndex
- {
- position264 := position
- {
- position267, tokenIndex267 := position, tokenIndex
- if buffer[position] != rune(' ') {
- goto l268
- }
- position++
- goto l267
- l268:
- position, tokenIndex = position267, tokenIndex267
- if buffer[position] != rune('\t') {
- goto l263
- }
- position++
- }
- l267:
- l265:
- {
- position266, tokenIndex266 := position, tokenIndex
- {
- position269, tokenIndex269 := position, tokenIndex
- if buffer[position] != rune(' ') {
- goto l270
- }
- position++
- goto l269
- l270:
- position, tokenIndex = position269, tokenIndex269
- if buffer[position] != rune('\t') {
- goto l266
- }
- position++
- }
- l269:
- goto l265
- l266:
- position, tokenIndex = position266, tokenIndex266
- }
- add(ruleWS, position264)
- }
- return true
- l263:
- position, tokenIndex = position263, tokenIndex263
- return false
- },
- /* 19 Comment <- <('#' (!'\n' .)*)> */
- func() bool {
position271, tokenIndex271 := position, tokenIndex
{
position272 := position
- if buffer[position] != rune('#') {
- goto l271
- }
- position++
- l273:
{
- position274, tokenIndex274 := position, tokenIndex
- {
- position275, tokenIndex275 := position, tokenIndex
- if buffer[position] != rune('\n') {
- goto l275
- }
- position++
- goto l274
- l275:
- position, tokenIndex = position275, tokenIndex275
- }
- if !matchDot() {
+ position273, tokenIndex273 := position, tokenIndex
+ if buffer[position] != rune('@') {
goto l274
}
+ position++
+ if buffer[position] != rune('f') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('u') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('n') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('c') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('i') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('o') {
+ goto l274
+ }
+ position++
+ if buffer[position] != rune('n') {
+ goto l274
+ }
+ position++
goto l273
l274:
- position, tokenIndex = position274, tokenIndex274
+ position, tokenIndex = position273, tokenIndex273
+ if buffer[position] != rune('@') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('o') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('b') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('j') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('e') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('c') {
+ goto l271
+ }
+ position++
+ if buffer[position] != rune('t') {
+ goto l271
+ }
+ position++
}
- add(ruleComment, position272)
+ l273:
+ add(ruleSymbolType, position272)
}
return true
l271:
position, tokenIndex = position271, tokenIndex271
return false
},
- /* 20 Label <- <((LocalSymbol / LocalLabel / SymbolName) ':')> */
+ /* 15 Dot <- <'.'> */
func() bool {
- position276, tokenIndex276 := position, tokenIndex
+ position275, tokenIndex275 := position, tokenIndex
{
- position277 := position
- {
- position278, tokenIndex278 := position, tokenIndex
- if !_rules[ruleLocalSymbol]() {
- goto l279
- }
- goto l278
- l279:
- position, tokenIndex = position278, tokenIndex278
- if !_rules[ruleLocalLabel]() {
- goto l280
- }
- goto l278
- l280:
- position, tokenIndex = position278, tokenIndex278
- if !_rules[ruleSymbolName]() {
- goto l276
- }
- }
- l278:
- if buffer[position] != rune(':') {
- goto l276
+ position276 := position
+ if buffer[position] != rune('.') {
+ goto l275
}
position++
- add(ruleLabel, position277)
+ add(ruleDot, position276)
}
return true
- l276:
- position, tokenIndex = position276, tokenIndex276
+ l275:
+ position, tokenIndex = position275, tokenIndex275
return false
},
- /* 21 SymbolName <- <(([a-z] / [A-Z] / '.' / '_') ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')*)> */
+ /* 16 TCMarker <- <('[' 'T' 'C' ']')> */
+ func() bool {
+ position277, tokenIndex277 := position, tokenIndex
+ {
+ position278 := position
+ if buffer[position] != rune('[') {
+ goto l277
+ }
+ position++
+ if buffer[position] != rune('T') {
+ goto l277
+ }
+ position++
+ if buffer[position] != rune('C') {
+ goto l277
+ }
+ position++
+ if buffer[position] != rune(']') {
+ goto l277
+ }
+ position++
+ add(ruleTCMarker, position278)
+ }
+ return true
+ l277:
+ position, tokenIndex = position277, tokenIndex277
+ return false
+ },
+ /* 17 EscapedChar <- <('\\' .)> */
+ func() bool {
+ position279, tokenIndex279 := position, tokenIndex
+ {
+ position280 := position
+ if buffer[position] != rune('\\') {
+ goto l279
+ }
+ position++
+ if !matchDot() {
+ goto l279
+ }
+ add(ruleEscapedChar, position280)
+ }
+ return true
+ l279:
+ position, tokenIndex = position279, tokenIndex279
+ return false
+ },
+ /* 18 WS <- <(' ' / '\t')+> */
func() bool {
position281, tokenIndex281 := position, tokenIndex
{
position282 := position
{
- position283, tokenIndex283 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l284
- }
- position++
- goto l283
- l284:
- position, tokenIndex = position283, tokenIndex283
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l285
- }
- position++
- goto l283
- l285:
- position, tokenIndex = position283, tokenIndex283
- if buffer[position] != rune('.') {
+ position285, tokenIndex285 := position, tokenIndex
+ if buffer[position] != rune(' ') {
goto l286
}
position++
- goto l283
+ goto l285
l286:
- position, tokenIndex = position283, tokenIndex283
- if buffer[position] != rune('_') {
+ position, tokenIndex = position285, tokenIndex285
+ if buffer[position] != rune('\t') {
goto l281
}
position++
}
+ l285:
l283:
- l287:
{
- position288, tokenIndex288 := position, tokenIndex
+ position284, tokenIndex284 := position, tokenIndex
{
- position289, tokenIndex289 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l290
- }
- position++
- goto l289
- l290:
- position, tokenIndex = position289, tokenIndex289
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l291
- }
- position++
- goto l289
- l291:
- position, tokenIndex = position289, tokenIndex289
- if buffer[position] != rune('.') {
- goto l292
- }
- position++
- goto l289
- l292:
- position, tokenIndex = position289, tokenIndex289
- {
- position294, tokenIndex294 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l295
- }
- position++
- goto l294
- l295:
- position, tokenIndex = position294, tokenIndex294
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l293
- }
- position++
- }
- l294:
- goto l289
- l293:
- position, tokenIndex = position289, tokenIndex289
- if buffer[position] != rune('$') {
- goto l296
- }
- position++
- goto l289
- l296:
- position, tokenIndex = position289, tokenIndex289
- if buffer[position] != rune('_') {
+ position287, tokenIndex287 := position, tokenIndex
+ if buffer[position] != rune(' ') {
goto l288
}
position++
+ goto l287
+ l288:
+ position, tokenIndex = position287, tokenIndex287
+ if buffer[position] != rune('\t') {
+ goto l284
+ }
+ position++
}
- l289:
- goto l287
- l288:
- position, tokenIndex = position288, tokenIndex288
+ l287:
+ goto l283
+ l284:
+ position, tokenIndex = position284, tokenIndex284
}
- add(ruleSymbolName, position282)
+ add(ruleWS, position282)
}
return true
l281:
position, tokenIndex = position281, tokenIndex281
return false
},
- /* 22 LocalSymbol <- <('.' 'L' ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')+)> */
+ /* 19 Comment <- <('#' (!'\n' .)*)> */
func() bool {
- position297, tokenIndex297 := position, tokenIndex
+ position289, tokenIndex289 := position, tokenIndex
{
- position298 := position
- if buffer[position] != rune('.') {
- goto l297
+ position290 := position
+ if buffer[position] != rune('#') {
+ goto l289
}
position++
- if buffer[position] != rune('L') {
- goto l297
+ l291:
+ {
+ position292, tokenIndex292 := position, tokenIndex
+ {
+ position293, tokenIndex293 := position, tokenIndex
+ if buffer[position] != rune('\n') {
+ goto l293
+ }
+ position++
+ goto l292
+ l293:
+ position, tokenIndex = position293, tokenIndex293
+ }
+ if !matchDot() {
+ goto l292
+ }
+ goto l291
+ l292:
+ position, tokenIndex = position292, tokenIndex292
+ }
+ add(ruleComment, position290)
+ }
+ return true
+ l289:
+ position, tokenIndex = position289, tokenIndex289
+ return false
+ },
+ /* 20 Label <- <((LocalSymbol / LocalLabel / SymbolName) ':')> */
+ func() bool {
+ position294, tokenIndex294 := position, tokenIndex
+ {
+ position295 := position
+ {
+ position296, tokenIndex296 := position, tokenIndex
+ if !_rules[ruleLocalSymbol]() {
+ goto l297
+ }
+ goto l296
+ l297:
+ position, tokenIndex = position296, tokenIndex296
+ if !_rules[ruleLocalLabel]() {
+ goto l298
+ }
+ goto l296
+ l298:
+ position, tokenIndex = position296, tokenIndex296
+ if !_rules[ruleSymbolName]() {
+ goto l294
+ }
+ }
+ l296:
+ if buffer[position] != rune(':') {
+ goto l294
}
position++
+ add(ruleLabel, position295)
+ }
+ return true
+ l294:
+ position, tokenIndex = position294, tokenIndex294
+ return false
+ },
+ /* 21 SymbolName <- <(([a-z] / [A-Z] / '.' / '_') ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')*)> */
+ func() bool {
+ position299, tokenIndex299 := position, tokenIndex
+ {
+ position300 := position
{
position301, tokenIndex301 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
@@ -2518,640 +2566,592 @@
goto l301
l304:
position, tokenIndex = position301, tokenIndex301
- {
- position306, tokenIndex306 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l307
- }
- position++
- goto l306
- l307:
- position, tokenIndex = position306, tokenIndex306
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l305
- }
- position++
- }
- l306:
- goto l301
- l305:
- position, tokenIndex = position301, tokenIndex301
- if buffer[position] != rune('$') {
- goto l308
- }
- position++
- goto l301
- l308:
- position, tokenIndex = position301, tokenIndex301
if buffer[position] != rune('_') {
- goto l297
+ goto l299
}
position++
}
l301:
- l299:
+ l305:
{
- position300, tokenIndex300 := position, tokenIndex
+ position306, tokenIndex306 := position, tokenIndex
{
- position309, tokenIndex309 := position, tokenIndex
+ position307, tokenIndex307 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l308
+ }
+ position++
+ goto l307
+ l308:
+ position, tokenIndex = position307, tokenIndex307
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l309
+ }
+ position++
+ goto l307
+ l309:
+ position, tokenIndex = position307, tokenIndex307
+ if buffer[position] != rune('.') {
goto l310
}
position++
- goto l309
+ goto l307
l310:
- position, tokenIndex = position309, tokenIndex309
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l311
- }
- position++
- goto l309
- l311:
- position, tokenIndex = position309, tokenIndex309
- if buffer[position] != rune('.') {
- goto l312
- }
- position++
- goto l309
- l312:
- position, tokenIndex = position309, tokenIndex309
+ position, tokenIndex = position307, tokenIndex307
{
- position314, tokenIndex314 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l315
- }
- position++
- goto l314
- l315:
- position, tokenIndex = position314, tokenIndex314
+ position312, tokenIndex312 := position, tokenIndex
if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l313
}
position++
+ goto l312
+ l313:
+ position, tokenIndex = position312, tokenIndex312
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l311
+ }
+ position++
}
- l314:
- goto l309
- l313:
- position, tokenIndex = position309, tokenIndex309
+ l312:
+ goto l307
+ l311:
+ position, tokenIndex = position307, tokenIndex307
if buffer[position] != rune('$') {
- goto l316
+ goto l314
}
position++
- goto l309
- l316:
- position, tokenIndex = position309, tokenIndex309
+ goto l307
+ l314:
+ position, tokenIndex = position307, tokenIndex307
if buffer[position] != rune('_') {
- goto l300
+ goto l306
}
position++
}
- l309:
- goto l299
- l300:
- position, tokenIndex = position300, tokenIndex300
+ l307:
+ goto l305
+ l306:
+ position, tokenIndex = position306, tokenIndex306
}
- add(ruleLocalSymbol, position298)
+ add(ruleSymbolName, position300)
}
return true
- l297:
- position, tokenIndex = position297, tokenIndex297
+ l299:
+ position, tokenIndex = position299, tokenIndex299
return false
},
- /* 23 LocalLabel <- <([0-9] ([0-9] / '$')*)> */
+ /* 22 LocalSymbol <- <('.' 'L' ([a-z] / [A-Z] / '.' / ([0-9] / [0-9]) / '$' / '_')+)> */
func() bool {
- position317, tokenIndex317 := position, tokenIndex
+ position315, tokenIndex315 := position, tokenIndex
{
- position318 := position
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l317
+ position316 := position
+ if buffer[position] != rune('.') {
+ goto l315
}
position++
- l319:
+ if buffer[position] != rune('L') {
+ goto l315
+ }
+ position++
{
- position320, tokenIndex320 := position, tokenIndex
- {
- position321, tokenIndex321 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l322
- }
- position++
- goto l321
- l322:
- position, tokenIndex = position321, tokenIndex321
- if buffer[position] != rune('$') {
- goto l320
- }
- position++
+ position319, tokenIndex319 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l320
}
- l321:
+ position++
goto l319
l320:
- position, tokenIndex = position320, tokenIndex320
+ position, tokenIndex = position319, tokenIndex319
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l321
+ }
+ position++
+ goto l319
+ l321:
+ position, tokenIndex = position319, tokenIndex319
+ if buffer[position] != rune('.') {
+ goto l322
+ }
+ position++
+ goto l319
+ l322:
+ position, tokenIndex = position319, tokenIndex319
+ {
+ position324, tokenIndex324 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l325
+ }
+ position++
+ goto l324
+ l325:
+ position, tokenIndex = position324, tokenIndex324
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l323
+ }
+ position++
+ }
+ l324:
+ goto l319
+ l323:
+ position, tokenIndex = position319, tokenIndex319
+ if buffer[position] != rune('$') {
+ goto l326
+ }
+ position++
+ goto l319
+ l326:
+ position, tokenIndex = position319, tokenIndex319
+ if buffer[position] != rune('_') {
+ goto l315
+ }
+ position++
}
- add(ruleLocalLabel, position318)
- }
- return true
- l317:
- position, tokenIndex = position317, tokenIndex317
- return false
- },
- /* 24 LocalLabelRef <- <([0-9] ([0-9] / '$')* ('b' / 'f'))> */
- func() bool {
- position323, tokenIndex323 := position, tokenIndex
- {
- position324 := position
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l323
- }
- position++
- l325:
+ l319:
+ l317:
{
- position326, tokenIndex326 := position, tokenIndex
+ position318, tokenIndex318 := position, tokenIndex
{
position327, tokenIndex327 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l328
}
position++
goto l327
l328:
position, tokenIndex = position327, tokenIndex327
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l329
+ }
+ position++
+ goto l327
+ l329:
+ position, tokenIndex = position327, tokenIndex327
+ if buffer[position] != rune('.') {
+ goto l330
+ }
+ position++
+ goto l327
+ l330:
+ position, tokenIndex = position327, tokenIndex327
+ {
+ position332, tokenIndex332 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l333
+ }
+ position++
+ goto l332
+ l333:
+ position, tokenIndex = position332, tokenIndex332
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l331
+ }
+ position++
+ }
+ l332:
+ goto l327
+ l331:
+ position, tokenIndex = position327, tokenIndex327
if buffer[position] != rune('$') {
- goto l326
+ goto l334
+ }
+ position++
+ goto l327
+ l334:
+ position, tokenIndex = position327, tokenIndex327
+ if buffer[position] != rune('_') {
+ goto l318
}
position++
}
l327:
- goto l325
- l326:
- position, tokenIndex = position326, tokenIndex326
+ goto l317
+ l318:
+ position, tokenIndex = position318, tokenIndex318
}
- {
- position329, tokenIndex329 := position, tokenIndex
- if buffer[position] != rune('b') {
- goto l330
- }
- position++
- goto l329
- l330:
- position, tokenIndex = position329, tokenIndex329
- if buffer[position] != rune('f') {
- goto l323
- }
- position++
- }
- l329:
- add(ruleLocalLabelRef, position324)
+ add(ruleLocalSymbol, position316)
}
return true
- l323:
- position, tokenIndex = position323, tokenIndex323
+ l315:
+ position, tokenIndex = position315, tokenIndex315
return false
},
- /* 25 Instruction <- <(InstructionName (WS InstructionArg (WS? ',' WS? InstructionArg)*)?)> */
+ /* 23 LocalLabel <- <([0-9] ([0-9] / '$')*)> */
func() bool {
- position331, tokenIndex331 := position, tokenIndex
+ position335, tokenIndex335 := position, tokenIndex
{
- position332 := position
- if !_rules[ruleInstructionName]() {
- goto l331
+ position336 := position
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l335
}
+ position++
+ l337:
{
- position333, tokenIndex333 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l333
- }
- if !_rules[ruleInstructionArg]() {
- goto l333
- }
- l335:
+ position338, tokenIndex338 := position, tokenIndex
{
- position336, tokenIndex336 := position, tokenIndex
- {
- position337, tokenIndex337 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l337
- }
- goto l338
- l337:
- position, tokenIndex = position337, tokenIndex337
- }
- l338:
- if buffer[position] != rune(',') {
- goto l336
+ position339, tokenIndex339 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l340
}
position++
- {
- position339, tokenIndex339 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l339
- }
- goto l340
- l339:
- position, tokenIndex = position339, tokenIndex339
- }
+ goto l339
l340:
- if !_rules[ruleInstructionArg]() {
- goto l336
+ position, tokenIndex = position339, tokenIndex339
+ if buffer[position] != rune('$') {
+ goto l338
}
- goto l335
- l336:
- position, tokenIndex = position336, tokenIndex336
+ position++
}
- goto l334
- l333:
- position, tokenIndex = position333, tokenIndex333
+ l339:
+ goto l337
+ l338:
+ position, tokenIndex = position338, tokenIndex338
}
- l334:
- add(ruleInstruction, position332)
+ add(ruleLocalLabel, position336)
}
return true
- l331:
- position, tokenIndex = position331, tokenIndex331
+ l335:
+ position, tokenIndex = position335, tokenIndex335
return false
},
- /* 26 InstructionName <- <(([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))* ('.' / '+' / '-')?)> */
+ /* 24 LocalLabelRef <- <([0-9] ([0-9] / '$')* ('b' / 'f'))> */
func() bool {
position341, tokenIndex341 := position, tokenIndex
{
position342 := position
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l341
+ }
+ position++
+ l343:
{
- position343, tokenIndex343 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l344
+ position344, tokenIndex344 := position, tokenIndex
+ {
+ position345, tokenIndex345 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l346
+ }
+ position++
+ goto l345
+ l346:
+ position, tokenIndex = position345, tokenIndex345
+ if buffer[position] != rune('$') {
+ goto l344
+ }
+ position++
}
- position++
+ l345:
goto l343
l344:
- position, tokenIndex = position343, tokenIndex343
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ position, tokenIndex = position344, tokenIndex344
+ }
+ {
+ position347, tokenIndex347 := position, tokenIndex
+ if buffer[position] != rune('b') {
+ goto l348
+ }
+ position++
+ goto l347
+ l348:
+ position, tokenIndex = position347, tokenIndex347
+ if buffer[position] != rune('f') {
goto l341
}
position++
}
- l343:
- l345:
- {
- position346, tokenIndex346 := position, tokenIndex
- {
- position347, tokenIndex347 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l348
- }
- position++
- goto l347
- l348:
- position, tokenIndex = position347, tokenIndex347
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l349
- }
- position++
- goto l347
- l349:
- position, tokenIndex = position347, tokenIndex347
- {
- position350, tokenIndex350 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l351
- }
- position++
- goto l350
- l351:
- position, tokenIndex = position350, tokenIndex350
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l346
- }
- position++
- }
- l350:
- }
- l347:
- goto l345
- l346:
- position, tokenIndex = position346, tokenIndex346
- }
- {
- position352, tokenIndex352 := position, tokenIndex
- {
- position354, tokenIndex354 := position, tokenIndex
- if buffer[position] != rune('.') {
- goto l355
- }
- position++
- goto l354
- l355:
- position, tokenIndex = position354, tokenIndex354
- if buffer[position] != rune('+') {
- goto l356
- }
- position++
- goto l354
- l356:
- position, tokenIndex = position354, tokenIndex354
- if buffer[position] != rune('-') {
- goto l352
- }
- position++
- }
- l354:
- goto l353
- l352:
- position, tokenIndex = position352, tokenIndex352
- }
- l353:
- add(ruleInstructionName, position342)
+ l347:
+ add(ruleLocalLabelRef, position342)
}
return true
l341:
position, tokenIndex = position341, tokenIndex341
return false
},
- /* 27 InstructionArg <- <(IndirectionIndicator? (RegisterOrConstant / LocalLabelRef / TOCRefHigh / TOCRefLow / MemoryRef) AVX512Token*)> */
+ /* 25 Instruction <- <(InstructionName (WS InstructionArg (WS? ',' WS? InstructionArg)*)?)> */
func() bool {
- position357, tokenIndex357 := position, tokenIndex
+ position349, tokenIndex349 := position, tokenIndex
{
- position358 := position
- {
- position359, tokenIndex359 := position, tokenIndex
- if !_rules[ruleIndirectionIndicator]() {
- goto l359
- }
- goto l360
- l359:
- position, tokenIndex = position359, tokenIndex359
+ position350 := position
+ if !_rules[ruleInstructionName]() {
+ goto l349
}
- l360:
+ {
+ position351, tokenIndex351 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l351
+ }
+ if !_rules[ruleInstructionArg]() {
+ goto l351
+ }
+ l353:
+ {
+ position354, tokenIndex354 := position, tokenIndex
+ {
+ position355, tokenIndex355 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l355
+ }
+ goto l356
+ l355:
+ position, tokenIndex = position355, tokenIndex355
+ }
+ l356:
+ if buffer[position] != rune(',') {
+ goto l354
+ }
+ position++
+ {
+ position357, tokenIndex357 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l357
+ }
+ goto l358
+ l357:
+ position, tokenIndex = position357, tokenIndex357
+ }
+ l358:
+ if !_rules[ruleInstructionArg]() {
+ goto l354
+ }
+ goto l353
+ l354:
+ position, tokenIndex = position354, tokenIndex354
+ }
+ goto l352
+ l351:
+ position, tokenIndex = position351, tokenIndex351
+ }
+ l352:
+ add(ruleInstruction, position350)
+ }
+ return true
+ l349:
+ position, tokenIndex = position349, tokenIndex349
+ return false
+ },
+ /* 26 InstructionName <- <(([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))* ('.' / '+' / '-')?)> */
+ func() bool {
+ position359, tokenIndex359 := position, tokenIndex
+ {
+ position360 := position
{
position361, tokenIndex361 := position, tokenIndex
- if !_rules[ruleRegisterOrConstant]() {
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
goto l362
}
+ position++
goto l361
l362:
position, tokenIndex = position361, tokenIndex361
- if !_rules[ruleLocalLabelRef]() {
- goto l363
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l359
}
- goto l361
- l363:
- position, tokenIndex = position361, tokenIndex361
- if !_rules[ruleTOCRefHigh]() {
- goto l364
- }
- goto l361
- l364:
- position, tokenIndex = position361, tokenIndex361
- if !_rules[ruleTOCRefLow]() {
- goto l365
- }
- goto l361
- l365:
- position, tokenIndex = position361, tokenIndex361
- if !_rules[ruleMemoryRef]() {
- goto l357
- }
+ position++
}
l361:
- l366:
+ l363:
{
- position367, tokenIndex367 := position, tokenIndex
- if !_rules[ruleAVX512Token]() {
- goto l367
+ position364, tokenIndex364 := position, tokenIndex
+ {
+ position365, tokenIndex365 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l366
+ }
+ position++
+ goto l365
+ l366:
+ position, tokenIndex = position365, tokenIndex365
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l367
+ }
+ position++
+ goto l365
+ l367:
+ position, tokenIndex = position365, tokenIndex365
+ {
+ position368, tokenIndex368 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l369
+ }
+ position++
+ goto l368
+ l369:
+ position, tokenIndex = position368, tokenIndex368
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l364
+ }
+ position++
+ }
+ l368:
}
- goto l366
- l367:
- position, tokenIndex = position367, tokenIndex367
+ l365:
+ goto l363
+ l364:
+ position, tokenIndex = position364, tokenIndex364
}
- add(ruleInstructionArg, position358)
- }
- return true
- l357:
- position, tokenIndex = position357, tokenIndex357
- return false
- },
- /* 28 AVX512Token <- <(WS? '{' '%'? ([0-9] / [a-z])* '}')> */
- func() bool {
- position368, tokenIndex368 := position, tokenIndex
- {
- position369 := position
{
position370, tokenIndex370 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l370
+ {
+ position372, tokenIndex372 := position, tokenIndex
+ if buffer[position] != rune('.') {
+ goto l373
+ }
+ position++
+ goto l372
+ l373:
+ position, tokenIndex = position372, tokenIndex372
+ if buffer[position] != rune('+') {
+ goto l374
+ }
+ position++
+ goto l372
+ l374:
+ position, tokenIndex = position372, tokenIndex372
+ if buffer[position] != rune('-') {
+ goto l370
+ }
+ position++
}
+ l372:
goto l371
l370:
position, tokenIndex = position370, tokenIndex370
}
l371:
- if buffer[position] != rune('{') {
- goto l368
- }
- position++
- {
- position372, tokenIndex372 := position, tokenIndex
- if buffer[position] != rune('%') {
- goto l372
- }
- position++
- goto l373
- l372:
- position, tokenIndex = position372, tokenIndex372
- }
- l373:
- l374:
- {
- position375, tokenIndex375 := position, tokenIndex
- {
- position376, tokenIndex376 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l377
- }
- position++
- goto l376
- l377:
- position, tokenIndex = position376, tokenIndex376
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l375
- }
- position++
- }
- l376:
- goto l374
- l375:
- position, tokenIndex = position375, tokenIndex375
- }
- if buffer[position] != rune('}') {
- goto l368
- }
- position++
- add(ruleAVX512Token, position369)
+ add(ruleInstructionName, position360)
}
return true
- l368:
- position, tokenIndex = position368, tokenIndex368
+ l359:
+ position, tokenIndex = position359, tokenIndex359
+ return false
+ },
+ /* 27 InstructionArg <- <(IndirectionIndicator? (RegisterOrConstant / LocalLabelRef / TOCRefHigh / TOCRefLow / MemoryRef) AVX512Token*)> */
+ func() bool {
+ position375, tokenIndex375 := position, tokenIndex
+ {
+ position376 := position
+ {
+ position377, tokenIndex377 := position, tokenIndex
+ if !_rules[ruleIndirectionIndicator]() {
+ goto l377
+ }
+ goto l378
+ l377:
+ position, tokenIndex = position377, tokenIndex377
+ }
+ l378:
+ {
+ position379, tokenIndex379 := position, tokenIndex
+ if !_rules[ruleRegisterOrConstant]() {
+ goto l380
+ }
+ goto l379
+ l380:
+ position, tokenIndex = position379, tokenIndex379
+ if !_rules[ruleLocalLabelRef]() {
+ goto l381
+ }
+ goto l379
+ l381:
+ position, tokenIndex = position379, tokenIndex379
+ if !_rules[ruleTOCRefHigh]() {
+ goto l382
+ }
+ goto l379
+ l382:
+ position, tokenIndex = position379, tokenIndex379
+ if !_rules[ruleTOCRefLow]() {
+ goto l383
+ }
+ goto l379
+ l383:
+ position, tokenIndex = position379, tokenIndex379
+ if !_rules[ruleMemoryRef]() {
+ goto l375
+ }
+ }
+ l379:
+ l384:
+ {
+ position385, tokenIndex385 := position, tokenIndex
+ if !_rules[ruleAVX512Token]() {
+ goto l385
+ }
+ goto l384
+ l385:
+ position, tokenIndex = position385, tokenIndex385
+ }
+ add(ruleInstructionArg, position376)
+ }
+ return true
+ l375:
+ position, tokenIndex = position375, tokenIndex375
+ return false
+ },
+ /* 28 AVX512Token <- <(WS? '{' '%'? ([0-9] / [a-z])* '}')> */
+ func() bool {
+ position386, tokenIndex386 := position, tokenIndex
+ {
+ position387 := position
+ {
+ position388, tokenIndex388 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l388
+ }
+ goto l389
+ l388:
+ position, tokenIndex = position388, tokenIndex388
+ }
+ l389:
+ if buffer[position] != rune('{') {
+ goto l386
+ }
+ position++
+ {
+ position390, tokenIndex390 := position, tokenIndex
+ if buffer[position] != rune('%') {
+ goto l390
+ }
+ position++
+ goto l391
+ l390:
+ position, tokenIndex = position390, tokenIndex390
+ }
+ l391:
+ l392:
+ {
+ position393, tokenIndex393 := position, tokenIndex
+ {
+ position394, tokenIndex394 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l395
+ }
+ position++
+ goto l394
+ l395:
+ position, tokenIndex = position394, tokenIndex394
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l393
+ }
+ position++
+ }
+ l394:
+ goto l392
+ l393:
+ position, tokenIndex = position393, tokenIndex393
+ }
+ if buffer[position] != rune('}') {
+ goto l386
+ }
+ position++
+ add(ruleAVX512Token, position387)
+ }
+ return true
+ l386:
+ position, tokenIndex = position386, tokenIndex386
return false
},
/* 29 TOCRefHigh <- <('.' 'T' 'O' 'C' '.' '-' (('0' 'b') / ('.' 'L' ([a-z] / [A-Z] / '_' / [0-9])+)) ('@' ('h' / 'H') ('a' / 'A')))> */
func() bool {
- position378, tokenIndex378 := position, tokenIndex
- {
- position379 := position
- if buffer[position] != rune('.') {
- goto l378
- }
- position++
- if buffer[position] != rune('T') {
- goto l378
- }
- position++
- if buffer[position] != rune('O') {
- goto l378
- }
- position++
- if buffer[position] != rune('C') {
- goto l378
- }
- position++
- if buffer[position] != rune('.') {
- goto l378
- }
- position++
- if buffer[position] != rune('-') {
- goto l378
- }
- position++
- {
- position380, tokenIndex380 := position, tokenIndex
- if buffer[position] != rune('0') {
- goto l381
- }
- position++
- if buffer[position] != rune('b') {
- goto l381
- }
- position++
- goto l380
- l381:
- position, tokenIndex = position380, tokenIndex380
- if buffer[position] != rune('.') {
- goto l378
- }
- position++
- if buffer[position] != rune('L') {
- goto l378
- }
- position++
- {
- position384, tokenIndex384 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l385
- }
- position++
- goto l384
- l385:
- position, tokenIndex = position384, tokenIndex384
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l386
- }
- position++
- goto l384
- l386:
- position, tokenIndex = position384, tokenIndex384
- if buffer[position] != rune('_') {
- goto l387
- }
- position++
- goto l384
- l387:
- position, tokenIndex = position384, tokenIndex384
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l378
- }
- position++
- }
- l384:
- l382:
- {
- position383, tokenIndex383 := position, tokenIndex
- {
- position388, tokenIndex388 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l389
- }
- position++
- goto l388
- l389:
- position, tokenIndex = position388, tokenIndex388
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l390
- }
- position++
- goto l388
- l390:
- position, tokenIndex = position388, tokenIndex388
- if buffer[position] != rune('_') {
- goto l391
- }
- position++
- goto l388
- l391:
- position, tokenIndex = position388, tokenIndex388
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l383
- }
- position++
- }
- l388:
- goto l382
- l383:
- position, tokenIndex = position383, tokenIndex383
- }
- }
- l380:
- if buffer[position] != rune('@') {
- goto l378
- }
- position++
- {
- position392, tokenIndex392 := position, tokenIndex
- if buffer[position] != rune('h') {
- goto l393
- }
- position++
- goto l392
- l393:
- position, tokenIndex = position392, tokenIndex392
- if buffer[position] != rune('H') {
- goto l378
- }
- position++
- }
- l392:
- {
- position394, tokenIndex394 := position, tokenIndex
- if buffer[position] != rune('a') {
- goto l395
- }
- position++
- goto l394
- l395:
- position, tokenIndex = position394, tokenIndex394
- if buffer[position] != rune('A') {
- goto l378
- }
- position++
- }
- l394:
- add(ruleTOCRefHigh, position379)
- }
- return true
- l378:
- position, tokenIndex = position378, tokenIndex378
- return false
- },
- /* 30 TOCRefLow <- <('.' 'T' 'O' 'C' '.' '-' (('0' 'b') / ('.' 'L' ([a-z] / [A-Z] / '_' / [0-9])+)) ('@' ('l' / 'L')))> */
- func() bool {
position396, tokenIndex396 := position, tokenIndex
{
position397 := position
@@ -3273,494 +3273,538 @@
position++
{
position410, tokenIndex410 := position, tokenIndex
- if buffer[position] != rune('l') {
+ if buffer[position] != rune('h') {
goto l411
}
position++
goto l410
l411:
position, tokenIndex = position410, tokenIndex410
- if buffer[position] != rune('L') {
+ if buffer[position] != rune('H') {
goto l396
}
position++
}
l410:
- add(ruleTOCRefLow, position397)
+ {
+ position412, tokenIndex412 := position, tokenIndex
+ if buffer[position] != rune('a') {
+ goto l413
+ }
+ position++
+ goto l412
+ l413:
+ position, tokenIndex = position412, tokenIndex412
+ if buffer[position] != rune('A') {
+ goto l396
+ }
+ position++
+ }
+ l412:
+ add(ruleTOCRefHigh, position397)
}
return true
l396:
position, tokenIndex = position396, tokenIndex396
return false
},
- /* 31 IndirectionIndicator <- <'*'> */
- func() bool {
- position412, tokenIndex412 := position, tokenIndex
- {
- position413 := position
- if buffer[position] != rune('*') {
- goto l412
- }
- position++
- add(ruleIndirectionIndicator, position413)
- }
- return true
- l412:
- position, tokenIndex = position412, tokenIndex412
- return false
- },
- /* 32 RegisterOrConstant <- <((('%' ([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))*) / ('$'? ((Offset Offset) / Offset))) !('f' / 'b' / ':' / '(' / '+' / '-'))> */
+ /* 30 TOCRefLow <- <('.' 'T' 'O' 'C' '.' '-' (('0' 'b') / ('.' 'L' ([a-z] / [A-Z] / '_' / [0-9])+)) ('@' ('l' / 'L')))> */
func() bool {
position414, tokenIndex414 := position, tokenIndex
{
position415 := position
+ if buffer[position] != rune('.') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('T') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('O') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('C') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('.') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('-') {
+ goto l414
+ }
+ position++
{
position416, tokenIndex416 := position, tokenIndex
- if buffer[position] != rune('%') {
+ if buffer[position] != rune('0') {
goto l417
}
position++
- {
- position418, tokenIndex418 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l419
- }
- position++
- goto l418
- l419:
- position, tokenIndex = position418, tokenIndex418
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l417
- }
- position++
+ if buffer[position] != rune('b') {
+ goto l417
}
- l418:
- l420:
- {
- position421, tokenIndex421 := position, tokenIndex
- {
- position422, tokenIndex422 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l423
- }
- position++
- goto l422
- l423:
- position, tokenIndex = position422, tokenIndex422
- if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l424
- }
- position++
- goto l422
- l424:
- position, tokenIndex = position422, tokenIndex422
- {
- position425, tokenIndex425 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l426
- }
- position++
- goto l425
- l426:
- position, tokenIndex = position425, tokenIndex425
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l421
- }
- position++
- }
- l425:
- }
- l422:
- goto l420
- l421:
- position, tokenIndex = position421, tokenIndex421
- }
+ position++
goto l416
l417:
position, tokenIndex = position416, tokenIndex416
+ if buffer[position] != rune('.') {
+ goto l414
+ }
+ position++
+ if buffer[position] != rune('L') {
+ goto l414
+ }
+ position++
{
- position427, tokenIndex427 := position, tokenIndex
- if buffer[position] != rune('$') {
- goto l427
+ position420, tokenIndex420 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l421
}
position++
- goto l428
- l427:
- position, tokenIndex = position427, tokenIndex427
- }
- l428:
- {
- position429, tokenIndex429 := position, tokenIndex
- if !_rules[ruleOffset]() {
- goto l430
+ goto l420
+ l421:
+ position, tokenIndex = position420, tokenIndex420
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l422
}
- if !_rules[ruleOffset]() {
- goto l430
+ position++
+ goto l420
+ l422:
+ position, tokenIndex = position420, tokenIndex420
+ if buffer[position] != rune('_') {
+ goto l423
}
- goto l429
- l430:
- position, tokenIndex = position429, tokenIndex429
- if !_rules[ruleOffset]() {
+ position++
+ goto l420
+ l423:
+ position, tokenIndex = position420, tokenIndex420
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
goto l414
}
+ position++
}
- l429:
+ l420:
+ l418:
+ {
+ position419, tokenIndex419 := position, tokenIndex
+ {
+ position424, tokenIndex424 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l425
+ }
+ position++
+ goto l424
+ l425:
+ position, tokenIndex = position424, tokenIndex424
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l426
+ }
+ position++
+ goto l424
+ l426:
+ position, tokenIndex = position424, tokenIndex424
+ if buffer[position] != rune('_') {
+ goto l427
+ }
+ position++
+ goto l424
+ l427:
+ position, tokenIndex = position424, tokenIndex424
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l419
+ }
+ position++
+ }
+ l424:
+ goto l418
+ l419:
+ position, tokenIndex = position419, tokenIndex419
+ }
}
l416:
- {
- position431, tokenIndex431 := position, tokenIndex
- {
- position432, tokenIndex432 := position, tokenIndex
- if buffer[position] != rune('f') {
- goto l433
- }
- position++
- goto l432
- l433:
- position, tokenIndex = position432, tokenIndex432
- if buffer[position] != rune('b') {
- goto l434
- }
- position++
- goto l432
- l434:
- position, tokenIndex = position432, tokenIndex432
- if buffer[position] != rune(':') {
- goto l435
- }
- position++
- goto l432
- l435:
- position, tokenIndex = position432, tokenIndex432
- if buffer[position] != rune('(') {
- goto l436
- }
- position++
- goto l432
- l436:
- position, tokenIndex = position432, tokenIndex432
- if buffer[position] != rune('+') {
- goto l437
- }
- position++
- goto l432
- l437:
- position, tokenIndex = position432, tokenIndex432
- if buffer[position] != rune('-') {
- goto l431
- }
- position++
- }
- l432:
+ if buffer[position] != rune('@') {
goto l414
- l431:
- position, tokenIndex = position431, tokenIndex431
}
- add(ruleRegisterOrConstant, position415)
+ position++
+ {
+ position428, tokenIndex428 := position, tokenIndex
+ if buffer[position] != rune('l') {
+ goto l429
+ }
+ position++
+ goto l428
+ l429:
+ position, tokenIndex = position428, tokenIndex428
+ if buffer[position] != rune('L') {
+ goto l414
+ }
+ position++
+ }
+ l428:
+ add(ruleTOCRefLow, position415)
}
return true
l414:
position, tokenIndex = position414, tokenIndex414
return false
},
- /* 33 MemoryRef <- <((SymbolRef BaseIndexScale) / SymbolRef / (Offset* BaseIndexScale) / (SegmentRegister Offset BaseIndexScale) / (SegmentRegister BaseIndexScale) / (SegmentRegister Offset) / BaseIndexScale)> */
+ /* 31 IndirectionIndicator <- <'*'> */
func() bool {
- position438, tokenIndex438 := position, tokenIndex
+ position430, tokenIndex430 := position, tokenIndex
{
- position439 := position
+ position431 := position
+ if buffer[position] != rune('*') {
+ goto l430
+ }
+ position++
+ add(ruleIndirectionIndicator, position431)
+ }
+ return true
+ l430:
+ position, tokenIndex = position430, tokenIndex430
+ return false
+ },
+ /* 32 RegisterOrConstant <- <((('%' ([a-z] / [A-Z]) ([a-z] / [A-Z] / ([0-9] / [0-9]))*) / ('$'? ((Offset Offset) / Offset))) !('f' / 'b' / ':' / '(' / '+' / '-'))> */
+ func() bool {
+ position432, tokenIndex432 := position, tokenIndex
+ {
+ position433 := position
{
- position440, tokenIndex440 := position, tokenIndex
- if !_rules[ruleSymbolRef]() {
- goto l441
+ position434, tokenIndex434 := position, tokenIndex
+ if buffer[position] != rune('%') {
+ goto l435
}
- if !_rules[ruleBaseIndexScale]() {
- goto l441
+ position++
+ {
+ position436, tokenIndex436 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l437
+ }
+ position++
+ goto l436
+ l437:
+ position, tokenIndex = position436, tokenIndex436
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l435
+ }
+ position++
}
- goto l440
- l441:
- position, tokenIndex = position440, tokenIndex440
- if !_rules[ruleSymbolRef]() {
- goto l442
+ l436:
+ l438:
+ {
+ position439, tokenIndex439 := position, tokenIndex
+ {
+ position440, tokenIndex440 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('z') {
+ goto l441
+ }
+ position++
+ goto l440
+ l441:
+ position, tokenIndex = position440, tokenIndex440
+ if c := buffer[position]; c < rune('A') || c > rune('Z') {
+ goto l442
+ }
+ position++
+ goto l440
+ l442:
+ position, tokenIndex = position440, tokenIndex440
+ {
+ position443, tokenIndex443 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l444
+ }
+ position++
+ goto l443
+ l444:
+ position, tokenIndex = position443, tokenIndex443
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l439
+ }
+ position++
+ }
+ l443:
+ }
+ l440:
+ goto l438
+ l439:
+ position, tokenIndex = position439, tokenIndex439
}
- goto l440
- l442:
- position, tokenIndex = position440, tokenIndex440
- l444:
+ goto l434
+ l435:
+ position, tokenIndex = position434, tokenIndex434
{
position445, tokenIndex445 := position, tokenIndex
- if !_rules[ruleOffset]() {
+ if buffer[position] != rune('$') {
goto l445
}
- goto l444
+ position++
+ goto l446
l445:
position, tokenIndex = position445, tokenIndex445
}
- if !_rules[ruleBaseIndexScale]() {
- goto l443
- }
- goto l440
- l443:
- position, tokenIndex = position440, tokenIndex440
- if !_rules[ruleSegmentRegister]() {
- goto l446
- }
- if !_rules[ruleOffset]() {
- goto l446
- }
- if !_rules[ruleBaseIndexScale]() {
- goto l446
- }
- goto l440
l446:
- position, tokenIndex = position440, tokenIndex440
- if !_rules[ruleSegmentRegister]() {
+ {
+ position447, tokenIndex447 := position, tokenIndex
+ if !_rules[ruleOffset]() {
+ goto l448
+ }
+ if !_rules[ruleOffset]() {
+ goto l448
+ }
goto l447
+ l448:
+ position, tokenIndex = position447, tokenIndex447
+ if !_rules[ruleOffset]() {
+ goto l432
+ }
}
- if !_rules[ruleBaseIndexScale]() {
- goto l447
- }
- goto l440
l447:
- position, tokenIndex = position440, tokenIndex440
- if !_rules[ruleSegmentRegister]() {
- goto l448
- }
- if !_rules[ruleOffset]() {
- goto l448
- }
- goto l440
- l448:
- position, tokenIndex = position440, tokenIndex440
- if !_rules[ruleBaseIndexScale]() {
- goto l438
- }
}
- l440:
- add(ruleMemoryRef, position439)
+ l434:
+ {
+ position449, tokenIndex449 := position, tokenIndex
+ {
+ position450, tokenIndex450 := position, tokenIndex
+ if buffer[position] != rune('f') {
+ goto l451
+ }
+ position++
+ goto l450
+ l451:
+ position, tokenIndex = position450, tokenIndex450
+ if buffer[position] != rune('b') {
+ goto l452
+ }
+ position++
+ goto l450
+ l452:
+ position, tokenIndex = position450, tokenIndex450
+ if buffer[position] != rune(':') {
+ goto l453
+ }
+ position++
+ goto l450
+ l453:
+ position, tokenIndex = position450, tokenIndex450
+ if buffer[position] != rune('(') {
+ goto l454
+ }
+ position++
+ goto l450
+ l454:
+ position, tokenIndex = position450, tokenIndex450
+ if buffer[position] != rune('+') {
+ goto l455
+ }
+ position++
+ goto l450
+ l455:
+ position, tokenIndex = position450, tokenIndex450
+ if buffer[position] != rune('-') {
+ goto l449
+ }
+ position++
+ }
+ l450:
+ goto l432
+ l449:
+ position, tokenIndex = position449, tokenIndex449
+ }
+ add(ruleRegisterOrConstant, position433)
}
return true
- l438:
- position, tokenIndex = position438, tokenIndex438
+ l432:
+ position, tokenIndex = position432, tokenIndex432
+ return false
+ },
+ /* 33 MemoryRef <- <((SymbolRef BaseIndexScale) / SymbolRef / (Offset* BaseIndexScale) / (SegmentRegister Offset BaseIndexScale) / (SegmentRegister BaseIndexScale) / (SegmentRegister Offset) / BaseIndexScale)> */
+ func() bool {
+ position456, tokenIndex456 := position, tokenIndex
+ {
+ position457 := position
+ {
+ position458, tokenIndex458 := position, tokenIndex
+ if !_rules[ruleSymbolRef]() {
+ goto l459
+ }
+ if !_rules[ruleBaseIndexScale]() {
+ goto l459
+ }
+ goto l458
+ l459:
+ position, tokenIndex = position458, tokenIndex458
+ if !_rules[ruleSymbolRef]() {
+ goto l460
+ }
+ goto l458
+ l460:
+ position, tokenIndex = position458, tokenIndex458
+ l462:
+ {
+ position463, tokenIndex463 := position, tokenIndex
+ if !_rules[ruleOffset]() {
+ goto l463
+ }
+ goto l462
+ l463:
+ position, tokenIndex = position463, tokenIndex463
+ }
+ if !_rules[ruleBaseIndexScale]() {
+ goto l461
+ }
+ goto l458
+ l461:
+ position, tokenIndex = position458, tokenIndex458
+ if !_rules[ruleSegmentRegister]() {
+ goto l464
+ }
+ if !_rules[ruleOffset]() {
+ goto l464
+ }
+ if !_rules[ruleBaseIndexScale]() {
+ goto l464
+ }
+ goto l458
+ l464:
+ position, tokenIndex = position458, tokenIndex458
+ if !_rules[ruleSegmentRegister]() {
+ goto l465
+ }
+ if !_rules[ruleBaseIndexScale]() {
+ goto l465
+ }
+ goto l458
+ l465:
+ position, tokenIndex = position458, tokenIndex458
+ if !_rules[ruleSegmentRegister]() {
+ goto l466
+ }
+ if !_rules[ruleOffset]() {
+ goto l466
+ }
+ goto l458
+ l466:
+ position, tokenIndex = position458, tokenIndex458
+ if !_rules[ruleBaseIndexScale]() {
+ goto l456
+ }
+ }
+ l458:
+ add(ruleMemoryRef, position457)
+ }
+ return true
+ l456:
+ position, tokenIndex = position456, tokenIndex456
return false
},
/* 34 SymbolRef <- <((Offset* '+')? (LocalSymbol / SymbolName) Offset* ('@' Section Offset*)?)> */
func() bool {
- position449, tokenIndex449 := position, tokenIndex
+ position467, tokenIndex467 := position, tokenIndex
{
- position450 := position
- {
- position451, tokenIndex451 := position, tokenIndex
- l453:
- {
- position454, tokenIndex454 := position, tokenIndex
- if !_rules[ruleOffset]() {
- goto l454
- }
- goto l453
- l454:
- position, tokenIndex = position454, tokenIndex454
- }
- if buffer[position] != rune('+') {
- goto l451
- }
- position++
- goto l452
- l451:
- position, tokenIndex = position451, tokenIndex451
- }
- l452:
- {
- position455, tokenIndex455 := position, tokenIndex
- if !_rules[ruleLocalSymbol]() {
- goto l456
- }
- goto l455
- l456:
- position, tokenIndex = position455, tokenIndex455
- if !_rules[ruleSymbolName]() {
- goto l449
- }
- }
- l455:
- l457:
- {
- position458, tokenIndex458 := position, tokenIndex
- if !_rules[ruleOffset]() {
- goto l458
- }
- goto l457
- l458:
- position, tokenIndex = position458, tokenIndex458
- }
- {
- position459, tokenIndex459 := position, tokenIndex
- if buffer[position] != rune('@') {
- goto l459
- }
- position++
- if !_rules[ruleSection]() {
- goto l459
- }
- l461:
- {
- position462, tokenIndex462 := position, tokenIndex
- if !_rules[ruleOffset]() {
- goto l462
- }
- goto l461
- l462:
- position, tokenIndex = position462, tokenIndex462
- }
- goto l460
- l459:
- position, tokenIndex = position459, tokenIndex459
- }
- l460:
- add(ruleSymbolRef, position450)
- }
- return true
- l449:
- position, tokenIndex = position449, tokenIndex449
- return false
- },
- /* 35 BaseIndexScale <- <('(' RegisterOrConstant? WS? (',' WS? RegisterOrConstant WS? (',' [0-9]+)?)? ')')> */
- func() bool {
- position463, tokenIndex463 := position, tokenIndex
- {
- position464 := position
- if buffer[position] != rune('(') {
- goto l463
- }
- position++
- {
- position465, tokenIndex465 := position, tokenIndex
- if !_rules[ruleRegisterOrConstant]() {
- goto l465
- }
- goto l466
- l465:
- position, tokenIndex = position465, tokenIndex465
- }
- l466:
- {
- position467, tokenIndex467 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l467
- }
- goto l468
- l467:
- position, tokenIndex = position467, tokenIndex467
- }
- l468:
+ position468 := position
{
position469, tokenIndex469 := position, tokenIndex
- if buffer[position] != rune(',') {
+ l471:
+ {
+ position472, tokenIndex472 := position, tokenIndex
+ if !_rules[ruleOffset]() {
+ goto l472
+ }
+ goto l471
+ l472:
+ position, tokenIndex = position472, tokenIndex472
+ }
+ if buffer[position] != rune('+') {
goto l469
}
position++
- {
- position471, tokenIndex471 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l471
- }
- goto l472
- l471:
- position, tokenIndex = position471, tokenIndex471
- }
- l472:
- if !_rules[ruleRegisterOrConstant]() {
- goto l469
- }
- {
- position473, tokenIndex473 := position, tokenIndex
- if !_rules[ruleWS]() {
- goto l473
- }
- goto l474
- l473:
- position, tokenIndex = position473, tokenIndex473
- }
- l474:
- {
- position475, tokenIndex475 := position, tokenIndex
- if buffer[position] != rune(',') {
- goto l475
- }
- position++
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l475
- }
- position++
- l477:
- {
- position478, tokenIndex478 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l478
- }
- position++
- goto l477
- l478:
- position, tokenIndex = position478, tokenIndex478
- }
- goto l476
- l475:
- position, tokenIndex = position475, tokenIndex475
- }
- l476:
goto l470
l469:
position, tokenIndex = position469, tokenIndex469
}
l470:
- if buffer[position] != rune(')') {
- goto l463
+ {
+ position473, tokenIndex473 := position, tokenIndex
+ if !_rules[ruleLocalSymbol]() {
+ goto l474
+ }
+ goto l473
+ l474:
+ position, tokenIndex = position473, tokenIndex473
+ if !_rules[ruleSymbolName]() {
+ goto l467
+ }
+ }
+ l473:
+ l475:
+ {
+ position476, tokenIndex476 := position, tokenIndex
+ if !_rules[ruleOffset]() {
+ goto l476
+ }
+ goto l475
+ l476:
+ position, tokenIndex = position476, tokenIndex476
+ }
+ {
+ position477, tokenIndex477 := position, tokenIndex
+ if buffer[position] != rune('@') {
+ goto l477
+ }
+ position++
+ if !_rules[ruleSection]() {
+ goto l477
+ }
+ l479:
+ {
+ position480, tokenIndex480 := position, tokenIndex
+ if !_rules[ruleOffset]() {
+ goto l480
+ }
+ goto l479
+ l480:
+ position, tokenIndex = position480, tokenIndex480
+ }
+ goto l478
+ l477:
+ position, tokenIndex = position477, tokenIndex477
+ }
+ l478:
+ add(ruleSymbolRef, position468)
+ }
+ return true
+ l467:
+ position, tokenIndex = position467, tokenIndex467
+ return false
+ },
+ /* 35 BaseIndexScale <- <('(' RegisterOrConstant? WS? (',' WS? RegisterOrConstant WS? (',' [0-9]+)?)? ')')> */
+ func() bool {
+ position481, tokenIndex481 := position, tokenIndex
+ {
+ position482 := position
+ if buffer[position] != rune('(') {
+ goto l481
}
position++
- add(ruleBaseIndexScale, position464)
- }
- return true
- l463:
- position, tokenIndex = position463, tokenIndex463
- return false
- },
- /* 36 Operator <- <('+' / '-')> */
- func() bool {
- position479, tokenIndex479 := position, tokenIndex
- {
- position480 := position
{
- position481, tokenIndex481 := position, tokenIndex
- if buffer[position] != rune('+') {
- goto l482
+ position483, tokenIndex483 := position, tokenIndex
+ if !_rules[ruleRegisterOrConstant]() {
+ goto l483
}
- position++
- goto l481
- l482:
- position, tokenIndex = position481, tokenIndex481
- if buffer[position] != rune('-') {
- goto l479
- }
- position++
+ goto l484
+ l483:
+ position, tokenIndex = position483, tokenIndex483
}
- l481:
- add(ruleOperator, position480)
- }
- return true
- l479:
- position, tokenIndex = position479, tokenIndex479
- return false
- },
- /* 37 Offset <- <('+'? '-'? (('0' ('b' / 'B') ('0' / '1')+) / ('0' ('x' / 'X') ([0-9] / [0-9] / ([a-f] / [A-F]))+) / [0-9]+))> */
- func() bool {
- position483, tokenIndex483 := position, tokenIndex
- {
- position484 := position
+ l484:
{
position485, tokenIndex485 := position, tokenIndex
- if buffer[position] != rune('+') {
+ if !_rules[ruleWS]() {
goto l485
}
- position++
goto l486
l485:
position, tokenIndex = position485, tokenIndex485
@@ -3768,295 +3812,409 @@
l486:
{
position487, tokenIndex487 := position, tokenIndex
- if buffer[position] != rune('-') {
+ if buffer[position] != rune(',') {
goto l487
}
position++
+ {
+ position489, tokenIndex489 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l489
+ }
+ goto l490
+ l489:
+ position, tokenIndex = position489, tokenIndex489
+ }
+ l490:
+ if !_rules[ruleRegisterOrConstant]() {
+ goto l487
+ }
+ {
+ position491, tokenIndex491 := position, tokenIndex
+ if !_rules[ruleWS]() {
+ goto l491
+ }
+ goto l492
+ l491:
+ position, tokenIndex = position491, tokenIndex491
+ }
+ l492:
+ {
+ position493, tokenIndex493 := position, tokenIndex
+ if buffer[position] != rune(',') {
+ goto l493
+ }
+ position++
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l493
+ }
+ position++
+ l495:
+ {
+ position496, tokenIndex496 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l496
+ }
+ position++
+ goto l495
+ l496:
+ position, tokenIndex = position496, tokenIndex496
+ }
+ goto l494
+ l493:
+ position, tokenIndex = position493, tokenIndex493
+ }
+ l494:
goto l488
l487:
position, tokenIndex = position487, tokenIndex487
}
l488:
- {
- position489, tokenIndex489 := position, tokenIndex
- if buffer[position] != rune('0') {
- goto l490
- }
- position++
- {
- position491, tokenIndex491 := position, tokenIndex
- if buffer[position] != rune('b') {
- goto l492
- }
- position++
- goto l491
- l492:
- position, tokenIndex = position491, tokenIndex491
- if buffer[position] != rune('B') {
- goto l490
- }
- position++
- }
- l491:
- {
- position495, tokenIndex495 := position, tokenIndex
- if buffer[position] != rune('0') {
- goto l496
- }
- position++
- goto l495
- l496:
- position, tokenIndex = position495, tokenIndex495
- if buffer[position] != rune('1') {
- goto l490
- }
- position++
- }
- l495:
- l493:
- {
- position494, tokenIndex494 := position, tokenIndex
- {
- position497, tokenIndex497 := position, tokenIndex
- if buffer[position] != rune('0') {
- goto l498
- }
- position++
- goto l497
- l498:
- position, tokenIndex = position497, tokenIndex497
- if buffer[position] != rune('1') {
- goto l494
- }
- position++
- }
- l497:
- goto l493
- l494:
- position, tokenIndex = position494, tokenIndex494
- }
- goto l489
- l490:
- position, tokenIndex = position489, tokenIndex489
- if buffer[position] != rune('0') {
- goto l499
- }
- position++
- {
- position500, tokenIndex500 := position, tokenIndex
- if buffer[position] != rune('x') {
- goto l501
- }
- position++
- goto l500
- l501:
- position, tokenIndex = position500, tokenIndex500
- if buffer[position] != rune('X') {
- goto l499
- }
- position++
- }
- l500:
- {
- position504, tokenIndex504 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l505
- }
- position++
- goto l504
- l505:
- position, tokenIndex = position504, tokenIndex504
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l506
- }
- position++
- goto l504
- l506:
- position, tokenIndex = position504, tokenIndex504
- {
- position507, tokenIndex507 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('f') {
- goto l508
- }
- position++
- goto l507
- l508:
- position, tokenIndex = position507, tokenIndex507
- if c := buffer[position]; c < rune('A') || c > rune('F') {
- goto l499
- }
- position++
- }
- l507:
- }
- l504:
- l502:
- {
- position503, tokenIndex503 := position, tokenIndex
- {
- position509, tokenIndex509 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l510
- }
- position++
- goto l509
- l510:
- position, tokenIndex = position509, tokenIndex509
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l511
- }
- position++
- goto l509
- l511:
- position, tokenIndex = position509, tokenIndex509
- {
- position512, tokenIndex512 := position, tokenIndex
- if c := buffer[position]; c < rune('a') || c > rune('f') {
- goto l513
- }
- position++
- goto l512
- l513:
- position, tokenIndex = position512, tokenIndex512
- if c := buffer[position]; c < rune('A') || c > rune('F') {
- goto l503
- }
- position++
- }
- l512:
- }
- l509:
- goto l502
- l503:
- position, tokenIndex = position503, tokenIndex503
- }
- goto l489
- l499:
- position, tokenIndex = position489, tokenIndex489
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l483
- }
- position++
- l514:
- {
- position515, tokenIndex515 := position, tokenIndex
- if c := buffer[position]; c < rune('0') || c > rune('9') {
- goto l515
- }
- position++
- goto l514
- l515:
- position, tokenIndex = position515, tokenIndex515
- }
+ if buffer[position] != rune(')') {
+ goto l481
}
- l489:
- add(ruleOffset, position484)
+ position++
+ add(ruleBaseIndexScale, position482)
}
return true
- l483:
- position, tokenIndex = position483, tokenIndex483
+ l481:
+ position, tokenIndex = position481, tokenIndex481
+ return false
+ },
+ /* 36 Operator <- <('+' / '-')> */
+ func() bool {
+ position497, tokenIndex497 := position, tokenIndex
+ {
+ position498 := position
+ {
+ position499, tokenIndex499 := position, tokenIndex
+ if buffer[position] != rune('+') {
+ goto l500
+ }
+ position++
+ goto l499
+ l500:
+ position, tokenIndex = position499, tokenIndex499
+ if buffer[position] != rune('-') {
+ goto l497
+ }
+ position++
+ }
+ l499:
+ add(ruleOperator, position498)
+ }
+ return true
+ l497:
+ position, tokenIndex = position497, tokenIndex497
+ return false
+ },
+ /* 37 Offset <- <('+'? '-'? (('0' ('b' / 'B') ('0' / '1')+) / ('0' ('x' / 'X') ([0-9] / [0-9] / ([a-f] / [A-F]))+) / [0-9]+))> */
+ func() bool {
+ position501, tokenIndex501 := position, tokenIndex
+ {
+ position502 := position
+ {
+ position503, tokenIndex503 := position, tokenIndex
+ if buffer[position] != rune('+') {
+ goto l503
+ }
+ position++
+ goto l504
+ l503:
+ position, tokenIndex = position503, tokenIndex503
+ }
+ l504:
+ {
+ position505, tokenIndex505 := position, tokenIndex
+ if buffer[position] != rune('-') {
+ goto l505
+ }
+ position++
+ goto l506
+ l505:
+ position, tokenIndex = position505, tokenIndex505
+ }
+ l506:
+ {
+ position507, tokenIndex507 := position, tokenIndex
+ if buffer[position] != rune('0') {
+ goto l508
+ }
+ position++
+ {
+ position509, tokenIndex509 := position, tokenIndex
+ if buffer[position] != rune('b') {
+ goto l510
+ }
+ position++
+ goto l509
+ l510:
+ position, tokenIndex = position509, tokenIndex509
+ if buffer[position] != rune('B') {
+ goto l508
+ }
+ position++
+ }
+ l509:
+ {
+ position513, tokenIndex513 := position, tokenIndex
+ if buffer[position] != rune('0') {
+ goto l514
+ }
+ position++
+ goto l513
+ l514:
+ position, tokenIndex = position513, tokenIndex513
+ if buffer[position] != rune('1') {
+ goto l508
+ }
+ position++
+ }
+ l513:
+ l511:
+ {
+ position512, tokenIndex512 := position, tokenIndex
+ {
+ position515, tokenIndex515 := position, tokenIndex
+ if buffer[position] != rune('0') {
+ goto l516
+ }
+ position++
+ goto l515
+ l516:
+ position, tokenIndex = position515, tokenIndex515
+ if buffer[position] != rune('1') {
+ goto l512
+ }
+ position++
+ }
+ l515:
+ goto l511
+ l512:
+ position, tokenIndex = position512, tokenIndex512
+ }
+ goto l507
+ l508:
+ position, tokenIndex = position507, tokenIndex507
+ if buffer[position] != rune('0') {
+ goto l517
+ }
+ position++
+ {
+ position518, tokenIndex518 := position, tokenIndex
+ if buffer[position] != rune('x') {
+ goto l519
+ }
+ position++
+ goto l518
+ l519:
+ position, tokenIndex = position518, tokenIndex518
+ if buffer[position] != rune('X') {
+ goto l517
+ }
+ position++
+ }
+ l518:
+ {
+ position522, tokenIndex522 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l523
+ }
+ position++
+ goto l522
+ l523:
+ position, tokenIndex = position522, tokenIndex522
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l524
+ }
+ position++
+ goto l522
+ l524:
+ position, tokenIndex = position522, tokenIndex522
+ {
+ position525, tokenIndex525 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('f') {
+ goto l526
+ }
+ position++
+ goto l525
+ l526:
+ position, tokenIndex = position525, tokenIndex525
+ if c := buffer[position]; c < rune('A') || c > rune('F') {
+ goto l517
+ }
+ position++
+ }
+ l525:
+ }
+ l522:
+ l520:
+ {
+ position521, tokenIndex521 := position, tokenIndex
+ {
+ position527, tokenIndex527 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l528
+ }
+ position++
+ goto l527
+ l528:
+ position, tokenIndex = position527, tokenIndex527
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l529
+ }
+ position++
+ goto l527
+ l529:
+ position, tokenIndex = position527, tokenIndex527
+ {
+ position530, tokenIndex530 := position, tokenIndex
+ if c := buffer[position]; c < rune('a') || c > rune('f') {
+ goto l531
+ }
+ position++
+ goto l530
+ l531:
+ position, tokenIndex = position530, tokenIndex530
+ if c := buffer[position]; c < rune('A') || c > rune('F') {
+ goto l521
+ }
+ position++
+ }
+ l530:
+ }
+ l527:
+ goto l520
+ l521:
+ position, tokenIndex = position521, tokenIndex521
+ }
+ goto l507
+ l517:
+ position, tokenIndex = position507, tokenIndex507
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l501
+ }
+ position++
+ l532:
+ {
+ position533, tokenIndex533 := position, tokenIndex
+ if c := buffer[position]; c < rune('0') || c > rune('9') {
+ goto l533
+ }
+ position++
+ goto l532
+ l533:
+ position, tokenIndex = position533, tokenIndex533
+ }
+ }
+ l507:
+ add(ruleOffset, position502)
+ }
+ return true
+ l501:
+ position, tokenIndex = position501, tokenIndex501
return false
},
/* 38 Section <- <([a-z] / [A-Z] / '@')+> */
func() bool {
- position516, tokenIndex516 := position, tokenIndex
+ position534, tokenIndex534 := position, tokenIndex
{
- position517 := position
+ position535 := position
{
- position520, tokenIndex520 := position, tokenIndex
+ position538, tokenIndex538 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l521
+ goto l539
}
position++
- goto l520
- l521:
- position, tokenIndex = position520, tokenIndex520
+ goto l538
+ l539:
+ position, tokenIndex = position538, tokenIndex538
if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l522
+ goto l540
}
position++
- goto l520
- l522:
- position, tokenIndex = position520, tokenIndex520
+ goto l538
+ l540:
+ position, tokenIndex = position538, tokenIndex538
if buffer[position] != rune('@') {
- goto l516
+ goto l534
}
position++
}
- l520:
- l518:
+ l538:
+ l536:
{
- position519, tokenIndex519 := position, tokenIndex
+ position537, tokenIndex537 := position, tokenIndex
{
- position523, tokenIndex523 := position, tokenIndex
+ position541, tokenIndex541 := position, tokenIndex
if c := buffer[position]; c < rune('a') || c > rune('z') {
- goto l524
+ goto l542
}
position++
- goto l523
- l524:
- position, tokenIndex = position523, tokenIndex523
+ goto l541
+ l542:
+ position, tokenIndex = position541, tokenIndex541
if c := buffer[position]; c < rune('A') || c > rune('Z') {
- goto l525
+ goto l543
}
position++
- goto l523
- l525:
- position, tokenIndex = position523, tokenIndex523
+ goto l541
+ l543:
+ position, tokenIndex = position541, tokenIndex541
if buffer[position] != rune('@') {
- goto l519
+ goto l537
}
position++
}
- l523:
- goto l518
- l519:
- position, tokenIndex = position519, tokenIndex519
+ l541:
+ goto l536
+ l537:
+ position, tokenIndex = position537, tokenIndex537
}
- add(ruleSection, position517)
+ add(ruleSection, position535)
}
return true
- l516:
- position, tokenIndex = position516, tokenIndex516
+ l534:
+ position, tokenIndex = position534, tokenIndex534
return false
},
/* 39 SegmentRegister <- <('%' ([c-g] / 's') ('s' ':'))> */
func() bool {
- position526, tokenIndex526 := position, tokenIndex
+ position544, tokenIndex544 := position, tokenIndex
{
- position527 := position
+ position545 := position
if buffer[position] != rune('%') {
- goto l526
+ goto l544
}
position++
{
- position528, tokenIndex528 := position, tokenIndex
+ position546, tokenIndex546 := position, tokenIndex
if c := buffer[position]; c < rune('c') || c > rune('g') {
- goto l529
+ goto l547
}
position++
- goto l528
- l529:
- position, tokenIndex = position528, tokenIndex528
+ goto l546
+ l547:
+ position, tokenIndex = position546, tokenIndex546
if buffer[position] != rune('s') {
- goto l526
+ goto l544
}
position++
}
- l528:
+ l546:
if buffer[position] != rune('s') {
- goto l526
+ goto l544
}
position++
if buffer[position] != rune(':') {
- goto l526
+ goto l544
}
position++
- add(ruleSegmentRegister, position527)
+ add(ruleSegmentRegister, position545)
}
return true
- l526:
- position, tokenIndex = position526, tokenIndex526
+ l544:
+ position, tokenIndex = position544, tokenIndex544
return false
},
}
diff --git a/src/util/fipstools/delocate/delocate_test.go b/src/util/fipstools/delocate/delocate_test.go
index 269b484..1778fa7 100644
--- a/src/util/fipstools/delocate/delocate_test.go
+++ b/src/util/fipstools/delocate/delocate_test.go
@@ -38,6 +38,7 @@
}
var delocateTests = []delocateTest{
+ {"generic-FileDirectives", []string{"in.s"}, "out.s"},
{"ppc64le-GlobalEntry", []string{"in.s"}, "out.s"},
{"ppc64le-LoadToR0", []string{"in.s"}, "out.s"},
{"ppc64le-Sample2", []string{"in.s"}, "out.s"},
diff --git a/src/util/fipstools/delocate/testdata/generic-FileDirectives/in.s b/src/util/fipstools/delocate/testdata/generic-FileDirectives/in.s
new file mode 100644
index 0000000..ca5f8c2
--- /dev/null
+++ b/src/util/fipstools/delocate/testdata/generic-FileDirectives/in.s
@@ -0,0 +1,6 @@
+.file 10 "some/path/file.c" "file.c"
+.file 1000 "some/path/file2.c" "file2.c"
+.file 1001 "some/path/file_with_md5.c" "other_name.c" md5 0x5eba7844df6449a7f2fff1556fe7ba8d239f8e2f
+
+# An instruction is needed to satisfy the architecture auto-detection.
+ movq %rax, %rbx
diff --git a/src/util/fipstools/delocate/testdata/generic-FileDirectives/out.s b/src/util/fipstools/delocate/testdata/generic-FileDirectives/out.s
new file mode 100644
index 0000000..bb33e52
--- /dev/null
+++ b/src/util/fipstools/delocate/testdata/generic-FileDirectives/out.s
@@ -0,0 +1,91 @@
+.text
+.file 1002 "inserted_by_delocate.c" md5 0x00000000000000000000000000000000
+.loc 1002 1 0
+BORINGSSL_bcm_text_start:
+.file 10 "some/path/file.c" "file.c"
+.file 1000 "some/path/file2.c" "file2.c"
+.file 1001 "some/path/file_with_md5.c" "other_name.c" md5 0x5eba7844df6449a7f2fff1556fe7ba8d239f8e2f
+
+# An instruction is needed to satisfy the architecture auto-detection.
+ movq %rax, %rbx
+.text
+.loc 1002 2 0
+BORINGSSL_bcm_text_end:
+.type OPENSSL_ia32cap_get, @function
+.globl OPENSSL_ia32cap_get
+.LOPENSSL_ia32cap_get_local_target:
+OPENSSL_ia32cap_get:
+ leaq OPENSSL_ia32cap_P(%rip), %rax
+ ret
+.extern OPENSSL_ia32cap_P
+.type OPENSSL_ia32cap_addr_delta, @object
+.size OPENSSL_ia32cap_addr_delta, 8
+OPENSSL_ia32cap_addr_delta:
+.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta
+.type BORINGSSL_bcm_text_hash, @object
+.size BORINGSSL_bcm_text_hash, 64
+BORINGSSL_bcm_text_hash:
+.byte 0xae
+.byte 0x2c
+.byte 0xea
+.byte 0x2a
+.byte 0xbd
+.byte 0xa6
+.byte 0xf3
+.byte 0xec
+.byte 0x97
+.byte 0x7f
+.byte 0x9b
+.byte 0xf6
+.byte 0x94
+.byte 0x9a
+.byte 0xfc
+.byte 0x83
+.byte 0x68
+.byte 0x27
+.byte 0xcb
+.byte 0xa0
+.byte 0xa0
+.byte 0x9f
+.byte 0x6b
+.byte 0x6f
+.byte 0xde
+.byte 0x52
+.byte 0xcd
+.byte 0xe2
+.byte 0xcd
+.byte 0xff
+.byte 0x31
+.byte 0x80
+.byte 0xa2
+.byte 0xd4
+.byte 0xc3
+.byte 0x66
+.byte 0xf
+.byte 0xc2
+.byte 0x6a
+.byte 0x7b
+.byte 0xf4
+.byte 0xbe
+.byte 0x39
+.byte 0xa2
+.byte 0xd7
+.byte 0x25
+.byte 0xdb
+.byte 0x21
+.byte 0x98
+.byte 0xe9
+.byte 0xd5
+.byte 0x53
+.byte 0xbf
+.byte 0x5c
+.byte 0x32
+.byte 0x6
+.byte 0x83
+.byte 0x34
+.byte 0xc
+.byte 0x65
+.byte 0x89
+.byte 0x52
+.byte 0xbd
+.byte 0x1f
diff --git a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s
index e97b280..8ee7089 100644
--- a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s
+++ b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s
@@ -35,6 +35,8 @@
.quad 42
.L2:
.quad .L2-.L1
+ .uleb128 .L2-.L1
+ .sleb128 .L2-.L1
# Local labels and their jumps are left alone.
.text
diff --git a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s
index 7a5a551..5027325 100644
--- a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s
+++ b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s
@@ -9,4 +9,6 @@
.quad 42
.L2:
.quad .L2-.L1
+ .uleb128 .L2-.L1
+ .sleb128 .L2-.L1
diff --git a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s
index 8cc27de..252f69c 100644
--- a/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s
+++ b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s
@@ -56,6 +56,8 @@
.L2:
.quad .L2-.L1
+ .uleb128 .L2-.L1
+ .sleb128 .L2-.L1
# Local labels and their jumps are left alone.
.text
@@ -82,6 +84,10 @@
# WAS .quad .L2-.L1
.quad .L2_BCM_1-.L1_BCM_1
+# WAS .uleb128 .L2-.L1
+ .uleb128 .L2_BCM_1-.L1_BCM_1
+# WAS .sleb128 .L2-.L1
+ .sleb128 .L2_BCM_1-.L1_BCM_1
.text
.loc 1 2 0
diff --git a/src/util/fipstools/inject_hash/inject_hash.go b/src/util/fipstools/inject_hash/inject_hash.go
index 29307c0..6bab143 100644
--- a/src/util/fipstools/inject_hash/inject_hash.go
+++ b/src/util/fipstools/inject_hash/inject_hash.go
@@ -22,12 +22,14 @@
"crypto/hmac"
"crypto/sha512"
"debug/elf"
+ "encoding/binary"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
+ "strings"
"boringssl.googlesource.com/boringssl/util/ar"
"boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
@@ -35,7 +37,10 @@
func do(outPath, oInput string, arInput string) error {
var objectBytes []byte
+ var isStatic bool
if len(arInput) > 0 {
+ isStatic = true
+
if len(oInput) > 0 {
return fmt.Errorf("-in-archive and -in-object are mutually exclusive")
}
@@ -63,6 +68,7 @@
if objectBytes, err = ioutil.ReadFile(oInput); err != nil {
return err
}
+ isStatic = strings.HasSuffix(oInput, ".o")
} else {
return fmt.Errorf("exactly one of -in-archive or -in-object is required")
}
@@ -72,15 +78,18 @@
return errors.New("failed to parse object: " + err.Error())
}
- // Find the .text section.
+ // Find the .text and, optionally, .data sections.
- var textSection *elf.Section
- var textSectionIndex elf.SectionIndex
+ var textSection, rodataSection *elf.Section
+ var textSectionIndex, rodataSectionIndex elf.SectionIndex
for i, section := range object.Sections {
- if section.Name == ".text" {
+ switch section.Name {
+ case ".text":
textSectionIndex = elf.SectionIndex(i)
textSection = section
- break
+ case ".rodata":
+ rodataSectionIndex = elf.SectionIndex(i)
+ rodataSection = section
}
}
@@ -90,8 +99,7 @@
// Find the starting and ending symbols for the module.
- var startSeen, endSeen bool
- var start, end uint64
+ var textStart, textEnd, rodataStart, rodataEnd *uint64
symbols, err := object.Symbols()
if err != nil {
@@ -99,50 +107,115 @@
}
for _, symbol := range symbols {
- if symbol.Section != textSectionIndex {
+ var base uint64
+ switch symbol.Section {
+ case textSectionIndex:
+ base = textSection.Addr
+ case rodataSectionIndex:
+ if rodataSection == nil {
+ continue
+ }
+ base = rodataSection.Addr
+ default:
continue
}
+ if isStatic {
+ // Static objects appear to have different semantics about whether symbol
+ // values are relative to their section or not.
+ base = 0
+ } else if symbol.Value < base {
+ return fmt.Errorf("symbol %q at %x, which is below base of %x", symbol.Name, symbol.Value, base)
+ }
+
+ value := symbol.Value - base
switch symbol.Name {
case "BORINGSSL_bcm_text_start":
- if startSeen {
+ if textStart != nil {
return errors.New("duplicate start symbol found")
}
- startSeen = true
- start = symbol.Value
+ textStart = &value
case "BORINGSSL_bcm_text_end":
- if endSeen {
+ if textEnd != nil {
return errors.New("duplicate end symbol found")
}
- endSeen = true
- end = symbol.Value
+ textEnd = &value
+ case "BORINGSSL_bcm_rodata_start":
+ if rodataStart != nil {
+ return errors.New("duplicate rodata start symbol found")
+ }
+ rodataStart = &value
+ case "BORINGSSL_bcm_rodata_end":
+ if rodataEnd != nil {
+ return errors.New("duplicate rodata end symbol found")
+ }
+ rodataEnd = &value
default:
continue
}
}
- if !startSeen || !endSeen {
- return errors.New("could not find module boundaries in object")
+ if textStart == nil || textEnd == nil {
+ return errors.New("could not find .text module boundaries in object")
}
- if max := textSection.Size; start > max || start > end || end > max {
- return fmt.Errorf("invalid module boundaries: start: %x, end: %x, max: %x", start, end, max)
+ if (rodataStart == nil) != (rodataSection == nil) {
+ return errors.New("rodata start marker inconsistent with rodata section presence")
+ }
+
+ if (rodataStart != nil) != (rodataEnd != nil) {
+ return errors.New("rodata marker presence inconsistent")
+ }
+
+ if max := textSection.Size; *textStart > max || *textStart > *textEnd || *textEnd > max {
+ return fmt.Errorf("invalid module .text boundaries: start: %x, end: %x, max: %x", *textStart, *textEnd, max)
+ }
+
+ if rodataSection != nil {
+ if max := rodataSection.Size; *rodataStart > max || *rodataStart > *rodataEnd || *rodataEnd > max {
+ return fmt.Errorf("invalid module .rodata boundaries: start: %x, end: %x, max: %x", *rodataStart, *rodataEnd, max)
+ }
}
// Extract the module from the .text section and hash it.
text := textSection.Open()
- if _, err := text.Seek(int64(start), 0); err != nil {
+ if _, err := text.Seek(int64(*textStart), 0); err != nil {
return errors.New("failed to seek to module start in .text: " + err.Error())
}
- moduleText := make([]byte, end-start)
+ moduleText := make([]byte, *textEnd-*textStart)
if _, err := io.ReadFull(text, moduleText); err != nil {
return errors.New("failed to read .text: " + err.Error())
}
+ // Maybe extract the module's read-only data too
+ var moduleROData []byte
+ if rodataSection != nil {
+ rodata := rodataSection.Open()
+ if _, err := rodata.Seek(int64(*rodataStart), 0); err != nil {
+ return errors.New("failed to seek to module start in .rodata: " + err.Error())
+ }
+ moduleROData = make([]byte, *rodataEnd-*rodataStart)
+ if _, err := io.ReadFull(rodata, moduleROData); err != nil {
+ return errors.New("failed to read .rodata: " + err.Error())
+ }
+ }
+
var zeroKey [64]byte
mac := hmac.New(sha512.New, zeroKey[:])
- mac.Write(moduleText)
+
+ if moduleROData != nil {
+ var lengthBytes [8]byte
+ binary.LittleEndian.PutUint64(lengthBytes[:], uint64(len(moduleText)))
+ mac.Write(lengthBytes[:])
+ mac.Write(moduleText)
+
+ binary.LittleEndian.PutUint64(lengthBytes[:], uint64(len(moduleROData)))
+ mac.Write(lengthBytes[:])
+ mac.Write(moduleROData)
+ } else {
+ mac.Write(moduleText)
+ }
calculated := mac.Sum(nil)
// Replace the default hash value in the object with the calculated
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index 1773cba..b59373c 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -325,6 +325,8 @@
files['test_support_headers'])
self.PrintVariableSection(out, 'crypto_test_sources',
files['crypto_test'])
+ self.PrintVariableSection(out, 'crypto_test_data',
+ files['crypto_test_data'])
self.PrintVariableSection(out, 'ssl_test_sources', files['ssl_test'])
@@ -590,7 +592,8 @@
def main(platforms):
cmake = ExtractVariablesFromCMakeFile(os.path.join('src', 'sources.cmake'))
crypto_c_files = (FindCFiles(os.path.join('src', 'crypto'), NoTestsNorFIPSFragments) +
- FindCFiles(os.path.join('src', 'third_party', 'fiat'), NoTestsNorFIPSFragments))
+ FindCFiles(os.path.join('src', 'third_party', 'fiat'), NoTestsNorFIPSFragments) +
+ FindCFiles(os.path.join('src', 'third_party', 'sike'), NoTestsNorFIPSFragments))
fips_fragments = FindCFiles(os.path.join('src', 'crypto', 'fipsmodule'), OnlyFIPSFragments)
ssl_source_files = FindCFiles(os.path.join('src', 'ssl'), NoTests)
tool_c_files = FindCFiles(os.path.join('src', 'tool'), NoTests)
@@ -655,7 +658,8 @@
ssl_internal_h_files = FindHeaderFiles(os.path.join('src', 'ssl'), NoTests)
crypto_internal_h_files = (
FindHeaderFiles(os.path.join('src', 'crypto'), NoTests) +
- FindHeaderFiles(os.path.join('src', 'third_party', 'fiat'), NoTests))
+ FindHeaderFiles(os.path.join('src', 'third_party', 'fiat'), NoTests) +
+ FindHeaderFiles(os.path.join('src', 'third_party', 'sike'), NoTests))
files = {
'crypto': crypto_c_files,
@@ -691,7 +695,7 @@
parser.add_option(
'--embed_test_data', type='choice', dest='embed_test_data',
action='store', default="true", choices=["true", "false"],
- help='For Bazel, don\'t embed data files in crypto_test_data.cc')
+ help='For Bazel or GN, don\'t embed data files in crypto_test_data.cc')
options, args = parser.parse_args(sys.argv[1:])
PREFIX = options.prefix
EMBED_TEST_DATA = (options.embed_test_data == "true")