external/boringssl: Sync to 8ca0b41.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/9d908ba519f2cfe5e21561bdee3e224b94d14a89..8ca0b4127da11d766067ea6ec4122017ba0edb0e
Change-Id: I732653bc8fcba70707c615f8731ca75397a08736
diff --git a/src/BUILDING.md b/src/BUILDING.md
index 33ad87a..0e34db3 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -79,18 +79,18 @@
been tested with version 10d of the NDK.
Unpack the Android NDK somewhere and export `ANDROID_NDK` to point to the
-directory. Clone https://github.com/taka-no-me/android-cmake into `util/`. Then
-make a build directory as above and run CMake *twice* like this:
+directory. Then make a build directory as above and run CMake like this:
- cmake -DANDROID_NATIVE_API_LEVEL=android-9 \
- -DANDROID_ABI=armeabi-v7a \
- -DCMAKE_TOOLCHAIN_FILE=../util/android-cmake/android.toolchain.cmake \
+ cmake -DANDROID_ABI=armeabi-v7a \
+ -DCMAKE_TOOLCHAIN_FILE=../third_party/android-cmake/android.toolchain.cmake \
-DANDROID_NATIVE_API_LEVEL=16 \
-GNinja ..
-Once you've run that twice, Ninja should produce Android-compatible binaries.
-You can replace `armeabi-v7a` in the above with `arm64-v8a` to build aarch64
-binaries.
+Once you've run that, Ninja should produce Android-compatible binaries. You
+can replace `armeabi-v7a` in the above with `arm64-v8a` and use API level 21 or
+higher to build aarch64 binaries.
+
+For other options, see [android-cmake's documentation](./third_party/android-cmake/README.md).
## Known Limitations on Windows
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f36cd38..3c5db63 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,8 +15,12 @@
if(ANDROID)
# Android-NDK CMake files reconfigure the path and so Go and Perl won't be
# found. However, ninja will still find them in $PATH if we just name them.
- set(PERL_EXECUTABLE "perl")
- set(GO_EXECUTABLE "go")
+ if(NOT PERL_EXECUTABLE)
+ set(PERL_EXECUTABLE "perl")
+ endif()
+ if(NOT GO_EXECUTABLE)
+ set(GO_EXECUTABLE "go")
+ endif()
else()
find_package(Perl REQUIRED)
find_program(GO_EXECUTABLE go)
@@ -70,8 +74,6 @@
"C4800" # 'int' : forcing value to bool 'true' or 'false'
# (performance warning)
"C4820" # 'bytes' bytes padding added after construct 'member_name'
- "C4996" # 'read': The POSIX name for this item is deprecated. Instead,
- # use the ISO C++ conformant name: _read.
"C5027" # move assignment operator was implicitly defined as deleted
)
set(MSVC_LEVEL4_WARNINGS_LIST
@@ -87,6 +89,7 @@
add_definitions(-D_HAS_EXCEPTIONS=0)
add_definitions(-DWIN32_LEAN_AND_MEAN)
add_definitions(-DNOMINMAX)
+ add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Allow use of fopen
endif()
if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.7.99") OR
diff --git a/src/FUZZING.md b/src/FUZZING.md
index 86d0930..9f4edef 100644
--- a/src/FUZZING.md
+++ b/src/FUZZING.md
@@ -30,15 +30,15 @@
The recommended values of `max_len` for each test are:
-| Test | `max_len` value |
-|-----------|-----------------|
-| `cert` | 3072 |
-| `client` | 20000 |
-| `pkcs8` | 2048 |
-| `privkey` | 2048 |
-| `server` | 4096 |
-| `spki` | 1024 |
-
+| Test | `max_len` value |
+|------------|-----------------|
+| `cert` | 3072 |
+| `client` | 20000 |
+| `pkcs8` | 2048 |
+| `privkey` | 2048 |
+| `server` | 4096 |
+| `spki` | 1024 |
+| `read_pem` | 512 |
These were determined by rounding up the length of the largest case in the corpus.
diff --git a/src/INCORPORATING.md b/src/INCORPORATING.md
index 58f37db..d53f2b5 100644
--- a/src/INCORPORATING.md
+++ b/src/INCORPORATING.md
@@ -46,6 +46,11 @@
[test](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/boringssl/BUILD.generated_tests.gni)
lists generated for GN in Chromium.
+Generally one checks in these generated files alongside the hand-written build
+files. Periodically an engineer updates the BoringSSL revision, regenerates
+these files and checks in the updated result. As an example, see how this is
+done [in Chromium](https://code.google.com/p/chromium/codesearch#chromium/src/third_party/boringssl/).
+
## Defines
BoringSSL does not present a lot of configurability in order to reduce the
diff --git a/src/crypto/base64/base64.c b/src/crypto/base64/base64.c
index 61f79cd..0763a3e 100644
--- a/src/crypto/base64/base64.c
+++ b/src/crypto/base64/base64.c
@@ -60,61 +60,42 @@
#include <limits.h>
#include <string.h>
+#include <openssl/type_check.h>
+
+
+/* Encoding. */
static const unsigned char data_bin2ascii[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define conv_bin2ascii(a) (data_bin2ascii[(a) & 0x3f])
-/* 64 char lines
- * pad input with 0
- * left over chars are set to =
- * 1 byte => xx==
- * 2 bytes => xxx=
- * 3 bytes => xxxx
- */
-#define BIN_PER_LINE (64/4*3)
-#define CHUNKS_PER_LINE (64/4)
-#define CHAR_PER_LINE (64+1)
+OPENSSL_COMPILE_ASSERT(sizeof(((EVP_ENCODE_CTX *)(NULL))->data) % 3 == 0,
+ data_length_must_be_multiple_of_base64_chunk_size);
-/* 0xF0 is a EOLN
- * 0xF1 is ignore but next needs to be 0xF0 (for \r\n processing).
- * 0xF2 is EOF
- * 0xE0 is ignore at start of line.
- * 0xFF is error */
-
-#define B64_EOLN 0xF0
-#define B64_CR 0xF1
-#define B64_EOF 0xF2
-#define B64_WS 0xE0
-#define B64_ERROR 0xFF
-#define B64_NOT_BASE64(a) (((a) | 0x13) == 0xF3)
-
-static const uint8_t data_ascii2bin[128] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0xFF,
- 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xF2, 0xFF, 0x3F,
- 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
- 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
- 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
- 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-};
-
-static uint8_t conv_ascii2bin(uint8_t a) {
- if (a >= 128) {
- return 0xFF;
+int EVP_EncodedLength(size_t *out_len, size_t len) {
+ if (len + 2 < len) {
+ return 0;
}
- return data_ascii2bin[a];
+ len += 2;
+ len /= 3;
+
+ if (((len << 2) >> 2) != len) {
+ return 0;
+ }
+ len <<= 2;
+
+ if (len + 1 < len) {
+ return 0;
+ }
+ len++;
+
+ *out_len = len;
+ return 1;
}
void EVP_EncodeInit(EVP_ENCODE_CTX *ctx) {
- ctx->length = 48;
- ctx->num = 0;
- ctx->line_num = 0;
+ memset(ctx, 0, sizeof(EVP_ENCODE_CTX));
}
void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
@@ -126,55 +107,72 @@
return;
}
- assert(ctx->length <= sizeof(ctx->enc_data));
- assert(ctx->num < ctx->length);
+ assert(ctx->data_used < sizeof(ctx->data));
- if (ctx->length - ctx->num > in_len) {
- memcpy(&ctx->enc_data[ctx->num], in, in_len);
- ctx->num += in_len;
+ if (sizeof(ctx->data) - ctx->data_used > in_len) {
+ memcpy(&ctx->data[ctx->data_used], in, in_len);
+ ctx->data_used += in_len;
return;
}
- if (ctx->num != 0) {
- size_t todo = ctx->length - ctx->num;
- memcpy(&ctx->enc_data[ctx->num], in, todo);
+ if (ctx->data_used != 0) {
+ const size_t todo = sizeof(ctx->data) - ctx->data_used;
+ memcpy(&ctx->data[ctx->data_used], in, todo);
in += todo;
in_len -= todo;
- size_t encoded = EVP_EncodeBlock(out, ctx->enc_data, ctx->length);
- ctx->num = 0;
+
+ size_t encoded = EVP_EncodeBlock(out, ctx->data, sizeof(ctx->data));
+ ctx->data_used = 0;
+
out += encoded;
*(out++) = '\n';
*out = '\0';
+
total = encoded + 1;
}
- while (in_len >= ctx->length) {
- size_t encoded = EVP_EncodeBlock(out, in, ctx->length);
- in += ctx->length;
- in_len -= ctx->length;
+ while (in_len >= sizeof(ctx->data)) {
+ size_t encoded = EVP_EncodeBlock(out, in, sizeof(ctx->data));
+ in += sizeof(ctx->data);
+ in_len -= sizeof(ctx->data);
+
out += encoded;
*(out++) = '\n';
*out = '\0';
+
+ if (total + encoded + 1 < total) {
+ *out_len = 0;
+ return;
+ }
+
total += encoded + 1;
}
if (in_len != 0) {
- memcpy(&ctx->enc_data[0], in, in_len);
+ memcpy(ctx->data, in, in_len);
}
- ctx->num = in_len;
+
+ ctx->data_used = in_len;
+
+ if (total > INT_MAX) {
+ /* We cannot signal an error, but we can at least avoid making *out_len
+ * negative. */
+ total = 0;
+ }
*out_len = total;
}
void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len) {
- unsigned ret = 0;
-
- if (ctx->num != 0) {
- ret = EVP_EncodeBlock(out, ctx->enc_data, ctx->num);
- out[ret++] = '\n';
- out[ret] = '\0';
- ctx->num = 0;
+ if (ctx->data_used == 0) {
+ *out_len = 0;
+ return;
}
- *out_len = ret;
+
+ size_t encoded = EVP_EncodeBlock(out, ctx->data, ctx->data_used);
+ out[encoded++] = '\n';
+ out[encoded] = '\0';
+ ctx->data_used = 0;
+ *out_len = encoded;
}
size_t EVP_EncodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len) {
@@ -209,246 +207,223 @@
return ret;
}
+
+/* Decoding. */
+
int EVP_DecodedLength(size_t *out_len, size_t len) {
if (len % 4 != 0) {
return 0;
}
+
*out_len = (len / 4) * 3;
return 1;
}
+void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) {
+ memset(ctx, 0, sizeof(EVP_ENCODE_CTX));
+}
+
+/* kBase64ASCIIToBinData maps characters (c < 128) to their base64 value, or
+ * else 0xff if they are invalid. As a special case, the padding character
+ * ('=') is mapped to zero. */
+static const uint8_t kBase64ASCIIToBinData[128] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
+ 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+static uint8_t base64_ascii_to_bin(uint8_t a) {
+ if (a >= 128) {
+ return 0xFF;
+ }
+
+ return kBase64ASCIIToBinData[a];
+}
+
+/* base64_decode_quad decodes a single “quad” (i.e. four characters) of base64
+ * data and writes up to three bytes to |out|. It sets |*out_num_bytes| to the
+ * number of bytes written, which will be less than three if the quad ended
+ * with padding. It returns one on success or zero on error. */
+static int base64_decode_quad(uint8_t *out, size_t *out_num_bytes,
+ const uint8_t *in) {
+ const uint8_t a = base64_ascii_to_bin(in[0]);
+ const uint8_t b = base64_ascii_to_bin(in[1]);
+ const uint8_t c = base64_ascii_to_bin(in[2]);
+ const uint8_t d = base64_ascii_to_bin(in[3]);
+ if (a == 0xff || b == 0xff || c == 0xff || d == 0xff) {
+ return 0;
+ }
+
+ const uint32_t v = ((uint32_t)a) << 18 | ((uint32_t)b) << 12 |
+ ((uint32_t)c) << 6 | (uint32_t)d;
+
+ const unsigned padding_pattern = (in[0] == '=') << 3 |
+ (in[1] == '=') << 2 |
+ (in[2] == '=') << 1 |
+ (in[3] == '=');
+
+ switch (padding_pattern) {
+ case 0:
+ /* The common case of no padding. */
+ *out_num_bytes = 3;
+ out[0] = v >> 16;
+ out[1] = v >> 8;
+ out[2] = v;
+ break;
+
+ case 1: /* xxx= */
+ *out_num_bytes = 2;
+ out[0] = v >> 16;
+ out[1] = v >> 8;
+ break;
+
+ case 3: /* xx== */
+ *out_num_bytes = 1;
+ out[0] = v >> 16;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
+ const uint8_t *in, size_t in_len) {
+ *out_len = 0;
+
+ if (ctx->error_encountered) {
+ return -1;
+ }
+
+ size_t bytes_out = 0, i;
+ for (i = 0; i < in_len; i++) {
+ const char c = in[i];
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ continue;
+ }
+
+ if (base64_ascii_to_bin(c) == 0xff || ctx->eof_seen) {
+ ctx->error_encountered = 1;
+ return -1;
+ }
+
+ ctx->data[ctx->data_used++] = c;
+ if (ctx->data_used == 4) {
+ size_t num_bytes_resulting;
+ if (!base64_decode_quad(out, &num_bytes_resulting, ctx->data)) {
+ ctx->error_encountered = 1;
+ return -1;
+ }
+
+ ctx->data_used = 0;
+ bytes_out += num_bytes_resulting;
+ out += num_bytes_resulting;
+
+ if (num_bytes_resulting < 3) {
+ ctx->eof_seen = 1;
+ }
+ }
+ }
+
+ if (bytes_out > INT_MAX) {
+ ctx->error_encountered = 1;
+ *out_len = 0;
+ return -1;
+ }
+ *out_len = bytes_out;
+
+ if (ctx->eof_seen) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len) {
+ *out_len = 0;
+ if (ctx->error_encountered || ctx->data_used != 0) {
+ return -1;
+ }
+
+ return 1;
+}
+
int EVP_DecodeBase64(uint8_t *out, size_t *out_len, size_t max_out,
const uint8_t *in, size_t in_len) {
- uint8_t a, b, c, d;
- size_t pad_len = 0, len = 0, max_len, i;
- uint32_t l;
+ *out_len = 0;
- if (!EVP_DecodedLength(&max_len, in_len) || max_out < max_len) {
+ if (in_len % 4 != 0) {
return 0;
}
+ size_t max_len;
+ if (!EVP_DecodedLength(&max_len, in_len) ||
+ max_out < max_len) {
+ return 0;
+ }
+
+ size_t i, bytes_out = 0;
for (i = 0; i < in_len; i += 4) {
- a = conv_ascii2bin(*(in++));
- b = conv_ascii2bin(*(in++));
- if (i + 4 == in_len && in[1] == '=') {
- if (in[0] == '=') {
- pad_len = 2;
- } else {
- pad_len = 1;
- }
- }
- if (pad_len < 2) {
- c = conv_ascii2bin(*(in++));
- } else {
- c = 0;
- }
- if (pad_len < 1) {
- d = conv_ascii2bin(*(in++));
- } else {
- d = 0;
- }
- if ((a & 0x80) || (b & 0x80) || (c & 0x80) || (d & 0x80)) {
+ size_t num_bytes_resulting;
+
+ if (!base64_decode_quad(out, &num_bytes_resulting, &in[i])) {
return 0;
}
- l = ((((uint32_t)a) << 18L) | (((uint32_t)b) << 12L) |
- (((uint32_t)c) << 6L) | (((uint32_t)d)));
- *(out++) = (uint8_t)(l >> 16L) & 0xff;
- if (pad_len < 2) {
- *(out++) = (uint8_t)(l >> 8L) & 0xff;
+
+ bytes_out += num_bytes_resulting;
+ out += num_bytes_resulting;
+ if (num_bytes_resulting != 3 && i != in_len - 4) {
+ return 0;
}
- if (pad_len < 1) {
- *(out++) = (uint8_t)(l) & 0xff;
- }
- len += 3 - pad_len;
}
- *out_len = len;
+
+ *out_len = bytes_out;
return 1;
}
-void EVP_DecodeInit(EVP_ENCODE_CTX *ctx) {
- ctx->length = 30;
- ctx->num = 0;
- ctx->line_num = 0;
- ctx->expect_nl = 0;
-}
-
-int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx, uint8_t *out, int *out_len,
- const uint8_t *in, size_t in_len) {
- int seof = -1, eof = 0, rv = -1, v, tmp, exp_nl;
- uint8_t *d;
- unsigned i, n, ln, ret = 0;
-
- n = ctx->num;
- d = ctx->enc_data;
- ln = ctx->line_num;
- exp_nl = ctx->expect_nl;
-
- /* last line of input. */
- if (in_len == 0 || (n == 0 && conv_ascii2bin(in[0]) == B64_EOF)) {
- rv = 0;
- goto end;
- }
-
- /* We parse the input data */
- for (i = 0; i < in_len; i++) {
- /* If the current line is > 80 characters, scream alot */
- if (ln >= 80) {
- rv = -1;
- goto end;
- }
-
- /* Get char and put it into the buffer */
- tmp = *(in++);
- v = conv_ascii2bin(tmp);
- /* only save the good data :-) */
- if (!B64_NOT_BASE64(v)) {
- assert(n < sizeof(ctx->enc_data));
- d[n++] = tmp;
- ln++;
- } else if (v == B64_ERROR) {
- rv = -1;
- goto end;
- }
-
- /* have we seen a '=' which is 'definitly' the last
- * input line. seof will point to the character that
- * holds it. and eof will hold how many characters to
- * chop off. */
- if (tmp == '=') {
- if (seof == -1) {
- seof = n;
- }
- eof++;
- if (eof > 2) {
- /* There are, at most, two equals signs at the end of base64 data. */
- rv = -1;
- goto end;
- }
- }
-
- if (v == B64_CR) {
- ln = 0;
- if (exp_nl) {
- continue;
- }
- }
-
- /* eoln */
- if (v == B64_EOLN) {
- ln = 0;
- if (exp_nl) {
- exp_nl = 0;
- continue;
- }
- }
- exp_nl = 0;
-
- /* If we are at the end of input and it looks like a
- * line, process it. */
- if ((i + 1) == in_len && (((n & 3) == 0) || eof)) {
- v = B64_EOF;
- /* In case things were given us in really small
- records (so two '=' were given in separate
- updates), eof may contain the incorrect number
- of ending bytes to skip, so let's redo the count */
- eof = 0;
- if (d[n - 1] == '=') {
- eof++;
- }
- if (d[n - 2] == '=') {
- eof++;
- }
- /* There will never be more than two '=' */
- }
-
- if ((v == B64_EOF && (n & 3) == 0) || n >= 64) {
- /* This is needed to work correctly on 64 byte input
- * lines. We process the line and then need to
- * accept the '\n' */
- if (v != B64_EOF && n >= 64) {
- exp_nl = 1;
- }
- if (n > 0) {
- /* TODO(davidben): Switch this to EVP_DecodeBase64. */
- v = EVP_DecodeBlock(out, d, n);
- n = 0;
- if (v < 0) {
- rv = 0;
- goto end;
- }
- if (eof > v) {
- rv = -1;
- goto end;
- }
- ret += (v - eof);
- } else {
- eof = 1;
- v = 0;
- }
-
- /* This is the case where we have had a short
- * but valid input line */
- if (v < (int)ctx->length && eof) {
- rv = 0;
- goto end;
- } else {
- ctx->length = v;
- }
-
- if (seof >= 0) {
- rv = 0;
- goto end;
- }
- out += v;
- }
- }
- rv = 1;
-
-end:
- *out_len = ret;
- ctx->num = n;
- ctx->line_num = ln;
- ctx->expect_nl = exp_nl;
- return rv;
-}
-
-int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, uint8_t *out, int *outl) {
- int i;
-
- *outl = 0;
- if (ctx->num != 0) {
- /* TODO(davidben): Switch this to EVP_DecodeBase64. */
- i = EVP_DecodeBlock(out, ctx->enc_data, ctx->num);
- if (i < 0) {
- return -1;
- }
- ctx->num = 0;
- *outl = i;
- return 1;
- } else {
- return 1;
- }
-}
-
int EVP_DecodeBlock(uint8_t *dst, const uint8_t *src, size_t src_len) {
- size_t dst_len;
+ /* Trim spaces and tabs from the beginning of the input. */
+ while (src_len > 0) {
+ if (src[0] != ' ' && src[0] != '\t') {
+ break;
+ }
- /* trim white space from the start of the line. */
- while (conv_ascii2bin(*src) == B64_WS && src_len > 0) {
src++;
src_len--;
}
- /* strip off stuff at the end of the line
- * ascii2bin values B64_WS, B64_EOLN, B64_EOLN and B64_EOF */
- while (src_len > 3 && B64_NOT_BASE64(conv_ascii2bin(src[src_len - 1]))) {
- src_len--;
+ /* Trim newlines, spaces and tabs from the end of the line. */
+ while (src_len > 0) {
+ switch (src[src_len-1]) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ src_len--;
+ continue;
+ }
+
+ break;
}
- if (!EVP_DecodedLength(&dst_len, src_len) || dst_len > INT_MAX) {
- return -1;
- }
- if (!EVP_DecodeBase64(dst, &dst_len, dst_len, src, src_len)) {
+ size_t dst_len;
+ if (!EVP_DecodedLength(&dst_len, src_len) ||
+ dst_len > INT_MAX ||
+ !EVP_DecodeBase64(dst, &dst_len, dst_len, src, src_len)) {
return -1;
}
@@ -461,21 +436,3 @@
return dst_len;
}
-
-int EVP_EncodedLength(size_t *out_len, size_t len) {
- if (len + 2 < len) {
- return 0;
- }
- len += 2;
- len /= 3;
- if (((len << 2) >> 2) != len) {
- return 0;
- }
- len <<= 2;
- if (len + 1 < len) {
- return 0;
- }
- len++;
- *out_len = len;
- return 1;
-}
diff --git a/src/crypto/base64/base64_test.cc b/src/crypto/base64/base64_test.cc
index da016e6..a608773 100644
--- a/src/crypto/base64/base64_test.cc
+++ b/src/crypto/base64/base64_test.cc
@@ -15,76 +15,203 @@
#include <stdio.h>
#include <string.h>
+#include <string>
+#include <vector>
+
#include <openssl/base64.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
+enum encoding_relation {
+ // canonical indicates that the encoding is the expected encoding of the
+ // input.
+ canonical,
+ // valid indicates that the encoding is /a/ valid encoding of the input, but
+ // need not be the canonical one.
+ valid,
+ // invalid indicates that the encoded data is valid.
+ invalid,
+};
+
struct TestVector {
+ enum encoding_relation relation;
const char *decoded;
const char *encoded;
};
// Test vectors from RFC 4648.
static const TestVector kTestVectors[] = {
- { "", "" },
- { "f" , "Zg==" },
- { "fo", "Zm8=" },
- { "foo", "Zm9v" },
- { "foob", "Zm9vYg==" },
- { "fooba", "Zm9vYmE=" },
- { "foobar", "Zm9vYmFy" },
+ {canonical, "", ""},
+ {canonical, "f", "Zg==\n"},
+ {canonical, "fo", "Zm8=\n"},
+ {canonical, "foo", "Zm9v\n"},
+ {canonical, "foob", "Zm9vYg==\n"},
+ {canonical, "fooba", "Zm9vYmE=\n"},
+ {canonical, "foobar", "Zm9vYmFy\n"},
+ {valid, "foobar", "Zm9vYmFy\n\n"},
+ {valid, "foobar", " Zm9vYmFy\n\n"},
+ {valid, "foobar", " Z m 9 v Y m F y\n\n"},
+ {invalid, "", "Zm9vYmFy=\n"},
+ {invalid, "", "Zm9vYmFy==\n"},
+ {invalid, "", "Zm9vYmFy===\n"},
+ {invalid, "", "Z"},
+ {invalid, "", "Z\n"},
+ {invalid, "", "ab!c"},
+ {invalid, "", "ab=c"},
+ {invalid, "", "abc"},
+
+ {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA==\n"},
+ {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA\n==\n"},
+ {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n=\n"},
+ {invalid, "",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=\n==\n"},
+ {canonical, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
+ "4eHh4eHh4\n"},
+ {canonical,
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4\neHh4eHh"
+ "4eHh4eHh4eHh4eA==\n"},
+ {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh\n4eHh4eHh"
+ "4eHh4eHh4eHh4eA==\n"},
+ {valid, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4e"
+ "Hh4eHh4eHh4eA==\n"},
+ {invalid, "",
+ "eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eA=="
+ "\neHh4eHh4eHh4eHh4eHh4eHh4\n"},
+
+ // A '-' has traditionally been treated as the end of the data by OpenSSL
+ // and anything following would be ignored. BoringSSL does not accept this
+ // non-standard extension.
+ {invalid, "", "Zm9vYmFy-anythinggoes"},
+ {invalid, "", "Zm9vYmFy\n-anythinggoes"},
+
+ // CVE-2015-0292
+ {invalid, "",
+ "ZW5jb2RlIG1lCg==========================================================="
+ "=======\n"},
};
static const size_t kNumTests = sizeof(kTestVectors) / sizeof(kTestVectors[0]);
-static bool TestEncode() {
- for (size_t i = 0; i < kNumTests; i++) {
+// RemoveNewlines returns a copy of |in| with all '\n' characters removed.
+static std::string RemoveNewlines(const char *in) {
+ std::string ret;
+ const size_t in_len = strlen(in);
+
+ size_t i;
+ for (i = 0; i < in_len; i++) {
+ if (in[i] != '\n') {
+ ret.push_back(in[i]);
+ }
+ }
+
+ return ret;
+}
+
+static bool TestEncodeBlock() {
+ for (unsigned i = 0; i < kNumTests; i++) {
const TestVector *t = &kTestVectors[i];
- uint8_t out[9];
- size_t len = EVP_EncodeBlock(out, (const uint8_t*)t->decoded,
- strlen(t->decoded));
- if (len != strlen(t->encoded) ||
- memcmp(out, t->encoded, len) != 0) {
+ if (t->relation != canonical) {
+ continue;
+ }
+
+ const size_t decoded_len = strlen(t->decoded);
+ size_t max_encoded_len;
+ if (!EVP_EncodedLength(&max_encoded_len, decoded_len)) {
+ fprintf(stderr, "#%u: EVP_EncodedLength failed\n", i);
+ return false;
+ }
+
+ std::vector<uint8_t> out_vec(max_encoded_len);
+ uint8_t *out = out_vec.data();
+ size_t len = EVP_EncodeBlock(out, (const uint8_t *)t->decoded, decoded_len);
+
+ std::string encoded(RemoveNewlines(t->encoded));
+ if (len != encoded.size() ||
+ memcmp(out, encoded.data(), len) != 0) {
fprintf(stderr, "encode(\"%s\") = \"%.*s\", want \"%s\"\n",
- t->decoded, (int)len, (const char*)out, t->encoded);
+ t->decoded, (int)len, (const char*)out, encoded.c_str());
return false;
}
}
+
return true;
}
-static bool TestDecode() {
- uint8_t out[6];
+static bool TestDecodeBase64() {
size_t len;
- for (size_t i = 0; i < kNumTests; i++) {
- // Test the normal API.
+ for (unsigned i = 0; i < kNumTests; i++) {
const TestVector *t = &kTestVectors[i];
- size_t expected_len = strlen(t->decoded);
- if (!EVP_DecodeBase64(out, &len, sizeof(out),
- (const uint8_t*)t->encoded, strlen(t->encoded))) {
- fprintf(stderr, "decode(\"%s\") failed\n", t->encoded);
- return false;
- }
- if (len != strlen(t->decoded) ||
- memcmp(out, t->decoded, len) != 0) {
- fprintf(stderr, "decode(\"%s\") = \"%.*s\", want \"%s\"\n",
- t->encoded, (int)len, (const char*)out, t->decoded);
- return false;
+
+ if (t->relation == valid) {
+ // The non-canonical encodings will generally have odd whitespace etc
+ // that |EVP_DecodeBase64| will reject.
+ continue;
}
+ const std::string encoded(RemoveNewlines(t->encoded));
+ std::vector<uint8_t> out_vec(encoded.size());
+ uint8_t *out = out_vec.data();
+
+ int ok = EVP_DecodeBase64(out, &len, out_vec.size(),
+ (const uint8_t *)encoded.data(), encoded.size());
+
+ if (t->relation == invalid) {
+ if (ok) {
+ fprintf(stderr, "decode(\"%s\") didn't fail but should have\n",
+ encoded.c_str());
+ return false;
+ }
+ } else if (t->relation == canonical) {
+ if (!ok) {
+ fprintf(stderr, "decode(\"%s\") failed\n", encoded.c_str());
+ return false;
+ }
+
+ if (len != strlen(t->decoded) ||
+ memcmp(out, t->decoded, len) != 0) {
+ fprintf(stderr, "decode(\"%s\") = \"%.*s\", want \"%s\"\n",
+ encoded.c_str(), (int)len, (const char*)out, t->decoded);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static bool TestDecodeBlock() {
+ for (unsigned i = 0; i < kNumTests; i++) {
+ const TestVector *t = &kTestVectors[i];
+ if (t->relation != canonical) {
+ continue;
+ }
+
+ std::string encoded(RemoveNewlines(t->encoded));
+
+ std::vector<uint8_t> out_vec(encoded.size());
+ uint8_t *out = out_vec.data();
+
// Test that the padding behavior of the deprecated API is preserved.
- int ret = EVP_DecodeBlock(out, (const uint8_t*)t->encoded,
- strlen(t->encoded));
+ int ret =
+ EVP_DecodeBlock(out, (const uint8_t *)encoded.data(), encoded.size());
if (ret < 0) {
- fprintf(stderr, "decode(\"%s\") failed\n", t->encoded);
+ fprintf(stderr, "EVP_DecodeBlock(\"%s\") failed\n", t->encoded);
return false;
}
if (ret % 3 != 0) {
fprintf(stderr, "EVP_DecodeBlock did not ignore padding\n");
return false;
}
+ size_t expected_len = strlen(t->decoded);
if (expected_len % 3 != 0) {
ret -= 3 - (expected_len % 3);
}
@@ -96,19 +223,155 @@
}
}
- if (EVP_DecodeBase64(out, &len, sizeof(out), (const uint8_t*)"a!bc", 4)) {
- fprintf(stderr, "Failed to reject invalid characters in the middle.\n");
- return false;
+ return true;
+}
+
+static bool TestEncodeDecode() {
+ for (unsigned test_num = 0; test_num < kNumTests; test_num++) {
+ const TestVector *t = &kTestVectors[test_num];
+
+ EVP_ENCODE_CTX ctx;
+ const size_t decoded_len = strlen(t->decoded);
+
+ if (t->relation == canonical) {
+ size_t max_encoded_len;
+ if (!EVP_EncodedLength(&max_encoded_len, decoded_len)) {
+ fprintf(stderr, "#%u: EVP_EncodedLength failed\n", test_num);
+ return false;
+ }
+
+ // EVP_EncodeUpdate will output new lines every 64 bytes of output so we
+ // need slightly more than |EVP_EncodedLength| returns. */
+ max_encoded_len += (max_encoded_len + 63) >> 6;
+ std::vector<uint8_t> out_vec(max_encoded_len);
+ uint8_t *out = out_vec.data();
+
+ EVP_EncodeInit(&ctx);
+
+ int out_len;
+ EVP_EncodeUpdate(&ctx, out, &out_len,
+ reinterpret_cast<const uint8_t *>(t->decoded),
+ decoded_len);
+ size_t total = out_len;
+
+ EVP_EncodeFinal(&ctx, out + total, &out_len);
+ total += out_len;
+
+ if (total != strlen(t->encoded) || memcmp(out, t->encoded, total) != 0) {
+ fprintf(stderr, "#%u: EVP_EncodeUpdate produced different output: '%s' (%u)\n",
+ test_num, out, static_cast<unsigned>(total));
+ return false;
+ }
+ }
+
+ std::vector<uint8_t> out_vec(strlen(t->encoded));
+ uint8_t *out = out_vec.data();
+
+ EVP_DecodeInit(&ctx);
+ int out_len;
+ size_t total = 0;
+ int ret = EVP_DecodeUpdate(&ctx, out, &out_len,
+ reinterpret_cast<const uint8_t *>(t->encoded),
+ strlen(t->encoded));
+ if (ret != -1) {
+ total = out_len;
+ ret = EVP_DecodeFinal(&ctx, out + total, &out_len);
+ total += out_len;
+ }
+
+ switch (t->relation) {
+ case canonical:
+ case valid:
+ if (ret == -1) {
+ fprintf(stderr, "#%u: EVP_DecodeUpdate failed\n", test_num);
+ return false;
+ }
+ if (total != decoded_len || memcmp(out, t->decoded, decoded_len)) {
+ fprintf(stderr, "#%u: EVP_DecodeUpdate produced incorrect output\n",
+ test_num);
+ return false;
+ }
+ break;
+
+ case invalid:
+ if (ret != -1) {
+ fprintf(stderr, "#%u: EVP_DecodeUpdate was successful but shouldn't have been\n", test_num);
+ return false;
+ }
+ break;
+ }
}
- if (EVP_DecodeBase64(out, &len, sizeof(out), (const uint8_t*)"a=bc", 4)) {
- fprintf(stderr, "Failed to reject invalid characters in the middle.\n");
- return false;
- }
+ return true;
+}
- if (EVP_DecodeBase64(out, &len, sizeof(out), (const uint8_t*)"abc", 4)) {
- fprintf(stderr, "Failed to reject invalid input length.\n");
- return false;
+static bool TestDecodeUpdateStreaming() {
+ for (unsigned test_num = 0; test_num < kNumTests; test_num++) {
+ const TestVector *t = &kTestVectors[test_num];
+ if (t->relation == invalid) {
+ continue;
+ }
+
+ const size_t encoded_len = strlen(t->encoded);
+
+ std::vector<uint8_t> out(encoded_len);
+
+ for (size_t chunk_size = 1; chunk_size <= encoded_len; chunk_size++) {
+ size_t out_len = 0;
+ EVP_ENCODE_CTX ctx;
+ EVP_DecodeInit(&ctx);
+
+ for (size_t i = 0; i < encoded_len;) {
+ size_t todo = encoded_len - i;
+ if (todo > chunk_size) {
+ todo = chunk_size;
+ }
+
+ int bytes_written;
+ int ret = EVP_DecodeUpdate(
+ &ctx, out.data() + out_len, &bytes_written,
+ reinterpret_cast<const uint8_t *>(t->encoded + i), todo);
+ i += todo;
+
+ switch (ret) {
+ case -1:
+ fprintf(stderr, "#%u: EVP_DecodeUpdate returned error\n", test_num);
+ return 0;
+ case 0:
+ out_len += bytes_written;
+ if (i == encoded_len ||
+ (i + 1 == encoded_len && t->encoded[i] == '\n') ||
+ /* If there was an '-' in the input (which means “EOF”) then
+ * this loop will continue to test that |EVP_DecodeUpdate| will
+ * ignore the remainder of the input. */
+ strchr(t->encoded, '-') != nullptr) {
+ break;
+ }
+
+ fprintf(stderr,
+ "#%u: EVP_DecodeUpdate returned zero before end of "
+ "encoded data\n",
+ test_num);
+ return 0;
+ default:
+ out_len += bytes_written;
+ }
+ }
+
+ int bytes_written;
+ int ret = EVP_DecodeFinal(&ctx, out.data() + out_len, &bytes_written);
+ if (ret == -1) {
+ fprintf(stderr, "#%u: EVP_DecodeFinal returned error\n", test_num);
+ return 0;
+ }
+ out_len += bytes_written;
+
+ if (out_len != strlen(t->decoded) ||
+ memcmp(out.data(), t->decoded, out_len) != 0) {
+ fprintf(stderr, "#%u: incorrect output\n", test_num);
+ return 0;
+ }
+ }
}
return true;
@@ -117,8 +380,11 @@
int main(void) {
CRYPTO_library_init();
- if (!TestEncode() ||
- !TestDecode()) {
+ if (!TestEncodeBlock() ||
+ !TestDecodeBase64() ||
+ !TestDecodeBlock() ||
+ !TestDecodeUpdateStreaming() ||
+ !TestEncodeDecode()) {
return 1;
}
diff --git a/src/crypto/bio/fd.c b/src/crypto/bio/fd.c
index 12e6a72..7d94843 100644
--- a/src/crypto/bio/fd.c
+++ b/src/crypto/bio/fd.c
@@ -108,20 +108,25 @@
}
#if defined(OPENSSL_WINDOWS)
-int bio_fd_should_retry(int i) {
- if (i == -1) {
- return bio_fd_non_fatal_error((int)GetLastError());
- }
- return 0;
-}
+ #define BORINGSSL_ERRNO (int)GetLastError()
+ #define BORINGSSL_CLOSE _close
+ #define BORINGSSL_LSEEK _lseek
+ #define BORINGSSL_READ _read
+ #define BORINGSSL_WRITE _write
#else
+ #define BORINGSSL_ERRNO errno
+ #define BORINGSSL_CLOSE close
+ #define BORINGSSL_LSEEK lseek
+ #define BORINGSSL_READ read
+ #define BORINGSSL_WRITE write
+#endif
+
int bio_fd_should_retry(int i) {
if (i == -1) {
- return bio_fd_non_fatal_error(errno);
+ return bio_fd_non_fatal_error(BORINGSSL_ERRNO);
}
return 0;
}
-#endif
BIO *BIO_new_fd(int fd, int close_flag) {
BIO *ret = BIO_new(BIO_s_fd());
@@ -145,7 +150,7 @@
if (bio->shutdown) {
if (bio->init) {
- close(bio->num);
+ BORINGSSL_CLOSE(bio->num);
}
bio->init = 0;
}
@@ -155,7 +160,7 @@
static int fd_read(BIO *b, char *out, int outl) {
int ret = 0;
- ret = read(b->num, out, outl);
+ ret = BORINGSSL_READ(b->num, out, outl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_fd_should_retry(ret)) {
@@ -167,7 +172,7 @@
}
static int fd_write(BIO *b, const char *in, int inl) {
- int ret = write(b->num, in, inl);
+ int ret = BORINGSSL_WRITE(b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (bio_fd_should_retry(ret)) {
@@ -188,14 +193,14 @@
case BIO_C_FILE_SEEK:
ret = 0;
if (b->init) {
- ret = (long)lseek(b->num, num, SEEK_SET);
+ ret = (long)BORINGSSL_LSEEK(b->num, num, SEEK_SET);
}
break;
case BIO_C_FILE_TELL:
case BIO_CTRL_INFO:
ret = 0;
if (b->init) {
- ret = (long)lseek(b->num, 0, SEEK_CUR);
+ ret = (long)BORINGSSL_LSEEK(b->num, 0, SEEK_CUR);
}
break;
case BIO_C_SET_FD:
diff --git a/src/crypto/bn/exponentiation.c b/src/crypto/bn/exponentiation.c
index bb7a2f4..eedc88e 100644
--- a/src/crypto/bn/exponentiation.c
+++ b/src/crypto/bn/exponentiation.c
@@ -576,41 +576,7 @@
int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx) {
- /* For even modulus m = 2^k*m_odd, it might make sense to compute
- * a^p mod m_odd and a^p mod 2^k separately (with Montgomery
- * exponentiation for the odd part), using appropriate exponent
- * reductions, and combine the results using the CRT.
- *
- * For now, we use Montgomery only if the modulus is odd; otherwise,
- * exponentiation using the reciprocal-based quick remaindering
- * algorithm is used.
- *
- * (Timing obtained with expspeed.c [computations a^p mod m
- * where a, p, m are of the same length: 256, 512, 1024, 2048,
- * 4096, 8192 bits], compared to the running time of the
- * standard algorithm:
- *
- * BN_mod_exp_mont 33 .. 40 % [AMD K6-2, Linux, debug configuration]
- * 55 .. 77 % [UltraSparc processor, but
- * debug-solaris-sparcv8-gcc conf.]
- *
- * BN_mod_exp_recp 50 .. 70 % [AMD K6-2, Linux, debug configuration]
- * 62 .. 118 % [UltraSparc, debug-solaris-sparcv8-gcc]
- *
- * On the Sparc, BN_mod_exp_recp was faster than BN_mod_exp_mont
- * at 2048 and more bits, but at 512 and 1024 bits, it was
- * slower even than the standard algorithm!
- *
- * "Real" timings [linux-elf, solaris-sparcv9-gcc configurations]
- * should be obtained when the new Montgomery reduction code
- * has been integrated into OpenSSL.) */
-
if (BN_is_odd(m)) {
- if (a->top == 1 && !a->neg && BN_get_flags(p, BN_FLG_CONSTTIME) == 0) {
- BN_ULONG A = a->d[0];
- return BN_mod_exp_mont_word(r, A, p, m, ctx, NULL);
- }
-
return BN_mod_exp_mont(r, a, p, m, ctx, NULL);
}
@@ -1234,151 +1200,21 @@
int BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx,
const BN_MONT_CTX *mont) {
- BN_MONT_CTX *new_mont = NULL;
- int b, bits, ret = 0;
- int r_is_one;
- BN_ULONG w, next_w;
- BIGNUM *d, *r, *t;
- BIGNUM *swap_tmp;
-#define BN_MOD_MUL_WORD(r, w, m) \
- (BN_mul_word(r, (w)) && \
- (/* BN_ucmp(r, (m)) < 0 ? 1 :*/ \
- (BN_mod(t, r, m, ctx) && (swap_tmp = r, r = t, t = swap_tmp, 1))))
- /* BN_MOD_MUL_WORD is only used with 'w' large, so the BN_ucmp test is
- * probably more overhead than always using BN_mod (which uses BN_copy if a
- * similar test returns true). We can use BN_mod and do not need BN_nnmod
- * because our accumulator is never negative (the result of BN_mod does not
- * depend on the sign of the modulus). */
-#define BN_TO_MONTGOMERY_WORD(r, w, mont) \
- (BN_set_word(r, (w)) && BN_to_montgomery(r, r, (mont), ctx))
+ BIGNUM a_bignum;
+ BN_init(&a_bignum);
- if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
- /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
- OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
- return 0;
- }
+ int ret = 0;
- if (!BN_is_odd(m)) {
- OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
- return 0;
- }
-
- if (m->top == 1) {
- a %= m->d[0]; /* make sure that 'a' is reduced */
- }
-
- bits = BN_num_bits(p);
- if (bits == 0) {
- /* x**0 mod 1 is still zero. */
- if (BN_is_one(m)) {
- BN_zero(rr);
- return 1;
- }
- return BN_one(rr);
- }
- if (a == 0) {
- BN_zero(rr);
- return 1;
- }
-
- BN_CTX_start(ctx);
- d = BN_CTX_get(ctx);
- r = BN_CTX_get(ctx);
- t = BN_CTX_get(ctx);
- if (d == NULL || r == NULL || t == NULL) {
+ if (!BN_set_word(&a_bignum, a)) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_INTERNAL_ERROR);
goto err;
}
- /* Allocate a montgomery context if it was not supplied by the caller. */
- if (mont == NULL) {
- new_mont = BN_MONT_CTX_new();
- if (new_mont == NULL || !BN_MONT_CTX_set(new_mont, m, ctx)) {
- goto err;
- }
- mont = new_mont;
- }
-
- r_is_one = 1; /* except for Montgomery factor */
-
- /* bits-1 >= 0 */
-
- /* The result is accumulated in the product r*w. */
- w = a; /* bit 'bits-1' of 'p' is always set */
- for (b = bits - 2; b >= 0; b--) {
- /* First, square r*w. */
- next_w = w * w;
- if ((next_w / w) != w) {
- /* overflow */
- if (r_is_one) {
- if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
- goto err;
- }
- r_is_one = 0;
- } else {
- if (!BN_MOD_MUL_WORD(r, w, m)) {
- goto err;
- }
- }
- next_w = 1;
- }
-
- w = next_w;
- if (!r_is_one) {
- if (!BN_mod_mul_montgomery(r, r, r, mont, ctx)) {
- goto err;
- }
- }
-
- /* Second, multiply r*w by 'a' if exponent bit is set. */
- if (BN_is_bit_set(p, b)) {
- next_w = w * a;
- if ((next_w / a) != w) {
- /* overflow */
- if (r_is_one) {
- if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
- goto err;
- }
- r_is_one = 0;
- } else {
- if (!BN_MOD_MUL_WORD(r, w, m)) {
- goto err;
- }
- }
- next_w = a;
- }
- w = next_w;
- }
- }
-
- /* Finally, set r:=r*w. */
- if (w != 1) {
- if (r_is_one) {
- if (!BN_TO_MONTGOMERY_WORD(r, w, mont)) {
- goto err;
- }
- r_is_one = 0;
- } else {
- if (!BN_MOD_MUL_WORD(r, w, m)) {
- goto err;
- }
- }
- }
-
- if (r_is_one) {
- /* can happen only if a == 1*/
- if (!BN_one(rr)) {
- goto err;
- }
- } else {
- if (!BN_from_montgomery(rr, r, mont, ctx)) {
- goto err;
- }
- }
- ret = 1;
+ ret = BN_mod_exp_mont(rr, &a_bignum, p, m, ctx, mont);
err:
- BN_MONT_CTX_free(new_mont);
- BN_CTX_end(ctx);
+ BN_free(&a_bignum);
+
return ret;
}
@@ -1387,37 +1223,12 @@
int BN_mod_exp2_mont(BIGNUM *rr, const BIGNUM *a1, const BIGNUM *p1,
const BIGNUM *a2, const BIGNUM *p2, const BIGNUM *m,
BN_CTX *ctx, const BN_MONT_CTX *mont) {
- int i, j, bits, b, bits1, bits2, ret = 0, wpos1, wpos2, window1, window2,
- wvalue1, wvalue2;
- int r_is_one = 1;
- BIGNUM *d, *r;
- const BIGNUM *a_mod_m;
- /* Tables of variables obtained from 'ctx' */
- BIGNUM *val1[TABLE_SIZE], *val2[TABLE_SIZE];
+ BIGNUM tmp;
+ BN_init(&tmp);
+
+ int ret = 0;
BN_MONT_CTX *new_mont = NULL;
- if (!(m->d[0] & 1)) {
- OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
- return 0;
- }
- bits1 = BN_num_bits(p1);
- bits2 = BN_num_bits(p2);
- if (bits1 == 0 && bits2 == 0) {
- ret = BN_one(rr);
- return ret;
- }
-
- bits = (bits1 > bits2) ? bits1 : bits2;
-
- BN_CTX_start(ctx);
- d = BN_CTX_get(ctx);
- r = BN_CTX_get(ctx);
- val1[0] = BN_CTX_get(ctx);
- val2[0] = BN_CTX_get(ctx);
- if (!d || !r || !val1[0] || !val2[0]) {
- goto err;
- }
-
/* Allocate a montgomery context if it was not supplied by the caller. */
if (mont == NULL) {
new_mont = BN_MONT_CTX_new();
@@ -1427,156 +1238,21 @@
mont = new_mont;
}
- window1 = BN_window_bits_for_exponent_size(bits1);
- window2 = BN_window_bits_for_exponent_size(bits2);
-
- /* Build table for a1: val1[i] := a1^(2*i + 1) mod m for i = 0 ..
- * 2^(window1-1) */
- if (a1->neg || BN_ucmp(a1, m) >= 0) {
- if (!BN_mod(val1[0], a1, m, ctx)) {
- goto err;
- }
- a_mod_m = val1[0];
- } else {
- a_mod_m = a1;
- }
-
- if (BN_is_zero(a_mod_m)) {
- BN_zero(rr);
- ret = 1;
+ /* BN_mod_mul_montgomery removes one Montgomery factor, so passing one
+ * Montgomery-encoded and one non-Montgomery-encoded value gives a
+ * non-Montgomery-encoded result. */
+ if (!BN_mod_exp_mont(rr, a1, p1, m, ctx, mont) ||
+ !BN_mod_exp_mont(&tmp, a2, p2, m, ctx, mont) ||
+ !BN_to_montgomery(rr, rr, mont, ctx) ||
+ !BN_mod_mul_montgomery(rr, rr, &tmp, mont, ctx)) {
goto err;
}
- if (!BN_to_montgomery(val1[0], a_mod_m, mont, ctx)) {
- goto err;
- }
-
- if (window1 > 1) {
- if (!BN_mod_mul_montgomery(d, val1[0], val1[0], mont, ctx)) {
- goto err;
- }
-
- j = 1 << (window1 - 1);
- for (i = 1; i < j; i++) {
- if (((val1[i] = BN_CTX_get(ctx)) == NULL) ||
- !BN_mod_mul_montgomery(val1[i], val1[i - 1], d, mont, ctx)) {
- goto err;
- }
- }
- }
-
- /* Build table for a2: val2[i] := a2^(2*i + 1) mod m for i = 0 ..
- * 2^(window2-1) */
- if (a2->neg || BN_ucmp(a2, m) >= 0) {
- if (!BN_mod(val2[0], a2, m, ctx)) {
- goto err;
- }
- a_mod_m = val2[0];
- } else {
- a_mod_m = a2;
- }
-
- if (BN_is_zero(a_mod_m)) {
- BN_zero(rr);
- ret = 1;
- goto err;
- }
-
- if (!BN_to_montgomery(val2[0], a_mod_m, mont, ctx)) {
- goto err;
- }
-
- if (window2 > 1) {
- if (!BN_mod_mul_montgomery(d, val2[0], val2[0], mont, ctx)) {
- goto err;
- }
-
- j = 1 << (window2 - 1);
- for (i = 1; i < j; i++) {
- if (((val2[i] = BN_CTX_get(ctx)) == NULL) ||
- !BN_mod_mul_montgomery(val2[i], val2[i - 1], d, mont, ctx)) {
- goto err;
- }
- }
- }
-
- /* Now compute the power product, using independent windows. */
- r_is_one = 1;
- wvalue1 = 0; /* The 'value' of the first window */
- wvalue2 = 0; /* The 'value' of the second window */
- wpos1 = 0; /* If wvalue1 > 0, the bottom bit of the first window */
- wpos2 = 0; /* If wvalue2 > 0, the bottom bit of the second window */
-
- if (!BN_to_montgomery(r, BN_value_one(), mont, ctx)) {
- goto err;
- }
-
- for (b = bits - 1; b >= 0; b--) {
- if (!r_is_one) {
- if (!BN_mod_mul_montgomery(r, r, r, mont, ctx)) {
- goto err;
- }
- }
-
- if (!wvalue1 && BN_is_bit_set(p1, b)) {
- /* consider bits b-window1+1 .. b for this window */
- i = b - window1 + 1;
- /* works for i<0 */
- while (!BN_is_bit_set(p1, i)) {
- i++;
- }
- wpos1 = i;
- wvalue1 = 1;
- for (i = b - 1; i >= wpos1; i--) {
- wvalue1 <<= 1;
- if (BN_is_bit_set(p1, i)) {
- wvalue1++;
- }
- }
- }
-
- if (!wvalue2 && BN_is_bit_set(p2, b)) {
- /* consider bits b-window2+1 .. b for this window */
- i = b - window2 + 1;
- while (!BN_is_bit_set(p2, i)) {
- i++;
- }
- wpos2 = i;
- wvalue2 = 1;
- for (i = b - 1; i >= wpos2; i--) {
- wvalue2 <<= 1;
- if (BN_is_bit_set(p2, i)) {
- wvalue2++;
- }
- }
- }
-
- if (wvalue1 && b == wpos1) {
- /* wvalue1 is odd and < 2^window1 */
- if (!BN_mod_mul_montgomery(r, r, val1[wvalue1 >> 1], mont, ctx)) {
- goto err;
- }
- wvalue1 = 0;
- r_is_one = 0;
- }
-
- if (wvalue2 && b == wpos2) {
- /* wvalue2 is odd and < 2^window2 */
- if (!BN_mod_mul_montgomery(r, r, val2[wvalue2 >> 1], mont, ctx)) {
- goto err;
- }
- wvalue2 = 0;
- r_is_one = 0;
- }
- }
-
- if (!BN_from_montgomery(rr, r, mont, ctx)) {
- goto err;
- }
ret = 1;
err:
BN_MONT_CTX_free(new_mont);
- BN_CTX_end(ctx);
+ BN_free(&tmp);
+
return ret;
}
diff --git a/src/crypto/bn/montgomery.c b/src/crypto/bn/montgomery.c
index f56998e..7c3b420 100644
--- a/src/crypto/bn/montgomery.c
+++ b/src/crypto/bn/montgomery.c
@@ -293,7 +293,7 @@
const BIGNUM *mod, BN_CTX *bn_ctx) {
CRYPTO_MUTEX_lock_read(lock);
BN_MONT_CTX *ctx = *pmont;
- CRYPTO_MUTEX_unlock(lock);
+ CRYPTO_MUTEX_unlock_read(lock);
if (ctx) {
return 1;
@@ -317,7 +317,7 @@
*pmont = ctx;
out:
- CRYPTO_MUTEX_unlock(lock);
+ CRYPTO_MUTEX_unlock_write(lock);
return ctx != NULL;
}
diff --git a/src/crypto/buf/buf.c b/src/crypto/buf/buf.c
index b918f01..efe9952 100644
--- a/src/crypto/buf/buf.c
+++ b/src/crypto/buf/buf.c
@@ -88,34 +88,26 @@
OPENSSL_free(buf);
}
-static size_t buf_mem_grow(BUF_MEM *buf, size_t len, char clean) {
- char *new_buf;
- size_t n, alloc_size;
-
- if (buf->length >= len) {
- buf->length = len;
- return len;
- }
- if (buf->max >= len) {
- memset(&buf->data[buf->length], 0, len - buf->length);
- buf->length = len;
- return len;
+static int buf_mem_reserve(BUF_MEM *buf, size_t cap, int clean) {
+ if (buf->max >= cap) {
+ return 1;
}
- n = len + 3;
- if (n < len) {
+ size_t n = cap + 3;
+ if (n < cap) {
/* overflow */
OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
return 0;
}
n = n / 3;
- alloc_size = n * 4;
+ size_t alloc_size = n * 4;
if (alloc_size / 4 != n) {
/* overflow */
OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
return 0;
}
+ char *new_buf;
if (buf->data == NULL) {
new_buf = OPENSSL_malloc(alloc_size);
} else {
@@ -128,14 +120,26 @@
if (new_buf == NULL) {
OPENSSL_PUT_ERROR(BUF, ERR_R_MALLOC_FAILURE);
- len = 0;
- } else {
- buf->data = new_buf;
- buf->max = alloc_size;
- memset(&buf->data[buf->length], 0, len - buf->length);
- buf->length = len;
+ return 0;
}
+ buf->data = new_buf;
+ buf->max = alloc_size;
+ return 1;
+}
+
+int BUF_MEM_reserve(BUF_MEM *buf, size_t cap) {
+ return buf_mem_reserve(buf, cap, 0 /* don't clear old buffer contents. */);
+}
+
+static size_t buf_mem_grow(BUF_MEM *buf, size_t len, int clean) {
+ if (!buf_mem_reserve(buf, len, clean)) {
+ return 0;
+ }
+ if (buf->length < len) {
+ memset(&buf->data[buf->length], 0, len - buf->length);
+ }
+ buf->length = len;
return len;
}
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index e98e249..c9f2bc8 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -131,6 +131,14 @@
#endif
}
+int CRYPTO_has_asm(void) {
+#if defined(OPENSSL_NO_ASM)
+ return 0;
+#else
+ return 1;
+#endif
+}
+
const char *SSLeay_version(int unused) {
return "BoringSSL";
}
diff --git a/src/crypto/dh/dh.c b/src/crypto/dh/dh.c
index a5cf94d..94eb364 100644
--- a/src/crypto/dh/dh.c
+++ b/src/crypto/dh/dh.c
@@ -291,8 +291,8 @@
}
BN_with_flags(&local_priv, priv_key, BN_FLG_CONSTTIME);
- if (!BN_mod_exp_mont(pub_key, dh->g, &local_priv, dh->p, ctx,
- dh->method_mont_p)) {
+ if (!BN_mod_exp_mont_consttime(pub_key, dh->g, &local_priv, dh->p, ctx,
+ dh->method_mont_p)) {
goto err;
}
@@ -353,8 +353,8 @@
}
BN_with_flags(&local_priv, dh->priv_key, BN_FLG_CONSTTIME);
- if (!BN_mod_exp_mont(shared_key, peers_key, &local_priv, dh->p, ctx,
- dh->method_mont_p)) {
+ if (!BN_mod_exp_mont_consttime(shared_key, peers_key, &local_priv, dh->p, ctx,
+ dh->method_mont_p)) {
OPENSSL_PUT_ERROR(DH, ERR_R_BN_LIB);
goto err;
}
diff --git a/src/crypto/dh/dh_asn1.c b/src/crypto/dh/dh_asn1.c
index 73cd4df..1a147ee 100644
--- a/src/crypto/dh/dh_asn1.c
+++ b/src/crypto/dh/dh_asn1.c
@@ -55,30 +55,106 @@
#include <openssl/dh.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
+#include <assert.h>
+#include <limits.h>
-#include "internal.h"
+#include <openssl/bn.h>
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
-/* Override the default free and new methods */
-static int dh_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
- void *exarg) {
- if (operation == ASN1_OP_NEW_PRE) {
- *pval = (ASN1_VALUE *)DH_new();
- if (*pval) {
- return 2;
- }
+#include "../bytestring/internal.h"
+
+
+static int parse_integer(CBS *cbs, BIGNUM **out) {
+ assert(*out == NULL);
+ *out = BN_new();
+ if (*out == NULL) {
return 0;
- } else if (operation == ASN1_OP_FREE_PRE) {
- DH_free((DH *)*pval);
- *pval = NULL;
- return 2;
+ }
+ return BN_parse_asn1_unsigned(cbs, *out);
+}
+
+static int marshal_integer(CBB *cbb, BIGNUM *bn) {
+ if (bn == NULL) {
+ /* A DH object may be missing some components. */
+ OPENSSL_PUT_ERROR(DH, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ return BN_marshal_asn1(cbb, bn);
+}
+
+DH *DH_parse_parameters(CBS *cbs) {
+ DH *ret = DH_new();
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) ||
+ !parse_integer(&child, &ret->p) ||
+ !parse_integer(&child, &ret->g)) {
+ goto err;
+ }
+
+ uint64_t priv_length;
+ if (CBS_len(&child) != 0) {
+ if (!CBS_get_asn1_uint64(&child, &priv_length) ||
+ priv_length > UINT_MAX) {
+ goto err;
+ }
+ ret->priv_length = (unsigned)priv_length;
+ }
+
+ if (CBS_len(&child) != 0) {
+ goto err;
+ }
+
+ return ret;
+
+err:
+ OPENSSL_PUT_ERROR(DH, DH_R_DECODE_ERROR);
+ DH_free(ret);
+ return NULL;
+}
+
+int DH_marshal_parameters(CBB *cbb, const DH *dh) {
+ CBB child;
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_SEQUENCE) ||
+ !marshal_integer(&child, dh->p) ||
+ !marshal_integer(&child, dh->g) ||
+ (dh->priv_length != 0 &&
+ !CBB_add_asn1_uint64(&child, dh->priv_length)) ||
+ !CBB_flush(cbb)) {
+ OPENSSL_PUT_ERROR(DH, DH_R_ENCODE_ERROR);
+ return 0;
}
return 1;
}
-ASN1_SEQUENCE_cb(DHparams, dh_cb) = {
- ASN1_SIMPLE(DH, p, BIGNUM), ASN1_SIMPLE(DH, g, BIGNUM),
- ASN1_OPT(DH, priv_length, ZLONG)} ASN1_SEQUENCE_END_cb(DH, DHparams);
+DH *d2i_DHparams(DH **out, const uint8_t **inp, long len) {
+ if (len < 0) {
+ return NULL;
+ }
+ CBS cbs;
+ CBS_init(&cbs, *inp, (size_t)len);
+ DH *ret = DH_parse_parameters(&cbs);
+ if (ret == NULL) {
+ return NULL;
+ }
+ if (out != NULL) {
+ DH_free(*out);
+ *out = ret;
+ }
+ *inp = CBS_data(&cbs);
+ return ret;
+}
-IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(DH, DHparams, DHparams)
+int i2d_DHparams(const DH *in, uint8_t **outp) {
+ CBB cbb;
+ if (!CBB_init(&cbb, 0) ||
+ !DH_marshal_parameters(&cbb, in)) {
+ CBB_cleanup(&cbb);
+ return -1;
+ }
+ return CBB_finish_i2d(&cbb, outp);
+}
diff --git a/src/crypto/dh/dh_test.cc b/src/crypto/dh/dh_test.cc
index 885bd32..1c24428 100644
--- a/src/crypto/dh/dh_test.cc
+++ b/src/crypto/dh/dh_test.cc
@@ -62,6 +62,7 @@
#include <vector>
#include <openssl/bn.h>
+#include <openssl/bytestring.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/mem.h>
@@ -73,13 +74,15 @@
static bool RunBasicTests();
static bool RunRFC5114Tests();
static bool TestBadY();
+static bool TestASN1();
int main(int argc, char *argv[]) {
CRYPTO_library_init();
if (!RunBasicTests() ||
!RunRFC5114Tests() ||
- !TestBadY()) {
+ !TestBadY() ||
+ !TestASN1()) {
ERR_print_errors_fp(stderr);
return 1;
}
@@ -533,3 +536,91 @@
return true;
}
+
+static bool BIGNUMEqualsHex(const BIGNUM *bn, const char *hex) {
+ BIGNUM *hex_bn = NULL;
+ if (!BN_hex2bn(&hex_bn, hex)) {
+ return false;
+ }
+ ScopedBIGNUM free_hex_bn(hex_bn);
+ return BN_cmp(bn, hex_bn) == 0;
+}
+
+static bool TestASN1() {
+ // kParams are a set of Diffie-Hellman parameters generated with
+ // openssl dhparam 256
+ static const uint8_t kParams[] = {
+ 0x30, 0x26, 0x02, 0x21, 0x00, 0xd7, 0x20, 0x34, 0xa3, 0x27,
+ 0x4f, 0xdf, 0xbf, 0x04, 0xfd, 0x24, 0x68, 0x25, 0xb6, 0x56,
+ 0xd8, 0xab, 0x2a, 0x41, 0x2d, 0x74, 0x0a, 0x52, 0x08, 0x7c,
+ 0x40, 0x71, 0x4e, 0xd2, 0x57, 0x93, 0x13, 0x02, 0x01, 0x02,
+ };
+
+ CBS cbs;
+ CBS_init(&cbs, kParams, sizeof(kParams));
+ ScopedDH dh(DH_parse_parameters(&cbs));
+ if (!dh || CBS_len(&cbs) != 0 ||
+ !BIGNUMEqualsHex(
+ dh->p,
+ "d72034a3274fdfbf04fd246825b656d8ab2a412d740a52087c40714ed2579313") ||
+ !BIGNUMEqualsHex(dh->g, "2") || dh->priv_length != 0) {
+ return false;
+ }
+
+ ScopedCBB cbb;
+ uint8_t *der;
+ size_t der_len;
+ if (!CBB_init(cbb.get(), 0) ||
+ !DH_marshal_parameters(cbb.get(), dh.get()) ||
+ !CBB_finish(cbb.get(), &der, &der_len)) {
+ return false;
+ }
+ ScopedOpenSSLBytes free_der(der);
+ if (der_len != sizeof(kParams) || memcmp(der, kParams, der_len) != 0) {
+ return false;
+ }
+
+ // kParamsDSA are a set of Diffie-Hellman parameters generated with
+ // openssl dhparam 256 -dsaparam
+ static const uint8_t kParamsDSA[] = {
+ 0x30, 0x81, 0x89, 0x02, 0x41, 0x00, 0x93, 0xf3, 0xc1, 0x18, 0x01, 0xe6,
+ 0x62, 0xb6, 0xd1, 0x46, 0x9a, 0x2c, 0x72, 0xea, 0x31, 0xd9, 0x18, 0x10,
+ 0x30, 0x28, 0x63, 0xe2, 0x34, 0x7d, 0x80, 0xca, 0xee, 0x82, 0x2b, 0x19,
+ 0x3c, 0x19, 0xbb, 0x42, 0x83, 0x02, 0x70, 0xdd, 0xdb, 0x8c, 0x03, 0xab,
+ 0xe9, 0x9c, 0xc4, 0x00, 0x4d, 0x70, 0x5f, 0x52, 0x03, 0x31, 0x2c, 0xa4,
+ 0x67, 0x34, 0x51, 0x95, 0x2a, 0xac, 0x11, 0xe2, 0x6a, 0x55, 0x02, 0x40,
+ 0x44, 0xc8, 0x10, 0x53, 0x44, 0x32, 0x31, 0x63, 0xd8, 0xd1, 0x8c, 0x75,
+ 0xc8, 0x98, 0x53, 0x3b, 0x5b, 0x4a, 0x2a, 0x0a, 0x09, 0xe7, 0xd0, 0x3c,
+ 0x53, 0x72, 0xa8, 0x6b, 0x70, 0x41, 0x9c, 0x26, 0x71, 0x44, 0xfc, 0x7f,
+ 0x08, 0x75, 0xe1, 0x02, 0xab, 0x74, 0x41, 0xe8, 0x2a, 0x3d, 0x3c, 0x26,
+ 0x33, 0x09, 0xe4, 0x8b, 0xb4, 0x41, 0xec, 0xa6, 0xa8, 0xba, 0x1a, 0x07,
+ 0x8a, 0x77, 0xf5, 0x5f, 0x02, 0x02, 0x00, 0xa0,
+ };
+
+ CBS_init(&cbs, kParamsDSA, sizeof(kParamsDSA));
+ dh.reset(DH_parse_parameters(&cbs));
+ if (!dh || CBS_len(&cbs) != 0 ||
+ !BIGNUMEqualsHex(dh->p,
+ "93f3c11801e662b6d1469a2c72ea31d91810302863e2347d80caee8"
+ "22b193c19bb42830270dddb8c03abe99cc4004d705f5203312ca467"
+ "3451952aac11e26a55") ||
+ !BIGNUMEqualsHex(dh->g,
+ "44c8105344323163d8d18c75c898533b5b4a2a0a09e7d03c5372a86"
+ "b70419c267144fc7f0875e102ab7441e82a3d3c263309e48bb441ec"
+ "a6a8ba1a078a77f55f") ||
+ dh->priv_length != 160) {
+ return false;
+ }
+
+ if (!CBB_init(cbb.get(), 0) ||
+ !DH_marshal_parameters(cbb.get(), dh.get()) ||
+ !CBB_finish(cbb.get(), &der, &der_len)) {
+ return false;
+ }
+ ScopedOpenSSLBytes free_der2(der);
+ if (der_len != sizeof(kParamsDSA) || memcmp(der, kParamsDSA, der_len) != 0) {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/crypto/err/dh.errordata b/src/crypto/err/dh.errordata
index 571e218..9e1b87d 100644
--- a/src/crypto/err/dh.errordata
+++ b/src/crypto/err/dh.errordata
@@ -1,4 +1,6 @@
DH,100,BAD_GENERATOR
+DH,104,DECODE_ERROR
+DH,105,ENCODE_ERROR
DH,101,INVALID_PUBKEY
DH,102,MODULUS_TOO_LARGE
DH,103,NO_PRIVATE_VALUE
diff --git a/src/crypto/err/err.c b/src/crypto/err/err.c
index 9221bf1..c4e4b13 100644
--- a/src/crypto/err/err.c
+++ b/src/crypto/err/err.c
@@ -325,7 +325,7 @@
CRYPTO_STATIC_MUTEX_lock_write(&global_next_library_mutex);
ret = global_next_library++;
- CRYPTO_STATIC_MUTEX_unlock(&global_next_library_mutex);
+ CRYPTO_STATIC_MUTEX_unlock_write(&global_next_library_mutex);
return ret;
}
diff --git a/src/crypto/ex_data.c b/src/crypto/ex_data.c
index 8fa1240..d67abba 100644
--- a/src/crypto/ex_data.c
+++ b/src/crypto/ex_data.c
@@ -163,7 +163,7 @@
ret = 1;
err:
- CRYPTO_STATIC_MUTEX_unlock(&ex_data_class->lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&ex_data_class->lock);
return ret;
}
@@ -217,7 +217,7 @@
if (n > 0) {
*out = sk_CRYPTO_EX_DATA_FUNCS_dup(ex_data_class->meth);
}
- CRYPTO_STATIC_MUTEX_unlock(&ex_data_class->lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&ex_data_class->lock);
if (n > 0 && *out == NULL) {
OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
diff --git a/src/crypto/hkdf/hkdf.c b/src/crypto/hkdf/hkdf.c
index f9cdcb0..d80834d 100644
--- a/src/crypto/hkdf/hkdf.c
+++ b/src/crypto/hkdf/hkdf.c
@@ -21,21 +21,49 @@
#include <openssl/hmac.h>
-int HKDF(uint8_t *out_key, size_t out_len,
- const EVP_MD *digest,
- const uint8_t *secret, size_t secret_len,
- const uint8_t *salt, size_t salt_len,
- const uint8_t *info, size_t info_len) {
+int HKDF(uint8_t *out_key, size_t out_len, const EVP_MD *digest,
+ const uint8_t *secret, size_t secret_len, const uint8_t *salt,
+ size_t salt_len, const uint8_t *info, size_t info_len) {
+ /* https://tools.ietf.org/html/rfc5869#section-2 */
+ uint8_t prk[EVP_MAX_MD_SIZE];
+ size_t prk_len;
+
+ if (!HKDF_extract(prk, &prk_len, digest, secret, secret_len, salt,
+ salt_len) ||
+ !HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int HKDF_extract(uint8_t *out_key, size_t *out_len, const EVP_MD *digest,
+ const uint8_t *secret, size_t secret_len, const uint8_t *salt,
+ size_t salt_len) {
/* https://tools.ietf.org/html/rfc5869#section-2.2 */
- const size_t digest_len = EVP_MD_size(digest);
- uint8_t prk[EVP_MAX_MD_SIZE], previous[EVP_MAX_MD_SIZE];
- size_t n, done = 0;
- unsigned i, prk_len;
- int ret = 0;
- HMAC_CTX hmac;
/* If salt is not given, HashLength zeros are used. However, HMAC does that
* internally already so we can ignore it.*/
+ unsigned len;
+ if (HMAC(digest, salt, salt_len, secret, secret_len, out_key, &len) == NULL) {
+ OPENSSL_PUT_ERROR(HKDF, ERR_R_HMAC_LIB);
+ return 0;
+ }
+ *out_len = len;
+ assert(*out_len == EVP_MD_size(digest));
+ return 1;
+}
+
+int HKDF_expand(uint8_t *out_key, size_t out_len, const EVP_MD *digest,
+ uint8_t *prk, size_t prk_len, const uint8_t *info,
+ size_t info_len) {
+ /* https://tools.ietf.org/html/rfc5869#section-2.3 */
+ const size_t digest_len = EVP_MD_size(digest);
+ uint8_t previous[EVP_MAX_MD_SIZE];
+ size_t n, done = 0;
+ unsigned i;
+ int ret = 0;
+ HMAC_CTX hmac;
/* Expand key material to desired length. */
n = (out_len + digest_len - 1) / digest_len;
@@ -45,13 +73,6 @@
}
HMAC_CTX_init(&hmac);
-
- /* Extract input keying material into pseudorandom key |prk|. */
- if (HMAC(digest, salt, salt_len, secret, secret_len, prk, &prk_len) == NULL) {
- goto out;
- }
- assert(prk_len == digest_len);
-
if (!HMAC_Init_ex(&hmac, prk, prk_len, digest, NULL)) {
goto out;
}
diff --git a/src/crypto/hkdf/hkdf_test.c b/src/crypto/hkdf/hkdf_test.c
index 53c0c5b..a0f75a9 100644
--- a/src/crypto/hkdf/hkdf_test.c
+++ b/src/crypto/hkdf/hkdf_test.c
@@ -31,6 +31,8 @@
const size_t salt_len;
const uint8_t info[80];
const size_t info_len;
+ const uint8_t prk[EVP_MAX_MD_SIZE];
+ const size_t prk_len;
const size_t out_len;
const uint8_t out[82];
} hkdf_test_vector_t;
@@ -50,6 +52,11 @@
{
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
}, 10,
+ {
+ 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d,
+ 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31,
+ 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5,
+ }, 32,
42, {
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64,
0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c,
@@ -86,6 +93,11 @@
0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
}, 80,
+ {
+ 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, 0x4c, 0x9c,
+ 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01,
+ 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44,
+ }, 32,
82, {
0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c,
0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8,
@@ -108,6 +120,11 @@
{
0,
}, 0,
+ {
+ 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d,
+ 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77,
+ 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04
+ }, 32,
42, {
0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a,
0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e,
@@ -127,6 +144,10 @@
{
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
}, 10,
+ {
+ 0x9b, 0x6c, 0x18, 0xc4, 0x32, 0xa7, 0xbf, 0x8f, 0x0e, 0x71, 0xc8, 0xeb,
+ 0x88, 0xf4, 0xb3, 0x0b, 0xaa, 0x2b, 0xa2, 0x43
+ }, 20,
42, {
0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06, 0x8b, 0x56,
0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, 0x2f, 0x5b, 0x09, 0x15,
@@ -163,6 +184,10 @@
0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
}, 80,
+ {
+ 0x8a, 0xda, 0xe0, 0x9a, 0x2a, 0x30, 0x70, 0x59, 0x47, 0x8d, 0x30, 0x9b,
+ 0x26, 0xc4, 0x11, 0x5a, 0x22, 0x4c, 0xfa, 0xf6,
+ }, 20,
82, {
0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1, 0x2c, 0xd5,
0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d, 0x92, 0x19,
@@ -185,6 +210,10 @@
{
0,
}, 0,
+ {
+ 0xda, 0x8c, 0x8a, 0x73, 0xc7, 0xfa, 0x77, 0x28, 0x8e, 0xc6, 0xf5, 0xe7,
+ 0xc2, 0x97, 0x78, 0x6a, 0xa0, 0xd3, 0x2d, 0x01,
+ }, 20,
42, {
0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5, 0x52, 0x98,
0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, 0x72, 0x20, 0xa3, 0x06,
@@ -204,6 +233,10 @@
{
0,
}, 0,
+ {
+ 0x2a, 0xdc, 0xca, 0xda, 0x18, 0x77, 0x9e, 0x7c, 0x20, 0x77, 0xad, 0x2e,
+ 0xb1, 0x9d, 0x3f, 0x3e, 0x73, 0x13, 0x85, 0xdd,
+ }, 20,
42, {
0x2c, 0x91, 0x11, 0x72, 0x04, 0xd7, 0x45, 0xf3, 0x50, 0x0d, 0x63, 0x6a,
0x62, 0xf6, 0x4f, 0x0a, 0xb3, 0xba, 0xe5, 0x48, 0xaa, 0x53, 0xd4, 0x23,
@@ -214,13 +247,36 @@
};
int main(void) {
- uint8_t buf[82];
- size_t i;
+ uint8_t buf[82], prk[EVP_MAX_MD_SIZE];
+ size_t i, prk_len;
CRYPTO_library_init();
for (i = 0; i < sizeof(kTests) / sizeof(kTests[0]); i++) {
const hkdf_test_vector_t *test = &kTests[i];
+ if (!HKDF_extract(prk, &prk_len, test->md_func(), test->ikm, test->ikm_len,
+ test->salt, test->salt_len)) {
+ fprintf(stderr, "Call to HKDF_extract failed\n");
+ ERR_print_errors_fp(stderr);
+ return 1;
+ }
+ if (prk_len != test->prk_len ||
+ memcmp(prk, test->prk, test->prk_len) != 0) {
+ fprintf(stderr, "%zu: Resulting PRK does not match test vector\n", i);
+ return 1;
+ }
+ if (!HKDF_expand(buf, test->out_len, test->md_func(), prk, prk_len,
+ test->info, test->info_len)) {
+ fprintf(stderr, "Call to HKDF_expand failed\n");
+ ERR_print_errors_fp(stderr);
+ return 1;
+ }
+ if (memcmp(buf, test->out, test->out_len) != 0) {
+ fprintf(stderr,
+ "%zu: Resulting key material does not match test vector\n", i);
+ return 1;
+ }
+
if (!HKDF(buf, test->out_len, test->md_func(), test->ikm, test->ikm_len,
test->salt, test->salt_len, test->info, test->info_len)) {
fprintf(stderr, "Call to HKDF failed\n");
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index ded43fe..433072c 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -355,12 +355,8 @@
* |CRYPTO_STATIC_MUTEX_INIT|.
*
* |CRYPTO_MUTEX| can appear in public structures and so is defined in
- * thread.h.
- *
- * The global lock is a different type because there's no static initialiser
- * value on Windows for locks, so global locks have to be coupled with a
- * |CRYPTO_once_t| to ensure that the lock is setup before use. This is done
- * automatically by |CRYPTO_STATIC_MUTEX_lock_*|. */
+ * thread.h as a structure large enough to fit the real type. The global lock is
+ * a different type so it may be initialized with platform initializer macros.*/
#if defined(OPENSSL_NO_THREADS)
struct CRYPTO_STATIC_MUTEX {
@@ -369,10 +365,9 @@
#define CRYPTO_STATIC_MUTEX_INIT { 0 }
#elif defined(OPENSSL_WINDOWS)
struct CRYPTO_STATIC_MUTEX {
- CRYPTO_once_t once;
- CRITICAL_SECTION lock;
+ SRWLOCK lock;
};
-#define CRYPTO_STATIC_MUTEX_INIT { CRYPTO_ONCE_INIT, { 0 } }
+#define CRYPTO_STATIC_MUTEX_INIT { SRWLOCK_INIT }
#else
struct CRYPTO_STATIC_MUTEX {
pthread_rwlock_t lock;
@@ -385,16 +380,18 @@
OPENSSL_EXPORT void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock);
/* CRYPTO_MUTEX_lock_read locks |lock| such that other threads may also have a
- * read lock, but none may have a write lock. (On Windows, read locks are
- * actually fully exclusive.) */
+ * read lock, but none may have a write lock. */
OPENSSL_EXPORT void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock);
/* CRYPTO_MUTEX_lock_write locks |lock| such that no other thread has any type
* of lock on it. */
OPENSSL_EXPORT void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock);
-/* CRYPTO_MUTEX_unlock unlocks |lock|. */
-OPENSSL_EXPORT void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock);
+/* CRYPTO_MUTEX_unlock_read unlocks |lock| for reading. */
+OPENSSL_EXPORT void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX *lock);
+
+/* CRYPTO_MUTEX_unlock_write unlocks |lock| for writing. */
+OPENSSL_EXPORT void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX *lock);
/* CRYPTO_MUTEX_cleanup releases all resources held by |lock|. */
OPENSSL_EXPORT void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock);
@@ -413,8 +410,12 @@
OPENSSL_EXPORT void CRYPTO_STATIC_MUTEX_lock_write(
struct CRYPTO_STATIC_MUTEX *lock);
-/* CRYPTO_STATIC_MUTEX_unlock unlocks |lock|. */
-OPENSSL_EXPORT void CRYPTO_STATIC_MUTEX_unlock(
+/* CRYPTO_STATIC_MUTEX_unlock_read unlocks |lock| for reading. */
+OPENSSL_EXPORT void CRYPTO_STATIC_MUTEX_unlock_read(
+ struct CRYPTO_STATIC_MUTEX *lock);
+
+/* CRYPTO_STATIC_MUTEX_unlock_write unlocks |lock| for writing. */
+OPENSSL_EXPORT void CRYPTO_STATIC_MUTEX_unlock_write(
struct CRYPTO_STATIC_MUTEX *lock);
diff --git a/src/crypto/lhash/lhash_test.c b/src/crypto/lhash/lhash_test.c
index 63748e7..309b765 100644
--- a/src/crypto/lhash/lhash_test.c
+++ b/src/crypto/lhash/lhash_test.c
@@ -152,7 +152,11 @@
case 1:
s = rand_string();
lh_insert(lh, (void **)&s1, s);
+#if defined(OPENSSL_WINDOWS)
+ dummy_lh_insert(&dummy_lh, &s2, _strdup(s));
+#else
dummy_lh_insert(&dummy_lh, &s2, strdup(s));
+#endif
if (s1 != NULL && (s2 == NULL || strcmp(s1, s2) != 0)) {
fprintf(stderr, "lh_insert failure\n");
diff --git a/src/crypto/mem.c b/src/crypto/mem.c
index df8e0e3..7d73c73 100644
--- a/src/crypto/mem.c
+++ b/src/crypto/mem.c
@@ -147,8 +147,6 @@
return h;
}
-char *OPENSSL_strdup(const char *s) { return strdup(s); }
-
size_t OPENSSL_strnlen(const char *s, size_t len) {
size_t i;
@@ -163,6 +161,8 @@
#if defined(OPENSSL_WINDOWS)
+char *OPENSSL_strdup(const char *s) { return _strdup(s); }
+
int OPENSSL_strcasecmp(const char *a, const char *b) {
return _stricmp(a, b);
}
@@ -173,6 +173,8 @@
#else
+char *OPENSSL_strdup(const char *s) { return strdup(s); }
+
int OPENSSL_strcasecmp(const char *a, const char *b) {
return strcasecmp(a, b);
}
diff --git a/src/crypto/newhope/CMakeLists.txt b/src/crypto/newhope/CMakeLists.txt
index 385cd6a..5d339ba 100644
--- a/src/crypto/newhope/CMakeLists.txt
+++ b/src/crypto/newhope/CMakeLists.txt
@@ -18,5 +18,14 @@
$<TARGET_OBJECTS:test_support>
)
+add_executable(
+ newhope_vectors_test
+
+ newhope_vectors_test.cc
+ $<TARGET_OBJECTS:test_support>
+)
+
target_link_libraries(newhope_test crypto)
+target_link_libraries(newhope_vectors_test crypto)
add_dependencies(all_tests newhope_test)
+add_dependencies(all_tests newhope_vectors_test)
diff --git a/src/crypto/newhope/error_correction.c b/src/crypto/newhope/error_correction.c
index e4ac1c2..2b822b0 100644
--- a/src/crypto/newhope/error_correction.c
+++ b/src/crypto/newhope/error_correction.c
@@ -81,15 +81,12 @@
return t & 1;
}
-void newhope_helprec(NEWHOPE_POLY* c, const NEWHOPE_POLY* v) {
+void newhope_helprec(NEWHOPE_POLY* c, const NEWHOPE_POLY* v,
+ const uint8_t rand[32]) {
int32_t v0[4], v1[4], v_tmp[4], k;
uint8_t rbit;
- uint8_t rand[32];
unsigned i;
- /* The reference implementation calls ChaCha20 here. */
- RAND_bytes(rand, sizeof(rand));
-
for (i = 0; i < 256; i++) {
rbit = (rand[i >> 3] >> (i & 7)) & 1;
@@ -117,7 +114,7 @@
int i;
int32_t tmp[4];
- memset(key, 0, KEY_LENGTH);
+ memset(key, 0, NEWHOPE_KEY_LENGTH);
for (i = 0; i < 256; i++) {
tmp[0] = 16 * PARAM_Q + 8 * (int32_t)v->coeffs[0 + i] -
diff --git a/src/crypto/newhope/internal.h b/src/crypto/newhope/internal.h
index a5473d8..5c82d68 100644
--- a/src/crypto/newhope/internal.h
+++ b/src/crypto/newhope/internal.h
@@ -30,19 +30,11 @@
/* Modulus. */
#define PARAM_Q 12289
-/* KEY_LENGTH is the size of the result of the key agreement. This result is
- * not exposed to callers: instead, it is whitened with SHA-256, whose output
- * happens to be the same size. */
-#define KEY_LENGTH 32
-
/* Polynomial coefficients in unpacked form. */
struct newhope_poly_st {
alignas(32) uint16_t coeffs[PARAM_N];
};
-/* The packed form is 14 bits per coefficient, or 1792 bytes. */
-#define POLY_BYTES ((1024 * 14) / 8)
-
/* SEED_LENGTH is the length of the AES-CTR seed used to derive a polynomial. */
#define SEED_LENGTH 32
@@ -57,15 +49,8 @@
* is given a random seed and a nonce.)*/
void newhope_poly_getnoise(NEWHOPE_POLY* r);
-/* newhope_poly_frombytes unpacks the packed polynomial coefficients in |a| into
- * |r|. */
-void newhope_poly_frombytes(NEWHOPE_POLY* r, const uint8_t* a);
-
-/* newhope_poly_tobytes packs the polynomial |p| into the compact representation
- * |r|. */
-void newhope_poly_tobytes(uint8_t* r, const NEWHOPE_POLY* p);
-
-void newhope_helprec(NEWHOPE_POLY* c, const NEWHOPE_POLY* v);
+void newhope_helprec(NEWHOPE_POLY* c, const NEWHOPE_POLY* v,
+ const uint8_t rbits[32]);
/* newhope_reconcile performs the error-reconciliation step using the input |v|
* and
diff --git a/src/crypto/newhope/newhope.c b/src/crypto/newhope/newhope.c
index 29e189f..c590cfa 100644
--- a/src/crypto/newhope/newhope.c
+++ b/src/crypto/newhope/newhope.c
@@ -46,14 +46,14 @@
}
}
-void NEWHOPE_keygen(uint8_t *servermsg, NEWHOPE_POLY *sk) {
- newhope_poly_getnoise(sk);
- newhope_poly_ntt(sk);
+void NEWHOPE_offer(uint8_t *offermsg, NEWHOPE_POLY *s) {
+ newhope_poly_getnoise(s);
+ newhope_poly_ntt(s);
- /* The first part of the server's message is the seed, which compactly encodes
+ /* The first part of the offer message is the seed, which compactly encodes
* a. */
NEWHOPE_POLY a;
- uint8_t *seed = &servermsg[POLY_BYTES];
+ uint8_t *seed = &offermsg[NEWHOPE_POLY_LENGTH];
RAND_bytes(seed, SEED_LENGTH);
newhope_poly_uniform(&a, seed);
@@ -61,100 +61,113 @@
newhope_poly_getnoise(&e);
newhope_poly_ntt(&e);
- /* The second part of the server's message is the polynomial pk = a*sk+e */
- NEWHOPE_POLY r, pk;
- newhope_poly_pointwise(&r, sk, &a);
- newhope_poly_add(&pk, &e, &r);
- newhope_poly_tobytes(servermsg, &pk);
+ /* The second part of the offer message is the polynomial pk = a*s+e */
+ NEWHOPE_POLY pk;
+ NEWHOPE_offer_computation(&pk, s, &e, &a);
+ NEWHOPE_POLY_tobytes(offermsg, &pk);
}
-int NEWHOPE_client_compute_key(
- uint8_t key[SHA256_DIGEST_LENGTH],
- uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH],
- const uint8_t servermsg[NEWHOPE_SERVERMSG_LENGTH], size_t msg_len) {
- if (msg_len != NEWHOPE_SERVERMSG_LENGTH) {
+int NEWHOPE_accept(uint8_t key[SHA256_DIGEST_LENGTH],
+ uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH],
+ const uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH],
+ size_t msg_len) {
+ if (msg_len != NEWHOPE_OFFERMSG_LENGTH) {
return 0;
}
- NEWHOPE_POLY sp;
+ /* Decode the |offermsg|, generating the same |a| as the peer, from the peer's
+ * seed. */
+ NEWHOPE_POLY pk, a;
+ const uint8_t *seed = &offermsg[NEWHOPE_POLY_LENGTH];
+ newhope_poly_uniform(&a, seed);
+ NEWHOPE_POLY_frombytes(&pk, offermsg);
+
+ /* Generate noise polynomials used to generate our key. */
+ NEWHOPE_POLY sp, ep, epp;
newhope_poly_getnoise(&sp);
newhope_poly_ntt(&sp);
+ newhope_poly_getnoise(&ep);
+ newhope_poly_ntt(&ep);
+ newhope_poly_getnoise(&epp); /* intentionally not NTT */
- /* The first part of the client's message is the polynomial bp=e'+a*s' */
- {
- NEWHOPE_POLY ep;
- newhope_poly_getnoise(&ep);
- newhope_poly_ntt(&ep);
+ /* Generate random bytes used for reconciliation. (The reference
+ * implementation calls ChaCha20 here.) */
+ uint8_t rand[32];
+ RAND_bytes(rand, sizeof(rand));
- /* Generate the same |a| as the server, from the server's seed. */
- NEWHOPE_POLY a;
- const uint8_t *seed = &servermsg[POLY_BYTES];
- newhope_poly_uniform(&a, seed);
+ /* Encode |bp| and |c| as the |acceptmsg|. */
+ NEWHOPE_POLY bp, c;
+ uint8_t k[NEWHOPE_KEY_LENGTH];
+ NEWHOPE_accept_computation(k, &bp, &c, &sp, &ep, &epp, rand, &pk, &a);
+ NEWHOPE_POLY_tobytes(acceptmsg, &bp);
+ encode_rec(&c, &acceptmsg[NEWHOPE_POLY_LENGTH]);
- NEWHOPE_POLY bp;
- newhope_poly_pointwise(&bp, &a, &sp);
- newhope_poly_add(&bp, &bp, &ep);
- newhope_poly_tobytes(clientmsg, &bp);
+ SHA256_CTX ctx;
+ if (!SHA256_Init(&ctx) ||
+ !SHA256_Update(&ctx, k, NEWHOPE_KEY_LENGTH) ||
+ !SHA256_Final(key, &ctx)) {
+ return 0;
}
+ return 1;
+}
+
+int NEWHOPE_finish(uint8_t key[SHA256_DIGEST_LENGTH], const NEWHOPE_POLY *sk,
+ const uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH],
+ size_t msg_len) {
+ if (msg_len != NEWHOPE_ACCEPTMSG_LENGTH) {
+ return 0;
+ }
+
+ /* Decode the accept message into |bp| and |c|. */
+ NEWHOPE_POLY bp, c;
+ NEWHOPE_POLY_frombytes(&bp, acceptmsg);
+ decode_rec(&acceptmsg[NEWHOPE_POLY_LENGTH], &c);
+
+ uint8_t k[NEWHOPE_KEY_LENGTH];
+ NEWHOPE_finish_computation(k, sk, &bp, &c);
+ SHA256_CTX ctx;
+ if (!SHA256_Init(&ctx) ||
+ !SHA256_Update(&ctx, k, NEWHOPE_KEY_LENGTH) ||
+ !SHA256_Final(key, &ctx)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+void NEWHOPE_offer_computation(NEWHOPE_POLY *out_pk,
+ const NEWHOPE_POLY *s, const NEWHOPE_POLY *e,
+ const NEWHOPE_POLY *a) {
+ NEWHOPE_POLY r;
+ newhope_poly_pointwise(&r, s, a);
+ newhope_poly_add(out_pk, e, &r);
+}
+
+void NEWHOPE_accept_computation(
+ uint8_t k[NEWHOPE_KEY_LENGTH], NEWHOPE_POLY *bp,
+ NEWHOPE_POLY *reconciliation,
+ const NEWHOPE_POLY *sp, const NEWHOPE_POLY *ep, const NEWHOPE_POLY *epp,
+ const uint8_t rand[32],
+ const NEWHOPE_POLY *pk, const NEWHOPE_POLY *a) {
+ /* bp = a*s' + e' */
+ newhope_poly_pointwise(bp, a, sp);
+ newhope_poly_add(bp, bp, ep);
+
/* v = pk * s' + e'' */
NEWHOPE_POLY v;
- {
- NEWHOPE_POLY pk;
- newhope_poly_frombytes(&pk, servermsg);
-
- NEWHOPE_POLY epp;
- newhope_poly_getnoise(&epp);
-
- newhope_poly_pointwise(&v, &pk, &sp);
- newhope_poly_invntt(&v);
- newhope_poly_add(&v, &v, &epp);
- }
-
- /* The second part of the client's message is the reconciliation data derived
- * from v. */
- NEWHOPE_POLY c;
- uint8_t *reconciliation = &clientmsg[POLY_BYTES];
- newhope_helprec(&c, &v);
- encode_rec(&c, reconciliation);
-
- uint8_t k[KEY_LENGTH];
- newhope_reconcile(k, &v, &c);
- SHA256_CTX ctx;
- if (!SHA256_Init(&ctx) ||
- !SHA256_Update(&ctx, k, KEY_LENGTH) ||
- !SHA256_Final(key, &ctx)) {
- return 0;
- }
-
- return 1;
+ newhope_poly_pointwise(&v, pk, sp);
+ newhope_poly_invntt(&v);
+ newhope_poly_add(&v, &v, epp);
+ newhope_helprec(reconciliation, &v, rand);
+ newhope_reconcile(k, &v, reconciliation);
}
-int NEWHOPE_server_compute_key(
- uint8_t key[SHA256_DIGEST_LENGTH], const NEWHOPE_POLY *sk,
- const uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH], size_t msg_len) {
- if (msg_len != NEWHOPE_CLIENTMSG_LENGTH) {
- return 0;
- }
- NEWHOPE_POLY bp;
- newhope_poly_frombytes(&bp, clientmsg);
-
+void NEWHOPE_finish_computation(uint8_t k[NEWHOPE_KEY_LENGTH],
+ const NEWHOPE_POLY *sk, const NEWHOPE_POLY *bp,
+ const NEWHOPE_POLY *reconciliation) {
NEWHOPE_POLY v;
- newhope_poly_pointwise(&v, sk, &bp);
+ newhope_poly_pointwise(&v, sk, bp);
newhope_poly_invntt(&v);
-
- NEWHOPE_POLY c;
- const uint8_t *reconciliation = &clientmsg[POLY_BYTES];
- decode_rec(reconciliation, &c);
-
- uint8_t k[KEY_LENGTH];
- newhope_reconcile(k, &v, &c);
- SHA256_CTX ctx;
- if (!SHA256_Init(&ctx) ||
- !SHA256_Update(&ctx, k, KEY_LENGTH) ||
- !SHA256_Final(key, &ctx)) {
- return 0;
- }
-
- return 1;
+ newhope_reconcile(k, &v, reconciliation);
}
diff --git a/src/crypto/newhope/newhope_test.c b/src/crypto/newhope/newhope_test.c
index 5d96007..f3e011f 100644
--- a/src/crypto/newhope/newhope_test.c
+++ b/src/crypto/newhope/newhope_test.c
@@ -22,34 +22,32 @@
#include "internal.h"
-#define NTESTS 1000
+#define NTESTS 1
static int test_keys(void) {
NEWHOPE_POLY *sk = NEWHOPE_POLY_new();
- uint8_t server_key[SHA256_DIGEST_LENGTH], client_key[SHA256_DIGEST_LENGTH];
- uint8_t servermsg[NEWHOPE_SERVERMSG_LENGTH];
- uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH];
+ uint8_t offer_key[SHA256_DIGEST_LENGTH], accept_key[SHA256_DIGEST_LENGTH];
+ uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
int i;
for (i = 0; i < NTESTS; i++) {
/* Alice generates a public key */
- NEWHOPE_keygen(servermsg, sk);
+ NEWHOPE_offer(offermsg, sk);
/* Bob derives a secret key and creates a response */
- if (!NEWHOPE_client_compute_key(client_key, clientmsg, servermsg,
- sizeof(servermsg))) {
- fprintf(stderr, "ERROR client key exchange failed\n");
+ if (!NEWHOPE_accept(accept_key, acceptmsg, offermsg, sizeof(offermsg))) {
+ fprintf(stderr, "ERROR accept key exchange failed\n");
return 0;
}
/* Alice uses Bob's response to get her secret key */
- if (!NEWHOPE_server_compute_key(server_key, sk, clientmsg,
- sizeof(clientmsg))) {
- fprintf(stderr, "ERROR server key exchange failed\n");
+ if (!NEWHOPE_finish(offer_key, sk, acceptmsg, sizeof(acceptmsg))) {
+ fprintf(stderr, "ERROR finish key exchange failed\n");
return 0;
}
- if (memcmp(server_key, client_key, SHA256_DIGEST_LENGTH) != 0) {
+ if (memcmp(offer_key, accept_key, SHA256_DIGEST_LENGTH) != 0) {
fprintf(stderr, "ERROR keys did not agree\n");
return 0;
}
@@ -61,33 +59,31 @@
static int test_invalid_sk_a(void) {
NEWHOPE_POLY *sk = NEWHOPE_POLY_new();
- uint8_t server_key[SHA256_DIGEST_LENGTH], client_key[SHA256_DIGEST_LENGTH];
- uint8_t servermsg[NEWHOPE_SERVERMSG_LENGTH];
- uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH];
+ uint8_t offer_key[SHA256_DIGEST_LENGTH], accept_key[SHA256_DIGEST_LENGTH];
+ uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
int i;
for (i = 0; i < NTESTS; i++) {
/* Alice generates a public key */
- NEWHOPE_keygen(servermsg, sk);
+ NEWHOPE_offer(offermsg, sk);
/* Bob derives a secret key and creates a response */
- if (!NEWHOPE_client_compute_key(client_key, clientmsg, servermsg,
- sizeof(servermsg))) {
- fprintf(stderr, "ERROR client key exchange failed\n");
+ if (!NEWHOPE_accept(accept_key, acceptmsg, offermsg, sizeof(offermsg))) {
+ fprintf(stderr, "ERROR accept key exchange failed\n");
return 0;
}
/* Corrupt the secret key */
- NEWHOPE_keygen(servermsg /* not used below */, sk);
+ NEWHOPE_offer(offermsg /* not used below */, sk);
/* Alice uses Bob's response to get her secret key */
- if (!NEWHOPE_server_compute_key(server_key, sk, clientmsg,
- sizeof(clientmsg))) {
- fprintf(stderr, "ERROR server key exchange failed\n");
+ if (!NEWHOPE_finish(offer_key, sk, acceptmsg, sizeof(acceptmsg))) {
+ fprintf(stderr, "ERROR finish key exchange failed\n");
return 0;
}
- if (memcmp(server_key, client_key, SHA256_DIGEST_LENGTH) == 0) {
+ if (memcmp(offer_key, accept_key, SHA256_DIGEST_LENGTH) == 0) {
fprintf(stderr, "ERROR invalid sk_a\n");
return 0;
}
@@ -99,34 +95,32 @@
static int test_invalid_ciphertext(void) {
NEWHOPE_POLY *sk = NEWHOPE_POLY_new();
- uint8_t server_key[SHA256_DIGEST_LENGTH], client_key[SHA256_DIGEST_LENGTH];
- uint8_t servermsg[NEWHOPE_SERVERMSG_LENGTH];
- uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH];
+ uint8_t offer_key[SHA256_DIGEST_LENGTH], accept_key[SHA256_DIGEST_LENGTH];
+ uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
int i;
for (i = 0; i < 10; i++) {
/* Alice generates a public key */
- NEWHOPE_keygen(servermsg, sk);
+ NEWHOPE_offer(offermsg, sk);
/* Bob derives a secret key and creates a response */
- if (!NEWHOPE_client_compute_key(client_key, clientmsg, servermsg,
- sizeof(servermsg))) {
- fprintf(stderr, "ERROR client key exchange failed\n");
+ if (!NEWHOPE_accept(accept_key, acceptmsg, offermsg, sizeof(offermsg))) {
+ fprintf(stderr, "ERROR accept key exchange failed\n");
return 0;
}
/* Change some byte in the "ciphertext" */
- clientmsg[42] ^= 1;
+ acceptmsg[42] ^= 1;
/* Alice uses Bob's response to get her secret key */
- if (!NEWHOPE_server_compute_key(server_key, sk, clientmsg,
- sizeof(clientmsg))) {
- fprintf(stderr, "ERROR server key exchange failed\n");
+ if (!NEWHOPE_finish(offer_key, sk, acceptmsg, sizeof(acceptmsg))) {
+ fprintf(stderr, "ERROR finish key exchange failed\n");
return 0;
}
- if (!memcmp(server_key, client_key, SHA256_DIGEST_LENGTH)) {
- fprintf(stderr, "ERROR invalid clientmsg\n");
+ if (!memcmp(offer_key, accept_key, SHA256_DIGEST_LENGTH)) {
+ fprintf(stderr, "ERROR invalid acceptmsg\n");
return 0;
}
}
diff --git a/src/crypto/newhope/newhope_test.txt b/src/crypto/newhope/newhope_test.txt
new file mode 100644
index 0000000..20d9c03
--- /dev/null
+++ b/src/crypto/newhope/newhope_test.txt
@@ -0,0 +1,206 @@
+# These vectors were generated by the reference implementation, lightly modified
+# to emit the arguments expected by BoringSSL's implementation. Most important,
+# this bypasses the random number generation and key whitening steps, which
+# differ between the two implementations, and focuses on the common and
+# interoperable part.
+
+InRandA = 420c3c6b581c9f4466cd807170629a84b681aa1063a341062aa37956d70ae68befe87727d99fc92e7c29e215c5f13b722587c0d675d79190096a280a192aa4f4e65f982a59130f9266d22e4e8cd9ef195b45813cc8233a253f8a752548529b463226d3ebcf422870739095d567e7f6a512630c6766aa2668e8477a78865849e0b16bd0e4919d1d7da0c5d8005104e6e4d6b6855590e5c6a4e649dc4a0ef5b5ae140251c26b8c32001ed7ad13cda56b94c3294010c84ab54fba1888686384d40e310eb6363b9b23057782805f62e378c27ab98f223324add836772c3e81b8212bdae3c864a59c6e63a72327c42e69711ffcb3b6d04c00293e91d76c50ef9fdf33fd382c769835e0bd4821f65e66797075915f138a6b49051ec406ea750d50384d5d4ce2c9c536d45d3f7a704c6cae8d82f07bc5590f908b308d8c1b8d5e9ee3931d2d55145236f10688ae192f531755b310ccaf3891125af5decb1adbaa85224c47b73c3c8d7fa92ae1635d03d64d73c600486b336c8a511a7e1675a3dec9fcde711601ab73fc4d788d9de3d9f8fe185b87e920550d705f89f3188d1a69f9efc036666d7f5321d71b7eb5436f9bbab400884c08c59bd0a9458fe1e2b7b73d1e2a5ca4b21be5d48125613f0884d15eea9393a4dbb225bb66e5da5d3d98fb20c10761fe0efa5cf00498b29a662df8f8f17a4de3851fa425552aca9544aa89c89981d0f85636f67a862ee4eb844579f5c0f1b9bc0d647b15223aaf0c9edce88815273a0b064772b46dfc933a4dd8db90aa585097b2c6c0564ca90164a43d798403426544bb66637597dcf92a747b7b5ce1f7ba266f5c482d73e8f8b27920a627d1e9154ea745c213988f405ac2a19ec0a37388f8f67cbd9bd7c64222bd14a664eb5146a0645ccb542c21bde65bd2093a9b7622158a9504b0f230276ae1afd7ba6d7267a546e8c08dbdf168be439fe9263427752658d225a2062202ad567235685ab76585b04151f65a63016c9981a635116ab7766789a066d40d122805178f6840ec225e22dc5147d09ed9a53d8582e0e15b25b9a163178314205722adad9e9798c1306adcc002ab59e146302f0e08124945a18bfb10666c6b429124c5f5e61b98e2e8e6defd58b0dd377924f6a8d788c5887c306cf5864931b171936ae80f4911d483ccc804c112a0bc88a80621b0a1a9f8dd9f44f4f84687c54b533b4614d82741fb78006cdd137c422e5441c4513de3ae8a4cf4db9c679fb90c986f97abe8e1060d0392b668de010a0f9f1447445557c2f069f76e285a94f257ed368a70ae2922926c9452c01fa1fad6aac2db43daa67d68b31f3889acd8649a0c7389336d5d6904715984020470493a18fc84aa2bc61e352184714d0f5f46df0e3fa5a3a8a852f981135399d5df8d44b46abd6bfc164c4587e2a79ae255dadd09f964809c57e753105e2575bc987d924f3d7b9bd90747d1b61ce06a56ba60f1813463c418a3ad09821846b6c2b5057aca1939f255acca579b310fc91d112b6669088a66299fb227a7ca15c686604bf47c10d6c4aa1b56d03a7fe23217294866ea820646c372579955ccc16471159b83d8ba016c114c10c961221c28a956fce40806780e043883349d8e8baf8f308610be18c6103a454c8f30cb9465cc9d3be2935e347fc691a503b4c92d311d29abba18497283e646c3ab030a1dcfd295a29bc6517dde0cb295ea89639824c7fa99a968dd7a1aeb1983925258f3d7853517d6b5db4060783b27c949b80989561303d95cea8059e5e7fa520c2d3141fec406d88535d7060b1f4ebbd08cc7687e3d83040c97005d450f4405c557c9e0b71f047ffac98aa6ffea1f5598223163ea918ad5319bb919d9633254156aae2c527521664a6e54abae312e704b0fd335014a66ad8d22bc9862a0d239a9deb2a011553fe38f0100be4705f0a25580ade9b4b24705264326030b06d512f2d4c815ed36827d1078c57882369c1c2129ee1e1f8d51d45ab0d573820f9618fc2a45c25769f99ca51554efa5d086d039655b6a23874e18d02bb3b28a2d43a293b7e0b32d5c5000a222ddb92f945f0bc40a49427d8ab084de1e89cf9c70c9d39e1f902844811bca5775ad2f73ca884314846d474ef957a4d98eb4c98eb94f9cc6b6edd101b78d9a4b5986626752363b3e2c030bc5b0eb49e558d55ebaa92e1627cc166790aba847725bb487d08930714922d764eeae45c2a4157311ae4fdf4995c7781559cd149124a86843682df807d60ee922471be84d830e1cbed3055e281a19b855121977435f057000329a9a0458e51fca4dac4fa36303c02967d7551759b153f653ca653f7016d427ed7bdc6c49896741853148338688a62745e144c2840cb2fd22aabacbd6e7378074da23a68689664d5e804307beeaaef7454101d34c28441ee8456384a752992494ec2c4d4381c3ba3441357d8c545baec472eac58ca0f665992d64e1e1342c639863a09779e88349afa0418f667aebc22b92e7722e956793653992ab5768c170e8e564
+InNoiseS = 2e6f8b71cc4ab67f9816984bce92458263794396bd66ec320565045122d1a427408678dc565386f2f993fb14e1422366ade64536811cf943271a185b747eb2d62d13b6d5028b755179d538f915bce3fd7b1e4a2d6d4e6bf29d06554a010502a70a516fd7c6564b323723eb2a8570ceb98603208a349c685ddc1366b55da84b801ec8a5ea3882c6668a394d98b4a1036946ad618f1cf427a9a05676dbc615acb25c53cb83a117964065618b213f1e4851dcf2663638315859704a44b5785bcff0f7fd2a6eff292c2950fc817d5a4b7bccd014d95c4565abf86a6e8cd2b32e9594482e46e505e57932d30ed6639853c74ff2138b122fc46f54c183f11858ebdb2b871ca916109922ea7e3556e63d95028c62d2652c0b9f35af121da795121d5872043711b6a56639e169eb41d9780f6462d140cc7ddeabaed49d9c4e4284d07132d92fc39b98a5c5cd278023e3a1fdfd8875e0a7a2effe4dd3e2897be58520d99c36308caead22987d959a480db2cb76488cf14e4c9bc92887d5107c6df91ab69abd9a11a50a91b41138c22ed8281c58ae49a512c5c203f563bc7950147ded01a7078f8ebeb324e6e305295522df6eb91035ace84be894fa824fe6415b664e1873f3a4ace6f2f5aaad6d121a489688d3c7d307ab2414b79822305f8842339540f42e089df2c1bf0620ec7a306f36b2d4b27c72439875109d6dfbcbc43358f068108555e0f7e2b047140a589cc4be815227c9a29eaf7914e381513e5d9d00fd3296ee894c9641d2b00e79f62f2f62d8b5ade28bdc26fc6e3711c2fe3694ec9c461e28b5524c59fd2a0ff2ff599c04d8b882f23a5ecf917d080130214c262d3826a616f093acfb64fc6c9c82c36708a628c652cbf0ec8911e902caa2e805268b8144e595f1bbfcccbdb8b7a64af5e101fb113e8abec686c1ee7c2a539593b6f80549be9d8b814f360d71d6bbeb569db2e94d52405b8895da9a02495a01ac93ce9847503e107f6b81fa598c539409a7873af606315f82b9eac49b68a6b9f214a84d58083e067edd52d167d1ea65969cb7a40299062a732d2fbfb26139570c97151187741b9d020113651e2ed0cc279893348b2592531a6a1874c317ec638b42339890205705a52099aebb745f2689c54954f32620598048557aafd940650451b5a0e246f683e507d72080ac8b92ea399023f4ca93dbfb54637cd0301171d124d34a48f0db1a2c5f5ae5725271249a1198347b724b22817f649fcc4b02ec374b10b0524e08a9a41771741c2234d6324856c1aad86051d831e62333162aa2a32905d618e76c353a496829e3cc7a07a66b975364907e6da5d983e65004a392b1b8ce692ce1b24030a6756d60f6972a2e8aefef7d761e15645043d7720d1d756d1713b06c16712716f9204e29b2f8a45172b0498328a0d1b8a7a664079123b9851059add99e2209b4911359f42659e24def8cda7bd4da1ae34fa7669f8218d786b8b9ca16520ad54b5553b92cc99541e6f51caec0176729687977979c4726abf5321344edd66f0e03bea512c6dd54b9d6c80f932d4e58e75f8bf97b3e0e15e412704b691334d10541cb7037eb653f084d96afa4c3ab78d2468e4242fbdb24b05db81815280ef458871dbad0c938719a855d80c3392506ac313b755e13e6d587d0e9c11775e8d588cb048f60c64c564024570b474ab83d9a8d2fc7a0eb465803aa2b643a15c59e3f156ad133aca6c9c968d1493d601fa59591c8d0aea49a697a2e7426c36d24f343b292ed2201bb1f2638a2923c8220e0321f081cf465f98b5f69ff3ab78574646e828487f40c6f4d7c28be30391f083e78640a28f7066ab9aac6138a106dc9c3497c38efbcf007062de29a4b7a38397d0305a04b5fca5f51ddf6b026dd9aa1ef88a394aea54f20be3800d488af630522dfbe8306a10f7178ce06c097a3d7ca62b7169365486b4a23c52c1017fd4513268e4a20c5702b8d624d028098bed550389ad8b987c26a0a0bca7478f3c8b5a4c926a2c10c34662de11b7a260539727e75304c0c28b15e7cc6105da18ab6ba720eb6e0bfce3b220f7f68bcbd845ac8c7d166a837c46714721c227b5bd263d2a6fd54c7a7f26267597953943ef22cb9607f65eb829b5e4ae84b1a263c69a77b16827a7dda44bad604831dee420100d2ab45d822cca55378fffc7377d57c381dd92661eb2271599a68f4a5c4811ad16f27c16103ee9e7a802e9011f7fa542f4799d65c448245424c9698fd3d10cde7cbcf6885f6d630e1a7248ca7ab16b6cd8d262e1cc20243d46c5e1c2aa508c22b6aacfa0a4b8c60e2056bc3365c5f3acc45c61b2d12778bdbaac7744423af4b32f52eafd9a7835400ae42d424d8a46c0c03acefe9a684f903f5c8761616cc82e05de264cb7278529f3c3255b8095f64a2f3f5299b3365efb356d856b04731a8c77845336db8c1952a6ee91b64578d620af6052a700989a2338041e767098c996ce97a6f9907c284bd4acd688937617003afb95af11447629d71222919bcd5b4204a
+InNoiseE = ec87b3c754016deb580970f1a4368e1befd757ba3720cfb430599d152515761942bd92cad5a6aaefbe10888f6cb7d4112044c2c1e973a9376cab5d17e746ade95e0ae29cceb213494a98e9985409452d91a0794ecbd947039459aed8227cdad69a175666302007ba6f195968c8244c251bc84e8aaf8298654c71b852e253ab286d74d95c934344c85123b44c59ac7772e5baa0b827b197b0b6763016e1765381bb0127f478989c7ab95ad88b5a3c5480a35b16ca8c1fe061727398120c774a8b13307804f06aac48d8b1867265275143d1a126694bf6e5d25b25850e4428593f2cee882b2dc914c7158a6604de4ff643db6a9c286a585710e004b83e63049ef0c902585d55ba708cf292576430b77a86ace85bcd72a95856af4491674a750b438860c7805ea177ac98f1980e98bc1f92a3c4d284e5414689f26d265d23f02322d0a0c8eb60a4f31227e4c5467494a850dc027ea59616135c2db689294ab033201a4298fd02126866a38d814b959bee0a1bd045ca6f47f57a663019196ae7124c357364cc1140213b1a0c9c3651f882f050fb450948710211bb520b3c27d4a078e8bbe4b4c829b1e11199ae5d9ce441ad9c4d456664ca38d15607afcd66b0dc1635e6f0a744c8a0710bba49d00a860a46e3180bed85d8a134a7676ea65d420122e14488d21190092da666a225721213da7cce85ee51ad303e418bdda631d96e94958d749994699bafc6586c652d82e3a2e8d5e9a836606e265ffa5b292db623b8cc3690a90f81d8c59ef1097eb2fe5144d26f3fb30b2253e9d39d708c887665ca2ea9d9415b2aee7adb12794a420c21752a2068bd98ba55276405030684eb3dad424707f7e95b552a8f1aa6696560822f4e4ac551855d4a8d65b334a1174adce5b941a5249ba3e5393fd099cd9dd5589761100ae40c275c344b4d9fd7e95136bb8609f6e458ac04ef59fd22a7d23b6ca3f5b0ede97ee8c1f2c70bee77c213f611a1899d191c302606c61e09d562a3f8e6899398ef3918c259d6950da390aa57e9112652035a03772a422359fe570ce6168374ed3e1ccf299633ba0adde0eec22129bb354fccb7974600ae8232b698492acbe28024671a7f03a0a196bf790ec3d217bb279eb4f6ea041b62d054e19a39658cc671bf15b9d4af6a5a482c49ab18419630afa2b714528ab02326633f70008c8ad77a2a8e4cad9ee226b8ca58f8189508a199129b097d90603536412eec9af1327bc46efac7ffc42894837de833993562616f61cfc8557b5f04b2e5e56ea0e5fb2106e834b70bdd6a1d45a19c020190fd069e950fdd13982994f841f6a4563a470ad65659984dbda7802209dac690ec1d33a099c676d0c0b6866de62b35250683bd363e38804ab077a61944529bb022926c228cbad041f0bbd8de4de790d01dd651ce133d641f45109d3099b82b905fb66aef2111929609e8f4a228262596524be270408c1f7a4d0db3ccc44c78ecbea76fadf63599018f22e99645b07847ee187e71ef72216d52427ac20858efafa1d89cc834bdd8b7f102ca3a08a331f8a2896d148bab795f8d098122d2c640a58221297a536b262bd0e9d4b184d81465ef9b2ae12ab0a71fb2b94bf097db1a2feb7be25de709c1e7e5ceb72c401f0713501cef040c5bccb64146c2cc48b6010496868b10a8fc9ac5438b34ba6970af8cbcf59ae07a72c0a5b0d31663f7212faa22954611abb43380cdb3e8e91ee2194d81efcf20a37b9c5e2f457babe49200772deb96d00857c06d3c8412c844d28654a1a77e2885c044c32581a438df44a1c7f567086687f0fc795fc20e7861b5fe26cb10d2b329ccad97e858e0720233844368ca503a58fc48ac3712bc1c01490c64b37a2f1f9f65a78c983b628568c025c83031e222895bf00decc3d99503887c9080a6269de9ae332a09e2f6be0c8ae34d70ef695b5f17b9f4f6a60443945e6ae69ea4750f65302d9d6a7cee8ae01ba54207645ed0e4e556badf3ac1d82c2a8e0042734c7a275c2a61c00f728eaf90830bcbc5737c22d17842252cfb8570f55ed18eaf5218825a7d81bb00eee79cfe92ee82189712665bfc593ebbb84d420ca07d0594ceb400e75be3774eac5e4925967c5a2e04625f69f5a8a5981dd1173af8a8f42671e73d7684feeec1658f147a09569825e90993bd5cf15491cc06ef6de889dd78bd1d22fcb874843ef6e59766095610d5d247f9be1ab709e621b476923a2647fca9f53da8c78b6d08c179b1be2c2d0a304960922d6ac8e2f61f9c08600bcca591f8de995519ee355a53ba7108f44a6297d2b931d07a96c9c0866c44843f06814b8ae00c5a598f03cd3a5fe5a9f615de8da4f0c4d6d6e2242a62e776033b2061e6af2468fd65a580854afbea8c82c549a0276ee0723bc5233ba4c506b365709b9d15403522ce98de9d3aaf8da8813b7111582b75fab1115d9cedbeba735e3c8de4a7730aac543b0aacf39620a00b686f79b5b48c63a54005c5010b92e980a8e275ac24ab163a14b8acd16a41c7da648f
+OutPK = bc8dcdf734ed3f2091a723a0b57fd80c53ba783e9b8f285214dc9805d528f47a6d622c54e160405fcd098ee293f34936927e9e33c7845d33e4d511c4ec8569af4d3af4f3c8165d9262993ef68dc74f2e0a963561320b6c40bbd639526201c952dc5f6e56c3975dccbb6968a566a35629f4867c88700a774dc26025302062db4d391bc4c8adfa270bb609122c45e35dd7cdf25e7b09170891fc0662e5b254731473a4ec5945177d817ad769670a4c6537839e720256b5cd5c8548c3de36d38a543a4f225ee2d526738436afdd80fc6aec16529853a20b1494a1c4589535d89eb9eb9c0008ae519e5d101ef4239c8f78d96746d196568c528bda9d9895b61633cbdc99a44fe794b43b600b4116bd617bb95e935b798a212a305f6e608bdcd09bd725ac7999784f06a05cf156ee93a78ad9c5af0e593be56af5677eb70195f025a87822c443d2a041ad4ae7e37a4a65651a056e3a667c720cd504dfe4ad4d54ba89a5c41e545f13ce34e614bd32029939a2ad98bacb066c71a01a462c45ec9564884649a1671797641895ab0c69d3664e4e42af843d5a7ae8ceb369f2405e6c111f70d7356c57a98086d9996650d0e89a64e003454c5857dc0183cbc4b305a24a5f8c8d8cc8c8b54ee9a2d73a2daa96a9c2eb3ba6298594d5a9ba58a587ce1cc4f34afebcfaee9858112d222106030b5d2923780df294ecd589df9039b2aeac35e640ac90d4b10d1e6b6f903df28898c84f759d3c9830aa6e4934f2a88f5bec4488a6021440271b986d44764825a1c7a62bac0e6812a0f64c10e21a1810ee19f36f931742747ec9997741cbec6def679364c1919307d4361aae1a880706061710417e144a1495f13e372268e2ba017a99d3c9025883445d8f148da36fd55a311e9f8104a974cf993c87029ca7d5ae1fb5cf0d4e6fc985fa6a5839d3d7addacd1c75cc5f903358c01e2c4f72499bfa58646a26e13c152b27e8acf3aa85adfc53b5009459b6b6e6e6311bc9732b823e084708938516d6b9451d4b1799f3a1c2ac60f70cfb8b92c55eb8d81b06bca5a64393a6d7386e1f1e20530310d8ab3f6d0d5bf06a7715ce157a9f622f164d6b1566ba82abaec91be9ac3ebe5a8b75f349600ee12e2685d505548cb66008b8318705c890810e4d04e784c2913c026642344647f8b97ea6ff7a0376b135dd1f9332887316e42da8ed32b42b1a42e4c5b4160457b2d898cd41b5df0ffa970183a59b26272002b2836866d32f696623a768116c2691d1666ac882b974e3cdd0d08e5e7d210db2f5d7349c5b130038e09e0a8fad2db1111d731944b3d6692671490a6af15cc69c1a29863b22e8a6de87239539ce0ceca541e6c4515821af61103ede0e3156ac71d3a29e3a8e4b84f4850b20d193ea55dc073a9d83c846ce3f55dc083ae93612c02f72117a4693a4c023f8e824b5500f61509094a89bab8d531ec8aef5291ac9ebd44cb0a2c9413274007488f32b12f459c5e10b849004902ac03273ce86a7a91a17ab46590ace5b4bd5333d8f5481e4e05fc94b5e68db79e30e6c08dd3c0578ec4fc0c55d6850313c8215b052a22d2f778dace1e33441406d5772a28a7ed892b867dd4c9e2818ff259e115bd4026437cffd7ef65dc4044e9830e64149e34cb695cddde34336b8bb40c00c15bfbdaded1593293a6d166ad39d8336b1178ed121bb13a98ea31a46d9f4817b3d8454e26bf405a32f51c15d1d419112ccd2af006e415bd5e3cfb67aa9d5ec64fc599b026934f7dbba1f4c120558364057a5cc5f901b2c0d411dcbe02342bfe32416277e1937e69a3e87f6ea0f214534f278a022231354653e3537e72b75923be4472a1275cb3cad4cf361dfe6be8c6fec69bd824f2a4d84878acac5e97287805279bdd92f48e8d1342f1a3cd8c711f837be0f188ed54071f9441b2b11b0acbc0c3510b4c4c5beababd27c7176f00c2396a3b81ecd9135e1f0171cd97976415671b28d63cb67996626f088f2de42125989581086dd29e87c6f4ee507537e2c9ac8e0f9827b9454770add19de3c3f8c093e85a93e9aeba6e157e38d8a42cb8893efc9ac78d79b9a02fc0500e4466a2942109de0f351ae313a534db4762802220ec7efd5bdc62ec04810e950dc16dc9f0ccafb6eb4a75db6f412ad3575094e8175290e70688f6780be7388846fc98a394c28ce11f51bc8b1cf54df58f06d4aebe2e185b2b48dd2d39483fe584b56dc1509c26ca5f89aacd27ba102232695a74e0613f91ceba0b385a0c59e60f24fce013b0646ba4d1e38e86a0962b065227b1f2a82e9d594e4125fa7660e7683a9a6d5f8ca7f9fc11f98828ab5f0edf94370e951c5e044190d367e4056ee54b97199d067505258aa5684db9dd24ea1ad52cc8744f5b2683584c131d244311a55985bf09c75d7237ab0607e334d6626278fb21a0150ebaa7ab0b78081bd43c706731ea3ccf9678822ade40320d51c040c9f1072c42562d18c894725ee7105da06e5b694420a639d67f8a1062491620238b0199b1a917b219049
+
+InPK = bc8dcdf734ed3f2091a723a0b57fd80c53ba783e9b8f285214dc9805d528f47a6d622c54e160405fcd098ee293f34936927e9e33c7845d33e4d511c4ec8569af4d3af4f3c8165d9262993ef68dc74f2e0a963561320b6c40bbd639526201c952dc5f6e56c3975dccbb6968a566a35629f4867c88700a774dc26025302062db4d391bc4c8adfa270bb609122c45e35dd7cdf25e7b09170891fc0662e5b254731473a4ec5945177d817ad769670a4c6537839e720256b5cd5c8548c3de36d38a543a4f225ee2d526738436afdd80fc6aec16529853a20b1494a1c4589535d89eb9eb9c0008ae519e5d101ef4239c8f78d96746d196568c528bda9d9895b61633cbdc99a44fe794b43b600b4116bd617bb95e935b798a212a305f6e608bdcd09bd725ac7999784f06a05cf156ee93a78ad9c5af0e593be56af5677eb70195f025a87822c443d2a041ad4ae7e37a4a65651a056e3a667c720cd504dfe4ad4d54ba89a5c41e545f13ce34e614bd32029939a2ad98bacb066c71a01a462c45ec9564884649a1671797641895ab0c69d3664e4e42af843d5a7ae8ceb369f2405e6c111f70d7356c57a98086d9996650d0e89a64e003454c5857dc0183cbc4b305a24a5f8c8d8cc8c8b54ee9a2d73a2daa96a9c2eb3ba6298594d5a9ba58a587ce1cc4f34afebcfaee9858112d222106030b5d2923780df294ecd589df9039b2aeac35e640ac90d4b10d1e6b6f903df28898c84f759d3c9830aa6e4934f2a88f5bec4488a6021440271b986d44764825a1c7a62bac0e6812a0f64c10e21a1810ee19f36f931742747ec9997741cbec6def679364c1919307d4361aae1a880706061710417e144a1495f13e372268e2ba017a99d3c9025883445d8f148da36fd55a311e9f8104a974cf993c87029ca7d5ae1fb5cf0d4e6fc985fa6a5839d3d7addacd1c75cc5f903358c01e2c4f72499bfa58646a26e13c152b27e8acf3aa85adfc53b5009459b6b6e6e6311bc9732b823e084708938516d6b9451d4b1799f3a1c2ac60f70cfb8b92c55eb8d81b06bca5a64393a6d7386e1f1e20530310d8ab3f6d0d5bf06a7715ce157a9f622f164d6b1566ba82abaec91be9ac3ebe5a8b75f349600ee12e2685d505548cb66008b8318705c890810e4d04e784c2913c026642344647f8b97ea6ff7a0376b135dd1f9332887316e42da8ed32b42b1a42e4c5b4160457b2d898cd41b5df0ffa970183a59b26272002b2836866d32f696623a768116c2691d1666ac882b974e3cdd0d08e5e7d210db2f5d7349c5b130038e09e0a8fad2db1111d731944b3d6692671490a6af15cc69c1a29863b22e8a6de87239539ce0ceca541e6c4515821af61103ede0e3156ac71d3a29e3a8e4b84f4850b20d193ea55dc073a9d83c846ce3f55dc083ae93612c02f72117a4693a4c023f8e824b5500f61509094a89bab8d531ec8aef5291ac9ebd44cb0a2c9413274007488f32b12f459c5e10b849004902ac03273ce86a7a91a17ab46590ace5b4bd5333d8f5481e4e05fc94b5e68db79e30e6c08dd3c0578ec4fc0c55d6850313c8215b052a22d2f778dace1e33441406d5772a28a7ed892b867dd4c9e2818ff259e115bd4026437cffd7ef65dc4044e9830e64149e34cb695cddde34336b8bb40c00c15bfbdaded1593293a6d166ad39d8336b1178ed121bb13a98ea31a46d9f4817b3d8454e26bf405a32f51c15d1d419112ccd2af006e415bd5e3cfb67aa9d5ec64fc599b026934f7dbba1f4c120558364057a5cc5f901b2c0d411dcbe02342bfe32416277e1937e69a3e87f6ea0f214534f278a022231354653e3537e72b75923be4472a1275cb3cad4cf361dfe6be8c6fec69bd824f2a4d84878acac5e97287805279bdd92f48e8d1342f1a3cd8c711f837be0f188ed54071f9441b2b11b0acbc0c3510b4c4c5beababd27c7176f00c2396a3b81ecd9135e1f0171cd97976415671b28d63cb67996626f088f2de42125989581086dd29e87c6f4ee507537e2c9ac8e0f9827b9454770add19de3c3f8c093e85a93e9aeba6e157e38d8a42cb8893efc9ac78d79b9a02fc0500e4466a2942109de0f351ae313a534db4762802220ec7efd5bdc62ec04810e950dc16dc9f0ccafb6eb4a75db6f412ad3575094e8175290e70688f6780be7388846fc98a394c28ce11f51bc8b1cf54df58f06d4aebe2e185b2b48dd2d39483fe584b56dc1509c26ca5f89aacd27ba102232695a74e0613f91ceba0b385a0c59e60f24fce013b0646ba4d1e38e86a0962b065227b1f2a82e9d594e4125fa7660e7683a9a6d5f8ca7f9fc11f98828ab5f0edf94370e951c5e044190d367e4056ee54b97199d067505258aa5684db9dd24ea1ad52cc8744f5b2683584c131d244311a55985bf09c75d7237ab0607e334d6626278fb21a0150ebaa7ab0b78081bd43c706731ea3ccf9678822ade40320d51c040c9f1072c42562d18c894725ee7105da06e5b694420a639d67f8a1062491620238b0199b1a917b219049
+InA = 420c3c6b581c9f4466cd807170629a84b681aa1063a341062aa37956d70ae68befe87727d99fc92e7c29e215c5f13b722587c0d675d79190096a280a192aa4f4e65f982a59130f9266d22e4e8cd9ef195b45813cc8233a253f8a752548529b463226d3ebcf422870739095d567e7f6a512630c6766aa2668e8477a78865849e0b16bd0e4919d1d7da0c5d8005104e6e4d6b6855590e5c6a4e649dc4a0ef5b5ae140251c26b8c32001ed7ad13cda56b94c3294010c84ab54fba1888686384d40e310eb6363b9b23057782805f62e378c27ab98f223324add836772c3e81b8212bdae3c864a59c6e63a72327c42e69711ffcb3b6d04c00293e91d76c50ef9fdf33fd382c769835e0bd4821f65e66797075915f138a6b49051ec406ea750d50384d5d4ce2c9c536d45d3f7a704c6cae8d82f07bc5590f908b308d8c1b8d5e9ee3931d2d55145236f10688ae192f531755b310ccaf3891125af5decb1adbaa85224c47b73c3c8d7fa92ae1635d03d64d73c600486b336c8a511a7e1675a3dec9fcde711601ab73fc4d788d9de3d9f8fe185b87e920550d705f89f3188d1a69f9efc036666d7f5321d71b7eb5436f9bbab400884c08c59bd0a9458fe1e2b7b73d1e2a5ca4b21be5d48125613f0884d15eea9393a4dbb225bb66e5da5d3d98fb20c10761fe0efa5cf00498b29a662df8f8f17a4de3851fa425552aca9544aa89c89981d0f85636f67a862ee4eb844579f5c0f1b9bc0d647b15223aaf0c9edce88815273a0b064772b46dfc933a4dd8db90aa585097b2c6c0564ca90164a43d798403426544bb66637597dcf92a747b7b5ce1f7ba266f5c482d73e8f8b27920a627d1e9154ea745c213988f405ac2a19ec0a37388f8f67cbd9bd7c64222bd14a664eb5146a0645ccb542c21bde65bd2093a9b7622158a9504b0f230276ae1afd7ba6d7267a546e8c08dbdf168be439fe9263427752658d225a2062202ad567235685ab76585b04151f65a63016c9981a635116ab7766789a066d40d122805178f6840ec225e22dc5147d09ed9a53d8582e0e15b25b9a163178314205722adad9e9798c1306adcc002ab59e146302f0e08124945a18bfb10666c6b429124c5f5e61b98e2e8e6defd58b0dd377924f6a8d788c5887c306cf5864931b171936ae80f4911d483ccc804c112a0bc88a80621b0a1a9f8dd9f44f4f84687c54b533b4614d82741fb78006cdd137c422e5441c4513de3ae8a4cf4db9c679fb90c986f97abe8e1060d0392b668de010a0f9f1447445557c2f069f76e285a94f257ed368a70ae2922926c9452c01fa1fad6aac2db43daa67d68b31f3889acd8649a0c7389336d5d6904715984020470493a18fc84aa2bc61e352184714d0f5f46df0e3fa5a3a8a852f981135399d5df8d44b46abd6bfc164c4587e2a79ae255dadd09f964809c57e753105e2575bc987d924f3d7b9bd90747d1b61ce06a56ba60f1813463c418a3ad09821846b6c2b5057aca1939f255acca579b310fc91d112b6669088a66299fb227a7ca15c686604bf47c10d6c4aa1b56d03a7fe23217294866ea820646c372579955ccc16471159b83d8ba016c114c10c961221c28a956fce40806780e043883349d8e8baf8f308610be18c6103a454c8f30cb9465cc9d3be2935e347fc691a503b4c92d311d29abba18497283e646c3ab030a1dcfd295a29bc6517dde0cb295ea89639824c7fa99a968dd7a1aeb1983925258f3d7853517d6b5db4060783b27c949b80989561303d95cea8059e5e7fa520c2d3141fec406d88535d7060b1f4ebbd08cc7687e3d83040c97005d450f4405c557c9e0b71f047ffac98aa6ffea1f5598223163ea918ad5319bb919d9633254156aae2c527521664a6e54abae312e704b0fd335014a66ad8d22bc9862a0d239a9deb2a011553fe38f0100be4705f0a25580ade9b4b24705264326030b06d512f2d4c815ed36827d1078c57882369c1c2129ee1e1f8d51d45ab0d573820f9618fc2a45c25769f99ca51554efa5d086d039655b6a23874e18d02bb3b28a2d43a293b7e0b32d5c5000a222ddb92f945f0bc40a49427d8ab084de1e89cf9c70c9d39e1f902844811bca5775ad2f73ca884314846d474ef957a4d98eb4c98eb94f9cc6b6edd101b78d9a4b5986626752363b3e2c030bc5b0eb49e558d55ebaa92e1627cc166790aba847725bb487d08930714922d764eeae45c2a4157311ae4fdf4995c7781559cd149124a86843682df807d60ee922471be84d830e1cbed3055e281a19b855121977435f057000329a9a0458e51fca4dac4fa36303c02967d7551759b153f653ca653f7016d427ed7bdc6c49896741853148338688a62745e144c2840cb2fd22aabacbd6e7378074da23a68689664d5e804307beeaaef7454101d34c28441ee8456384a752992494ec2c4d4381c3ba3441357d8c545baec472eac58ca0f665992d64e1e1342c639863a09779e88349afa0418f667aebc22b92e7722e956793653992ab5768c170e8e564
+InNoiseSP = b1e6653ab5f2447f5ca626e751a87a4b61dbcc0637a84ff2a9eb2d26e99d642bb6d18b0f0c7f4b29a2a671a88f3723ec76fce25f3ac5c681d1de5909d68ca63991af658c4d1e6ea18df2e97675e4840fc5667c353d8d11a92f6da0aad1e4a104066bb3c82ea9fca43d9c837741010924e5cfc3a235045edf4bf2a5187861fa974dbaca44873e93f9b498f2129111978a7d841e1da8362110c1ba1cd9467b7e664d6485b63a0fa2555e51bbc126b811f19dca151df2bbe8675e992bfc4dac16c207044234120c36b94fbe9fccccd2d6ac293497260de16d0843d2679201520208705b9f1be4debb05893b4622bd9a56c0922bb72508b7ee4278f6410300ae47bba8b086c54a6fcb0202563b42ce667ce869cfa00e3041dc09f08d1a926cad88f5d88037081a2c98053aab975868fa5e4c3ba6febc2f469462ef95a05a8ea492c24615fb4b20699c187718065ae3a6ce3fe3ddcdf257661093d8a712a91aaefe6b17083dacb09011f580b23e6c05cf56457195aa2465bd32769c86d19146062a7a26a756233a301e3e31ccf33268003871e44549df44ba2f025f5a95f8356185e6935df047f3e05f585e515531653255770402ee467e696ec5992a24be84aa5dbffc081c118d865a03016eb2abe0b3ae9413e51971503f1ce58b88f194c1ac7378b7c62a48155c18355d4ea50f4d49a8756399a17717d68a76592d7c02024c7cdd13075b61b98d6055fc64990d36de2769e220b902544fff89bd3a6fcc930be2975c36c25dfe4a38243c95209b2784010a8ea5ae3af906058c563f51ca99b892d523f4a4fa79942a38539b6656d95582901cccb28d63ee45c4c13ef9c22429bd1c1727cd7b203f1936fcefe8d9da9016c58c8ba916e5624d2bb151249527a31f9a866cd278bce94791cfb1263fcd3a15e03a1e50420b157e0478818ae5968d06746ac14352740e258715f1a7a54c5d9849f61204bdb50d55ea545df435fa6efa575e2157b7eb5df2305512c4a91112422824c50411b1b4c5a670e9bb848c395f6d09215b30d954280a0ea720706f8bcc6ed6983ebcf95650d1ed163e3505693abb788879047fb088eabb80e99443120d6a2f166c136bda3e825292c815a668dfd74ea013e62da1a8e036c2c077b11a591a9ec195d3c02258fa817a5ee1d94b0fc4be254387069784b7358f0888dbba567a1db5372c24a7d66a315be8f16f65a46a08986191668b2249ad6d3d87240c4047d59f648a790a7536b9e145c95997058db3679ce3a3691b8f528c36f8269686835deaf32a080dc45fd8fbc1a260998fe9c58c832d52923649954efe60f2d059bc119e07b41917a60eb2a7ad1b84cc9c8a5df830f4a16bc7abad4ab0d5246c9fc2c2d0cd120657d25a3ace31e653aed39b822ea9cc37e6d67c2168c64cb63ade7016ee4a1679c25d1a947901bf4100dc6d1eb69ef88ba9cfd309a385af4baefbc8bb48861199bc810390446367b127921e7f70071cb36775a054d327d71b9d9879e82d1721a686118faf08b400316514d9008248b6375245464d89a6fd8be7725ee1bc6e51963a1a20181ad6eb54a76c019e2fe0c9a981381818d2f6de38011a2bdf2b7391b19ee37f1485e1715a17a3b9b65843470aa3d766f0b4b48a0a566ee8962fcd46c2dfbca77e003b5bccc45a53d874633e899363d497741282a33b66cd01bc306b310d442968121914d9c715f00c1809cc84460fb23c00a9c4b6cc6806cbe3198a9721bfb4d10f14572aa9406023abe1f2282d2cf6c1791a88810b5f8568b123e84144b6aab07657c1d57a473d53e54dccd32aeea0210e7f50ee098dd797b079eddd236ad0675698949f50ce5899dda92611df2b0002e043eba7f05a447e20f2d673c5d0e29b758ebf7959b168e1cb0ad218fe80861a4eb1c665aa7618c09014e893cac6d9487d0d5c8a090035f8c549a80af46158f90de1891579d50839384c4b3bdb66263463c7ca03257acbdfe5a233b98db00c412371bc32788bb9b536f27dc24c2e328f85287acbe2d28add4f4e4908b177b54e994521e53001b63dad66f82ca476612a47f690c09d1109cac06f510d684132c8764e88b0ea5525509472cc27d696412c2172996af3495285ebcbf0499396211d9a1183910262c267d020cf70a09097df20cc41a4d79acb536051b65ad12122e2586028547a6bc2420e97d6f4b6123c42229fec6775bd6fb6c51140bff9b2d19388a5554aa83316556109a544eb67e403a9d59a3619fafbce218b3e47d73829c65a196c87d66ba16079151887d9667abe006646ff1ea7b65ddb2ba39d7a6899fe82b6e8112509ea09fab937be208e6015029143688513836cb1016a12572348b997ac4a52384c60b05933e91650697203106467088d451e145ba368bdda6808d36f7d99120fb349300d949bcd8a18c5001eab4488dbc6ce5beb47733609429e9c895e749141238d044fe08a0d1b662b9e767a65a53767e6924d2d1a88eaab2b6e902309036a6b87bc0a7d9d1e81f8d56b9fac7361e2b837
+InNoiseEP = ec67edf27a627e3d6101bbf3f07462e567d1fb24819f549b56922c94641e03c5a90a9e4ded6385f1dea354a97571acc95144eccf6341f42cb8001ddbd2152e5fe9375b6cc01ff0a8ee6545dd1ebe834c6bd860565da3ff58bbdc4ad502de4b7f795ac8de9236822cafe0819d9a7a721c845a9133aff25ef3e75789927214aa0ce6b52591a0f1a104c5da1ebc4e96a65a5d00b4b65365a2e586519f641d17aae4041cea14573b448bda8688588f8495daeb90e1b704a6786ecfb4968682782947bae6847868ace2e111396fc3da551160d56ea6545ab825aaa53a282c2166842b710fbe331a65438b139d9b79815d6660081091f565d1cd65944b9d99429a9198aa2874818b86fb0bd07e16ee14980d79215d958a4ba4385a130c14f8871090deacfb4aa43a9242a09fa8549c19ef5b7139132c62d05128904d0e96e80e54c54a8627bb1b36e6aeb507fc49a8e98671727bc9bb932a7e0bc39e7e81e69e48f596cba6fc4984501a2d49a5be0309d8e434584669af04230797ec7aff98425b4f6a78f78c8455f97273b95e4f97681c11e35ffa533f887cebd95406887a4d78e9594694622d828ffa58516e986ae41f06b004adcaa4db09cf1e3829943859b50d6f5386e31643f210e3ab017b6c3d3de2699aa1a5c47e01907284ff5a194c2f0a743a6888041217e6d2c4584f6050f6665da7782a4a63bc580d1d421248f18940526d45c59e143c835e44e83c6a41b806840725ad43b208496a9ab14667598c97006c00ce0f8a71ad51051840f4c5a2b5ac240af17671d806181b9a6a831a209c52a4b1df3d7f591abd87e6ba422ba8fc77393ea2eb5ed786b6eea749e084dbee8519b5846530da1d0054d683591fda950c99de294471855c0bc774801a2debe19de54ae6a9da175d92fa809152e41ed5d521b57fd1e79a9cb8bb1ee678b4d204524ed97e093f3da40399a24ab468392403769947693182181c0b0c467f0329d7517292240c4a5616db815cdc82f929140d0a511b19407a22a7c2a1caa9783c5985d5947a6bfa5241631182eff0164b9f14602d7a13925146913f70083109e7b4582914821e532841e539ce5e826ad0683b62ee2ca51bca3cc88f1f27c5be927220c0c3495e7bd958ff10effe94d59fe1fadc5a5923e1301b1f4a125fabd6c4dcc4618ddfbf7b5f0ab37d9769a029c90f738a4c949b082a2f6276aa911a1a21cda9c2a2806c35abea606faab2b189420a7b3c48806d2393c9fe67176681490b4a9e340aa68b4dd4aafadd01c4d88c360019aa90ce59612cc30f6004da46ed40acd4a53aa1369687799f150516dee99820044e2a87683b12454afbab7a649498796f00d23314b062bae57a995ab27eafde22e3a4a240be4560821bb64551279b62f326a62adf55d8da21644b68dc93a8a5ab21e10304de99a17c666c1ae81a18d927ecdb9708b3c1dbf9ca21e55af3c3aab449ec089fd672fa3c3d38b82f8186ddf17ec5944c21de687ecae803c7a32a1c5f1546f6387eb32d1c75d006cd6e8b9c6d399c402c6c65e9e512628272e930a4c240989f0bb7fadad62dd4a9e4a4b9ca0c106a1cd8e7842ac8c0dc2a26f6b3f96b484861be2f0981cbf07b6a9598601285885924506d0b8b693a0f590b12a71de0674c2ebb85a094806c57b338b026253b881a34b5915f22d62ca0f231732a851c51295a66f6453865de154323960bc66f7e60c79a2615db49a2b7ad80647e007a909b129750581c1ba4eff7e79675311d3b0961ca335c9a9e54b59911894cef01bace82a6259643c3725d5f6eafc7de7225324e14035adc9f43dc586334dc1c1952ca861939a6f8cdb1f47f321d9be91372989d50b79dce772741870b8fbd1140f812a342b1606b6921bb8d3986d816197e1959c156318ca2c0eda96ba84f7cd2df147a2d3eee5472c548b066665b3a173e2c33970df3c3e25e043542585206f4250ec54978c398731983736165da2628c6d19a035a0e6e8e6147a59055ded74c146655309a28dce3b6cc5c1a6d18c52be99f74abe2f5534832451494647b4e2ed9931fc2fc2d530101ef97d92670cfd5d1d9b95412595a9e6871202e70ada42698aa52b181beb5fe260306271c8b6c5eaa3f257f5080f5866159f9b2470c0a83eda50745d02be4016414979a80be53da68a172882003b788b28a6abf4d1de8c850325f25c309e12c27d2a1464bfcac812e61960130bdbae3907904b6ba3d1ee35b7734428e3a86f6b1b6a079896622c171b29a87a899e12dc929dce6d669630276dba5ebe4636e36e940ea65fc7a2978e4f6d4660c532608f6707c81b15ae7c9787948ad1c6481e978100208497c19d69924e741393722b1880ddd94c2eb75b65b2fce25d63451eb1f0068debd8fa25383462f4295b3ee17b094693dca876e75091a019368388175f025594c8fd2f8a81244868f4229926fb2d4992519eca660accb71739dab701052929c8c375ad8f8763d58611ad8b4e394422d1aadc88055c43decce2a907fc8bdde6b5a5759bd9a
+InNoiseEPP = fbafff0b0003000180fffbff0a00038000b0ffeebffd2f0130000800fe2f00100000c00100000000030000b0002000ecbf03c0fffbfff2bf0240fefbffeebf040000f0fff6bf0180ffdbff060006c0000000fcbffe2f0000000300ffafff4b000000ff2f015000fcbf0600003c0008000600002000fcbf01c0ff0b000700007000f0ff06000340ff6b0008000440ff0b000400008000f0ff0a00014000000000c00200000c001400fc2f000c00fbbffeafff1b00fcbf0440ff1b000c000140002000fcbffe2f00bcff0a00fc2fff0b000300ffefff0b0004000180ff2b00f8bf0100003c000800ffefff1b000400000000ecff0e0000b0002000100001000000000c000070fffbff06000300007000f8bf0070010000fbbf00b0000000fcbf00f0ff0b00070006c001200000c00280002000000000f000100004000030001c000800ffefff3b001000f92f00c0fff6bf044000f0ff120005c0ff5b00f4bf0300001c0000000080fffbff020002c0fffbff06000400010000f0bfff6f000000fcbf0000020000f8bf007000f0ff0e0000c0ff0b000400004000c0fffebf01c0ff0b00f4bf007000000003c005c000100004000000000000140000c0004000f4bf0500004c00180005c00040000000020000300000c00180ff0b00fcbf04800100001c0000400000000b0000c0ff1b00ecbf003001300010000030004c00000003800010000400028000f0ff0e000140ff9bff06000540fedbff02c00380001000f8bffe6f00a0ff02c00080ff0b00fbbfff6f004000f0bf0040ffebfffabf01c0fffbff12000040ffdbff0200000000000000c0003000d0fffebf05c0ff0b000b000030ffdbfff2bf028000f0ff0e0003400020000c00ff2f00ccff120000f0ffdbff06000100ff5b0000c001000000000400fe2f00000014000180ff4b00fcbf00b0fffbff0200020001200000c0fbaf00c0ff0a00030000fcff020000c0ff0b001b00fcef000000040000c0ffdbff06000380fffbfffebf000000fcff0e0000b0000000f0bf01c00050000400fd2fff0b000f00040000f0ff02000100011000080002c000300000000200000c0000c0ff2fff0b000f0008c0000000100007c0ff0b00f7bffdef008000f4bf0280ff3b000400feefff0b000800ff2f013000180000b0ff0b000f0001c0001000000000b0ff2b00100002000120000800003000ecff060000c0ffebfffabf02400000000c0000c000000003c0fe2f00f0fff6bf00000000000b000280fffbff02c00030fffbff020002c0ff0b00f7bf0200ffbbff060000b0ff0b00f7bffe6fffdbff02c0faaf0000000c0004c000100010000080ffebfffebf003000ecfffabf05c0fefbff1e00f96f00500004000080ff2b000400010001000003c0038001e0ff1200ffef00c0fffebf0100ff2b00f4bffd6f00d0ff02c000000000000400030000e0ff020007c0ff0b000000fe2f01200000c0fd6f00e0fff6bf0070004000040001400020000c000100002c00f0bf03400030000800038000e0ff020000f0ff0b00fcbf000000ecff02c0ffafff4b0004000140ff6b00f4bf00800020000800000000fcfff6bf02800020000400020000f0ff02c0048000300008000070ff2b000400020000c0fffebffd2f00e0ff0e0000f0ffcbff02c00240000000040002c0ff3b0000000100003c00fcbfff2f000c00f8bf0600004c00080000f00000000800004000e0ffeebfffafff1b000000fe2f004000080002c000c0ff0600fd2f0180000000fd6f002000080000c0ff6b000400040000ccff1200ffef00f0ff02000140002000000002000100000b0000700010000000ff2f00ecff0200fd6f00f0ff0200003001000003c0000001100010000140ff5b00040002c0ff0b000b00ff2f0000000f00ff2f002c00f4bfff2f004000fcbf018000000000000500000c000000034001000018000480fffbfffabf007001e0fffebf050000e0ff0a00000000c0ff1600ff2f002c000400024000f0fffebf0030fffbfff6bf03800010000c0000c0ff1b0000000300001c0000c00280fffbff0600014000000003c0fdafff3b000400fb2f00f0ff020000c0ffdbfff2bf00300010000400060000fcfffebf004000300004000080ff0b00efbf02c00120000400ffafff5b0000c0044000e0ff1a00008001e0ff0e00034000f0ff0a0000b0ff0b00f7bf018000000003000340004000fcbf0030000c0000c0038000d0ff0600fe2f0100000b00feaf004000f8bf0100001000000008c000f0ff0e00000000c0fffebf0280fffbff12000080ffdbff06000380ff4b00f4bf0100000c00f0bf04000200001000ffaf000000f8bf030000fcff02c0007000d0fffebf0380000000fcbfffafff7b000c00028000000004000200002c00fcbfffef002000fcbf02400030000c00fd2f00fcff0a00030000e0ff0a0003c0ffabffeebf0240000000f3bffd2f00ecff16000140ffbbff0a00010001f0ff0600ffefff2b001000
+InRand = 51c3483b5f31a30d7cc8457959f83f0d3848d0d82a9af0371d9e54519ccc6e45
+OutPK = ac839551f2d26b42e6bab93668afac931834e5d0230e84f450cb25ae52a0e32850da5f44d42cda563e95a64f0e18636a43c50e0d79a7d69a15c5a43a9d2d8db99bd0a43d3d60cecd0ae499aa5c2280daa1ca09b927a8e84abd068e684d0f025d606f60632d85f70e7301444d1495351d018c690aa2ac9e8da8d598dafc87d4921adb9eec3818d91e981da53a9be7fb9053f5a0b107225bff688e01eb1dd0245d30fb2680b2baad0e81495bd21a0d5cf24b96b830b12971a0478902bd6277104e3b11e138b523e5d9d8d6bb4d099c66e92693371558b2ee3222f58db087a1ba2953c8a147b8b21714ece87242d47523c004071306b6eb14358be901b0a88d9e12ccc682d204533a2fe87b578b449bd88a6af1a1c0787a8914dc85ee81ae6cbd53a0f0cbbb3d7aa8965bd7c34c6507d3709211129d802c2eab3e3e1a589948130665b4ff9cb9a050701a0aa3dc62b4999b29a4b604205c0f995d27411260bd93abc5d2566a4b9141c75ba03e28e3223ed9a661bfa9589826a10d525b5332b59f6163ce559ac3cb9cb0352b55d633d5517885c0bb9ea5aeef9f0a586fc0a171ae5b5bf7f6576caa6067b76ab5682a8260d0be3c80219ec7746a9accc6ad47d2994615cae6590214b29f230cb881ec7d59c29be2c5380e3c15ef34a3543dc751e2182a4e397c6d02b0df746741cebc41e6d59f045e8a71086140c212aae224d53eda4812d3ee2c120ceaf7d82814a163dd23682f6902320372e3dacd1cdd4b2d835d493300ec572944d44ee0892d712c144c4706ac47a5307abc08f9f0fd905a6b26af28c050232fe707e08c8217774612e9d5e9206697b8612bf2bff7d50282683ca6d3ecb088bd3eb917e37e4a449a294c6bfa5b6519a0dca0101872c10efed1d9607b102cdacdd893c72e804a96781ad08c89966fb07591696a389948363450bbda9b903aec4d47be629143d7ba1b2f260b88dcd182d345aec4a928766c5bd89b35980fcddae4b80f322e9d2b87d0dd9539330d82c930b15e0bd9b05a73fe6426e9da685b540e3c1d35448aa4a0119bfa30c6326877abf850ed1822ae000132b1501a69865329c74a2b03eea490f6fd1891214d73ba6a4ca5e0db87c70c63a619f7075d348969eb46bb9f5d34926df75586fa49f3cc3975053eba33107a714d627075883bc44f6267ce16971ac5f0ad3c481f421afc9c1a12aef556392d2497ef884abc7a8b8fde85229d42896abbe913b23ec32a136ae3117fdbeca943f91db8c5c41d0037e8a7bf42eba1df45295ed7cc876cda4b07e25106f97435614200e2a74c5781987770c2d0157e558cb64c20029d6774853e2c5da3da0a0973d4bd53e35f96c171002e4312fa6c518fc909690b8fd06436e5b8da9dc297c8cdb7146daa29ad15eefb0f2168b8a3a6c7243a8714106d35adaa29206ad2509ee61f082c5739385d9fcb0b2ab0f5fc8dab55dc670484871c69fcd361987ff1cb6797ae3e51d9974ca6b5087f9a229519e9008ccbef4ab6daa24fcae95be3c4a1167b5e1c460da56b642362e7ed8c7eba8100d793e52bab2082b40e3d8340905dd4dbf660119081160739b281cd16f7a8bd020f69e06aa79505870771fa4219008b9d1ca1b0bd28afe029f299e037dd82c0aa17781009591ce628242f74a0ba1526c100e26caba892868da9680b07904545aeda84154384886a4d55118e7948fa2cdaa586f29c25e4fa317ecd3a995c793921106a7d8dc3b2fe3432214bb339614a579661dbbbf07104afa6ad0201ad4b6fd80e90427e6102eb1c02c4d62fbc25464760b6368da0fcb26cb5903eadf6d284328d9867ce610a026d65c1f80a4c7482799e1d3827f945dc6f4a49d6e6667ac5ade72d8804a81830d49c85bd9c431d38372a90198463d2f17a10f1961aa01d0495bf6e8c098248ae5d0740d7c7b266481438e49bf44edf5f9ef345920339a60495c70613aa2ce2988ca67e66572267dcb1aeeaa6146a2a6e69bbe60243fbe2141a6ff802bd581a4d8b351558608ad1db2f0a818c7fea8b49d3e3b17779ce6b5a84a100f368826b15462f0b9f466a947171ac8c0f214f5e25d2c9fe3374e4b121c82d26b45a6fd122662880966b07ca3ea54d1a6c98ae4071866a2c028bd745fd5c91eec2164bb00c1d19131769d196b8f513cec2dbdc562ca2d82261860115d693e232ada6f8446c073e4c1529d34479a5640c648b0b74d1da3a480941ea584d42608bb8e7ddceaed49a9d81d30654866abb70e1924db0c615932f463b749253a4981050ea498ada4edd6728a44538f7982e1b6927d637da0ebb593aa5fa1813c7834027309d1212c7fa29f7204d1da46de261250ad8c0bb725f46742c14f0881d88702fa43efa706406e7c5082bd82a03c92240a01c115a6faef5830ab109491aeadb01ec562b67fc5ac3c529a9db48369788369013d4d298ac375ed33e44371a31b21585c7112c85aae85ea7206c28ee48a7c253561efd56d7652da8b72d082ed40ebc595d6bd2942440ae674804016a6e5d
+OutRec = 0040001000040001c00010000c0000800020000c000180001000040003c0001000080001800030000c000300002000040000400030000c0001400010000c0001c00020000000034000000000000000002000080000000030000c00018000300000000140001000080001c000200004000380001000000001c00000000c00008000200008000080003000080003c00030000400014000100004000280000000000002800020000000030000000000000000002000040000400000000c00010000000008000040003000080000c00000000c000180002000000002c00010000c0003400020000000004000200000000300002000000001c00000000c00004000300008000000000000080003c0003000000002000010000c000340002000040002c00000000000038000100004000280002000000000c00030000400034000200008000280001000080003c0002000080003400000000c0000800010000c0001c0003000080003c0000000040003800000000c000280002000040002000020000c00000000000000000100001000080001000000000c000340000000000003c00020000000030000000008000040001000040001c00010000c0002c0003000080002800020000c0001400000000400004000200008000380000000040001800000000400014000000000000040003000080001000020000c0002800020000c0000c0003000000001000030000c0001c00030000c000280002000080000000030000c0000c000200004000200000000000001c00000000c0001c000300000000140002000080000400000000400010000000004000300003000040002c00020000c0003c000000008000280001000080003400000000000038000300004000380001000080000c0001000040000c00010000c00028000200008000340001000040000800020000c00024000000004000380000000000001c000300008000300001000000000c00010000c0003c0002000000003c000000008000280003000040003000020000400034000000008000300003000040001800000000c000300000000000003400010000c0003400020000c0002400010000c000140000000000003400000000c0001400030000c0000c00020000c0001400020000c000300003000000003c00000000c0000000030000c0000800020000400028000300000000240003000080003c00000000c00008000000004000100001000080002c0001000040003000020000c000280001000000001000020000c0001800000000c0000400010000c00030000100004000080002000080001800030000c0002c00030000c0002000030000c0000c0000000080003c0001000080001c0003000000000800020000c00018000000004000040000000000003c000200008000380000000000000000020000c0001000030000c0000c00020000c0003800010000800004000100008000380002000000003800010000c0002800020000000008000300008000340003000000002c00010000c000300001000080003c0003000040001c0003000040002000010000400010000100000000140001000080003c000200008000100001000000001c000100008000380000000000001400010000400020000100000000300001000040002800010000800000000200008000040003000040002800010000c0000400030000c000200001000000002c000100008000080000000000003000010000c0002800020000c0001000010000c0002800000000000038000200000000080001000040000800020000c0003000000000000010000300004000100001000000000800000000c00008000200004000040001000040002400030000400038000000000000140001000000002000010000c0002400000000000008000200000000280002000000001c0001000040001800020000c00038000100008000040003000040002c0003000040002000020000800030000200004000340002000040001c000200008000300003000040003800030000400008000100000000100001000000000c0002000040003400020000c000100002000080002c00020000000014000200004000000000000080002c0001000040000c0001000000000c000100004000340000000080001c00010000c0000c000300004000180003000000003800020000c000080003000040001000000000c0001400010000000010000100004000240000000000003c0003000000001400020000c0003c000300008000100003000080003c00010000800038000200008000380000000040002c0001000080000c000300004000280002000000001400030000c000040000000000000400000000c000140000000000002800030000c0001c00000000c000300001000000001800020000000018000300000000000001000080003400020000c0000c0003000000002400010000000008000000008000380001000080000400010000c00
+Key = 11e452a7b206e159ac85ea8b6553d01e246d084cad52fb7b29028d79962091dd
+
+InNoiseS = 2e6f8b71cc4ab67f9816984bce92458263794396bd66ec320565045122d1a427408678dc565386f2f993fb14e1422366ade64536811cf943271a185b747eb2d62d13b6d5028b755179d538f915bce3fd7b1e4a2d6d4e6bf29d06554a010502a70a516fd7c6564b323723eb2a8570ceb98603208a349c685ddc1366b55da84b801ec8a5ea3882c6668a394d98b4a1036946ad618f1cf427a9a05676dbc615acb25c53cb83a117964065618b213f1e4851dcf2663638315859704a44b5785bcff0f7fd2a6eff292c2950fc817d5a4b7bccd014d95c4565abf86a6e8cd2b32e9594482e46e505e57932d30ed6639853c74ff2138b122fc46f54c183f11858ebdb2b871ca916109922ea7e3556e63d95028c62d2652c0b9f35af121da795121d5872043711b6a56639e169eb41d9780f6462d140cc7ddeabaed49d9c4e4284d07132d92fc39b98a5c5cd278023e3a1fdfd8875e0a7a2effe4dd3e2897be58520d99c36308caead22987d959a480db2cb76488cf14e4c9bc92887d5107c6df91ab69abd9a11a50a91b41138c22ed8281c58ae49a512c5c203f563bc7950147ded01a7078f8ebeb324e6e305295522df6eb91035ace84be894fa824fe6415b664e1873f3a4ace6f2f5aaad6d121a489688d3c7d307ab2414b79822305f8842339540f42e089df2c1bf0620ec7a306f36b2d4b27c72439875109d6dfbcbc43358f068108555e0f7e2b047140a589cc4be815227c9a29eaf7914e381513e5d9d00fd3296ee894c9641d2b00e79f62f2f62d8b5ade28bdc26fc6e3711c2fe3694ec9c461e28b5524c59fd2a0ff2ff599c04d8b882f23a5ecf917d080130214c262d3826a616f093acfb64fc6c9c82c36708a628c652cbf0ec8911e902caa2e805268b8144e595f1bbfcccbdb8b7a64af5e101fb113e8abec686c1ee7c2a539593b6f80549be9d8b814f360d71d6bbeb569db2e94d52405b8895da9a02495a01ac93ce9847503e107f6b81fa598c539409a7873af606315f82b9eac49b68a6b9f214a84d58083e067edd52d167d1ea65969cb7a40299062a732d2fbfb26139570c97151187741b9d020113651e2ed0cc279893348b2592531a6a1874c317ec638b42339890205705a52099aebb745f2689c54954f32620598048557aafd940650451b5a0e246f683e507d72080ac8b92ea399023f4ca93dbfb54637cd0301171d124d34a48f0db1a2c5f5ae5725271249a1198347b724b22817f649fcc4b02ec374b10b0524e08a9a41771741c2234d6324856c1aad86051d831e62333162aa2a32905d618e76c353a496829e3cc7a07a66b975364907e6da5d983e65004a392b1b8ce692ce1b24030a6756d60f6972a2e8aefef7d761e15645043d7720d1d756d1713b06c16712716f9204e29b2f8a45172b0498328a0d1b8a7a664079123b9851059add99e2209b4911359f42659e24def8cda7bd4da1ae34fa7669f8218d786b8b9ca16520ad54b5553b92cc99541e6f51caec0176729687977979c4726abf5321344edd66f0e03bea512c6dd54b9d6c80f932d4e58e75f8bf97b3e0e15e412704b691334d10541cb7037eb653f084d96afa4c3ab78d2468e4242fbdb24b05db81815280ef458871dbad0c938719a855d80c3392506ac313b755e13e6d587d0e9c11775e8d588cb048f60c64c564024570b474ab83d9a8d2fc7a0eb465803aa2b643a15c59e3f156ad133aca6c9c968d1493d601fa59591c8d0aea49a697a2e7426c36d24f343b292ed2201bb1f2638a2923c8220e0321f081cf465f98b5f69ff3ab78574646e828487f40c6f4d7c28be30391f083e78640a28f7066ab9aac6138a106dc9c3497c38efbcf007062de29a4b7a38397d0305a04b5fca5f51ddf6b026dd9aa1ef88a394aea54f20be3800d488af630522dfbe8306a10f7178ce06c097a3d7ca62b7169365486b4a23c52c1017fd4513268e4a20c5702b8d624d028098bed550389ad8b987c26a0a0bca7478f3c8b5a4c926a2c10c34662de11b7a260539727e75304c0c28b15e7cc6105da18ab6ba720eb6e0bfce3b220f7f68bcbd845ac8c7d166a837c46714721c227b5bd263d2a6fd54c7a7f26267597953943ef22cb9607f65eb829b5e4ae84b1a263c69a77b16827a7dda44bad604831dee420100d2ab45d822cca55378fffc7377d57c381dd92661eb2271599a68f4a5c4811ad16f27c16103ee9e7a802e9011f7fa542f4799d65c448245424c9698fd3d10cde7cbcf6885f6d630e1a7248ca7ab16b6cd8d262e1cc20243d46c5e1c2aa508c22b6aacfa0a4b8c60e2056bc3365c5f3acc45c61b2d12778bdbaac7744423af4b32f52eafd9a7835400ae42d424d8a46c0c03acefe9a684f903f5c8761616cc82e05de264cb7278529f3c3255b8095f64a2f3f5299b3365efb356d856b04731a8c77845336db8c1952a6ee91b64578d620af6052a700989a2338041e767098c996ce97a6f9907c284bd4acd688937617003afb95af11447629d71222919bcd5b4204a
+InPK = ac839551f2d26b42e6bab93668afac931834e5d0230e84f450cb25ae52a0e32850da5f44d42cda563e95a64f0e18636a43c50e0d79a7d69a15c5a43a9d2d8db99bd0a43d3d60cecd0ae499aa5c2280daa1ca09b927a8e84abd068e684d0f025d606f60632d85f70e7301444d1495351d018c690aa2ac9e8da8d598dafc87d4921adb9eec3818d91e981da53a9be7fb9053f5a0b107225bff688e01eb1dd0245d30fb2680b2baad0e81495bd21a0d5cf24b96b830b12971a0478902bd6277104e3b11e138b523e5d9d8d6bb4d099c66e92693371558b2ee3222f58db087a1ba2953c8a147b8b21714ece87242d47523c004071306b6eb14358be901b0a88d9e12ccc682d204533a2fe87b578b449bd88a6af1a1c0787a8914dc85ee81ae6cbd53a0f0cbbb3d7aa8965bd7c34c6507d3709211129d802c2eab3e3e1a589948130665b4ff9cb9a050701a0aa3dc62b4999b29a4b604205c0f995d27411260bd93abc5d2566a4b9141c75ba03e28e3223ed9a661bfa9589826a10d525b5332b59f6163ce559ac3cb9cb0352b55d633d5517885c0bb9ea5aeef9f0a586fc0a171ae5b5bf7f6576caa6067b76ab5682a8260d0be3c80219ec7746a9accc6ad47d2994615cae6590214b29f230cb881ec7d59c29be2c5380e3c15ef34a3543dc751e2182a4e397c6d02b0df746741cebc41e6d59f045e8a71086140c212aae224d53eda4812d3ee2c120ceaf7d82814a163dd23682f6902320372e3dacd1cdd4b2d835d493300ec572944d44ee0892d712c144c4706ac47a5307abc08f9f0fd905a6b26af28c050232fe707e08c8217774612e9d5e9206697b8612bf2bff7d50282683ca6d3ecb088bd3eb917e37e4a449a294c6bfa5b6519a0dca0101872c10efed1d9607b102cdacdd893c72e804a96781ad08c89966fb07591696a389948363450bbda9b903aec4d47be629143d7ba1b2f260b88dcd182d345aec4a928766c5bd89b35980fcddae4b80f322e9d2b87d0dd9539330d82c930b15e0bd9b05a73fe6426e9da685b540e3c1d35448aa4a0119bfa30c6326877abf850ed1822ae000132b1501a69865329c74a2b03eea490f6fd1891214d73ba6a4ca5e0db87c70c63a619f7075d348969eb46bb9f5d34926df75586fa49f3cc3975053eba33107a714d627075883bc44f6267ce16971ac5f0ad3c481f421afc9c1a12aef556392d2497ef884abc7a8b8fde85229d42896abbe913b23ec32a136ae3117fdbeca943f91db8c5c41d0037e8a7bf42eba1df45295ed7cc876cda4b07e25106f97435614200e2a74c5781987770c2d0157e558cb64c20029d6774853e2c5da3da0a0973d4bd53e35f96c171002e4312fa6c518fc909690b8fd06436e5b8da9dc297c8cdb7146daa29ad15eefb0f2168b8a3a6c7243a8714106d35adaa29206ad2509ee61f082c5739385d9fcb0b2ab0f5fc8dab55dc670484871c69fcd361987ff1cb6797ae3e51d9974ca6b5087f9a229519e9008ccbef4ab6daa24fcae95be3c4a1167b5e1c460da56b642362e7ed8c7eba8100d793e52bab2082b40e3d8340905dd4dbf660119081160739b281cd16f7a8bd020f69e06aa79505870771fa4219008b9d1ca1b0bd28afe029f299e037dd82c0aa17781009591ce628242f74a0ba1526c100e26caba892868da9680b07904545aeda84154384886a4d55118e7948fa2cdaa586f29c25e4fa317ecd3a995c793921106a7d8dc3b2fe3432214bb339614a579661dbbbf07104afa6ad0201ad4b6fd80e90427e6102eb1c02c4d62fbc25464760b6368da0fcb26cb5903eadf6d284328d9867ce610a026d65c1f80a4c7482799e1d3827f945dc6f4a49d6e6667ac5ade72d8804a81830d49c85bd9c431d38372a90198463d2f17a10f1961aa01d0495bf6e8c098248ae5d0740d7c7b266481438e49bf44edf5f9ef345920339a60495c70613aa2ce2988ca67e66572267dcb1aeeaa6146a2a6e69bbe60243fbe2141a6ff802bd581a4d8b351558608ad1db2f0a818c7fea8b49d3e3b17779ce6b5a84a100f368826b15462f0b9f466a947171ac8c0f214f5e25d2c9fe3374e4b121c82d26b45a6fd122662880966b07ca3ea54d1a6c98ae4071866a2c028bd745fd5c91eec2164bb00c1d19131769d196b8f513cec2dbdc562ca2d82261860115d693e232ada6f8446c073e4c1529d34479a5640c648b0b74d1da3a480941ea584d42608bb8e7ddceaed49a9d81d30654866abb70e1924db0c615932f463b749253a4981050ea498ada4edd6728a44538f7982e1b6927d637da0ebb593aa5fa1813c7834027309d1212c7fa29f7204d1da46de261250ad8c0bb725f46742c14f0881d88702fa43efa706406e7c5082bd82a03c92240a01c115a6faef5830ab109491aeadb01ec562b67fc5ac3c529a9db48369788369013d4d298ac375ed33e44371a31b21585c7112c85aae85ea7206c28ee48a7c253561efd56d7652da8b72d082ed40ebc595d6bd2942440ae674804016a6e5d
+InRec = 0040001000040001c00010000c0000800020000c000180001000040003c0001000080001800030000c000300002000040000400030000c0001400010000c0001c00020000000034000000000000000002000080000000030000c00018000300000000140001000080001c000200004000380001000000001c00000000c00008000200008000080003000080003c00030000400014000100004000280000000000002800020000000030000000000000000002000040000400000000c00010000000008000040003000080000c00000000c000180002000000002c00010000c0003400020000000004000200000000300002000000001c00000000c00004000300008000000000000080003c0003000000002000010000c000340002000040002c00000000000038000100004000280002000000000c00030000400034000200008000280001000080003c0002000080003400000000c0000800010000c0001c0003000080003c0000000040003800000000c000280002000040002000020000c00000000000000000100001000080001000000000c000340000000000003c00020000000030000000008000040001000040001c00010000c0002c0003000080002800020000c0001400000000400004000200008000380000000040001800000000400014000000000000040003000080001000020000c0002800020000c0000c0003000000001000030000c0001c00030000c000280002000080000000030000c0000c000200004000200000000000001c00000000c0001c000300000000140002000080000400000000400010000000004000300003000040002c00020000c0003c000000008000280001000080003400000000000038000300004000380001000080000c0001000040000c00010000c00028000200008000340001000040000800020000c00024000000004000380000000000001c000300008000300001000000000c00010000c0003c0002000000003c000000008000280003000040003000020000400034000000008000300003000040001800000000c000300000000000003400010000c0003400020000c0002400010000c000140000000000003400000000c0001400030000c0000c00020000c0001400020000c000300003000000003c00000000c0000000030000c0000800020000400028000300000000240003000080003c00000000c00008000000004000100001000080002c0001000040003000020000c000280001000000001000020000c0001800000000c0000400010000c00030000100004000080002000080001800030000c0002c00030000c0002000030000c0000c0000000080003c0001000080001c0003000000000800020000c00018000000004000040000000000003c000200008000380000000000000000020000c0001000030000c0000c00020000c0003800010000800004000100008000380002000000003800010000c0002800020000000008000300008000340003000000002c00010000c000300001000080003c0003000040001c0003000040002000010000400010000100000000140001000080003c000200008000100001000000001c000100008000380000000000001400010000400020000100000000300001000040002800010000800000000200008000040003000040002800010000c0000400030000c000200001000000002c000100008000080000000000003000010000c0002800020000c0001000010000c0002800000000000038000200000000080001000040000800020000c0003000000000000010000300004000100001000000000800000000c00008000200004000040001000040002400030000400038000000000000140001000000002000010000c0002400000000000008000200000000280002000000001c0001000040001800020000c00038000100008000040003000040002c0003000040002000020000800030000200004000340002000040001c000200008000300003000040003800030000400008000100000000100001000000000c0002000040003400020000c000100002000080002c00020000000014000200004000000000000080002c0001000040000c0001000000000c000100004000340000000080001c00010000c0000c000300004000180003000000003800020000c000080003000040001000000000c0001400010000000010000100004000240000000000003c0003000000001400020000c0003c000300008000100003000080003c00010000800038000200008000380000000040002c0001000080000c000300004000280002000000001400030000c000040000000000000400000000c000140000000000002800030000c0001c00000000c000300001000000001800020000000018000300000000000001000080003400020000c0000c0003000000002400010000000008000000008000380001000080000400010000c00
+Key = 11e452a7b206e159ac85ea8b6553d01e246d084cad52fb7b29028d79962091dd
+
+InRandA = 16598e075b0e7c09cd899080a945dbe249066492b8c9829f42e7d131c5017540839464989c68ba38dd2365e97c6b03c83f2fa68c84cb8c70a05e961772ccaf358dbbea4f043fa5d8db73c062932cce55f07ec98cfd84b1b2d13c33e94d94a616318b1e400f42b7424e256655d1d810114842d92882dd4d286a2f9ae8e065bac291bb133d462c531c4b902a664d5d31817828ae784b9dd1cdf193824f5578345a33b34a07e7add5246d933330f9a25b72c79f57920da7be25408b26b42a142248c4bec048ada2ff02fbd9414ac16c490ca636082e27c7e502a3a027c822b0bc558d0a48680d89ace313195b8bbd5e7d144d394890594a07e8a6f266562c628744a1e85492e97bd9444d7a69dc74c3005053d81ce2f358d54c84159f48c0f26c3e58aa2a3fc012fa5ada92e0ee63a39c189b61b129fea5ea2243e077e598f9e2dc95a8e31c632ab1b975740685e5d389b785cb95e572e6951f11e401d598b359464a0645882375c667e416c4b2b684269235a29890d85cf6c16c554501f709d09654a6001ac7a2ae52e3c3f275e01eb0366ff80bc34d96a6a877a925690ead51da259710bbcc1c4675b9a97ce6e8b93b4a4c93e7d95ba2ca42adc3a0a5431b05b0e818a04a655db5986514d3b9b905e50c0e9919818efa1d867ab90e190b0fd55704f18791143822a771711289a7009d2c0eff5e09f99c520ce791ba00c8046a0f5e2f30af489c565229f99bc66a42608345769a353f4079906d6d27334a5b11eee821a398b56ab310791d86d5b2925c62ed2d17e28fb89720d180654c394de526dc1558545a796807d33f86ae96953d06dd2a5913a0b9a99846960ac6f6453a125eaa0f4e475a82149485569bd5261084e76dca5291548ccd22f4a2f218d5084342387c1a78a04403c1b8215a9899859e5c1c75e359cace8069e4c85aa8b6a846218a4338ac58447758cd2807c8b1cedbf0319fba190d49dcc554788558c943c65ab65edb113e3656ca1ba6c6344910d26a441f41426afd7bfa103353037057396ad70237d854ee80661a406d0b85c29bca460cb44b5e90e500d10c9dd106d1a10d49b4046178ce003b18eac14517c5aeb2854ef8fcd6362a0e3513cfd24c1f151045f0f1a7fb1554283a5682ed6e75f1a0edbad88f822632b90be8d458c6dcbe1f865f4b265ab27e2fc4bec75eee311261e3f52264014656094205b193315be6a3e5114663429e8823697d504b71fa9ad615acdd36625720ab40956501d998a3fe8037c7bb75032a4d53767b701ab3c150179bd50aa5553a4a272e48c71539d7da256e041ddd9a85b0a8d9dcdbcbdff94d80a4d8e5cefd292fe938f04cad77ba2ac86b57dc5660ee05f7220812ba6a70a2ca1cbc5f1736a5cf293f1acf96268df65e3e92362843d81337c88053aeed7ab2831b98f8494190eff3272d687c171af50468c49578a05a7be6c237460f93f9ab7553981761d483064233e7d4fad5104f5b001ac03ede1776ec0096ba323b765ea74a8c2d9a0d91e130ac9a4807dc6a8376fc362f4645d614d80f3f55e2cb49866a62ada62ae74d9288160bd5cc1d49b88a3326c5f447f9d47405a1b8b9eb23bd03c03e207c09cb59cd4dab8755d8d016bd198e45d1d5ec6017a61fed12b719970fda2a89874662a7e16ea11b185377c37977a1ed0af78fa3042dc17e192e4b3b2f7212964e36d357555d445348c2759d8e3bc1ba94d9e047ad8a40a05bae9f918187039bed17086ea457d1954d4c5575e9ca58040c129bc6fef20fe628d6d8eddead751729fa7d710802db77c1cdc47176512b098fc524024448230e4e95880fb5e375fa7f8300ff67b05861b605636aa04c167b4700b50656054c4ca323534921679aebb3be185412cd9ec0218046e224cd1b886d6a74d0e4479fe502d7038ea08a9a67da97eaf09e59046f6efa65348085d80d1832ed720eefc294365dad860b4afb412a0d7109e90f224cf87fc84338e15ad99e4d523651e0202aca201e98c9e440a4a07274c8dc95146a2e37a8841b6b6d48431f88373ecb7ff13b2c1c366aacf477e58a33af37c3ad1e8e708a10e5685878ce5db4074838b68907176239b6bfba643d88aac92bde285cf142952a135fce2308d9bcfcdb21955eb0342b6f9f18caf8b6f408c6c15cb976108097ba52ca2af509b489d379b27deb0470e6c40cd62021a74cedaae3dda546f6b86c1e5271d011d9145c55d407b359141d5cfeab5c820f221d199764fa369242efdae1210dd7e4cc66c3deb0ba8442bbd27c0e688415f2ddb961e72044b8846c911816b87384260dc81b717a5eed06724e8d5334008fcce70985b3c29166cd5f01e2901f44ce0f75ce0038d48353c2e7512bbc5acce012b90984d2558386a2a33a2e69a3c68e3daf841876a4a668f25cb061b1dc289c863b050351a0ddd787e3f9711d92e61037f180b0820adab8afd5a5fd2e7f198e6e1b9c8291012ef543585371128c62917167ba43a96aa42b296958753a91d16f5cba922d58cd8d2d1621922d
+InNoiseS = 819fc864be3149721adb2aafc882e380dcf06d756412c8a01283e20e6ac1a396c0e14228016352fab19a6b41009a054ebc5ad9a4dbb34014e1df2a7388be6d9593d71860109479801ea9c464ba476edb713664a3245822999c2d8490db2aa7874aaaaf4cc0a1a3fc4617ed5c86f25985078fe7d6f791984d55944621aa86fa6d07763f2e57122e50d89235b3a5ad9bc47684b5f1a0d9005b059b70038f319e31137bc116d7229cab130a331403eaa76aa2a5225b7c9e7f57470b029ebd8a402f55a83e10d6290c391cb90c549c1de65bf83ac64253674c2804701c5bb1e616bc806b72224d8d1c43e049ca146cae42c75569c6797073dcde9028345ce38021a535daa7288018e56b16406a4e8869bbc656070e2699625d14ba996e81431a2d0a14e2405a082e40e13fa2b37a1ecf9a3923a68e1d990b812a1ffe9a674df40029747d846ffc894238b9279901c21489b975aa670448a4766a484918ed7551f9ee7a66bc26b88b002c7bd8f51f9b8c42a8e3ba214815bd687648459f10f2a391856ec3c5bc1a358980b1aea10ab26e430663ecc38018323f2c36daafaa1ce252774913ac30b6d27564c5c20e11e74606b1c1075ba60e451756591d443ec60ce49398d7b4ba3dd000630aab95b8111652e0c110fc04059a68a5c17c5e0a8382f3a32de475f01082a2fe62264b6841a44da669475348dbfda93a9304c502abf4b1710a5b27cc39a8f0e705a4633c5fc65951e4c7f86525c0d760079a75aa587a32ff0e9b82ccfb324a7e8abe9eadb940e10da2ce00f82362b26e8447a1eba21643c0b0e02a2d1190ef51db4a5969aa5c50d2bbe64d8fce2186f2647dff4aea76b78d33fbdf7d74bfca0cb20125eb5a74b1b26d68e3531db09f634bd134603438492df7c4d1a7402f7559e0a4ac8b8e0c417569478eae95226a747d7ead94390c5633041b27559d1e27bfe9104209897e3700edeba72d6f496cd87b7d446a1569b490dd8abadea4096b67396ec8c269bbb7f264bc9d9af9423829a149c3433b215528a90bb1a44878a2c84a35e23220eace1072dbb03c7c1f6059da627e9ca408c572b03ba1ee726739d8b32229f4ebc87c9b7fddff1652a24d02868c50f7cca61da9fdcabf6e733a1d90fa49d66fe8e73d80b0b6a567ace5b62b4e12cf1c5e738199881795ceabe2a517ab2009a90999ac002a40147ec93511e5b484b38213b09200549e1e386b9aad924605721498c0c2105a5129c0877aefdc25319380735eaa680a97c0b51abca6605a9af4d9167e87cb14a6c7424b4d427fe4db4e5fe9867004833a159069a3f6592c232a4e2db38512bee2a9b991d415352b26d83115e92d6567e0d943c67f8ae7b694c1e6beb1e857267311310426713a842e46aec68c0b4e9825a3133273c76475804aed47f31c36f8eba860a600967953152372cc1cdf893e325ec92d14ca45a9135a9c5b5cab4413fdac9f90cbd74be6beeacb6b248d328f081efbe6f268672cc244c04fd6509f2df12628b4a1c9e614eb1c6157d8981e9a6a019e2cd745733765582e92a518b2785056c7e932fc860f162c4c62e738baef86e588e3155515e0fedb0bcd997094d93b122d7d54c01296704e38b4ee47d0b67d5cc784f95a3b845969c1cf9ae3364a314f8aab0a19279c1784f6dfdc77c4e65fe5f43a77a8cabed319cd69ce96ebe04b254465da81e561103b242a2ca971927371d985327b850c998965327d18273f66c4f98281070e940ddbe46e8a4dac8130c4690b561f7193b3518cc464f096e720097dab1a17b4d4bb2ad6abf84b39246f470fe4589410a52888c6c00163d16df7c2225ea8071b9154a0fa340a29cc4bae8a37640c86f9ee7ebb1fda52040b09979a85a1b09f5432c884296191167e1802949857d1490c862ebaf40104a9cd22425338a27de46c08e34299090679fb115d473947831052dd883805d70625944e1723de9217857ab05aa2f70461982527d7d07db403b960800991fa06d7299a77447043eacdb52036082403867f0a182031179cedf1de9d0a5da71782718d6c874dd359be309f7090a94835e10f732c9dc9dff59abe1ed8441680117811f2d909d53a29a26bf47756444424f548e89abebaa25652e10c6373127e489d0d2123e278d05c842ac99ec321b6ed6af94eafc6216a54f62f00e0dfdd44b40eaf85cbed5819c0e553537182b1d9f9548c0e08845ffe8aa53631f76814d759a55fc9d020d2148415b61634864e6e590f5fec73cbca732c1fd19329d90e7821dd106515442d4698b957f697b2c9fb652826283287a064ae861c9c96c58261c20c51d642c5e0e09e84288dc2fd16550787a8d6b1929adfeb1b2392854977ade19af270825f9a20c6c2c5685b08b4f7c27123494a0365bdf50d8acc29f53afe2ee3a743c0b19d7d6613a39a19a20884d98086a5ae633e1a150a2fe2ad5853614b3ce94fbe401be3223d260a5a6994c321366e6daeb06f166989acf825aede985d4ab7b1948d81f7eb27e946948f5dec13765bd203
+InNoiseE = dc9f14a1206da08c2b96b75e5d74d3c5c6b5f5583beb6d67d5db42b7529d062a43192d945a769500e257641e1eb74db99eff89ce0812c958738c45d753c8389d535e1595467b57cd6e75ddf9ae2eeabec6d119545bee4a6196916c14ef934756eaa4d015a783ae9940728d687223447c041c8f60fcf4440cd1c9f138724147a17c2406e57b5a054230653890a014e2200fda3ed3ad0c53ed18aa00a861b66c0c1be983737326320dcc84f914fde4157c48aee7686552c2261e580d988677c1ea18d7e04b711932d0789075b3d67ef34b1e1745c7fc597dc422258faab8edd85d2917b762136a62d5a61df989629f3996cf24d7d240d4d2e5270b5927b727a242f16584c15a142a5fb87b955c5da059b82bdd509374d53e90c5899504c506979f4a000287b8b7f3ce8d64bd066bae117ef3b4cc0caa97dd8614f02cdb57559a11bc222e5cf882026c3597471cba5cb653c665db66abe67073cac8d2c8325d5cc5d259a5a88a1a03e6f8d7c9a7991b83f2fff57054acd8e0434d1729989d0b7b4d27802550654719b79a55abe58736a4646e36d2a5e19307517ddb4b2c69f9203a18b07cadc39603c759f100b28f9c744345085e86f578829a3c0c0136c75dd209615cc399276171e448b626181aa575a7ada14b7865ce62b06363995a0a0d78e07414204b9e1af8d78c3a27d9c9ba54401744ccfd969efd00cf4f5e96c2e84510006a52908881b0a04ac9bb31b2234f769791e92dee6dfa77d71aa97c56caa98b3e4b5029324b570da91d0a4a64f4f518e4e7d660a368514a8750306af83e034d4e975f58765381a7e5dec50c91a68548352e8fdb40d000a50da373a1ddf1eb2227f6004410fd351756e2c760db9e038e9a0d69820c69ea177345b70e72d0edae137446b6b1a7d584fdaa4a50e5af7ba61c5c1386fcfb2325b87546ce1a7901148edd875b5e1693196d7f29c67caa9ee9e604ee0051792302fa7ab812a86b811590deade5a89be2e1c61e6942c8e0bcb2a5435dac7333688b98155b52936c79b3e67753ddd612e5486fa59d92763fceda460d1d59381203fb4bb13b8567f8c5e67e4addc587d9dc78923f0ba902cf015b43a7976751042dd05d3fe87fe0abdf41a4272de09d090831035d094c05528101dffc53501698938bb9a0889fbe266c1a134cd13173883104a5c830f49671923bea9f66507653f2e59f20ffc9bffe43149354198f24d52118bfa0a07f551eba245d46328f5bb79920816d025b28f90d68dca36bca1b393db89d028ab2264c00e3a03a7a74361c25ea962bc147aa255bc3bf2c77d6666e90d304b1298044ebb50076c69f08669269b2b2a7a846ae7687facc9d99d61a24c7d891b6e3e6a78be9343b1f269d6ea8f6a4d588115051bb453c2d34346b7a79a58556af009789ea260e668d721a2dd14f429fd991d2dab08ca44234ef7896eb097f2841493f565f5eb649a0b39425af5a19d089e3c4f8381c2e38dde698602229a5a613884a92517393de89e3f234930fc3c7da7b7299566678371b62a790c3cd64565e13279015e537fc63f64ac4b96ee285de818ea8dcd6942862c83b377cb0d609db996ad60c2d5023ab5c6aa004acb86271a03f8e5c8e514d602662e42a106a7602ede316d3a7e919b7984004d2aa8494644a5390418174668b4b48104f86d795c02aa93768c0bcc97721af092017faa5720a93ac8218b3c855460c529b5184a70fe4ed355b6e1fe00dac5942995d9c6f3eb5501975e22cee248321b59b563c913b7027bb10f3716fb294d6dea9a52bdc8258689461c92016d8de9bbb8390344b9506e07f2d2c95245a8762827f034ab6e53f38a8aba872aaefd589ab6c86526f6146e94af09417a48a04eae574ffe533a3c53fe9027a0401aec5453b0213d49917f1ff3294cb9d911540a88b630cb7a4bf0057ff29b983bac2102ea58acad6ee9cdb1536cb7e7203b09c41bb856471b9d0ab34d965405f6ca24854f4188b2854e20fad6ae8c404438b82214f62cb51ed5d5215c430dace71863c580653ad309f6f57bc0904fe9eab19ba11ec50560ac22860d8086a8388c4c2682480fd279c065c49ae208d96f813669141199af5786eb58e94d946db10b4dc5dea981fd16eee5dabdb6a464ad963c55204f5ae74085cb02e280e10a6a588fce4b437c804550058a89206401ad81d8b75067648d3ec385e469977e55e9fe2a37200dfdeb3b02ab55178ad12ad6ee99572423088aff8253681a19d4a1d699d554b1ba265a196717aa00d4783b8a47c03a42fa06c64a864157884e740378be46f772c092f6fd06f39d44850639e8dd19af8a2a1e8344a2b786bf7d7ca2450c04965e049919acc92d2d2ca212b5c3be0d035010bc478e028f390147800bb62560974c9222cd0e455d7ca5b6f6e5b87b386a6ac44b054f0f416550260a7fcc22465e7cb3200e297144db7e1f5609ddc00bb25bc753329a4c764f29c2f6bdcafd2fba66e6809c32124c5417741744090ce1b6828d00370e6b760a09da6f73089
+OutPK = 7b4db63984b59c5f4c76202fbdb360ed6a27ee1d9395e7721787e4b0d7ed20c9937214a4690ae10bce3a621f08d37706945b1912e0dfa911bde2a1099ac611131dcff41abe5a16d33978094874b852743799d6316c873bf2f8e17827ddac9b33da6a4a5229e01e6d85f1973f4aa7cdbb45934dcb680a625fc4a715dfc0804e8a2f644694390c14e053a8ea5e65a4168a52629fece156b3ab995ac385c7a8ab0c60969ae05563956fd0a3f1d5b009a05a5c1ac384f85c8bafcb9bc3d856de6f39e8c7f908d7acd743ee058e38cad1b0640c0ccd2e5a13e640bdac1dbac4a08cba19038f49175817e00d69092b26395a96be87af3c8bc08cddf4faf21c19147e08eb1d796ec96c624b657687552b702164705782cda3142213254414a6bf0d17edaa692690ba78c4e46ab936d03a7dd785f27576754a0dfa5079517c0b264a20f65c1ea211870a0af071e2234e967a70945e01fb9460cc9b49d66e1b253a2fb709d674c45e588f2420013abe918e4c09f9e1e9aaf0d91daafb158193c14e8622b583dea1a3a8f6997ac5cbf640d0504e3629b1387e70286a244300b4141cbf9e45d87ec26992ecd7b44de2a2b02fd2aacc9565c990c0e300407255e04901102446e3d128aa684d878a1a5d087b51af20907ddaa44190d7113d7af655b7e2244a96b1117df9ce63e5d4612ed0d953358e81178b65f0c46d5e8abf414518169e5726247cc0298d140f2ced8059baadddc21a1521468a0887b09860f8011defcf06879573aac97a8084aea2b915cab6c4f900de1fb7e164b97976219178e4a17abc1a8c3938e1744a240f7446627549176f66c2e946becbaa200c90ba07a892607910363558ca43a9da560b8742667dc981b842dd07b76d2bd1abdc005bc1b1b587b3a3ae4d39bb4d7c218ad27acd522d71dbe953964468e15cb5464042b45118eaaa5fab1d3bbc917d9b57b33be2e56dc062804bb5064fd48b6d68dad551e12ff329fdc5971813121aa4c48fb75f30236d4946d593c8a295900f6a49f143d62a5a682ce686eee16457ef90380cb61d09a1ca514c4646ae5f0a1448c5bec2996f1328da72809e3f0a13344d5e6699786b386211e170b44cfd985ed473ba2a962e4c6b6170a53c638584dcf0438516cbac9ac1c028159087d17b8b403a949a50a0d170aa7d66cd5a97c83231012fa4c49a724b01caf7839a45655df3f7f9caaa192d94e9b40d05352137a9d2906712c25bda8c5522a59f8b190c400b2febbdcb1cd0154e1fe59935842a3cca3aca97fa46fc081a08a37aa6b310e6d07f3694a60ee142b9f86df643256888ad5540817fd3abb4a4ed88b4e8d5b983d8db27e517d1964cdbb377e6681354c23a016dbd15ed9fd287b084b4682dc6bba72e2de24eecf9f5cd0a378bdeeb477da863408e707af9b97424832a69d0280767bd10aae8be374897d6f70a107acc5aa5e4655c32ccfb288dfe6dd0c44434bd15b9c7a02a2565f96d7ce38126f3a425ebd133d55d561bea5a2db19619167e2614a93ca8b0bc4576cb1d225ca8c990a36a2ea86d4797eb71d242f3e04b6177d932dd4ca7a01974636342dc239a5d393f141f610fbc35b243c057a1fa80a3d83c10620c6c880cf9a354c25b368d98d636f1521524c7a3a3be0757dec839ec3c15cb66efa82c8931c384581a6d324e13e471545515a2a008d9c24b857e32659980a40e3a39197029b8e452aac99aeaf5b58651c82bb0ddf68950654c4be82837d000daf1e4c89d1691c757a4cd57db8e0389818c6a80492db4544e6ab764a0181ccead3306e1c0f2665d4e28d09ba7ba513047c501763678ca9bb0a8a4967e4f6e6760d7ac58e5e2ccdbf5784ec84a148052593d6c89b4b3c70411e51bf664fed6af66d220838cb2769565667b2b0a3b31c598f8582078a25092164275aab4178a6a1d083f0f50237a5fa63d3080ab2510b04cd1c42af9ec321d566d4e6851d0a919da49323d196a314d83d1aedc23b6d5140825e54cfa8b1032a0a88ec1a2b116eaf51d0c050c9011862c45c69c9106d425bdd561a415100d401e76a728de8c3198d34607685d290f30c5c3fc07ca07d8f779ae929a0bd3c17eed78e8c472892f3d05a6c8051646c4912f11efba3cf0a561ad7b3697468b2f5be4b20a68884a82ed4a2978b49be5983bc16462b3ea04453521b824c33aba2e5d0e4a4b9c753da12c532ba88993f4507561f2fb2c7d9339043f727ba4749d97e350de064eeb539db2c10d2282adb54908adab2328dff4082d7d85eb194820ed3bbaa9aad149e6055d1ed5335ca8adebd9703949ba1a3981bcab3816a0bee550455fa4343a9c9de43ba673cb1d1f301f9a48978cce66895db5be1ae4e9eeb142f405863228b0bf932afb804ac87f5d8b3eb4c1ad4f4919410b998820f86e35d7d54d60292897130646bc9a888d65e3491bab5d5ba4cd0c74888b94e0d66445e9ef58566b568bd6847bd87e141aec81e3009ef60d4adc1412ae3d81338be513737c6244aa3478125159186387db3d8875
+
+InPK = 7b4db63984b59c5f4c76202fbdb360ed6a27ee1d9395e7721787e4b0d7ed20c9937214a4690ae10bce3a621f08d37706945b1912e0dfa911bde2a1099ac611131dcff41abe5a16d33978094874b852743799d6316c873bf2f8e17827ddac9b33da6a4a5229e01e6d85f1973f4aa7cdbb45934dcb680a625fc4a715dfc0804e8a2f644694390c14e053a8ea5e65a4168a52629fece156b3ab995ac385c7a8ab0c60969ae05563956fd0a3f1d5b009a05a5c1ac384f85c8bafcb9bc3d856de6f39e8c7f908d7acd743ee058e38cad1b0640c0ccd2e5a13e640bdac1dbac4a08cba19038f49175817e00d69092b26395a96be87af3c8bc08cddf4faf21c19147e08eb1d796ec96c624b657687552b702164705782cda3142213254414a6bf0d17edaa692690ba78c4e46ab936d03a7dd785f27576754a0dfa5079517c0b264a20f65c1ea211870a0af071e2234e967a70945e01fb9460cc9b49d66e1b253a2fb709d674c45e588f2420013abe918e4c09f9e1e9aaf0d91daafb158193c14e8622b583dea1a3a8f6997ac5cbf640d0504e3629b1387e70286a244300b4141cbf9e45d87ec26992ecd7b44de2a2b02fd2aacc9565c990c0e300407255e04901102446e3d128aa684d878a1a5d087b51af20907ddaa44190d7113d7af655b7e2244a96b1117df9ce63e5d4612ed0d953358e81178b65f0c46d5e8abf414518169e5726247cc0298d140f2ced8059baadddc21a1521468a0887b09860f8011defcf06879573aac97a8084aea2b915cab6c4f900de1fb7e164b97976219178e4a17abc1a8c3938e1744a240f7446627549176f66c2e946becbaa200c90ba07a892607910363558ca43a9da560b8742667dc981b842dd07b76d2bd1abdc005bc1b1b587b3a3ae4d39bb4d7c218ad27acd522d71dbe953964468e15cb5464042b45118eaaa5fab1d3bbc917d9b57b33be2e56dc062804bb5064fd48b6d68dad551e12ff329fdc5971813121aa4c48fb75f30236d4946d593c8a295900f6a49f143d62a5a682ce686eee16457ef90380cb61d09a1ca514c4646ae5f0a1448c5bec2996f1328da72809e3f0a13344d5e6699786b386211e170b44cfd985ed473ba2a962e4c6b6170a53c638584dcf0438516cbac9ac1c028159087d17b8b403a949a50a0d170aa7d66cd5a97c83231012fa4c49a724b01caf7839a45655df3f7f9caaa192d94e9b40d05352137a9d2906712c25bda8c5522a59f8b190c400b2febbdcb1cd0154e1fe59935842a3cca3aca97fa46fc081a08a37aa6b310e6d07f3694a60ee142b9f86df643256888ad5540817fd3abb4a4ed88b4e8d5b983d8db27e517d1964cdbb377e6681354c23a016dbd15ed9fd287b084b4682dc6bba72e2de24eecf9f5cd0a378bdeeb477da863408e707af9b97424832a69d0280767bd10aae8be374897d6f70a107acc5aa5e4655c32ccfb288dfe6dd0c44434bd15b9c7a02a2565f96d7ce38126f3a425ebd133d55d561bea5a2db19619167e2614a93ca8b0bc4576cb1d225ca8c990a36a2ea86d4797eb71d242f3e04b6177d932dd4ca7a01974636342dc239a5d393f141f610fbc35b243c057a1fa80a3d83c10620c6c880cf9a354c25b368d98d636f1521524c7a3a3be0757dec839ec3c15cb66efa82c8931c384581a6d324e13e471545515a2a008d9c24b857e32659980a40e3a39197029b8e452aac99aeaf5b58651c82bb0ddf68950654c4be82837d000daf1e4c89d1691c757a4cd57db8e0389818c6a80492db4544e6ab764a0181ccead3306e1c0f2665d4e28d09ba7ba513047c501763678ca9bb0a8a4967e4f6e6760d7ac58e5e2ccdbf5784ec84a148052593d6c89b4b3c70411e51bf664fed6af66d220838cb2769565667b2b0a3b31c598f8582078a25092164275aab4178a6a1d083f0f50237a5fa63d3080ab2510b04cd1c42af9ec321d566d4e6851d0a919da49323d196a314d83d1aedc23b6d5140825e54cfa8b1032a0a88ec1a2b116eaf51d0c050c9011862c45c69c9106d425bdd561a415100d401e76a728de8c3198d34607685d290f30c5c3fc07ca07d8f779ae929a0bd3c17eed78e8c472892f3d05a6c8051646c4912f11efba3cf0a561ad7b3697468b2f5be4b20a68884a82ed4a2978b49be5983bc16462b3ea04453521b824c33aba2e5d0e4a4b9c753da12c532ba88993f4507561f2fb2c7d9339043f727ba4749d97e350de064eeb539db2c10d2282adb54908adab2328dff4082d7d85eb194820ed3bbaa9aad149e6055d1ed5335ca8adebd9703949ba1a3981bcab3816a0bee550455fa4343a9c9de43ba673cb1d1f301f9a48978cce66895db5be1ae4e9eeb142f405863228b0bf932afb804ac87f5d8b3eb4c1ad4f4919410b998820f86e35d7d54d60292897130646bc9a888d65e3491bab5d5ba4cd0c74888b94e0d66445e9ef58566b568bd6847bd87e141aec81e3009ef60d4adc1412ae3d81338be513737c6244aa3478125159186387db3d8875
+InA = 16598e075b0e7c09cd899080a945dbe249066492b8c9829f42e7d131c5017540839464989c68ba38dd2365e97c6b03c83f2fa68c84cb8c70a05e961772ccaf358dbbea4f043fa5d8db73c062932cce55f07ec98cfd84b1b2d13c33e94d94a616318b1e400f42b7424e256655d1d810114842d92882dd4d286a2f9ae8e065bac291bb133d462c531c4b902a664d5d31817828ae784b9dd1cdf193824f5578345a33b34a07e7add5246d933330f9a25b72c79f57920da7be25408b26b42a142248c4bec048ada2ff02fbd9414ac16c490ca636082e27c7e502a3a027c822b0bc558d0a48680d89ace313195b8bbd5e7d144d394890594a07e8a6f266562c628744a1e85492e97bd9444d7a69dc74c3005053d81ce2f358d54c84159f48c0f26c3e58aa2a3fc012fa5ada92e0ee63a39c189b61b129fea5ea2243e077e598f9e2dc95a8e31c632ab1b975740685e5d389b785cb95e572e6951f11e401d598b359464a0645882375c667e416c4b2b684269235a29890d85cf6c16c554501f709d09654a6001ac7a2ae52e3c3f275e01eb0366ff80bc34d96a6a877a925690ead51da259710bbcc1c4675b9a97ce6e8b93b4a4c93e7d95ba2ca42adc3a0a5431b05b0e818a04a655db5986514d3b9b905e50c0e9919818efa1d867ab90e190b0fd55704f18791143822a771711289a7009d2c0eff5e09f99c520ce791ba00c8046a0f5e2f30af489c565229f99bc66a42608345769a353f4079906d6d27334a5b11eee821a398b56ab310791d86d5b2925c62ed2d17e28fb89720d180654c394de526dc1558545a796807d33f86ae96953d06dd2a5913a0b9a99846960ac6f6453a125eaa0f4e475a82149485569bd5261084e76dca5291548ccd22f4a2f218d5084342387c1a78a04403c1b8215a9899859e5c1c75e359cace8069e4c85aa8b6a846218a4338ac58447758cd2807c8b1cedbf0319fba190d49dcc554788558c943c65ab65edb113e3656ca1ba6c6344910d26a441f41426afd7bfa103353037057396ad70237d854ee80661a406d0b85c29bca460cb44b5e90e500d10c9dd106d1a10d49b4046178ce003b18eac14517c5aeb2854ef8fcd6362a0e3513cfd24c1f151045f0f1a7fb1554283a5682ed6e75f1a0edbad88f822632b90be8d458c6dcbe1f865f4b265ab27e2fc4bec75eee311261e3f52264014656094205b193315be6a3e5114663429e8823697d504b71fa9ad615acdd36625720ab40956501d998a3fe8037c7bb75032a4d53767b701ab3c150179bd50aa5553a4a272e48c71539d7da256e041ddd9a85b0a8d9dcdbcbdff94d80a4d8e5cefd292fe938f04cad77ba2ac86b57dc5660ee05f7220812ba6a70a2ca1cbc5f1736a5cf293f1acf96268df65e3e92362843d81337c88053aeed7ab2831b98f8494190eff3272d687c171af50468c49578a05a7be6c237460f93f9ab7553981761d483064233e7d4fad5104f5b001ac03ede1776ec0096ba323b765ea74a8c2d9a0d91e130ac9a4807dc6a8376fc362f4645d614d80f3f55e2cb49866a62ada62ae74d9288160bd5cc1d49b88a3326c5f447f9d47405a1b8b9eb23bd03c03e207c09cb59cd4dab8755d8d016bd198e45d1d5ec6017a61fed12b719970fda2a89874662a7e16ea11b185377c37977a1ed0af78fa3042dc17e192e4b3b2f7212964e36d357555d445348c2759d8e3bc1ba94d9e047ad8a40a05bae9f918187039bed17086ea457d1954d4c5575e9ca58040c129bc6fef20fe628d6d8eddead751729fa7d710802db77c1cdc47176512b098fc524024448230e4e95880fb5e375fa7f8300ff67b05861b605636aa04c167b4700b50656054c4ca323534921679aebb3be185412cd9ec0218046e224cd1b886d6a74d0e4479fe502d7038ea08a9a67da97eaf09e59046f6efa65348085d80d1832ed720eefc294365dad860b4afb412a0d7109e90f224cf87fc84338e15ad99e4d523651e0202aca201e98c9e440a4a07274c8dc95146a2e37a8841b6b6d48431f88373ecb7ff13b2c1c366aacf477e58a33af37c3ad1e8e708a10e5685878ce5db4074838b68907176239b6bfba643d88aac92bde285cf142952a135fce2308d9bcfcdb21955eb0342b6f9f18caf8b6f408c6c15cb976108097ba52ca2af509b489d379b27deb0470e6c40cd62021a74cedaae3dda546f6b86c1e5271d011d9145c55d407b359141d5cfeab5c820f221d199764fa369242efdae1210dd7e4cc66c3deb0ba8442bbd27c0e688415f2ddb961e72044b8846c911816b87384260dc81b717a5eed06724e8d5334008fcce70985b3c29166cd5f01e2901f44ce0f75ce0038d48353c2e7512bbc5acce012b90984d2558386a2a33a2e69a3c68e3daf841876a4a668f25cb061b1dc289c863b050351a0ddd787e3f9711d92e61037f180b0820adab8afd5a5fd2e7f198e6e1b9c8291012ef543585371128c62917167ba43a96aa42b296958753a91d16f5cba922d58cd8d2d1621922d
+InNoiseSP = 2ed210c039786d1ec41d71737a4b8292d1686c60529403f5780d3292398c5da63f30ba6a67115432a95b5b59e1c981bc40b755ee043840591cde37a3b3ba8e0c82c297fbf569af43d5f2ac2c50aa0fce00d6f23a155a57c1ea9d6ad9649a23e482741ca773d07f52bde1dd9206fd41783e6849666cba022e6c3b84bed2060e0a57a38a255e7998c9d4c32d0d22980652ba2ca899eaaa69e28eb92cc0b6f9ea48725d1d41a86fea16bc12cd9b7efd5be446221b1b023570da87c3f2d2b49804c9f4abb5bae0823bb8cd066c492f1b64f1c95bc2202b3149d520478ef8f2dfcd4600d4c95481d99daba1fc389f8898a16d2e03fb7842e599c414162602e868c968ff3d06170f3eb845583f05e18d02be726e02051d25a9ac48856f7f33c1596c8f86a5ca98352f4c95ea0a13769398853878f62052eb8ebe57637610886331d5bfc4707d58f3f8b50dbc63c0f6d6213da93189b7c65c755a8a516ac9ec7d918aa53bc191ec6879c75273e31972f3d5d3652ec824be0490c2814abe918caa37e65e4c5dc42316e67d8638e6f2aae10a75c3adc9f2a92a11f4efc6262e1d609ae3a2e3358242f36d08d047911699ee00c319e22b7de9c9d1a0a40e78cf0bbbde864c6986cbf2fad8a50e429c794ff6a06b1f65b275523af0a14b4917f97ec51ac8d1a8da6cba92bef44a28b234dfd1160684461090c72790459247ef0d782331735a01c7194d6923d65be972a0d811900b3ef3000879c1eadbc3f06463772b822104bd32520ee9131efe630c2e40e06cae62b2c95204a4f0b3326bc6036cb53103402925da36a1ed822fd63dacb220e5f5f862ec123946a0a000612de19afe6361e52eeaecb4c40ec617344e3f4af8b16f3086347363b21ccb44b0cbdeae894f0a85b1cb6e68b6596ed29f418cb745a63ace6033a5a19d5479f4a3bacd9a7b39413dd4a21ac132b04d69e62ea8841963715c5e81705e35f496a712ca0a025dae4ad3d3a43b03acc6c0dee55b27936340211c9d55a43ab2acd6b47fad52fac08e3f7e5af0b1196062364cb730a79d097308551b8d4424fe1aa15b07f95afd076dca7430a95a0e2e8b2b78a1d254c184ef4d9a7fc2ab840476541aaf6d71d24e98d40d578675e004a18a9c2b40e1a85adc1da3f892911b887a8270f6beba60fc599f640cc7d3caf5589c8a0d911f6a35ac1492045c54fec2472f076f08c8ae15f9a684e0dfa98eae1d0112854601c92ef8805d112370c143a8a570a80b93a4d01c32ba416740a930042224d47ca0c058822c088d01c13287ab5bee6310fa17ee0a45053f99a7a3672d24a4594ce9a6db96bb0a9858d037d9b03db92417f728c1e58bc24829f1c73465fed9a49aaba0ba2e9606b6e2c16b23054a571c5565729d80793fd875f70074fa8dac536d67b491152d15790233d9fa4234d4cefffb52106ff65b965a857e0e3294962a35e4a782d5b67b6cce0876600b435176703f8f2b46417eaf41832dc6a3e4a481daa54224ea4e0c2894633d5493fa9f09d9a4ea40108644e0a6d434a6edbc07316e9ca2cf99a7dff61e39d93fd408940047626fc0b70082ac612d04db5c40805b5b176ab16f4f483f11aac0473ce84b79ee0db1049db25bbc047bd56fad872a0aaeb6ce3f894d02431b24f49b6255a99c865a3666287e74ddfb6a5e40111824f44b77ceacc1caab124390928da4abeab49198cacf408a6fb118609735a419f94589ca8ad1928907734eaf76e5e69a2ca75564530d7ac991a3a211f433ff9006daee25b0245943c394951786c37af063d0306a8d56e6ab862d0d0069f459904edbca539643a69ad0da4f9ab1906cabd2a22263bd41aba937f9fea83355ae32e3f49c7507c79775c4326a2715356b1b880c79dd0d58177839a21aa6a2fd726a318ab2c849c2004041e591a0c637abedea31f9e97958aaae0393c48934a94fd9d9c0ae6d871ee8c7e8b8a92c13862ee2a025dd668393fea8b6d09106d320bbf0889d73422c441cdfe66aa0127cce695b3a59da27dee407b837c47b52673142a4799ec8eae5607bf8bfdec37748793052abe594c51e0d67f424d013a640222604f8f1a2f97fe90a6a9897005d75452be1b940786013d61085b50de4819f36a3e80a93a162705f961e9e0e22b61b14164f06b9f3a9e23ae10e9648e1c92c70a92582b44d012e9d79ea7d067461a72103a0744766c2f1625ade67726d8e2927e8a8bfc56f20d9b83ae9d7aa121ac2439a424e2acd2233230936a3d7908d699582e3da922a0eafc0a3b8912d17ec1b043f4801bd0aa73789b263c3d9b5c3e08575c25642b38128927b8004746365ba536256f42b01bf58d661d0b8b0b3542b61f50b92782a8ddad8e9a4865dcefd08d7b43b0960ad5af436253fe7db64396d83481002c8dae47cf8a9a4196bb4a51f00e5c462059a7647229b548e5d04c06966956a0548189a6a0d59b1c05c80c6751976408bf4d649de7e60e909d11c1a8309e1c955a34d8beda46a47597c7ef9e3c965915c09c983d8d5c3e22f
+InNoiseEP = d6d8b161e8b409512642cb760a3fd3d00d19e4067a48850e88533eb4c6489ac45bc0846650116500303bb19c96b2f8ea0f1f939fb26411bdb40a50654ebc5d4eac55d8b3dc5327eb5a642eed5761ac7695d02a1d266491d63e515518c1cbc400d2231e1d9163208a5c6ba32a471815aaee089ce4fe0c921125232908eea86059ea99e116b9261841e264a43aa2136dca03ad235d2ccf4095bc9ab1eaeea623e6af79db4ad67af97d73c34698ea1c0f13cd52d05c6a61eb284469f8b00af6c24ac6c5a4281ec9290895564a96d6f8bae1389cfccea802499c2e9e8be0cb50743c835b4e670fb82930c7fdf6e3da7d0e50ec0444d68dafd62571f1e4a2ddee6e8687b4b18228d3c21cf152f5e3b442cdd8afdbe31a7002f876c8e662f4682dafc5477f843fb44db1dae122e37945dd465b97ec41824364f79474b2bff2567a05a66eacc3ae13e333ea6889565189feca2050cb1f0abde21115dae186cd728b73e00452981a39a1e8aa04b0da22f26eb3cbb35c7959959558d6821d4b0a1ba426be7b6623646352947d5562fa4189caa8b812b5a89106274c0aaf02eae228969f946a2d1da600216058956d460366a9b6df6e223d59e0a30d22bbbc21d5427eb2b48dd367649b267330273e73b342551daed107bff228402033cac3fa0fa31e58ba56bd5725167e28852da5c58271dbee696785ed77a35f804e684ef452462d6444574362c99135fa59565b9dec543a25661121e49da42487aaa509b95d26eec71ffe4e4f0f578907fe6c40ecc862fc7e78ea5eb9366a5d1c0aa729da74e07b51dfe4ca54e03b04543f53a612012dda1ce517610b755d9f38d35ebce0cd89b7f2fc2386e5659be02a03e0195a05e1d222f2949fc95921bf211633d4de7c3f2ec7aa50df7592660a81b827b2030aead161ff29a34ea6df36ffaa5560d6a2e033226c30cbd2d776c9651dc42006ef140aa62fd37accc05cd307d2f64b3e0580a0df4b47751b3da41f7aec11219a9ea77baa3050d1aba37549704fbf6982bb294618c01f0953663157bea914ea6b0027642625a70ee6be3a899192378a1d9bd8e578b802237e558811f1a0610b5b0f6bbe3139f2defd8614a419b99597fbb38e0a088124d8f29c14661785888c5579ccd995461b1637cdd49ec49b700fc49671f37e985f72231255b62e78cd045b9b4d3c3d4ee294bad23194714b5f73dcf4a4d5612060301ea6b9931f81d8f5b4a32521fb0de6b9902b76f136796fd382d23163fc08e6e955e754a9b7816ac5172e5a41805d33732dbe910b862b6661bc72200c9a7b11821c65a90255077492a6d864aab89447016989a6ae4072084d4f438040237ea9d5b6f25152824f927afee5483a20750b994a3936a2466a05be166bc7a809cf88162e8eb1f22168a8262e36198a4519ba621b8787061564d013a07be92a74064b5bbf20a500a4eb4be5812b8d432d33a3d835630ae9d0d5f6a67c607b0aaf2d30ac6546e14f7e719ba2df78e7ca5ba25c4b6861be97349e7bc964860db41dba835a4403245b7b07c64d987698e8f02b999add2f0bfb42a244ea0f43e4c64cb5359478244f78a129c5c476abd5b36b6a82424bc07efa50e615f1cc65240c5c9216253f65c22042e1d1a7d4e657ca821c5f3da26a04ef0c96de9f60b9fafa56e19dfff8c43c173b12b8ab429e8dbdd3c4a978c89912d0ad98b6163604c52a6320d868391ec323e5c89d015e89e7a2a267f9168293d315a97a102a13c0a91e6f9d3f51925471ffdb0225c1f5b70094d211d9d27ef4d23e421bbaa1408aa478d4611a2d19f7e8b71e17d9aba5e39a2a620f6d7468ebca1b65488177590c3de42d8cb0dc3ca6d55eadd560158dbe53f380b805b34bd55d9255dc98ac204f470e06171e8a0a25c29c9eaba9719510dc8e2bc9026675060d4be392566b6c43ab83edf2dfc44cdf065963fa7cb11f152dabe87502e194ca241a4e823e110f53b1448f1fc09e204316bc8d4eb7559ea47806287ac463949d8a4dea242ed6495ac5b3aa85f2885d0d1935614a8d8c0d6cc8b93a7a3264741a0c8c3dcea7bba25c62678c80e46b52f97e2911b7056235138e20640217a6241097c598537e612b0fb6d8b7c665678e8c1b92f142d30b7e0b65cabbcee0d180dc02855f8ba9831612351bd84b76d9b89ebf4abdcaa5b9b7fe295fd6e5c8740e4ff713cfbd89d2a1adc8b9391ce76cd2a384aa35aaac6441fc522a47ae7cd9b11ea22ccc1efbd6853637448a04fed08be92fc748bcd98b1f8304a52756a7dfcd42f9a8426246def0bab7ee43dc238656904662695703265cc506b6d02568208c9ff5d006db90b48c9e22a53766717772ac05666d4927be969803418a8ffaaf76e799e069145f793ae4406d1bd7c8a7a9b11005ec0ab5fbc6ad4363b60aeeca0cac2ecf987dbc4222dd7f7398c573c166007b9f15bd90eeb195c40e72ecc62d274b389d2c188696bbcc9f26223fb68c448912ca34b1c43640f90f6a52173a9eba97e6a65a2d8f438815a5262e4f
+InNoiseEPP = 00f0ff2b0000c0058000200000c0010000600000c00180000000fcbffc6f01f0fff6bf00b0ff0b000000ff2f000c000c00feafffebff02000070fffbfffebf000000200000000200011000fcbf0030000c0003c0003000b0ff12000100ff6b00ecbf00c00110000400007000b0fff6bffeaf000000070000300040001000014000e0fffabf004000e0fffebfff2f002c00fcbf00f0ff2b00fcbf02c0ff1b0000c002400110000c00fe2f00d0ff06000400000c00140006c0ff0b000300fe6f01400008000030003c000800ffefff1b00f8bf00b000c0ff0e00ffefff0b00f8bf010001100000000100001c000400ffafff2b0000c000300000000800ffef0000000b000400001c00fcbf0000010000fbbfff2f00acff02c00000fe0b001700fd2f01000003000000ffabff06000100004c0000c00140010000fcbf0040ff0b0003000100003c00040000000000000f0002c0ff1b00000003c0ff2b000000fcaf00c0ff120000f00050001800014001e0fffabf020001000003000100001c000800018000e0fffabffe2fffdbff0600fe6f0000000c000140010000f3bf030000f0ff1a000180ff0b000400007000e0ff1200028000f0ff02c00080ff3b00f8bf0070fffbff0a00fe2f003c00fcbf00f0ff1b00080001c000d0fff6bf02c0fe1b0000c0feef0000001300feaf00f0fff6bffe2fff2b000000ffefff2b0000c00100004000040000c0ff0b00ffbf0200004000040000c000e0ff0200fd2f00fcff0e00068000300000c00200001c000c00ffefff1b0018000600003000f8bffcaf000000f7bffc2f00fcfffebf02800000000c0001c0ff4b00ecbf04c0fffbff0200fe2f00c0fffebf00f000200000000400001c00fcbf0070ff4b00140000c0ff1b00140000b0ffbbff0600010000f0ff120000800000000b00034000700004000180003000f8bf0000ff5b00000000f0fefbff160000b0003000080000f0fd1b000c00ffafff0b00070001800100000400024000d0ff0e000200002c00f8bf01800000000c00028000e0ff120000f0fe2b0000c00740ffebffeebffcaf000000f8bf007000500000000040003000f8bfff6fff4b00fcbfff6ffffbff0a00feef00300004000180fffbff06000080002000f4bf03800000000800ff6f000000fcbffd6f0020001000018000f0ff06000100002c000c000000003c000800ff2f01200000c0007001e0ff02c003400090ff02c0fd2f003c00fcbf0500020000fbbfffaf00200008000300013000f0bf040000bcff1e00ff6fff0b00030004c0ff2b00040000700010000c00fe2f002000f0bf050001f0ff020000c0ff0b0003c00100010000f8bffd2f000000000005000170000400020000fcff0a00028001200000c006c0ffebff0600008000100000c0fe2f006c0000c0faaf0000002300fe2f00100008000180ff4b000800050001200008000080ff0b00f8bf00f0fe0b00f8bffcefffdbff02c0fd6fff1b00f4bf00c0000000f8bfffafff1b00fcbf040000e0ff1e00024000c0ffeebf01000040000c00030000000003c0fdef00100000c00880003000f8bffc2f0130000800ffaffffbff02c00400001c001000003000ecfffabf0240ff1b001400ff2f002000040002400020000c0000b0012000fcbf010000fcfffebf03800040000000feeffffbff0a00010001000000c00380ff0b000b0000b00120000400fcafffdbfffebfffaf0010000000ff2f001c000400030001e0fffabf0080010000ffbffeaf0010000400fe2f00dcff02000200003c00040003c0ff2b0000c0feefff0b0000c00180ff0b00070000c0fe0b00fcbffe2f02300000c000c0fffbff02c00200001c00040000c0000000f4bf038000400000c0fc2f00e0fff6bffe6f00d0fff6bf02c0fe0b00070002000050000800fc6f00d0ff0e000080fe2b00f4bf00000000000c00050000300000c00040010000f4bf014001100000c000c00010000000ff2f000000ffbf000001d0fffebf00000040001400020000f0ff0e000000ff0b00ffbf010000000007000280ff0b000b00fe6fff0b000700ff6f01f0fff6bf040001300000000200000c0003c000c0ff0b0007000780014000fcbf00c00010000400fe2f01f0ff0600ff6f0030000c00007000e0fffebf0040000000ffbf00400000000700ffefff1b00fcbf05c000f0ff0e000200000c00f7bf01c00010000000fc6f003000f4bffc2f001000f0bf0030010000030002800010000c00ff6f00000004000040001000e8bf01400000000000ffafff3b00000000b0004000f8bf00b001f0ff1200fc6f002000ecbf0000000c000000040000bcfff2bffa6f0040000800ff2f00300000000000003c001400008000f0ff120004400000000c00fdafff1b00e4bf030001200000c0ff2f010000fcbffeaf0130000800feef00000003c0028000b0ff02c0feef000000fcbf00b000f0ff0200034000000003c0010001400000c00700000c00fcbfff6f0030000c00
+InRand = 7329c31474ae53402ff402d394b9567d25ab9fadeaca5a4f39805974ee711114
+OutPK = 4a1de2132f661340403d82cb585b995cd9b1e55500474cafe43c0c0c30594a673d283cdcd7b427c52c99bb6a319256203072c8c9eb9582bcff2029566fbdbb390438f9570e9e0d41a8508a846ff9e4729530d58431dc3f183b04bdec9420898961a54dc20277b764bc3263e992e1649075e49fc62b793c74014aeb25cc26fa22d3c9e6c0902c1ec424425e9921113a0b11dc16e70c0c37c699a1fac49d846f8479f56ed2842a9c9feb290a238c99278a29becabb4d5241c3b496f00d7394ebb5c62caa4b6b68a4477426af6b59a0b55b2041a3a12a60f8c191354dc0abf24e4b1eabf82aa1941adfa450fb9e92af32681a679db4b5f281b76673725d9e596ab4cf8a448ec53148b5225a77d78d93b4ac748842db60cf80690fcb02da11559e17c34a79c3d94cf112f8e190782b4f9362172cee959e8c16c3e4c9492d5d304418c1bddd929bc557c89e5865e4550e0272912a5cd88a18108a2dead400c548761467371709a6ecc797b6cb383b8c530902d1618bccdde989ea75940b8ec3e5de652fa5274827d28048e5aa4b6ab1aa50d9e986331e927be94971cbed86868c5bcfd59b520f1347629224689bc583e7fb10642aa597c021022a4d954fdc10b312ab6267ae621de96ae42337e2e64c2d63141b58deac9f6f6f20e03c500ab647ce02f04c0779914dba1a4ca288d206986ae17715c37113ad727ed8ec887af2c58fb1c6079243106e3c957366b5595195e80e120e4d9debe9b2d9c52eafd66b1c422fa2932ce69f4890e004bec53968af64596d89b575035047e11f41515fbda8fd832b338b517b926b1187fedca03b8a9a724cd04288ef9661eb3a6c492dc5d16d94364260d28176214d494228e8929d069a10de59933c6bcba2de70beee7904a19da3c120675206d8347c912e79419754603159dd1cc34774157f7d0dfdb48114022b5903b993412759530ebafa0aaf1a0d2545f426abe3c1f5e949c54a722c05c200e0876f5d04da5b4d093da15d93587c88bb65a250c7da11aee77f5818a51381ea9a81b1fa4bb513f2c022e696b18ffb60c4ad3d9ca40ee745da79d6a880a932344a5e5372eb25059a7910d036104c1ff4956ba2358d5b17cee3076b11219fdb37f3fcdebc6353de299315516799b5b24f901de88d0fe113a6b21389f6c8eaea6a9f596c7b688961b418b5fa3782366c631d8b006905f618fc458ad87e6ac4a5b2f5ac28894828381f0aa26c0c21f148350cec11e5e39bde902584a2788f7e31941b7d52ce7e14e366c9772108adfa5f2823250e7fc965aab81162212d1ce245dfba74fbc67b7bcd821ec5984a75f5c9359348a333cb8586ca1c2a7a665c0aa6d29999b4b540a619251696a1bce815a287e2f801b36c8ce9bfb115bcd91efa22bd84a24752768e0937d1475361d8dd6aab55ceb4fbc5bff3d42ca85f955a5c9618faef90aac71db8c0d184b4422c96189124ba7fdad9c440bd2a966684fb84dc76c12db5570afda885ed7d09a35414c000e2323f0d588a8c4a40b46c679a24e8c32b5022bd2aacd4732e9f69a4e970c0e188d6141ff3892c698c6f33647dcc915088b9f43c4540a668e293a06e65b811e86b6ff466175295516bc2ac3dd0ab4350645d03aedac83e609125a1295ae7667c384a45ab77fa8700e739e5655253ad681258cc06b062117140afce9b4244860510755b9555326ba4be03568e509aff5001bf00370a06d6033e945669213443a34894d3f355a74a0cd551a918b837dab47608229b8f4f3ceb2ac478eda449a03744bb79077aaa875216888bb7c551ae06264c8d205aa5a1a76c7603b31a81e2e2cfa37c527ab3c534bbbae210812f6c1601b49174ed8a19159d72050c56900dfda35ae2baf78f8e0627c0fff839ec64ace027f2086d356084b8f3de9a59d26592425b096d38dd9044ae9ea2de966af0077e5add4289670c5aa16e9d9c61abc1abccd060440b363ea1cbc2f636ac4cffee0dd94d050a555bdb23f71199de9e44b90c351315495494d9266861427f80d87a966fc118151d659df787e9c5dcb6259fcd7381b7c906d5083b9149705644abe895b8005b2aa3ebc772562d8804050725cd60e54d5c56a79032edeb3d28c2c0bf1a5d765738299df565452527d5b8fb1804eb7ecd167ed57427fceeb426d6cb917acc4757da781098691304495f910bd52d356f61c9c9dc43c66e2429e6f57e1c632f4005448cbf95b74897e947abe126e0eaa6a40c559722d9a5b9b04f7be2a3a102a94e4961c69589a94684a1d155a02b8fdeb362882e75a71b9bf8fc2748c4e73852de28cd8726ca96b82c57d1e36225bd8ba44358536fba4b710bd8d4a9a9b2e1c99f26e62164794af4464df9584666aff8ee34b65d69a21028ac649657ee73c9550da69bbd4566399264465fa1b8358b8bcb92fb8978aa4a04df60b0bc5705c1e6e1851905cf1d70460a1d7e88d487a18ae1228f4278430b52127aea496afa4496e7628800dba95a1dc9f17b305ab82e19b593d9d6992264fe29d15157
+OutRec = 01c00000000800038000100000000180001000080002c00010000400020000300000000340000000080003000010000c0002c0000000040000c00030000c0003000020000c0002800030000c00018000100000000100003000080001800000000c0002000030000400010000000008000300001000000000800020000c0002800030000c0001800020000c0003c00030000c000180001000000003400020000c00000000100004000000002000080002c00000000c00000000100000000100002000000000800010000c0003c00030000c0003400010000400038000200000000240003000000002c000200008000100002000040002c000200000000100000000080002c00020000c0002000010000c000240003000000003400010000000010000100008000380000000040001c0002000080003000000000c0000400010000c00038000100004000040001000000003c000100004000300000000040000400000000c0003c00020000c0002800010000800034000200004000300002000080001000010000c0001800020000c00014000000004000380000000080001c00010000800028000300000000180000000000002c00000000c0001400020000c00020000300008000080001000040001400020000000028000000000000000001000040003c00030000c0000800000000c00004000100000000280001000040002c00020000c0000c00000000c0000c0002000000003c000300008000000001000000000c0000000040002c00020000c0000400010000400010000100004000000001000040001400030000000024000300000000340001000000002400020000c00034000000004000340001000080001000010000800000000100004000280001000040002c000300004000180001000080000000030000c0002c000300008000280003000040002c0000000000002400000000c0000800020000c00008000300008000140000000040001000020000c00000000000000000140002000080001c000300004000340003000000001c0003000000002800000000c0002000010000c0003000020000c0003c000000000000180003000000002400000000c000040001000080003c00000000800004000000004000080003000080001c000100004000040001000000003c0000000080001c000000000000200003000040000c0003000040001000000000800008000300004000280001000040002c00010000c000380002000000001c0001000080003c00010000000000000200000000380003000080001000000000000014000000008000140003000080003000010000800008000300000000080001000080003c0002000040001c00000000c0000000000000c0002c000300004000180001000040003c00020000400034000100008000040000000000002000010000c0001400010000c0000400030000c00024000200004000100003000000001800000000c000240001000000000c0002000080001c0001000040002c000000008000000002000040003000020000400014000200000000180003000040000800000000c000380002000080000000000000c0002800010000c000280002000080003000030000c0001400010000000004000000004000340003000040000c00030000c000080003000000003800010000c00018000100008000300001000080000c000100000000040002000040003800010000c00008000200008000280000000000000c000200004000080001000000001400030000400000000000008000340002000040003800000000400010000200000000340002000040000c00000000c0001c00010000c0003c0003000080001000030000c000380002000000002800010000c000340003000080002800030000000010000200004000240001000040001000030000c00024000200000000140002000080003400030000c0001c00030000c0003c00000000000020000300004000380002000040000c000100008000280001000000001c0000000080001400000000c0001000010000c00014000300000000240000000000000c00000000800004000100008000100000000040000400020000c00018000300000000240002000040003800030000000034000000004000000002000040000400030000c0000c0003000080002c0001000040003c00030000c0000c000300008000040003000040001400010000c000180000000040000400030000c00004000300004000000003000080001c00010000c00018000000000000100002000080003c0003000000002c0000000040000400020000c00028000200000000380002000080000400000000c0003800030000c00034000000004000140000000080002000010000c000080002000000003c0001000040001800000000c00028000200004000140000000000000800020000400004000000004000240001000000001000000000c0002800000000c00
+Key = 20eae83d111358edd1ef4c2dd731ec8ab172c5baabb832d8198b7f8f615a6939
+
+InNoiseS = 819fc864be3149721adb2aafc882e380dcf06d756412c8a01283e20e6ac1a396c0e14228016352fab19a6b41009a054ebc5ad9a4dbb34014e1df2a7388be6d9593d71860109479801ea9c464ba476edb713664a3245822999c2d8490db2aa7874aaaaf4cc0a1a3fc4617ed5c86f25985078fe7d6f791984d55944621aa86fa6d07763f2e57122e50d89235b3a5ad9bc47684b5f1a0d9005b059b70038f319e31137bc116d7229cab130a331403eaa76aa2a5225b7c9e7f57470b029ebd8a402f55a83e10d6290c391cb90c549c1de65bf83ac64253674c2804701c5bb1e616bc806b72224d8d1c43e049ca146cae42c75569c6797073dcde9028345ce38021a535daa7288018e56b16406a4e8869bbc656070e2699625d14ba996e81431a2d0a14e2405a082e40e13fa2b37a1ecf9a3923a68e1d990b812a1ffe9a674df40029747d846ffc894238b9279901c21489b975aa670448a4766a484918ed7551f9ee7a66bc26b88b002c7bd8f51f9b8c42a8e3ba214815bd687648459f10f2a391856ec3c5bc1a358980b1aea10ab26e430663ecc38018323f2c36daafaa1ce252774913ac30b6d27564c5c20e11e74606b1c1075ba60e451756591d443ec60ce49398d7b4ba3dd000630aab95b8111652e0c110fc04059a68a5c17c5e0a8382f3a32de475f01082a2fe62264b6841a44da669475348dbfda93a9304c502abf4b1710a5b27cc39a8f0e705a4633c5fc65951e4c7f86525c0d760079a75aa587a32ff0e9b82ccfb324a7e8abe9eadb940e10da2ce00f82362b26e8447a1eba21643c0b0e02a2d1190ef51db4a5969aa5c50d2bbe64d8fce2186f2647dff4aea76b78d33fbdf7d74bfca0cb20125eb5a74b1b26d68e3531db09f634bd134603438492df7c4d1a7402f7559e0a4ac8b8e0c417569478eae95226a747d7ead94390c5633041b27559d1e27bfe9104209897e3700edeba72d6f496cd87b7d446a1569b490dd8abadea4096b67396ec8c269bbb7f264bc9d9af9423829a149c3433b215528a90bb1a44878a2c84a35e23220eace1072dbb03c7c1f6059da627e9ca408c572b03ba1ee726739d8b32229f4ebc87c9b7fddff1652a24d02868c50f7cca61da9fdcabf6e733a1d90fa49d66fe8e73d80b0b6a567ace5b62b4e12cf1c5e738199881795ceabe2a517ab2009a90999ac002a40147ec93511e5b484b38213b09200549e1e386b9aad924605721498c0c2105a5129c0877aefdc25319380735eaa680a97c0b51abca6605a9af4d9167e87cb14a6c7424b4d427fe4db4e5fe9867004833a159069a3f6592c232a4e2db38512bee2a9b991d415352b26d83115e92d6567e0d943c67f8ae7b694c1e6beb1e857267311310426713a842e46aec68c0b4e9825a3133273c76475804aed47f31c36f8eba860a600967953152372cc1cdf893e325ec92d14ca45a9135a9c5b5cab4413fdac9f90cbd74be6beeacb6b248d328f081efbe6f268672cc244c04fd6509f2df12628b4a1c9e614eb1c6157d8981e9a6a019e2cd745733765582e92a518b2785056c7e932fc860f162c4c62e738baef86e588e3155515e0fedb0bcd997094d93b122d7d54c01296704e38b4ee47d0b67d5cc784f95a3b845969c1cf9ae3364a314f8aab0a19279c1784f6dfdc77c4e65fe5f43a77a8cabed319cd69ce96ebe04b254465da81e561103b242a2ca971927371d985327b850c998965327d18273f66c4f98281070e940ddbe46e8a4dac8130c4690b561f7193b3518cc464f096e720097dab1a17b4d4bb2ad6abf84b39246f470fe4589410a52888c6c00163d16df7c2225ea8071b9154a0fa340a29cc4bae8a37640c86f9ee7ebb1fda52040b09979a85a1b09f5432c884296191167e1802949857d1490c862ebaf40104a9cd22425338a27de46c08e34299090679fb115d473947831052dd883805d70625944e1723de9217857ab05aa2f70461982527d7d07db403b960800991fa06d7299a77447043eacdb52036082403867f0a182031179cedf1de9d0a5da71782718d6c874dd359be309f7090a94835e10f732c9dc9dff59abe1ed8441680117811f2d909d53a29a26bf47756444424f548e89abebaa25652e10c6373127e489d0d2123e278d05c842ac99ec321b6ed6af94eafc6216a54f62f00e0dfdd44b40eaf85cbed5819c0e553537182b1d9f9548c0e08845ffe8aa53631f76814d759a55fc9d020d2148415b61634864e6e590f5fec73cbca732c1fd19329d90e7821dd106515442d4698b957f697b2c9fb652826283287a064ae861c9c96c58261c20c51d642c5e0e09e84288dc2fd16550787a8d6b1929adfeb1b2392854977ade19af270825f9a20c6c2c5685b08b4f7c27123494a0365bdf50d8acc29f53afe2ee3a743c0b19d7d6613a39a19a20884d98086a5ae633e1a150a2fe2ad5853614b3ce94fbe401be3223d260a5a6994c321366e6daeb06f166989acf825aede985d4ab7b1948d81f7eb27e946948f5dec13765bd203
+InPK = 4a1de2132f661340403d82cb585b995cd9b1e55500474cafe43c0c0c30594a673d283cdcd7b427c52c99bb6a319256203072c8c9eb9582bcff2029566fbdbb390438f9570e9e0d41a8508a846ff9e4729530d58431dc3f183b04bdec9420898961a54dc20277b764bc3263e992e1649075e49fc62b793c74014aeb25cc26fa22d3c9e6c0902c1ec424425e9921113a0b11dc16e70c0c37c699a1fac49d846f8479f56ed2842a9c9feb290a238c99278a29becabb4d5241c3b496f00d7394ebb5c62caa4b6b68a4477426af6b59a0b55b2041a3a12a60f8c191354dc0abf24e4b1eabf82aa1941adfa450fb9e92af32681a679db4b5f281b76673725d9e596ab4cf8a448ec53148b5225a77d78d93b4ac748842db60cf80690fcb02da11559e17c34a79c3d94cf112f8e190782b4f9362172cee959e8c16c3e4c9492d5d304418c1bddd929bc557c89e5865e4550e0272912a5cd88a18108a2dead400c548761467371709a6ecc797b6cb383b8c530902d1618bccdde989ea75940b8ec3e5de652fa5274827d28048e5aa4b6ab1aa50d9e986331e927be94971cbed86868c5bcfd59b520f1347629224689bc583e7fb10642aa597c021022a4d954fdc10b312ab6267ae621de96ae42337e2e64c2d63141b58deac9f6f6f20e03c500ab647ce02f04c0779914dba1a4ca288d206986ae17715c37113ad727ed8ec887af2c58fb1c6079243106e3c957366b5595195e80e120e4d9debe9b2d9c52eafd66b1c422fa2932ce69f4890e004bec53968af64596d89b575035047e11f41515fbda8fd832b338b517b926b1187fedca03b8a9a724cd04288ef9661eb3a6c492dc5d16d94364260d28176214d494228e8929d069a10de59933c6bcba2de70beee7904a19da3c120675206d8347c912e79419754603159dd1cc34774157f7d0dfdb48114022b5903b993412759530ebafa0aaf1a0d2545f426abe3c1f5e949c54a722c05c200e0876f5d04da5b4d093da15d93587c88bb65a250c7da11aee77f5818a51381ea9a81b1fa4bb513f2c022e696b18ffb60c4ad3d9ca40ee745da79d6a880a932344a5e5372eb25059a7910d036104c1ff4956ba2358d5b17cee3076b11219fdb37f3fcdebc6353de299315516799b5b24f901de88d0fe113a6b21389f6c8eaea6a9f596c7b688961b418b5fa3782366c631d8b006905f618fc458ad87e6ac4a5b2f5ac28894828381f0aa26c0c21f148350cec11e5e39bde902584a2788f7e31941b7d52ce7e14e366c9772108adfa5f2823250e7fc965aab81162212d1ce245dfba74fbc67b7bcd821ec5984a75f5c9359348a333cb8586ca1c2a7a665c0aa6d29999b4b540a619251696a1bce815a287e2f801b36c8ce9bfb115bcd91efa22bd84a24752768e0937d1475361d8dd6aab55ceb4fbc5bff3d42ca85f955a5c9618faef90aac71db8c0d184b4422c96189124ba7fdad9c440bd2a966684fb84dc76c12db5570afda885ed7d09a35414c000e2323f0d588a8c4a40b46c679a24e8c32b5022bd2aacd4732e9f69a4e970c0e188d6141ff3892c698c6f33647dcc915088b9f43c4540a668e293a06e65b811e86b6ff466175295516bc2ac3dd0ab4350645d03aedac83e609125a1295ae7667c384a45ab77fa8700e739e5655253ad681258cc06b062117140afce9b4244860510755b9555326ba4be03568e509aff5001bf00370a06d6033e945669213443a34894d3f355a74a0cd551a918b837dab47608229b8f4f3ceb2ac478eda449a03744bb79077aaa875216888bb7c551ae06264c8d205aa5a1a76c7603b31a81e2e2cfa37c527ab3c534bbbae210812f6c1601b49174ed8a19159d72050c56900dfda35ae2baf78f8e0627c0fff839ec64ace027f2086d356084b8f3de9a59d26592425b096d38dd9044ae9ea2de966af0077e5add4289670c5aa16e9d9c61abc1abccd060440b363ea1cbc2f636ac4cffee0dd94d050a555bdb23f71199de9e44b90c351315495494d9266861427f80d87a966fc118151d659df787e9c5dcb6259fcd7381b7c906d5083b9149705644abe895b8005b2aa3ebc772562d8804050725cd60e54d5c56a79032edeb3d28c2c0bf1a5d765738299df565452527d5b8fb1804eb7ecd167ed57427fceeb426d6cb917acc4757da781098691304495f910bd52d356f61c9c9dc43c66e2429e6f57e1c632f4005448cbf95b74897e947abe126e0eaa6a40c559722d9a5b9b04f7be2a3a102a94e4961c69589a94684a1d155a02b8fdeb362882e75a71b9bf8fc2748c4e73852de28cd8726ca96b82c57d1e36225bd8ba44358536fba4b710bd8d4a9a9b2e1c99f26e62164794af4464df9584666aff8ee34b65d69a21028ac649657ee73c9550da69bbd4566399264465fa1b8358b8bcb92fb8978aa4a04df60b0bc5705c1e6e1851905cf1d70460a1d7e88d487a18ae1228f4278430b52127aea496afa4496e7628800dba95a1dc9f17b305ab82e19b593d9d6992264fe29d15157
+InRec = 01c00000000800038000100000000180001000080002c00010000400020000300000000340000000080003000010000c0002c0000000040000c00030000c0003000020000c0002800030000c00018000100000000100003000080001800000000c0002000030000400010000000008000300001000000000800020000c0002800030000c0001800020000c0003c00030000c000180001000000003400020000c00000000100004000000002000080002c00000000c00000000100000000100002000000000800010000c0003c00030000c0003400010000400038000200000000240003000000002c000200008000100002000040002c000200000000100000000080002c00020000c0002000010000c000240003000000003400010000000010000100008000380000000040001c0002000080003000000000c0000400010000c00038000100004000040001000000003c000100004000300000000040000400000000c0003c00020000c0002800010000800034000200004000300002000080001000010000c0001800020000c00014000000004000380000000080001c00010000800028000300000000180000000000002c00000000c0001400020000c00020000300008000080001000040001400020000000028000000000000000001000040003c00030000c0000800000000c00004000100000000280001000040002c00020000c0000c00000000c0000c0002000000003c000300008000000001000000000c0000000040002c00020000c0000400010000400010000100004000000001000040001400030000000024000300000000340001000000002400020000c00034000000004000340001000080001000010000800000000100004000280001000040002c000300004000180001000080000000030000c0002c000300008000280003000040002c0000000000002400000000c0000800020000c00008000300008000140000000040001000020000c00000000000000000140002000080001c000300004000340003000000001c0003000000002800000000c0002000010000c0003000020000c0003c000000000000180003000000002400000000c000040001000080003c00000000800004000000004000080003000080001c000100004000040001000000003c0000000080001c000000000000200003000040000c0003000040001000000000800008000300004000280001000040002c00010000c000380002000000001c0001000080003c00010000000000000200000000380003000080001000000000000014000000008000140003000080003000010000800008000300000000080001000080003c0002000040001c00000000c0000000000000c0002c000300004000180001000040003c00020000400034000100008000040000000000002000010000c0001400010000c0000400030000c00024000200004000100003000000001800000000c000240001000000000c0002000080001c0001000040002c000000008000000002000040003000020000400014000200000000180003000040000800000000c000380002000080000000000000c0002800010000c000280002000080003000030000c0001400010000000004000000004000340003000040000c00030000c000080003000000003800010000c00018000100008000300001000080000c000100000000040002000040003800010000c00008000200008000280000000000000c000200004000080001000000001400030000400000000000008000340002000040003800000000400010000200000000340002000040000c00000000c0001c00010000c0003c0003000080001000030000c000380002000000002800010000c000340003000080002800030000000010000200004000240001000040001000030000c00024000200000000140002000080003400030000c0001c00030000c0003c00000000000020000300004000380002000040000c000100008000280001000000001c0000000080001400000000c0001000010000c00014000300000000240000000000000c00000000800004000100008000100000000040000400020000c00018000300000000240002000040003800030000000034000000004000000002000040000400030000c0000c0003000080002c0001000040003c00030000c0000c000300008000040003000040001400010000c000180000000040000400030000c00004000300004000000003000080001c00010000c00018000000000000100002000080003c0003000000002c0000000040000400020000c00028000200000000380002000080000400000000c0003800030000c00034000000004000140000000080002000010000c000080002000000003c0001000040001800000000c00028000200004000140000000000000800020000400004000000004000240001000000001000000000c0002800000000c00
+Key = 20eae83d111358edd1ef4c2dd731ec8ab172c5baabb832d8198b7f8f615a6939
+
+InRandA = 312dee62b48992a86afc71cfed062c85e5f76ce279f42a4e0561540685c1cf72fcde7f4720ef87581496c59d3cf0d14a745aa1f23292c56ca380c12045b184669164c73c3a2492af440a78453a1012fca13d290be6122cb832ba9de59019b8d0a40b6e2a8a99483931b5ee2b4971256cc31107c2cb14503e079d27570e37ac03c2aabce21990c0e4693c2a4c0eeded78361503d9023fd60edc742463daa39d32826ba8eb136dc02e6fcad62aaec67d9392a958ee094ba90b53b78c7496941644a04e7654ef51d18b66c80d4b55d9c3d5153760e8cb8495182e0a8ce50327142e286c27303ce56edaa6f74683cd8d10ef12d9970c30352574c06bde230f00d089214c6ad913333430a903f5253f13fcc4249b50efd5f0ad0c7b450713f84190e722c8b368b044744ee5c04172719d48a2919d3815c0c47a04848011c5843ea755007b9458a7c82340a243045d017946651ed32172980e353495df914ad64c2ad4ba925e4abd2b2fddb544386e4dd65feae8e2b0ccd0a2d2742c8666266762b4b6713b17240b5776278a6e1ad4c2d42ed64d32b14cbc66e7cc8844510a30faa9bfc179566034865ff0f7fc5510ca31e3093d39ca23c5c5d2e4b5b61b1c41be690b3822f6624cc99c2754710893f01105682c8685c11e36536114f07261e1e6a659f7e25c59824af93030a773450567a6c5a7f0150c7331cd4f60566c13a2320f2bcf6536e6b16ede24c3cb69f451de672451367969a6dd6d0afa58afc7daa9b6fc450d802ba879fc22207289783a79e614d3cd4f6ab0652a6a17bbb7f9659d4f14241a08382a549f4ac102ad0f8d8dd9706f0db4efa9c2dbbb9885b4e961289961ba8b8887d0b9dd20f5101e0890012d39a9e115c87a255001287b08b29ed9989b670b802e485813155bf1398c0b0409be7c1cba4b0a388014bf5bd5218199e63afc8d5a1548342fa14fa0e135ac7e05a5b9e1c475b6e7e30c1a75d630c05b43125b75410d055744f35805232183d57915a0e6a28eba865c61201baeb03cac68fb23fe6090f67f80cdb5039bc45638afa0a36520ac20c9ebd3a0d55d3c46d0ca48e52de52bbe70c3c911fceaf6b96a2e748a69b9d94bde44d5d5fda738226d3768a5d96a0e20b112af01ff59bbdf9c9bbbcea6af9ac22b74c2721222c07842296c5a1de44053eea0870b94ec94a1b0b1710e3dabfa3aab9c49ac1b3e019b712c8da748b1859b2aa5590ec4a7e78f1f1461f4b23015a5a9f5dd2e2624be2098580cf8de4b470a799a7e6c8753bb02b10c1dc1e70bc26e9c6d89039d282a8edd20274b5dbee6184327249664742e733a6d7d7e01100b638408b3d0479e80980d58c026a347fad6733de51b013f983455e257215adc3761624288cbfe8cda20459104bd18958415f2c120704cae55d2ff9637fb599835745869c127a4522e1e26fa0eb8f04c8a6cce41b09be1b1965c05a6551f74530986b6d7adfc53ff80bbb018b56e66665df1b0455b25d34c18b4b5b616051af4ca559010da68b8fdc6bf998b39f0def85c9fd9db86960d9ac5d99ae685bd2686546b6391c08426dfdeb133de4939689c67c569a2f7958ae70d31b5620db92963e0af8e2c25377258aee1051c9adc749a0d1e87d1b1bc0c9a5c13468b408172832cd893e025076b77d5a496b1776053fbac2a8251c5773632b91c7505bcabb57605ae2280e30b94131b63fca8caa8b6ed8477e60cb5005c48d91952b25caf5c2c8743be2e6754b712c0710d4f9f94317fa24a411271f8b4e8669241d6ba9cf65b7493fba1adcd0b4faaaca6bb2d8d79cb4e233d704a6725e5a4f19e1f498a96e783817b470140c0a25551650cd88ae4fd330b4212f6d848ae902ff662573a0ca7a6cc63898a6eaccfb97613456309e32e800fa039735a486962c9a58493afb7b9dc0c4bd9b12bff7e767b8f6f42f7bd53d0837a7574ddafd04cde42234a4919981cfb6d8841a3c063e03a52bf8598b0f12d6a28f82a4177a5ee7c3c2af1f0675685f8ab3c41cdec3046081b268f7999d9a17555826aa6052b8357d0b68b606a1d503d6627bbe608a4ccf94851502946390448779e9ffbc99a8d66874e988774b508ee66a03c5c94ca6224a618caa1ad9e69091ca4aaf840b856e2c1742a0ef0b067eca80849e7145ea574c093a12736bd207a51f51379bcb2ac2f08532e5097b6e0455516b0aa27c2af52f406b3f29cdaa7cc9609becf1821dd9c9839221b86f01c6c1c93169bc0fc6f0864adf9cfd1966e00cd52fad9a9f522ca821a2a2dab40fab8e199aaac12e2f1154032a49c5ea7a4ec2341051714136ab7e5193de862f12005881933b4601603daf7056e2c8582ce56b2a9aca49b5f3411cb182fd85f5e586bbc8124cac764eefe16242de424d5f89d1f5ace352829554c6cf10b488c5e7513dcf4a2685c904dc0727b040d14e7d92115ec256b590ff1a9b2295d4e7be92638b21fd55d8ad23165318d844b252523fca19961b9d60cd76597541ca04dd310080127a2ab8145fe9a8ba5a4
+InNoiseS = c1e9a276ceae5b944e79f8c57ab825a58ba9d01a23cedd5b2b2172a3f8ac35e80b5a3f84c034e226219e78e800453f3ab95329bfe0f4c17246ab6193db9dbe324b5f07f0998d6d83d24208d1b51298b5166c66258d26d86278843e3ddd13da7ebe0ed24902765149465113fee9461a84af1a68c8bf76769603e82a21a693fb0442bb70f599d8982076b9fc36a42c46b75db8b1699544e557fe21330ee6b53bd2310e6dee459b7a6fb55cd640ab1881334542aa920a8ac4e77c87c60ab1c98ae888f42134db67a7b809c69fc81194c487f24b61a011b086d058cb4556b582c54e4f047f50531416539ea7c1f04e7db95bacd3afb616812285354344167ee4745512601dfdae2a9afaf531b40e16d86569420b2805722ad890ed17308035c8943a0daad4e8c8325e8cd7d0263803879ebf420df2b761a86939b4e19ab161ae22bc045acd0af1985dca8230a752150191b20890d0788b9e8e299d08fbd7664e9645c43940c1b06d1c8251cf859d158fb5b515e40c0aaccd90b370020383b71298cdb335803d4b58fd3c8217435218b6b970d180a284d17b7f61ebeb9a46b8bc476bf321010f06128c02e171763dcd10b655aa2542213b6467800f134284da21622f6c20c522d1d05adf9bed5b7a6d63a3ab7236074e1f9b999138ac258900eeaf68a8328ed5084a23cf7cbd851ba7c222d1975363a0770cc446e7c4ef51e49457eaab2ef1fce02a1629c68b12d1aaa3a9bf041a765030d0e2a719ca712fec479ae3f5a4c2caba024ddc2c4c2c354bfb5c55b62daccb7a068adb03253437f87438643ee9d86929d5681b52b87664ace83a2672daae7c1f6c6aa2c486f9e3bb31c4f27b32d047a6af990e08d36e3a8fadb376cecf67989c4977d33d56db106107de6e89849646132a23858514a8931ebd4383d2635f90b2f17b7d92eae259e6ab4da034992a1b6ff215254e78a930fa6cf12283b7d23c73e08788a645b55268ca37e6c49227bb5f2f536caeab9344b7904519509990616250f127201bf5631ec28f5b9b7897870282b69bb46a8854bb5a66f626f9b5a394e062e4208c194cd1b963747216d8975f28a2480dc04a0f31462287d463ab14969827aa2f4fc4c30e977153e9e992688c8ad0b5c3efd17a4b95ec910881311649e223dd8d9348e11f0183560b345a4064c1205d4d41fa5b2b5c60e00dfdb2f31199d8897ea960fd78e6000b7bbb94720d275d9bbc819ca124dcf706a27615209cea60b952fb90455a2bbe3d311b9da08798491684a6291e915aa80b1983d6190610a094173575a7772587408338571e8e248a0b255c18a2cf4684040d215f88da53e9a6114903c3683d3eed814de857742e76a95282d87877289abbe54efdbc9d1961b1176cb5066d3f0bf7556be86106d1d6e9a8bfa8d5fbecf460095821989abd55b60261c8287a137feaedb0515e45694721081851957e88d105db0d974d5d5cd5a9386a8a037f315abe3b809b7f1b6656ac2d269452a39868e66b8e49543584f0667614a9f0aef09a7d956370a6a58353b70196498924bfa073ad687b1ee1a768ecabb7ab15f22fbc71579f7463429d69670d2ec605119f319574e572ee229f6241fab912addb1b83d71bd143149408abb1c15bde84ef93e6d2693c9423d99128832629e6d75b682e0e0181a0dcf442b4c27c32846431a3a6d8690f1c188a9a4274bf284bf12bf0a0dc9672d358b1b6243a631fdefa6464f9463c89f7e423ce03fcd5dd22fd606372296b024baa86c8c922071842a5084a799392ad39880a8b50d9386c7d60f3920db18c1feceda8d72e36e3db48858c00b0e921e35a09e1b16bc54428e0f119b7cd07466af576ec1908f489206e8fcb16580a8271d612ef272f8a5e18c947e10ff2263b08e46527b42d1c993774ed4139ccc0b39566adbacdd1aacb0eb2aceb6dacaa2cb1818b53e8e4b2995ddbc4f399f86d80e6644a2550d129f0a21e84f8950aed8cdd59fd1d09470ea43a19ca7f6a8de69422f0b3078a87a3129a8d819478a470d0624593620182087f57c62450c5f1121486c377648c3e5118e59659c56fd408dee705beae9b412467884904c1488cea3fc51696471ef154f9547ee6ee4636ae578f0611b4311c83e744f186ce2013e8eb00633a3a9223e39ea9107a13639a4dc0457116d20899da92e623b64240d090460a43d88476564c630f99a049f5ce5e34d645c8890e71099f16ebc72121aa044685fdf187e166155736bc1429c85cab2c808c0a1eda3bfbcc737395b4845031807b872407381212efa6856b69b145db0c4fa936ad6f05a824c10f500ad9e91cca8b7c0d57e38c3b82a8bc6d8132b6ae16381604ebfbfd8e8416177ca560e2983963fb1b25ae28d85071d251493e4c27a34319f04eb387a592e62c95b7e8b426dffe2b6d2ca89a71e091bb15c542ad9cb4211cd789b95c13972b9aea65e12051af44020f52971e61324245e9154d528879e559644ec8f7470e06a6d897cb84a13a656ecd55a69dad7234c79da3450a4d
+InNoiseE = b123f788afbd9623d25ab427c24266da1a3104cead00810400d2c49d3a0ccdd384345791ac7f18527ea9968bcf80a5ecbc5b9830a46989995409b4c674442c820498c56acd262fcd76487e79186163bea2d9fd39a90f8d067da930e8ae17ab53cab136acf4c052613794e58c9b2d2d2cd087bca67714a6958189da4be411241d22b59fe2a571982203dda53ae4a19684ff5a6f461b5df8065d8fc10ccfc58c5a82b194e67663ed26dcebe96960287e2345a102d9712a1688f915e59e84608471cabc182963d809636669bf58ce70867c8d093ee4681416ca1daeacee7996aa15c8e5e4980621241354e8d6424a6cc42720683b509fb5a26566b1d17cd6c02367cfd67348128973544429c48a77f842f93eb9c2ea51ab5e0adde93a3abfdabae3dc6b73fa20a21b9e5984945621a49b0ba465f279b440c0c43f3252656d300429429c859aacd889127c880fe62167ba2afda491f25fac8cd09ae3c540c802b366be5787a264dbdc5608ca5863d5a964a39f58b5f84d122af9d11a8e63a2a5da74b205e7827baa9c23c9d6428117be6ebc6a204bd1243c406de5962e3c40a2561cfb612e45584d8d3b51a84e13d57ba4cba4077912f35654b62f5acd9c40368e12a45d14f661bab0aa1b03d547a430cf92b69424a4202e092f943e1d88b60d4f04c3345c6bec4cb1f3959d3d4041354bf492d318ebd3cbe4b6d02efd49d16e2c47a988233f39731fd68e8964758223a0e86829b1307b2b0828090849a5037b73cc49558ecebc25881d4dae4dca4967d81c7c623871315d0347ec9d9a74e2864a0542e4930036f487c8d980d043611dfe2b5e554678d1dcb75e3e489c6f6192275ca935d04c0af8a48e17cf6dd807d9a0df204889c2cc71e291d0b2db7dbe978729fb9130455bd91144405c8c7b006a69a258a0dc9bdfa20824b1665778896d41816e90348b88e811ee1e001692624f3d27ed5115545bb80928413a4a1dcb0d00e92ea29b55c7a2b4801a54776d63422650039044ef7baeee3bce2cbca65928252415e3f131e17d0625e155315144195c48f0317c22346595a360cc8f8be07f492e1e3a31e066074f3170a4e954f5af0672db877f1726d9b3e304b85ad81a80715b7272f13d9bbe2b1055535578ed19bd32327e9a9659d317b129545c8f2d1600da2cb4a5fc73452c839297695b211d52a021c5b283c97d4443f2523df1252fde96b1007467fec8a6157dc6426ee63fb8a56239cdc202a18c591fec875c39f98081d7ed08466344a48b244f40836eadaf219bb7f81ab67459a7189568047d811938feb0542cde5333b932ba2cdef78ada3d36cd10b2b553c025f4ae3e07ac82042cabdeea9fa69a0ad6048b6c113e2317808278f29d679ecb0b6bbd5a84e8f94b059a0a510a389add6e3f97d49a38646041802a13757bc69f16916d320f258d81ecb0484fb46098d2b64b171e418388fea898349d29d4e3452ff2a273810d24ae118d3de6364b3dea83d4a124340be5be610af4351f38acc2073880fea0705ae68c550a7488dc8de10be321987c47fd36949c8dc20603b73ed20699da946a5449b9220be249c9c1609bceea464fa834d9565bd7f9cd1b95e349978a8aa727a03f74778698164d9e537c04aae48333a0265563b1a7c8e3ec3043d5277fc79fa876d6c464c4d17e7d745cfd20f3786c65e546eb74565984aaff0550427a074bafc6bc583806a2eb5acb151deeca2c35a8fa2efaefda17f5242df2c2f6a4baf16b2d689995ce7896ec93c0f81901a0789ca76814b49c8d56d846546472984603d521924841def323a36c1c0d23f551151124c40c1cd964481888aca737640128cc9b6feb821855cf1392b5ad0071fe6389386ded227eeb8f4af27a2fa6128fc4c0a9578484182830d97594ca8b856c3c860d9896383649aceb8240aa500dbf1b540d0820a232d4e86b676ecd59b94993452b1e20152385784af462b806434398262ee884d4dbaf1c5f9e5404d13f9c5a5e83ffb39eb26a5414cab5f479653bc916d95b49b5d8a621e95a588246052602da45a080d3f928c4c4820d455a31673924dcced89af2a4bc2a815621fc4d9500eaf499d2f42ee7938a28dfd8089acc467bcd104a93a097b211a4767f9846a75cecac405e35228efd3ecb0ed72a85cd3607832f4b5c6e8d7b97319afa1836eca366343a896439de14d3ce3e2f0740e3bb79e9ab275e2b7523f6528d0ae6d22185ad89a61d5541ee33940e3e9ae556891f09090a22f4bdb303dea398bc99790a1333555d9a50aabde3b9e6238200b8254a8eb8c2920711a04978508615a3a6803b5ae4125890fa1ddb2e7ada364a05dfc13d645f8d8c7aed656a62c4a1f0804bc4405c2cbb5ff068d0e4979f232508a6166deea5c10149567b3b8b0ceb967d002ed3deb381c68a8d0fe71ad4301fb815c2a5c9f46f0ddca0d8f20205b38aa1b0fd32b0f6e91ed8f4b0b6ae5f02523772599e50f1f770aca9705cb563600863a81f4f74073ca4374cc89737f2601e0214e143920a
+OutPK = 535792f6599d034f5ed9e9b5c5b3c10110f32ec45aad4ad1315f9268a65f8b97d6f1b2d4200711378574d62e6c9b71b16ad3d5b70bbe4a8f538646973f5c076028a0e7386962f49059e3dc20bc724f4ad20eb9402a5d1df57c09762ecc2804a9fe11c42f085a174c2f0dc98be0bea24e3e20b1f88df0545f59bee80282b166861a43ca8273a6013f760fb2bef65c37789b1aaf02220dc155d153884a40359e511e9c23a6f2434916190a11fa75320d6945af461a921ed2ea3068cacc33822147e9b8cc59f4c958483039a50d145d5b7974136255185478587528697cc20f989a2912c6324ad4273690b34baee43c26dfcf01dcbc4dab41407bf246942dd41d1926b24150aad8892b58116895ef53bf281088439c8947189d00879e931192379b9633a1235452fa283718f692a7f566272575be6076a995ca80149841abbc43d7941d810b07687b0e1e9e6d2d439c1d1c8a47f486a41a2443e7d95962224af6562c23e6e5533c2a82009d241424daff48325aab87867924d0ed1b57166934afdaa52296ab097aa2181328f88995b4095519f7545518add8610e0aa5ad32776986fab0a9b83cd860f054318b1de8e366df454bc26d8672dc89b4aa268066067e2727469d32463253f0aae277bead7f9d1090d215e6b3a116268617766b75263c4ab6ec010918e566696a93dd2267d30321897e8507fb348511276df33497726bb4c1d4d38f0049a5cf4cb5841c61e96dec029218579b100539be5216891914606f1a97cfabc284167e8ccf1acde59d0c61542085a6ec490913500e488a389940a376d1d799b4332e470a44190ea2a435770feca6bb46ded13790206fccedca4192a77948eee594ce6e064f49f5e7bd85baa272a67db9a68a5d9b44ee0495fb434579bb9a2c6be16c79d1545b85226b469bb857e2115317bdc91c0c2333c1e7000f476c522685b52d98dacf7325241900f08ac60aa225f52ee134978617c78d68726d76ad7e27b7776c1827674d267a1f0551f503ee960b3e3a7aa19af0640e3cdc1381f74542a13b6629d337c2980f40ba664461a982c5e736ba4d841c248ec345b05f9b46669a44043903453f03dda48e236d6714bd4e7fa89912d991a1696e40bd82fc0669b7d883840d624a74ed7fd7d6b57a549c231a2a4c1fd5aad98699607be149d65244644656d93f0173c02f53cd1c79b62e80ecd82cf5774a69e05ae0ea7a1c02352d499821894e9286d4077af49e9441acfad4b21465dc0dd359b07fc4617cc316e1a4954b55542bf07d818efec0ea15abbd04e2e90230ac6f6dde24084238cdc56649f1209f0ea0e3611c2abf8d45710184f4a54a22202652ae4a5ee9f720b4a023fe4493c6d22971b693f0fb9a194a8c9a41789b1d7fd020faa3c936a91de323d0290c84ff978695b09643512f6205a1e1150d842521718c0544812a6855ba63cfe15cb2bfb16dd9aaa54b1ce8302f568062c14122d767082afaad9c3d85ad148e969a1b0ed8d19516427f297ab8eb1e719e81a2b6e6da0148a933e830fd16650c66a3b2d57b4c0269504c11121c4ad1a0c839b8650416850deda33fa31d35d7442fdac994daef5127b3eab1a1587034a5ebe7911ec46139ef3b4b8c8a9889ed2412172d3c8a163f413d1dadf51c3e60df100cfd2474730cc63aa221b863b2a2271b8b1109610d417f489ecbeed2362385c127625c0d50a0ebc3fda5a5d1e645e890924dd38044eb645887795a02b6b0ac48fe813f17a47c7b53e58d15176110fc9b16b503f297b4dc6b28fe1148e96ffbd408e41489d201c590b2285c0309832a3e22a7441c70501857f1ca0744a0e20910683bf023b6397485246600f161d927d0d9c34d7bfa59fc7326b450edd9ba45b621a3ce9315914c798d96a98d55544051536cf082674e13ea14d8b028bdbe99451d0774cc85354afa87f3648d36c9b8a657bd595e2ea80061b6a9059baf41a652961265aa42882d926fcd4b91860a229ec22561d596b26950976b08719ced1e8469536d43f9a8a69998ae4ebe435739b811474b1c48721bae7aa36d40655a9ea69a1a5894033953f0d6e488d1d62133a9c309c3d49e8c4cae0acba512519d11a0997e81215cf55ac0ccaa055d6deb039c672ea8ec81b34a175b45c9448d45a1479c84d95031216c0b8f30cc286859daff9a6a8e4d836d5ea33cc030532de579560846311e7b496a501bec2de3ba760d3f0e609659f4109f0fc1f6c70df56eb6122bc08082784748f375fa9859bb2b6207a9550bea67af4bbd141a5e591ea83d392af2675575ba4e19636bf6c1670a813414d08b5fc989e49d8840ecba24a6d192d63a11bb6167a610f786a769039c83d99d9f0fc6fa89ee21a475c7960918fe5e70dea2da3c5985aa63f6889886a4b8631eab5910b9f0578ac94c919a5c42167435be09c487e496465213bb5e9db5d8658abc2a6416c68e873a8a3e224c9c18bea9b8c983c53654e5df65e065205721dc199cc442440b6727b610822cc9f4d044511e7fd673f168cd26
+
+InPK = 535792f6599d034f5ed9e9b5c5b3c10110f32ec45aad4ad1315f9268a65f8b97d6f1b2d4200711378574d62e6c9b71b16ad3d5b70bbe4a8f538646973f5c076028a0e7386962f49059e3dc20bc724f4ad20eb9402a5d1df57c09762ecc2804a9fe11c42f085a174c2f0dc98be0bea24e3e20b1f88df0545f59bee80282b166861a43ca8273a6013f760fb2bef65c37789b1aaf02220dc155d153884a40359e511e9c23a6f2434916190a11fa75320d6945af461a921ed2ea3068cacc33822147e9b8cc59f4c958483039a50d145d5b7974136255185478587528697cc20f989a2912c6324ad4273690b34baee43c26dfcf01dcbc4dab41407bf246942dd41d1926b24150aad8892b58116895ef53bf281088439c8947189d00879e931192379b9633a1235452fa283718f692a7f566272575be6076a995ca80149841abbc43d7941d810b07687b0e1e9e6d2d439c1d1c8a47f486a41a2443e7d95962224af6562c23e6e5533c2a82009d241424daff48325aab87867924d0ed1b57166934afdaa52296ab097aa2181328f88995b4095519f7545518add8610e0aa5ad32776986fab0a9b83cd860f054318b1de8e366df454bc26d8672dc89b4aa268066067e2727469d32463253f0aae277bead7f9d1090d215e6b3a116268617766b75263c4ab6ec010918e566696a93dd2267d30321897e8507fb348511276df33497726bb4c1d4d38f0049a5cf4cb5841c61e96dec029218579b100539be5216891914606f1a97cfabc284167e8ccf1acde59d0c61542085a6ec490913500e488a389940a376d1d799b4332e470a44190ea2a435770feca6bb46ded13790206fccedca4192a77948eee594ce6e064f49f5e7bd85baa272a67db9a68a5d9b44ee0495fb434579bb9a2c6be16c79d1545b85226b469bb857e2115317bdc91c0c2333c1e7000f476c522685b52d98dacf7325241900f08ac60aa225f52ee134978617c78d68726d76ad7e27b7776c1827674d267a1f0551f503ee960b3e3a7aa19af0640e3cdc1381f74542a13b6629d337c2980f40ba664461a982c5e736ba4d841c248ec345b05f9b46669a44043903453f03dda48e236d6714bd4e7fa89912d991a1696e40bd82fc0669b7d883840d624a74ed7fd7d6b57a549c231a2a4c1fd5aad98699607be149d65244644656d93f0173c02f53cd1c79b62e80ecd82cf5774a69e05ae0ea7a1c02352d499821894e9286d4077af49e9441acfad4b21465dc0dd359b07fc4617cc316e1a4954b55542bf07d818efec0ea15abbd04e2e90230ac6f6dde24084238cdc56649f1209f0ea0e3611c2abf8d45710184f4a54a22202652ae4a5ee9f720b4a023fe4493c6d22971b693f0fb9a194a8c9a41789b1d7fd020faa3c936a91de323d0290c84ff978695b09643512f6205a1e1150d842521718c0544812a6855ba63cfe15cb2bfb16dd9aaa54b1ce8302f568062c14122d767082afaad9c3d85ad148e969a1b0ed8d19516427f297ab8eb1e719e81a2b6e6da0148a933e830fd16650c66a3b2d57b4c0269504c11121c4ad1a0c839b8650416850deda33fa31d35d7442fdac994daef5127b3eab1a1587034a5ebe7911ec46139ef3b4b8c8a9889ed2412172d3c8a163f413d1dadf51c3e60df100cfd2474730cc63aa221b863b2a2271b8b1109610d417f489ecbeed2362385c127625c0d50a0ebc3fda5a5d1e645e890924dd38044eb645887795a02b6b0ac48fe813f17a47c7b53e58d15176110fc9b16b503f297b4dc6b28fe1148e96ffbd408e41489d201c590b2285c0309832a3e22a7441c70501857f1ca0744a0e20910683bf023b6397485246600f161d927d0d9c34d7bfa59fc7326b450edd9ba45b621a3ce9315914c798d96a98d55544051536cf082674e13ea14d8b028bdbe99451d0774cc85354afa87f3648d36c9b8a657bd595e2ea80061b6a9059baf41a652961265aa42882d926fcd4b91860a229ec22561d596b26950976b08719ced1e8469536d43f9a8a69998ae4ebe435739b811474b1c48721bae7aa36d40655a9ea69a1a5894033953f0d6e488d1d62133a9c309c3d49e8c4cae0acba512519d11a0997e81215cf55ac0ccaa055d6deb039c672ea8ec81b34a175b45c9448d45a1479c84d95031216c0b8f30cc286859daff9a6a8e4d836d5ea33cc030532de579560846311e7b496a501bec2de3ba760d3f0e609659f4109f0fc1f6c70df56eb6122bc08082784748f375fa9859bb2b6207a9550bea67af4bbd141a5e591ea83d392af2675575ba4e19636bf6c1670a813414d08b5fc989e49d8840ecba24a6d192d63a11bb6167a610f786a769039c83d99d9f0fc6fa89ee21a475c7960918fe5e70dea2da3c5985aa63f6889886a4b8631eab5910b9f0578ac94c919a5c42167435be09c487e496465213bb5e9db5d8658abc2a6416c68e873a8a3e224c9c18bea9b8c983c53654e5df65e065205721dc199cc442440b6727b610822cc9f4d044511e7fd673f168cd26
+InA = 312dee62b48992a86afc71cfed062c85e5f76ce279f42a4e0561540685c1cf72fcde7f4720ef87581496c59d3cf0d14a745aa1f23292c56ca380c12045b184669164c73c3a2492af440a78453a1012fca13d290be6122cb832ba9de59019b8d0a40b6e2a8a99483931b5ee2b4971256cc31107c2cb14503e079d27570e37ac03c2aabce21990c0e4693c2a4c0eeded78361503d9023fd60edc742463daa39d32826ba8eb136dc02e6fcad62aaec67d9392a958ee094ba90b53b78c7496941644a04e7654ef51d18b66c80d4b55d9c3d5153760e8cb8495182e0a8ce50327142e286c27303ce56edaa6f74683cd8d10ef12d9970c30352574c06bde230f00d089214c6ad913333430a903f5253f13fcc4249b50efd5f0ad0c7b450713f84190e722c8b368b044744ee5c04172719d48a2919d3815c0c47a04848011c5843ea755007b9458a7c82340a243045d017946651ed32172980e353495df914ad64c2ad4ba925e4abd2b2fddb544386e4dd65feae8e2b0ccd0a2d2742c8666266762b4b6713b17240b5776278a6e1ad4c2d42ed64d32b14cbc66e7cc8844510a30faa9bfc179566034865ff0f7fc5510ca31e3093d39ca23c5c5d2e4b5b61b1c41be690b3822f6624cc99c2754710893f01105682c8685c11e36536114f07261e1e6a659f7e25c59824af93030a773450567a6c5a7f0150c7331cd4f60566c13a2320f2bcf6536e6b16ede24c3cb69f451de672451367969a6dd6d0afa58afc7daa9b6fc450d802ba879fc22207289783a79e614d3cd4f6ab0652a6a17bbb7f9659d4f14241a08382a549f4ac102ad0f8d8dd9706f0db4efa9c2dbbb9885b4e961289961ba8b8887d0b9dd20f5101e0890012d39a9e115c87a255001287b08b29ed9989b670b802e485813155bf1398c0b0409be7c1cba4b0a388014bf5bd5218199e63afc8d5a1548342fa14fa0e135ac7e05a5b9e1c475b6e7e30c1a75d630c05b43125b75410d055744f35805232183d57915a0e6a28eba865c61201baeb03cac68fb23fe6090f67f80cdb5039bc45638afa0a36520ac20c9ebd3a0d55d3c46d0ca48e52de52bbe70c3c911fceaf6b96a2e748a69b9d94bde44d5d5fda738226d3768a5d96a0e20b112af01ff59bbdf9c9bbbcea6af9ac22b74c2721222c07842296c5a1de44053eea0870b94ec94a1b0b1710e3dabfa3aab9c49ac1b3e019b712c8da748b1859b2aa5590ec4a7e78f1f1461f4b23015a5a9f5dd2e2624be2098580cf8de4b470a799a7e6c8753bb02b10c1dc1e70bc26e9c6d89039d282a8edd20274b5dbee6184327249664742e733a6d7d7e01100b638408b3d0479e80980d58c026a347fad6733de51b013f983455e257215adc3761624288cbfe8cda20459104bd18958415f2c120704cae55d2ff9637fb599835745869c127a4522e1e26fa0eb8f04c8a6cce41b09be1b1965c05a6551f74530986b6d7adfc53ff80bbb018b56e66665df1b0455b25d34c18b4b5b616051af4ca559010da68b8fdc6bf998b39f0def85c9fd9db86960d9ac5d99ae685bd2686546b6391c08426dfdeb133de4939689c67c569a2f7958ae70d31b5620db92963e0af8e2c25377258aee1051c9adc749a0d1e87d1b1bc0c9a5c13468b408172832cd893e025076b77d5a496b1776053fbac2a8251c5773632b91c7505bcabb57605ae2280e30b94131b63fca8caa8b6ed8477e60cb5005c48d91952b25caf5c2c8743be2e6754b712c0710d4f9f94317fa24a411271f8b4e8669241d6ba9cf65b7493fba1adcd0b4faaaca6bb2d8d79cb4e233d704a6725e5a4f19e1f498a96e783817b470140c0a25551650cd88ae4fd330b4212f6d848ae902ff662573a0ca7a6cc63898a6eaccfb97613456309e32e800fa039735a486962c9a58493afb7b9dc0c4bd9b12bff7e767b8f6f42f7bd53d0837a7574ddafd04cde42234a4919981cfb6d8841a3c063e03a52bf8598b0f12d6a28f82a4177a5ee7c3c2af1f0675685f8ab3c41cdec3046081b268f7999d9a17555826aa6052b8357d0b68b606a1d503d6627bbe608a4ccf94851502946390448779e9ffbc99a8d66874e988774b508ee66a03c5c94ca6224a618caa1ad9e69091ca4aaf840b856e2c1742a0ef0b067eca80849e7145ea574c093a12736bd207a51f51379bcb2ac2f08532e5097b6e0455516b0aa27c2af52f406b3f29cdaa7cc9609becf1821dd9c9839221b86f01c6c1c93169bc0fc6f0864adf9cfd1966e00cd52fad9a9f522ca821a2a2dab40fab8e199aaac12e2f1154032a49c5ea7a4ec2341051714136ab7e5193de862f12005881933b4601603daf7056e2c8582ce56b2a9aca49b5f3411cb182fd85f5e586bbc8124cac764eefe16242de424d5f89d1f5ace352829554c6cf10b488c5e7513dcf4a2685c904dc0727b040d14e7d92115ec256b590ff1a9b2295d4e7be92638b21fd55d8ad23165318d844b252523fca19961b9d60cd76597541ca04dd310080127a2ab8145fe9a8ba5a4
+InNoiseSP = c64a30f40c2146684b38004f9863741b2214bf060817d2d629f93e74cbc804336cc16adedf04d06e9d15a08426d15811a0a649cdd63ec59b4cafb7713b793d3c06f35b70a516eb932cb0086ebde5c3d7ab1f9017a10a0c382d3a7add8ea8195fc1436da06591293178f58ec70abb2e967ea9b6da0488995f593608c815a48c9c26b8035a33496efde470043b6cecb9f1c1c4b1eca56d572a382fe34df759cd15aa0465aa29e16e9fa71e673784662d534152db401db62bd0762864e84f392da410da063ca169646429fa5f361798e26fde806c9f268879669791adee4721d071b1ef4f7a6f64508e44e1b47e2c3bf0c12f77b4350cb2cd1f96d76415af453d016bbd638aafa1606d8e58eec8e1a0de86b466d6f7b7cf469d1d6e1847bc5c6874d78bd9b14a630be4c6db7b8529ef8fd18832084491635ad6c97e55b9a60bf0a4ea4899017481dd39a3420269417c71a520d7a2b3458d4f4e4a63777b14bb84ab31f2561c14005466c58a9e9a588d96c252e17d8e82cb3509d406c151f0521d9d774ad166675801a54ca6f0993dd12cf15f44d20da8b9fb052265bcc6524ac95221377834348cca46a23804874b12f9641418f6454b4612ecb7ab1ea664e4047fec42b2790cc6903b1205c8ab668a3596b3838ee958996f09d4be74ae0a18ae68ebf99c2d51df8ac3789cd2ee2521bbce27b4c718a6a4a210c124e54898d9ab04295e11e7ed01bb5b4d12cd9e341e0293b539a8ba619924db875d1f30959499d70186af9b64922099989596ea22f9399a0da0c2b049913bba52836a0b9cb0e6a0b225bcfd2c808e9292082977b7ecdca3b556bb354eaa2ab2a5bec6cd1d029b561789d28c523eea3822ab1f6468beb9b06878bb7ca69ebcc5e0c4ce9831ed2ed4e6ef4c01812e20054966b06a93e5896c2a054e14c90761793b3b8ea562dffd99a71b6854283833b590b5f03d7ca3f25534684491091e17dff72c9174474f4ad26ace5e51a5f78308716878d1ee07bf441f172b3472ec324cdda53bd04ac00faecac6a012a97a6fa22817ca217517e2b6c9dc74245a6a0b76260d4bfcc5d4a2de267815968c580d5dbeb2ad5ca39c0e979c184d39cac65cff1860f589c895bdde0dfbf45450361bfac39d8208df9d87f476c966d8daa8da7d1a5ff25a00d3a771706cce66a2e0e827fc9c55c2f442a831012df93b3035a51a4371049155cf154a96f5546d65c14e0136483bbfddfcc1aa2928b71010830a60191f9808840c3c894cefc2755ada5ce0a3dbb34eee0a0725979259425ac7678d834296039201cc4786ee974fa49497931461c95fbccb07555d0e60184664344992da6d42c9253c176deb548996a41be23e7a03e04badcbebf70dc6231a176aabb310438e25c379f4796d9d038cf7f8ac99aae2abc5639abd3f038e722465b8e45f01a2587534de24fa77d2b1ac5d882346a4422ae3211fb0f751a25c92393524619bbb014eb1564a245be8fbc55eed48356004e56d396f2f55a9daf9e0a35da47f23b7940caa0d2e26d4d68d88c3dc7a180637726da7336ea68fa4e5a8c124342c93e825a47419bc47e43b747e8c45558fba94e8ac0b5a0a5b51bc667b114c94064fb192b7182ab46908916a233fc959903e065b4e14991224c1a31424a18a0445e5c1ebad02ae87a88b72800d694c6bc940df3574f7501893835298b842c4d2b31063d91a1399e08a66a6886b59ba426f022d2317f5b298cd1dc1b1df3d8d4bcca913754a4b1946b39156357add4143948ac49c8644779b5ab1b7036193b7788d9c2ba55d74fe8d46c4840d7b328574320942f5f3e5150feb3079ce2c7abe5923e62c217e6f4e1f36b5796838c617e75f7921cb9a0043cfbd4da225f6f45492ba7f85dd51c62695f94bfee8d674b32b81de685530b7bde2825b000c3e2a8eb1f44c1a52cf23cea72fd517f36f4e587dde8161093f7801693e71e9f1d757f8532499a414a96aa16ac8b1409bad18205ec0646c5546b4047b06382101c521ba414d6c3b5f81c3b2c1898fe22538222cf825ce50a8b3dbad37e04db1f538d1701061ed41ba593acbcd3ee198442b5f1966914930129c8196e5cc422183ed2726b6fcba19c8d285ff0ab0908261d713856e12a5c198f9b2913fecfe95a0a4b4c5125eb87d640514961b5720582fa4d6ef87ee653a805570b4a326a6c2c2a46398fea5ed1e1fca0c857799de7a4b2258846adcae3282417e936bda359cd4893e118b363379125d13de48764a8cc2ae391745b263c0d55978900a7ff39902c5f6f85a8a254858dc529f4c6e2451ccbd140e82e9ab60f045efa83a12390e62060e920bb30eb09c289325597e0a819da13a0a555bdd8491599975a12c9f2aa7ffc660b75a2a0735749467ea7475131da2488427db46725242819dbb7bda96acef61c1367c62254f4868eb0d6de1082d70370849764b3bc02035af46dfa474791a82ca87e433d62174b6dca59532a59d0560d6b0779e7e3be36d9ad9723abd0e9bf8e9d56914e50140d8de2c
+InNoiseEP = 1084eda5639e2a95dd2c094281973c88e05852785c2d8e11822d4e257b8299449f751d69d084fac98c6a216e028ba52dba4015ead204e893ebd62d323cde8580a99f05b56676a441b1d636fe553143cc573e1855f24c667a9d8433efa38af3e0de544743e69769880796e95e6006919ef5ac9c9bb7d416719b2ad80de0812e11e3d3c541976e1f405662f27aa11707b8a858bee897caa5e0d06d9d92ca099c75a747e4dda9340a8b2c8d63ab767279e7669e7b66ca2718851bf0adc494906799c5b25e744c06c74445feb2e8ae88161079b46bec4cd920de6fc59de5d2d3f96f8463b4ba77ec7eb32fb3b0b1903661072bdb6018aeffa56125b09016e89a40817e38607eaa801191a493cf0e96207972a24561bdb40c3da2d78903957ff268f0e9dc3a3f3486fca5075a7cb832b2a08919e9366f1d4b2761a63dbec44fc5b5aed5b0742773e58d1a7cdb044204883d31b3a1d8634ab26f53846f7372587afbe4cf471fec7068e574e89d9c2e6ce65217d0cc6fe6620d1601e5b762d36a54eb6612d7ad663bad406111d2cc3618dd891d42ca66fe3944f38812392bbe8b9086e4f259be81b7953756532d741a51087377a8044013760a72806b108c055173dea0dd813f8a70349b9fad63b44f20a3b0418f4548316fd26cc740b6e9b324930b252822267b656fc1882ea0856301971d1a7e3fd1630a9590b96d16e7c71e1d808d00ab899f780809a612c2fa152fc4497e50622c37d69447c6ccfd0d355a10778cd1b88894c9d47ac12ac7a4b311d359863caef86aea8d7416576f161fd92bab081cb8e06d1d198d6e9b0895583a651a229b250778dc049710542594c79c45f338861e679cc554e6be3c2740a8733655e6c5aa5b8bae797f5b442a58599f321ae316e592bc380b879a87483eeb9d9761a550ae9ad46940056582d11ddd6870108b16a4d53b11896c6ce60ce1415e3f3c082a6a10413b354250b0d97561d4a0b9f36588137555c21824440b2f928aa6e3403a9061fa4b53cc73dda4cfa867449d5c65a090b16a87500df94b4a08882e11910bc51ea7fd06eda4a7a66640c6ca915ca89ee5cf322b57412d8c4d8d3385ad147241ea978f4282acc3dd10f3ba036be51646a3c63f3c578380b6687a816fd0c2dba86d9e8ba79940cc0b7c49e1034681227cddedf2ee6e638549141af0744d7d817730ea98aae4d0bdea72fa9506511714c64574da0e3221b1ee52d4deadd3bd9934752fb0c3a4f148f64f21a2815131b842ddb6680c49c21ae3a7c9dc4e5c405b8964728d67499fe48665ab7993b91a972e543699e7336dca6a889a8a3636a86cc7df9ef9234a1855629a01f592a6b8c6509bff789f2c446f81ae591b8458b5089f10984fdd8bbbf46259b91009174b4a8d2ee32fe1c190360d91d251638661b4c7e8ea623e6a2ac138f1aa8d9a2a2c5c87d83a972d4b36133b69416e8af142a5a612f0d7b80138b9750ddf813b7ac8204ad789bad2e166a5e3560589c14e9389a6932c743fbfc78cc047ca3a06da436baf8c0a651dcb74e356b9e9e7b7e3d92c88ab0920e54dae161491d1907159b7bad30e16db74547e061f3b732a819ec9d565cd769e915c8802a59528f05e2d70a04d991b6af9d4df6c122b6ffa15c62db8f6eedc120f7232a2e03a1b1442a67da1d6463af40d1ca8b2e2ccc5845c530036a6085b7f2969162c58aec6ed2ec71334583f93c47043e94c34d2cb2640f5b0884f1bc3ae9e2658e9d40b18c89540542c877e7252daebbdcb1c8d0c5ee5eb6ab46a3efa8ec02877362f562190614c65893a0240185ab1445727e5b400ed996184f0076a155134242de6923095fc65812a548d57de2882483c194f8a92d765d51119ac195ce56b785865c3837806f516c5566c5a32e5493e6da2f7959c7f8e2ca03a5b46b488629e463436956f2381f938a0311dc4950b3b56164598b2d71b1515492630449465bc888dc14b7bbe7e3cce4fa2f0861d52633f3689f068f19645525c4c09b01af422d95c9b480e1f0a08691ad98d29940a22260296e85436e47f891eb2f27261050464c7c1603c904b59c268c98490b42f4b7992c952d316c14a76928b6c0d3a19a93427ab6f8f503bc087bc4bc8227990378ec9a4c8bda8282f4aac76572519802d89e606a031209bec03b242ae73a6a1c839e513b50415d9684e58eb44e90a7c7957f4accdf87fe46ebf45eb12e8665395a5e357ae64284c841ec664bd9b1f2f9f53594d96bfa367e47fe6338804c6e03409aae54c9a3032702bb50fb3039bce16b0d00f1a6d747dd74d17054f2923f8e43316006c3b82e4b3b003b551ba534f1a8a45bdbf1729813f2005812edf29df358f629047ab933dafe59f998a854421621fcdc10726309122843347a07671c91f048dcd1d0b2369988ddc13616064838be1852765ec9a145a2dd6247afa521a42ef885cbb0e226fd84f8693d0f836daec92c75486a8434d9cc40436b9866c90600e000e3ade202446c145866931a138e66d96cd25a256219b
+InNoiseEPP = 00c0ff2b00fcbffe2f001c00fcbf00c00010000000040000200000c0fc6f0030000800ff2f002c00080000c00040000c000080001000fcbf04800030000800fdaf000000fcbf00b0ff1b00fcbf094000e0ff0a00030001000000c00580ff0b000000ff6fffdbff16000180ff0b00130002c0ff0b00f8bf00c0ffcbff0a00fe6f00b0ff1200fdefffebff02c0faefff1b0000000400fffbfffebffc2f00f0ff060001c0ff2b00fcbf0440013000fcbfffeffffbfff6bffd2fff1b00f8bffe2f002c000400040000acfffabfffafff1b00040000c0ff6b00f8bf0200002c0004000300002000f8bf0080000000efbffdafff1b000000ff6f001000000000f0fffbff06000580ff2b00fcbf0400ff2b00f4bf0400000c00ffbf0180ff0b0023000380005000fcbf030000e0ff02c00040001000f8bf0780ff2b000800fd2f002c00fcbf03c0004000fcbfff6f00f0fffebf0380ffdbff120000b0000000fcbf00f00010000c00fc6fff1b00f8bf00b0010000070000f000e0ff02c002c000200000c00030009cfffabf024000f0ff02c0030000000007000200002000040004800010001000040000e0ff02c00030ff0b00fbbf0080ffcbff02c00180ffcbff160003c0000000070001c0ff0b00f8bf0140ffdbfffebf010000f0ff02c0ff2f004c00fcbf044001b0ff0e0001c0001000f8bffbaf0010000400000000f0fffabf007000e0fffebf00f0005000f8bf04c0ff0b000c00fc2f00000010000480ff4b000c00feaf000000f7bf007001000004000030001c0008000600ffebff0e000000000c001700fe2f01000003c0fd2f01f0ff0e00ffefff1b00f0bfff2f00fcfff2bffcaf0020000c00feefff0b00e8bf0380ff2b000000fd6f00d0fff6bf0440ffdbff060003c0febbff0200ff2f002000f0bf0280ff1b0010000000ff3b00f0bf0030ff2b000400007000f0fffabffe6f0020000400feaf000000ffbf03400040001000fdefff7b00000000f0ff1b0000c000c000f0ff0600feaf0000000000ffaf0060000000ff2f00ecfff6bf0200001000f8bf00300000001300fd6fff0b00f0bffaaffffbff0200fc2f0110000800034000d0fff6bf0030000c00100000000010000c00010000dcff1a00fc2f000c00f8bfff2f00300008000500001c000800020000f0ff02c00070000000140000b0ffebff0600010000f0ff060000f0ff0b000700fe6fff0b0000000100003000f8bf01400020001000fd2f00dcff0600ff6f00400008000480000000f3bf0400003c0000c001c0000000070000300000000300ff6f01100000c008c0fffbff020001000000000f00054000c0ff02c0020000000003c00040ff0b00fbbf01c0003000f8bff82fff1b00fcbffd6f004000fcbf0100000c000b00ffef001000f8bf02400030000000fe2f000c00f8bf030000e0fffebf04c0ff1b0020000240ff0b000b00fdeffffbfff6bf07c0ff4b0018000180ff1b00fcbf00f0ff0b0003c000f0feebfffebfff6f0130000800058000100008000640fffbff1600fe6f010000efbf014000000008000000000000fcbf007000e0ff02c000f0fe0b00000005c0ff0b000000003000100008000100ff0b0003c0fe6f01c0fffabffb2f00ecff0a00030001d0ff0a00ffefff1b00f8bffd2f00ccff06000000001c000800ffafffabff0600010000acfffebf0300000000f3bf00b0ff2b00f4bf007000e0ff02c0014000f0ff0a00010000fcff0a00030000ecfffebf00800000000f0002400100001000feefff1b00080000b0fe0b00fcbf00700010000c00ff2f00f0ff160003400020001000ff2f00e0ff0e000030002c00f8bf0280ff3b000000020001c0ff160000b00000000700feefff6b0000c0018000f0ff1200feef01300000c0003000dcfffebf0100000c000400010000f0ff02c00000004c00fcbffe2fff0b0000c0feaf0100000800010000000010000080ff4b00f0bf007000f0ff0a000040fe3b00080000c000f0ff0a00038000f0ff0a00fb2f00c0ff1600fd2f003c0000c000000000000b00ffaf003000e4bf02c0ff4b00f8bffcefff0b000300060000fcff02c0feefff0b00000000f0002000ecbf034000f0ff02c00080ffebfffebf00b001d0fffebf054000000003c0ff6ffffbfffabffeefffdbff1600fc2f0000000c000000002c000c00fb6fff3b00080002800020000800004000f0ff02c0018000e0ff160001c00000000400000000acff0e0000f0ffcbff02000030002000100002c0000000070002800040000400fdef0010000400ff2f000c00fcbffc2f0030000400fd6f000000040003400040000000034000f0ff0e0004800010000c0001c0000000f4bf030000ecff02c000300110000000fe2f00e0ff06000680fe5b00f8bf0380001000f8bfffeffffbff0a000000001c001000010000000003c00100002000fcbf02400000001300fd2f00100000c000c0ff0b00f8bf0000ff0b00f0bffd2f0010000800fc2f01c0ff0200
+InRand = dcc79ac886c7ea2fb60721ed2c18a72a4684f1a2668c8bab4b7cd6de29e9834c
+OutPK = d2d75576a45915b2cfd75b4bf06b375114e4f74011e94172661f6644abce4f161ea0558ea13e358eb55f624a6191b25c6ae818f358443078ae43df8a800024254fd9191219b3ee6e2cea5fac0e0a0a5b8aff580b69972b592c311b65ea283267803a7407a663685e87419482505288759406c311ff79b69fd50923ba10120fd14849689471f3d74e74fbb17dd8a7c6e3315147b4d2b6e10b1d8e5fe5c0f9af15b7c80d2d165779a2ec26518bc9d8400def4707f2d8598e56f20501729f3fedd5fb136dba0e5539670a89906e9fab0327cd647d5a9c80e9dd5501469123deb9654d477b297cc4b4efa0f39aada4a37b54b94715a53f3b4bb2f2af109627504db876f505d020fa2103ee81862992a3be24b14749f52017460e83adf847a049108eca92294d117b1e6a146829a5b5699b73312d228a5808e1b11ff174484cf9920f20a016cb8aa461ad41ee13ada4f26d223d8e0678205a079e0de22b51a1265b86c015ab1132c19de6fb09650cb1a33f14eed944291b4629133a3c156e7bf85dfc0887a753a72a5930e8890bfb59fc1d5fe926fb30be72d520c8b6a5908cb5da4df7a34135b3862cb85c1162202dcea8a0a843ea441004d0d4a7ee90372174ba8a680b0137ec4887f44898e3c0a22e3696c33a7a76980b8f5c45ea126d769c6a337574384927e5c17090bf8a506edb6b60be7a9a7b5a32851aab83b2c4d8a66a44636261a6f0b5e72830c2edcc38069039701d58aa0f4730385f449cb3069e52b38d55f2d7ac981bfa15af4bad04200964b8e8aee47a7420f8e428ba9a5005ba89b323a8501fa3a5b0332895a865c408b24c7e8d8ae0f710880dbad5d0920bf676a1340c7e77b1fd2e890f3aa492ae2c2662bdb451f09e17623353d37eb0cd20a7c96abe0ffb2ffc42f88171374580e27b64a550ca636aed8e12758de313d2f22a6c6feec89dca4bd0cd69a5cb588b0a9f6d61a67d9ffd062627aba0653f5a5025385423a51bc5570afd0d88d196b2a8855ce606d2d5227cb1182a90b224ee39f52f5aa9d6b685210ec6db683caeaeddee41c30c26304464855b7562898482a361f4567d53c703559850d9436480c9d2923f1c4264721893798fe950b8dea6466eb51a045943faa88cf31c1e4dcac6a741ebad731a604ea3e04c558dcdbb82d361a1de843b59587c593f0c12a5b7c4acc325c41a8cc9271ba5d6763de51c34412aa3fc951bc4072c22feea35528d65a55eb554641d7ae6ce6086668e1d2758a8a7d50ddea987ad5bc2619e4be2cc8f49e10a13436225e08b7da09861207e2b7cfbd8c56d5b5dc7002fd6b3dae095189c591225153e237c703da222dac22b6897936d567222d66d2641b23037d0299b8a0e33e3c5839dc16a54e81499f2a13c305534283641e820e8363a17e62a86d49d7bb8eb85f5cfad9ea62dfc99374e0d75d5a1695bfd74b362ff561d41433a234b274c2dabdd5254561365b64968f105618aa8444daa44dae694766df7213fc4255fe4c3841ca975d10bc89b198e31531f7a14873cabfa048d00a53d8c3dca1cb1934d206ced70652fe0b592898ffa20244e23413f164ad90f7d43bf55ce346930efc89b1a4c02ccde523b408526c0970b4b6d8806510cb0d435ba7aada855c56ce66457ab562a124d591d9be210c4566bd54ce5091e39275cd2d49053b900518535a1d1c43b98a01ab99d8d41068a461831fc740a5c6a6a3bc25a16a7bfaadd80b5dd288058f341244b520b2ade65b6fd98dd197c14bd410e7b93eef102a45fc6d066610ff6af25414a7c96d9e625fbd28924bb9dd090f1f51f4b2de892ec0e536fa8c4eb80015136454c656566800d2871e4d5ce6a021db100c7296f2e0f066b336a880d24d6d5b7346cee9356f1f2426cc9d40100572a13eaa0f4d15ad5a4e24ecb94fbb2a85f0b06e9b2749758adcf88662836bfae9126f7ac9e941081288f944a84957ce3e5a9041f5ffee3990ca002596aa382ce515b27a171dc8c9cfea4ef282a25089aa99a84bd1d8e062631b7b6a00bb551f749466814f9a401b6b2a2b94986a618b98e1e076091144e628bd6619083bf8d33c36c2488be8194bcee7b95cd6480e097d6f6af604f864ff3275e02976995b7191b80b23f22cf60d11651775184f1c6088420edc352b5619f0badcc90f355575d2cbe5082e9775b01fe880c325ff688dc90e63c61acabaa1927fd89eecb7b99ce84b3ef0839887ad0bff75e3765e5ee2e09857106190e191e5437facc5a90798b442729f824cfadef839fe21daa64ba8a86ce72ce0e03e369c6b0e85aea721b9c534c900ef6db02773853eea69db49b80477efbc0e0ab92a37076a5314c181fb564affd4bfd011bc0e75a2357cc306796e0547951d0f771ec642dcc36b1e40ebfd51b06bb88a1bd7aa98d07ea217daa0ad9220d15bbaf460e30a7003a2f068e4783845230a3759ae509bbebe620a0be8807e28ceda71ed044354258f5461d42c76f510382da4f0140821527a23b220ad423126d3f6a1c18d725e55f
+OutRec = 00000030000000030000100008000000001000040003c0002000000002c00010000c000180003000080002000030000400014000200008000040000000040002400000000c0002c00030000000038000100000000140000000000000800000000c000180000000080000000010000800000000200008000380000000000003800000000400034000200004000040000000040001800020000400014000300004000180002000000002c0000000080000000020000c0001c0002000000002800020000c0002000030000c0002800030000c0001c000300000000200002000040001c000100000000040000000040002c000000004000140002000080003c0002000040002800030000c0002800020000000028000300004000180002000000000c00000000c00004000200000000340001000000001800000000c00000000100000000080001000080000c000200000000340000000000002000000000c0003000020000c000040001000040002800000000c0003400010000c0002c000100004000300002000040000000010000c0003800000000c000340003000040002000010000000030000200004000140000000000002c0000000080001c0000000000001800010000800018000200008000000000000040000c00010000c000040001000080003800020000c0003000010000000028000200008000100000000040001800030000c000000000000080000c0001000040003800010000c000300000000000002800000000c000040001000080000c000200000000300002000000002400010000c0003c000100008000280002000080001800000000400000000300004000200003000000000000020000000000000100004000100001000000002c00010000400000000300000000040000000000000c00000000400014000200004000300003000000003000030000c0001c0001000000000000030000c00010000300000000300002000080003c0001000000002c00010000400014000100000000040003000000003c000100008000200000000080003400010000c00038000100000000140001000000001800010000c00010000200008000180003000080000c000200000000280000000000003c00020000800038000300008000140000000040000400020000c0002800010000c0003c00000000c000340000000000000c0002000080000c0002000000003000020000c0000400020000000034000300004000000000000080002000000000000030000300004000100000000080003000010000c0001c000200004000340001000000001800030000c0000800000000c00010000200004000300001000040002400000000c00038000200004000280002000080003800000000c00034000000008000240003000080001000010000c0002000010000c00028000300000000100000000040001800000000400024000100000000000001000080003c0003000000001800020000c0002c000000004000280003000080001000020000c000080002000080002c00010000000000000300004000100002000040000000010000c0001800000000c0003000010000c0003c0001000080000400020000c0003800010000c0000800000000800030000200004000200002000000000c0002000080003c000300004000100001000080000400020000c00020000100000000200000000000002400000000c0002c0002000040002800010000c00030000200008000280002000040003800020000800018000100004000180002000080002000020000800028000200004000380002000000000c00000000800010000000004000180003000080003400010000c00038000100000000340002000040001c00010000c0002400000000c0001c00020000400010000200000000340003000040001400030000c000180001000000003c00020000800000000200004000180001000000000c0002000080003c000300000000040000000000003c000200004000140001000040001400000000c0000400030000c0000400020000400010000000008000100001000000000400030000800024000200000000180002000080001400000000c00038000300008000380002000000001400030000c0000000030000c000240002000000001c0000000080002800010000400018000200008000380000000000000c0000000080000c0002000080001c000300004000340003000080002c0002000040000400010000400014000300000000180000000080001400010000c0003c00020000000010000200004000000001000080003400030000000024000300000000280001000080000400020000c00024000200008000200003000040001000010000c000340000000000003000030000000034000300008000040002000040001000020000c0001800020000c0003c000200004000180002000000003000000000c0003c0002000000002800010000000
+Key = 4eba286d54cc289caf78c9964256be52db2f2cdc020ce12fb2c472f2e7913d0d
+
+InNoiseS = c1e9a276ceae5b944e79f8c57ab825a58ba9d01a23cedd5b2b2172a3f8ac35e80b5a3f84c034e226219e78e800453f3ab95329bfe0f4c17246ab6193db9dbe324b5f07f0998d6d83d24208d1b51298b5166c66258d26d86278843e3ddd13da7ebe0ed24902765149465113fee9461a84af1a68c8bf76769603e82a21a693fb0442bb70f599d8982076b9fc36a42c46b75db8b1699544e557fe21330ee6b53bd2310e6dee459b7a6fb55cd640ab1881334542aa920a8ac4e77c87c60ab1c98ae888f42134db67a7b809c69fc81194c487f24b61a011b086d058cb4556b582c54e4f047f50531416539ea7c1f04e7db95bacd3afb616812285354344167ee4745512601dfdae2a9afaf531b40e16d86569420b2805722ad890ed17308035c8943a0daad4e8c8325e8cd7d0263803879ebf420df2b761a86939b4e19ab161ae22bc045acd0af1985dca8230a752150191b20890d0788b9e8e299d08fbd7664e9645c43940c1b06d1c8251cf859d158fb5b515e40c0aaccd90b370020383b71298cdb335803d4b58fd3c8217435218b6b970d180a284d17b7f61ebeb9a46b8bc476bf321010f06128c02e171763dcd10b655aa2542213b6467800f134284da21622f6c20c522d1d05adf9bed5b7a6d63a3ab7236074e1f9b999138ac258900eeaf68a8328ed5084a23cf7cbd851ba7c222d1975363a0770cc446e7c4ef51e49457eaab2ef1fce02a1629c68b12d1aaa3a9bf041a765030d0e2a719ca712fec479ae3f5a4c2caba024ddc2c4c2c354bfb5c55b62daccb7a068adb03253437f87438643ee9d86929d5681b52b87664ace83a2672daae7c1f6c6aa2c486f9e3bb31c4f27b32d047a6af990e08d36e3a8fadb376cecf67989c4977d33d56db106107de6e89849646132a23858514a8931ebd4383d2635f90b2f17b7d92eae259e6ab4da034992a1b6ff215254e78a930fa6cf12283b7d23c73e08788a645b55268ca37e6c49227bb5f2f536caeab9344b7904519509990616250f127201bf5631ec28f5b9b7897870282b69bb46a8854bb5a66f626f9b5a394e062e4208c194cd1b963747216d8975f28a2480dc04a0f31462287d463ab14969827aa2f4fc4c30e977153e9e992688c8ad0b5c3efd17a4b95ec910881311649e223dd8d9348e11f0183560b345a4064c1205d4d41fa5b2b5c60e00dfdb2f31199d8897ea960fd78e6000b7bbb94720d275d9bbc819ca124dcf706a27615209cea60b952fb90455a2bbe3d311b9da08798491684a6291e915aa80b1983d6190610a094173575a7772587408338571e8e248a0b255c18a2cf4684040d215f88da53e9a6114903c3683d3eed814de857742e76a95282d87877289abbe54efdbc9d1961b1176cb5066d3f0bf7556be86106d1d6e9a8bfa8d5fbecf460095821989abd55b60261c8287a137feaedb0515e45694721081851957e88d105db0d974d5d5cd5a9386a8a037f315abe3b809b7f1b6656ac2d269452a39868e66b8e49543584f0667614a9f0aef09a7d956370a6a58353b70196498924bfa073ad687b1ee1a768ecabb7ab15f22fbc71579f7463429d69670d2ec605119f319574e572ee229f6241fab912addb1b83d71bd143149408abb1c15bde84ef93e6d2693c9423d99128832629e6d75b682e0e0181a0dcf442b4c27c32846431a3a6d8690f1c188a9a4274bf284bf12bf0a0dc9672d358b1b6243a631fdefa6464f9463c89f7e423ce03fcd5dd22fd606372296b024baa86c8c922071842a5084a799392ad39880a8b50d9386c7d60f3920db18c1feceda8d72e36e3db48858c00b0e921e35a09e1b16bc54428e0f119b7cd07466af576ec1908f489206e8fcb16580a8271d612ef272f8a5e18c947e10ff2263b08e46527b42d1c993774ed4139ccc0b39566adbacdd1aacb0eb2aceb6dacaa2cb1818b53e8e4b2995ddbc4f399f86d80e6644a2550d129f0a21e84f8950aed8cdd59fd1d09470ea43a19ca7f6a8de69422f0b3078a87a3129a8d819478a470d0624593620182087f57c62450c5f1121486c377648c3e5118e59659c56fd408dee705beae9b412467884904c1488cea3fc51696471ef154f9547ee6ee4636ae578f0611b4311c83e744f186ce2013e8eb00633a3a9223e39ea9107a13639a4dc0457116d20899da92e623b64240d090460a43d88476564c630f99a049f5ce5e34d645c8890e71099f16ebc72121aa044685fdf187e166155736bc1429c85cab2c808c0a1eda3bfbcc737395b4845031807b872407381212efa6856b69b145db0c4fa936ad6f05a824c10f500ad9e91cca8b7c0d57e38c3b82a8bc6d8132b6ae16381604ebfbfd8e8416177ca560e2983963fb1b25ae28d85071d251493e4c27a34319f04eb387a592e62c95b7e8b426dffe2b6d2ca89a71e091bb15c542ad9cb4211cd789b95c13972b9aea65e12051af44020f52971e61324245e9154d528879e559644ec8f7470e06a6d897cb84a13a656ecd55a69dad7234c79da3450a4d
+InPK = d2d75576a45915b2cfd75b4bf06b375114e4f74011e94172661f6644abce4f161ea0558ea13e358eb55f624a6191b25c6ae818f358443078ae43df8a800024254fd9191219b3ee6e2cea5fac0e0a0a5b8aff580b69972b592c311b65ea283267803a7407a663685e87419482505288759406c311ff79b69fd50923ba10120fd14849689471f3d74e74fbb17dd8a7c6e3315147b4d2b6e10b1d8e5fe5c0f9af15b7c80d2d165779a2ec26518bc9d8400def4707f2d8598e56f20501729f3fedd5fb136dba0e5539670a89906e9fab0327cd647d5a9c80e9dd5501469123deb9654d477b297cc4b4efa0f39aada4a37b54b94715a53f3b4bb2f2af109627504db876f505d020fa2103ee81862992a3be24b14749f52017460e83adf847a049108eca92294d117b1e6a146829a5b5699b73312d228a5808e1b11ff174484cf9920f20a016cb8aa461ad41ee13ada4f26d223d8e0678205a079e0de22b51a1265b86c015ab1132c19de6fb09650cb1a33f14eed944291b4629133a3c156e7bf85dfc0887a753a72a5930e8890bfb59fc1d5fe926fb30be72d520c8b6a5908cb5da4df7a34135b3862cb85c1162202dcea8a0a843ea441004d0d4a7ee90372174ba8a680b0137ec4887f44898e3c0a22e3696c33a7a76980b8f5c45ea126d769c6a337574384927e5c17090bf8a506edb6b60be7a9a7b5a32851aab83b2c4d8a66a44636261a6f0b5e72830c2edcc38069039701d58aa0f4730385f449cb3069e52b38d55f2d7ac981bfa15af4bad04200964b8e8aee47a7420f8e428ba9a5005ba89b323a8501fa3a5b0332895a865c408b24c7e8d8ae0f710880dbad5d0920bf676a1340c7e77b1fd2e890f3aa492ae2c2662bdb451f09e17623353d37eb0cd20a7c96abe0ffb2ffc42f88171374580e27b64a550ca636aed8e12758de313d2f22a6c6feec89dca4bd0cd69a5cb588b0a9f6d61a67d9ffd062627aba0653f5a5025385423a51bc5570afd0d88d196b2a8855ce606d2d5227cb1182a90b224ee39f52f5aa9d6b685210ec6db683caeaeddee41c30c26304464855b7562898482a361f4567d53c703559850d9436480c9d2923f1c4264721893798fe950b8dea6466eb51a045943faa88cf31c1e4dcac6a741ebad731a604ea3e04c558dcdbb82d361a1de843b59587c593f0c12a5b7c4acc325c41a8cc9271ba5d6763de51c34412aa3fc951bc4072c22feea35528d65a55eb554641d7ae6ce6086668e1d2758a8a7d50ddea987ad5bc2619e4be2cc8f49e10a13436225e08b7da09861207e2b7cfbd8c56d5b5dc7002fd6b3dae095189c591225153e237c703da222dac22b6897936d567222d66d2641b23037d0299b8a0e33e3c5839dc16a54e81499f2a13c305534283641e820e8363a17e62a86d49d7bb8eb85f5cfad9ea62dfc99374e0d75d5a1695bfd74b362ff561d41433a234b274c2dabdd5254561365b64968f105618aa8444daa44dae694766df7213fc4255fe4c3841ca975d10bc89b198e31531f7a14873cabfa048d00a53d8c3dca1cb1934d206ced70652fe0b592898ffa20244e23413f164ad90f7d43bf55ce346930efc89b1a4c02ccde523b408526c0970b4b6d8806510cb0d435ba7aada855c56ce66457ab562a124d591d9be210c4566bd54ce5091e39275cd2d49053b900518535a1d1c43b98a01ab99d8d41068a461831fc740a5c6a6a3bc25a16a7bfaadd80b5dd288058f341244b520b2ade65b6fd98dd197c14bd410e7b93eef102a45fc6d066610ff6af25414a7c96d9e625fbd28924bb9dd090f1f51f4b2de892ec0e536fa8c4eb80015136454c656566800d2871e4d5ce6a021db100c7296f2e0f066b336a880d24d6d5b7346cee9356f1f2426cc9d40100572a13eaa0f4d15ad5a4e24ecb94fbb2a85f0b06e9b2749758adcf88662836bfae9126f7ac9e941081288f944a84957ce3e5a9041f5ffee3990ca002596aa382ce515b27a171dc8c9cfea4ef282a25089aa99a84bd1d8e062631b7b6a00bb551f749466814f9a401b6b2a2b94986a618b98e1e076091144e628bd6619083bf8d33c36c2488be8194bcee7b95cd6480e097d6f6af604f864ff3275e02976995b7191b80b23f22cf60d11651775184f1c6088420edc352b5619f0badcc90f355575d2cbe5082e9775b01fe880c325ff688dc90e63c61acabaa1927fd89eecb7b99ce84b3ef0839887ad0bff75e3765e5ee2e09857106190e191e5437facc5a90798b442729f824cfadef839fe21daa64ba8a86ce72ce0e03e369c6b0e85aea721b9c534c900ef6db02773853eea69db49b80477efbc0e0ab92a37076a5314c181fb564affd4bfd011bc0e75a2357cc306796e0547951d0f771ec642dcc36b1e40ebfd51b06bb88a1bd7aa98d07ea217daa0ad9220d15bbaf460e30a7003a2f068e4783845230a3759ae509bbebe620a0be8807e28ceda71ed044354258f5461d42c76f510382da4f0140821527a23b220ad423126d3f6a1c18d725e55f
+InRec = 00000030000000030000100008000000001000040003c0002000000002c00010000c000180003000080002000030000400014000200008000040000000040002400000000c0002c00030000000038000100000000140000000000000800000000c000180000000080000000010000800000000200008000380000000000003800000000400034000200004000040000000040001800020000400014000300004000180002000000002c0000000080000000020000c0001c0002000000002800020000c0002000030000c0002800030000c0001c000300000000200002000040001c000100000000040000000040002c000000004000140002000080003c0002000040002800030000c0002800020000000028000300004000180002000000000c00000000c00004000200000000340001000000001800000000c00000000100000000080001000080000c000200000000340000000000002000000000c0003000020000c000040001000040002800000000c0003400010000c0002c000100004000300002000040000000010000c0003800000000c000340003000040002000010000000030000200004000140000000000002c0000000080001c0000000000001800010000800018000200008000000000000040000c00010000c000040001000080003800020000c0003000010000000028000200008000100000000040001800030000c000000000000080000c0001000040003800010000c000300000000000002800000000c000040001000080000c000200000000300002000000002400010000c0003c000100008000280002000080001800000000400000000300004000200003000000000000020000000000000100004000100001000000002c00010000400000000300000000040000000000000c00000000400014000200004000300003000000003000030000c0001c0001000000000000030000c00010000300000000300002000080003c0001000000002c00010000400014000100000000040003000000003c000100008000200000000080003400010000c00038000100000000140001000000001800010000c00010000200008000180003000080000c000200000000280000000000003c00020000800038000300008000140000000040000400020000c0002800010000c0003c00000000c000340000000000000c0002000080000c0002000000003000020000c0000400020000000034000300004000000000000080002000000000000030000300004000100000000080003000010000c0001c000200004000340001000000001800030000c0000800000000c00010000200004000300001000040002400000000c00038000200004000280002000080003800000000c00034000000008000240003000080001000010000c0002000010000c00028000300000000100000000040001800000000400024000100000000000001000080003c0003000000001800020000c0002c000000004000280003000080001000020000c000080002000080002c00010000000000000300004000100002000040000000010000c0001800000000c0003000010000c0003c0001000080000400020000c0003800010000c0000800000000800030000200004000200002000000000c0002000080003c000300004000100001000080000400020000c00020000100000000200000000000002400000000c0002c0002000040002800010000c00030000200008000280002000040003800020000800018000100004000180002000080002000020000800028000200004000380002000000000c00000000800010000000004000180003000080003400010000c00038000100000000340002000040001c00010000c0002400000000c0001c00020000400010000200000000340003000040001400030000c000180001000000003c00020000800000000200004000180001000000000c0002000080003c000300000000040000000000003c000200004000140001000040001400000000c0000400030000c0000400020000400010000000008000100001000000000400030000800024000200000000180002000080001400000000c00038000300008000380002000000001400030000c0000000030000c000240002000000001c0000000080002800010000400018000200008000380000000000000c0000000080000c0002000080001c000300004000340003000080002c0002000040000400010000400014000300000000180000000080001400010000c0003c00020000000010000200004000000001000080003400030000000024000300000000280001000080000400020000c00024000200008000200003000040001000010000c000340000000000003000030000000034000300008000040002000040001000020000c0001800020000c0003c000200004000180002000000003000000000c0003c0002000000002800010000000
+Key = 4eba286d54cc289caf78c9964256be52db2f2cdc020ce12fb2c472f2e7913d0d
+
+InRandA = 115dd662e4a18f069d8ef08e92843a45f73aecf6409390a45b46d41b5f92c6a85fe601c90487263afa39a28fa153054098a02fe501abb9abff56ac36378cb6cec3f2a962f6a8072601481922bde99367e2f0b4330ed70bd8c24295c31c9965b5917ed0ed48250f42468f22bb92ed3961b6e248848e2a2565def2a0657c8b49d027a5457d1f824fd23bfaa9a813eac634dc31025fe16bc067e640da864cf22621b422ee506a37343e41a23a56b2025d325d6129ecbd47f984fe0950c45c5d2a1bda0048b8fb91984b66d4930d667ad8f67d0ad2484ecabb3e4a6a4426d225846449457e21cb429bffe97c9510822a8bad238bde2a453e193b9a6b5d1dc8ab36d9ee36633298b6754e710b52afcf043ce9b9ccc2e902ffd6617442e902e4e529afccfa91b70984921c4809e39d9eee9d5be346988ee2da5cf9863864504fcda0ac8e9dea662555476647a8232564fd46ae420463137d947915d8d3c1207a6b5a0fdb9531a559169730a9fd7e6b70ce388673cd80586d10a4a7a08d1504cb4ad8ed3c3a14b667aaf477b4155c8afe75775299cd7388f57dcc590c0ae8cdbc0e95bdc9c9f5bf0e82dbc2726cb9a60be67395febcf3ec47802ad5b0eee7a1734d016fc41bba9056568cb5ae15c71a82acf6a6f9d53e643196ed6d680a4c8e5dea93cb80e03f6203c4f1e9ad1dd2e93968eb2959095d8c91c36c8e7e98a3ea23441f1a4b9a5a3ba24592a69d386bc8a516cb4cd0498905506ed6d26c68b7ba6291a584468b708f8731d13e8634dc2ea7663825aa6e7d27370866950ae18737049cd84f0763499223b2473ed72a5207b60aadeacc68a9d3934a55c8be32b2d44f253e0daf2c6cbf6a777e4784c34de26eb2ab1656210235813817a8793108317c05a569f1b74e2f47cccf691858b28c535d873f507e65a9b9e1a6da65532af12028ddabed18dc4bb8882f8c848c9bb23c7f99d893b9375e6b14dc71f834747cca9e081198c90e589d738895fd06eb1a9674b9b939b5e7b42a2c0d1167e13c13c8e27cec18c13506de7205917d59da589c04c1f45ab5c0b3a80120f86c7021dba9bf4a05c65ad1d27e153e2e44706caac82d99a8cf40e6c59cfeb7ec07e0db3ba0163e2ed5b05c60764562d60500a69a59831178a616b1f35ebd15876062f9437f8a8e78247d0cdc41f658672f4ea16a087c0a415402792838598eae4895ab79b1c4c69d5b144c4e82eabb748eec724454c3f6f0c935349a7d51d8e5a86a50ff5146e6a4c22ce0456f6d0ad50d0778d500326ad33bf7933d62cfd334316bf182ce109b51615679c02e0165d5f8ae54976cd837661324e4765792e4681e2ef7d134859424958ca5d6e1220fc00620df4295680b84574e985e1ba7e08b752805374332867659a273d91ed99dc5774ec798c06f8298ccab90d53bc950bbea2e25492e8c67deb3874bc4368b079d2a73499fc3c51c16c6b8b704967a71b795034d1e2da91cdcb246149c270095999e6be233d86538e0e572e3ef7190d58256a188f88628c81600337a563d4179c1d9a18744cea2279551af93aa278bb88a5ffe45e3e99a492ac86bd5c8957a7c018dcfb9ff56489084af406151658fde5f0b9ca6145098c9c1edfd08f7e0e3d4c162a58628a7d23009a4e4560723a694332fca49b902396b98d014bbabd262f21b780b4a056a878b55a8921293bedf45602eed1b4b4d80717492a9bcd9b8e99ef152bfe21c205b719a1d4d54a7df9579cfaa999404a13178efb236029d52e0a261a1973c5efd966b2219847b14065856272dac65c2d2614406740aa5f9145c859a7be9d6a48b8689b2ced1959b5c8d4b18f735268e70c65b4664481801916400d2b5d0739544bbd178d12cd2d718ea06f9649feee0850fb1584d1e18568e26833bc93d53261a488d261c295d2963b706d992e01eb2d78c5e548b2d483e410ccbf74ab5aad7d0e2637e0d3660fe3115cd5cea046f4778b426d5e079f9c7f09ac448a6d5bed819ec5b8bc525e504b8079650b10ca4036557b4f91c870fa56c200f220216125739e52ebac76f85abc64e1aeb5f3dbb250e50f69756a708cdac4aef845ae31c9a03091ff6d3123a65878e08dc1cb768a6f404ef9d52da0c2ce1f52431cde64e3432982b744710550c023d699b376054445df81699d411648e74cc7506b1506df3aceb7ba80aac6b21379062346c77d5cf68dbbc12e316f823ce2c33cc24c6e22afd7ed62134d744155cc9d007e010026c1e91121292a27efcc5d0213f1996458994b02e886966e97ff8a84d106c09f84aab516cb1d0f7ca443126c30dc0062598a3a21d32cb2fea35fe51a7a83892be55ca24971ac4863b0f4d557cd9a3420ea71b1f1a61de8912d2d6d59f23c425f9ac6077e86e50c333da23c7da73a5e47a804016819bf1c2a75f6c33522a505a1a9a5e313c901214cf33533ec8a6d2218900f2856b858b9bb49255236e090a90c49c84f32bc5f123d6072517e0ea091e6a04117c60639d2ada6f5d0bcd8d5729efa7394bfc85
+InNoiseS = b25e0026c0f1ac209932a42dac2b2a94f3d3a08078080b8ad39ddd85fe04106bad6c591655d7e6dc9936362a1442a112bfddef1594169d3b929b77663dae11971e713bb5d06882cc5933a4b55be9154f6b2da674e96cbc3ac56145875888931f64669d57c5637dbd469bd3b6504124227e5e86789b7403c7dcda60e2012bc20b28d116209cd9d26be498249ad7a933e63e587d4f8c8c728ef5a0e64fa532c8c56db5d629a803c451d0cabfa7fe185c559da2f73136b79418dd245cdd990fa948a7297591195b1fa8d6b953b3a4dc91692c84bada53c4ec8539394de26358102b509549a1dbaaa0ecd9a614b4e96f17d26ce3507c510d1798d11f218ce41a7900249e0ce70ca624560d0a74a23591f8fa553c1cda6b995c951ad4d965a8fea143523358b3147412422e629de92d64820fdb897a9ee22b5be0190e6ca889bf8b27914c61568bda84651b17a787d700aa1c8d0eed5b669d69b644b1283f61266f6d369a50f446e59c7bb911d1515023e484e7d949a9c6498368fd4291433629adfa93c993f5732e7e12a0cc3f79b94dbd84010f5711103f5d622fcbcbac55d511f8ba02ce47e00a4ec84d10af6684829275598c635defc620f59c5a1ce97399d12de74e9f84b8c25ccc87b4e4b7691d6cc595b40b6e7a2d0ea93773d60edb498ba0dc59906a930557d0529b4d975d68e1d0850f601b490f9a107847e909f8c8573c61927374d9a6bfac48f4ddedaeacc39c3037ecb7d251ebb98231bda7035ec9e2349ac3537496f609016d6a79d394ae43e951abe87fad7d7344148943fdb2b76b56c8e6316315a94eda5a3151a65e7608b80968442e33a7cfb1872ce5e2a1f462bd6d65c8d09a8023e944ccd88050a0c1d488843add51135b4ae73d90aef5143bf2016a668ccbfc6bc5d2275302ccb225a269dc08b26a83d96d16ab68e36eb19da009a87195e9200045d07172984ee427b983ec54af056df6caa32e24b2d579862ce60eac0a13a97da0312edf20d38cf94551a8fd61a78232b387a8b217f98ebb29737793d98a364c78a471dc04b0a3eb48cc9e43b6ae6d3875025d58e2aee7a95e69902f4c8bf34e73ce072572883aa930c1665055a07fec86c229dd9d5d4c8c80d227b278fadca1b00da0885b04c6959b96bedb6a5aeb5322a18cc116f29d3c1574cefd7513945479937a20040402011bacf9ddd010da0c7e19e8a65977d4bd96726951c18bd380c62e2fc861e338ab86587a69e49699c1587fc5737613405c50ca11a365b40ac58eb867f37d9aa61157621fb09737970451286e4e1e9d2cb01a1a0c1187bc491e2e1d75e08c02bcfe5cae870bd073ba05bf2b2f4be0d61a4b461970a5d930b5c9bf111b11252e61d3f4a7bd549ec44c0beff42a9c6273b03cad315907f490808144904388d4760e446e6bdadab21593a363f2db448987425f6963012e2803a4c50500ca9a1f22f9d8b64a4c59545bcba76813b7d41fa40d741ffa0d1ef60a040b4441951245c1b4aad9af707c1815959d0bd0bcceb80cce721b83ad1cdb9c1d5b45ae816403d63794cd285f169ec609791aa24f82d58e04436ec63d4a6086803ba584c83446519207c072d24a6ec7a696625e1cbff76c54468e458190e28e6848a4b6a52ab867fb9c0ec845bd26927d1d8b9f24f7afdc72402e65a1e0de44f45eec51585c594eebfee849f63ddc54ce073081837945a121f92e02f104c386df92b927ad02aba9ccbc2857f2557c7aae8eaf8524cae5620d86a2ebe43e40b108ea592b5450f10fcff13bf068771cc7440e7d6ae66c9af4a3340b6fcabee9ef459d8f8dd4b804717b3ec54ae2246abb7a2bd5647712a1694262eb4886befb8a11d4fd297f512e18b819121f4de1af51036a64fb97a111385d7a218ba794b9ddb7066fce7bb3f1b86590b7eb1b753a29d3bc584ecd300fdb33db715c5a15cc1bc3f2fa49a9abc52815c464af878781e85e15fee92788950005deae6822e084a4c9cbdae862c58f76a6fd25262a3a42004881a7160bf546e066f376163ee028b587014a2b2b269a19dea85c4a7690f3d2065dcb200af1e6714199d7498f3858e61d0d11e8bdaa85288c256cb8bab2982e038a084f459ba0697e7e9159ca0de7b2ecab32a8a0e2b3e26097e321868aedb9318c7972bf6e011e03db452ef8a07ce49d129d12b7e55818e9c4ada4dad00e8722764009ec09082a7870402abd180d050dab6cc5e110a290db641ae620ed5ec58a7f97821e07cb54de427454a5d90d28926af2008525d9f053487aea87972764023751e5cc959a793aa986230814082e8d0747d908027b0e0baf36e8768776a79a7056b438b8a7d3a134da4b698c8dfad12e418b2435f4aeda6cf0a6d4d4906e3d4845606947f87963c7238592e81aca8b03405365408383d120fc3178696d6599ca124fc6014ec6f2e93d274a8e28e3c64a395e433043283d81a91839d8984f642fa1c1043932596fade0cb561e08cffb08af0861535276d622b225d38c755b969c1971e24d600a9a5c
+InNoiseE = 410847f4c6bd1eed95a6d38272884feff2d3d58c25185437064cc584875e0901c0fe1d81e802e4efaa0e8654f95005f206854dc318e06a6bcb022923c26e521a6d98d8b15589e4efb1d5c3f57719dcabf362f9bfff4955b81759aad88856b08c3a08d661a50bf4e5a6ed00e4100bc0a483e460b138361c0806fdc791712a840dc500bbba42f60b87b52a963d15e6f4c7053d716d0ea5c27de87d2b00d4d60838760f56c570fd6942340dc0733f708493a99f78e81840da8fc4d94c914e01ce5cc4cc8183d6972ff4029537a18c756bc35a24588e3fe05abc340c6d593b68585293c04afbc18c179d8585c2a5f0aee36c0ae37ea8694fc79c51c5a87faf6126b766303a6963844b8538976752a363847064724018c939e1763c14a0b3667d841651f1b3753966d22c9bf76d2e0de9ab2091f1d439490fb796acdc55a34dbd02417d5914a55fc1271827c6d77862c330bac48c9ae334d20f499555c872716afdc9f6b219a18f7d47e608ebda1617c547903d6513cba84ed05132ab08272985778654bd1220534ab1134b5595b1880864224c316126547a9bde73001dc49e0b86516aa99951af2ab5e5417d2ba51032976d9449b58ab8d13d5e78a219beb77159072a27690842b134321f2a21508821c9810ed5bd4c1715d8bc02108538a01cd9517428a4cd4331f3a216474825ecd7607e3b2f5e6f08bb851e405b6995b11a5dbf2a72c4a0624446dac7c4e0d89eede099c9292567b318c81702268e1d9932b87ebebecb4ff258a428ab724993583f7e5123ccf2799e091d81a2e4723d9d71a8e40dcb29d8846dd910fa433436b005b624f16932d38321e14c6a41494ef3446c85b8f1d4607943868ddb9556ab50f1a6c10fe30055c699501a3c9525e60b7de7ca304a929b71eac5a903dea06f442882ef5594f453cd15c1be16b1cabdb200813c00e9cc0939349ba25643c968091de29e35d20015a3c89fe5c38f9c9e06402524eada65c7d344249a0c7c315b841bf5402c7241371718b22755ce4cc6d7b056031fc902ccd43f089db16b2b0e98fca82085ba4d8d8e1a00a4751c3b39d8956adb4154d42e100ae3916ae107ba130cb64a756666e4d9655f27d1e4f075a1170b24c873c919000222a413ba0d57a8cf2ca2d3313e3410be1369e06bdd4bab58d8843dda874d5420e458f32fb7c4e7d5b281a27aea3cf82a85d416c79b7a59cbd6ee2929c0307d13f0e8911430ce1b2f778481bc451da9a4677cb28858ab34f5983076c720871a9e1d0853af298369b6906cef6630c802ce4d868996e82d28c5583870e98d5695928abd709a2501b9c383b6022109223ba1b8a7d66358c37ef0518d484f7afb31199d1f4b0bb8509ad02fe8f52e3095f5088db3419081dcc4c66bc46558e8618e2ae52ca3bba1e2423824ad496046a3c72c4877c178478bc635061bcaa39fce6eada2cf50463c8023dafb634a5094179a208b18412a145aabf2f018a9fad81d941d529d46a534d8fa827525c363ea52463473e22ea27f3002c362f96a8e8284a9103db4b7328790dd537a6a387810e777b6c52c7cd989a208911a2d8d9e3e36adf4baed646b34fe397e1754981b4afa1d7917d11b6a623832113883c88d00766bcb044888bd76dda1b08cd47e0d6bf8a8e6c5675e9406682daab1a78cb736e22ea477c6b99091e53c4f41d278d37d9b6926488a54c93100698731a14927c4a9743980964fcb29f2641ed6534a0b21879776518711efd92635790ccefea8acdc73eeedecf345a1a92085f0bb3d34919e85cbd8c84a752ad9ccbb192911645c87677e3e1c61c556871ca9b09ae567c488d888088fb11130615dba6448c7b14d9b4b533ee1036451f3084714d8a2974c2448295e4479d76bf8e0bb4a4858a02924c49e7ce5c80111b4163139da0942aae538198d72d328d8bee651b85b11225a572f1c399d2e7309ef52770a085e241abb1969de0047574c46176a687a58de29b41a0ba02531586912397eda0e783dac53ffd9424b1172b0349ab3d16138079de655f95c9644640d4d5b8b82ab2b1ff8644d8485f461deb18579753b1ba189820226d7dad76627769e909bf6e6c10aad8c2e89db4b6d09481bc938223a46ded67842fa3c0d02abf02674bd147c022ce97fb67ccf827de700012c494223da7d6d60d86ef0c224714ac2858013e0690c48e75430c31689ee55240be39843bd6d75954dec140be06053073818374c08465d629ddbd6810966788e76e88cc4057024eceb2465200971d29d13d906d1584de81b5a4d19b78ec654202379a3780ada0937783fcc2fe582c5c92a04ef9223f9be72ee12f73b1d815863987cc07662450f41edc90e923c042d748559265e8bdffcf1e3a0220c2146417038565ee46dd5b9393dbb2c1a36a3a80fa04b91d623e89297e31979b70e4d53ee7472c5e69e18c2e62ad0fe7f51488df6b06e0ce96525d6862c322f40e069bf843ea6d511aabb92441a0d4ac7e2627c28e7ae654f12997babb4399ba84bd923d750791c9a
+OutPK = 7d49b21bf5e4a67ccde547f4783aa0000ca4174eb3e1557c33a9e934e0a29294662961ad6c55a4f301adf396d0ea8a7480291bf130682239040830034b247e2f5db7a8fba80689d9dca8b9403414ef2963cf600c2ca8b219606802fbcc691145cd38dde3330073c112658cb3f49d6d1a0a88f0b21114af5320a2b9273640d9131e78f60189122f26983c1a285ea26b6b23f0bb30e619a9dcda9dc3ebd885866271476a6ef088e26c4d1400c707d6455663944278347942858990c8597b38cad094e9118a3328cdd55a3a36a14e5594e0750c0316ee00b73e476e49dfc23ede180b621f0b4e294e045d6d40de291736192f93dc2996560561641a1ca94847d301a3982c06daa93a27ee1099659c866766599dc66d970ad48dfd92cdf611414910dc62e43f9192da51f3f62b9cb1639731082dfaad3a1851368620720f271aab645ea69aade694f3b1b8d556354aa25553d41fd9d6b1807fda9575fac5361c7c659eb487948046983a0b3a3c0d79edeac375e4179851eb20d029a49f55f3cab6ba07f1d9b8104bb11816a03dd700e2349eeb1ec5bec50b3fc32191fbccafdf90baeb5971b277c23e43d3205db85b5dd52f1d712a18c89bcb5416b9236e045de1b86754f3b24fa84cd82206218622ac2bdb90f9745d57fda12ab4b8466d7c86cbf57b65b103cf002199854925d38eea4a7e8b52def9049a41a6808481f4b514312797c6e22ea12740951fe06ce938dc249085aa31762225f9379250a4cf2b30e64b5a9bc04b3f2b69e505a9ca102765c9ae740daea6149db775e61f1b4a2db7a251b851eb394971d6320848dd1ede24d4c68b7139c40e5303fde09edbe6aa200802ac3d9d1411d0ce6ca3d8c793895a81cbe420719e2c45696a4ce8b9c0791cc9c999be0d22512b6c649dc24fe111ad97545a7f1cc72fe49d0c96d688fb0b8131b420cda989b5dc172922b11972dc1db5115483739888d7e5da77292534900cc2f2ae3dab61142bda269d20ef5414bb0fc9375e486158a5b41affe4862904f407bde90226c0308dc22bf63467fd02fa4696b1a2424061460b41feee968fd396b6cd12a3bfe067d831e541c8e29a9a2d724440acdc545b7ca593a5fc71f1fa069aca45cb28348eb450d1fbb0a55afd436feabc545c749c2f39065684e84013885c8452bc22a4ea411a93692bef741035bb5dd643c61a8822fa04a51751d8a005a91c36904a86172b11a52bfd18ab167741a27e82699763a8832ea73d5d3daa51a02b791ec062860440b029b3008e2e5c2846bf88388070b39e2156a1c28f1bd91846153c9a3449756b90810ac46a8c404af2bad14f71b636929a319ce7028a8556541fb2962995ab3561833a0e80629d8c9679fd5941dcc3972845f05e0f560870a12c29feeb104722f13410d7d9cb0c9a80518e0270883654d0e7446512ac3dc8563421eac4a5a50f33f84ee9afa82e451b9c469e26d862d7f2ee450c2ad7b050b9b76fdd5cc12f148517c8fd275a2da7a6a54eaa990893ad97b8234bb63340e2ada835bd051a6bc3eb9d7a5b1e5b89a2bce85fc541d5b542f054a2e495f4a5a07ea0a87522781c7312df0bf5c130927f1df0890e681bf3a986f63f500b9c4f8884873e12aa2c4c69b8982cd4530ddb0c914b6b1eb5e1bfc44f32d68e737f8e76aac22c51a76c8b1a83dba8ddedaa0b87e7d2dbda6ae667e2d3fc280defa6745a2a92b61941e3e83b2d053557f7fae24c907f9c6542805548f16664842e443c28c15b45fe5e2f7dd44485b120adab1445a55aa5a85025e52764213dab690a70319939ad971d81d5745d5fd4d03427b07d1b9d46859f3a67c6e556f5552a448ba312e61f4d32195fa2d8afd696ad2d5431c6a18ff02fd4281aa87d714350109d963f3e809cc4c9c51a3d6cd140274621af026df3663c1abad25a13092a9d42880dd2ce1e121c418f9b87c8b6bda67469d7b047e693c5ea3de27b272d30d39564082217c3aa294daf5de1428b3a1182e7dad9935e192d64c7f2ebb1357cb662ee6a67981f5ba73c8494c693d3280f47ae3ebf5fc6b9f52b044ec52bbaca15a82b169adfaa89c511df1be347829286846af2d797714bf866cd5aa44a557fa7f8cb40851fe39f3c468f8db78e867c917e0288625e271007a690f206cf443c7e52dc29710bfa4505912dc4930b7923d29bb304e13a26aa80e18403a4266c87e304b3a87a58a0f1864da113110236d371f63f4f9cb2724679bec457ed715f797ed6ab4c76de8da2ce6849858bed5d6c108b18ef2e75b5c22384286c8767c66f028cdd7b0043d6931eb12a0847ad660dfc030ded0e0acafd3d38214897c58cbfefefd8d1c07c18520082f9c63c45ff57aab3132caaaa5b94e289dc2b6f49e6d2d6a44bde58d6462424932d146d9aec4c30c0ac0e7ab0e986952f75e73e1e3d78e7e898c688a1f412a4c61f559fc8467f66bb5a7af8562e9415025dd8c70673d762bac81e4100a9b85d031a46d66df941f51c88468a820660170e032252811ce5625307e9a0
+
+InPK = 7d49b21bf5e4a67ccde547f4783aa0000ca4174eb3e1557c33a9e934e0a29294662961ad6c55a4f301adf396d0ea8a7480291bf130682239040830034b247e2f5db7a8fba80689d9dca8b9403414ef2963cf600c2ca8b219606802fbcc691145cd38dde3330073c112658cb3f49d6d1a0a88f0b21114af5320a2b9273640d9131e78f60189122f26983c1a285ea26b6b23f0bb30e619a9dcda9dc3ebd885866271476a6ef088e26c4d1400c707d6455663944278347942858990c8597b38cad094e9118a3328cdd55a3a36a14e5594e0750c0316ee00b73e476e49dfc23ede180b621f0b4e294e045d6d40de291736192f93dc2996560561641a1ca94847d301a3982c06daa93a27ee1099659c866766599dc66d970ad48dfd92cdf611414910dc62e43f9192da51f3f62b9cb1639731082dfaad3a1851368620720f271aab645ea69aade694f3b1b8d556354aa25553d41fd9d6b1807fda9575fac5361c7c659eb487948046983a0b3a3c0d79edeac375e4179851eb20d029a49f55f3cab6ba07f1d9b8104bb11816a03dd700e2349eeb1ec5bec50b3fc32191fbccafdf90baeb5971b277c23e43d3205db85b5dd52f1d712a18c89bcb5416b9236e045de1b86754f3b24fa84cd82206218622ac2bdb90f9745d57fda12ab4b8466d7c86cbf57b65b103cf002199854925d38eea4a7e8b52def9049a41a6808481f4b514312797c6e22ea12740951fe06ce938dc249085aa31762225f9379250a4cf2b30e64b5a9bc04b3f2b69e505a9ca102765c9ae740daea6149db775e61f1b4a2db7a251b851eb394971d6320848dd1ede24d4c68b7139c40e5303fde09edbe6aa200802ac3d9d1411d0ce6ca3d8c793895a81cbe420719e2c45696a4ce8b9c0791cc9c999be0d22512b6c649dc24fe111ad97545a7f1cc72fe49d0c96d688fb0b8131b420cda989b5dc172922b11972dc1db5115483739888d7e5da77292534900cc2f2ae3dab61142bda269d20ef5414bb0fc9375e486158a5b41affe4862904f407bde90226c0308dc22bf63467fd02fa4696b1a2424061460b41feee968fd396b6cd12a3bfe067d831e541c8e29a9a2d724440acdc545b7ca593a5fc71f1fa069aca45cb28348eb450d1fbb0a55afd436feabc545c749c2f39065684e84013885c8452bc22a4ea411a93692bef741035bb5dd643c61a8822fa04a51751d8a005a91c36904a86172b11a52bfd18ab167741a27e82699763a8832ea73d5d3daa51a02b791ec062860440b029b3008e2e5c2846bf88388070b39e2156a1c28f1bd91846153c9a3449756b90810ac46a8c404af2bad14f71b636929a319ce7028a8556541fb2962995ab3561833a0e80629d8c9679fd5941dcc3972845f05e0f560870a12c29feeb104722f13410d7d9cb0c9a80518e0270883654d0e7446512ac3dc8563421eac4a5a50f33f84ee9afa82e451b9c469e26d862d7f2ee450c2ad7b050b9b76fdd5cc12f148517c8fd275a2da7a6a54eaa990893ad97b8234bb63340e2ada835bd051a6bc3eb9d7a5b1e5b89a2bce85fc541d5b542f054a2e495f4a5a07ea0a87522781c7312df0bf5c130927f1df0890e681bf3a986f63f500b9c4f8884873e12aa2c4c69b8982cd4530ddb0c914b6b1eb5e1bfc44f32d68e737f8e76aac22c51a76c8b1a83dba8ddedaa0b87e7d2dbda6ae667e2d3fc280defa6745a2a92b61941e3e83b2d053557f7fae24c907f9c6542805548f16664842e443c28c15b45fe5e2f7dd44485b120adab1445a55aa5a85025e52764213dab690a70319939ad971d81d5745d5fd4d03427b07d1b9d46859f3a67c6e556f5552a448ba312e61f4d32195fa2d8afd696ad2d5431c6a18ff02fd4281aa87d714350109d963f3e809cc4c9c51a3d6cd140274621af026df3663c1abad25a13092a9d42880dd2ce1e121c418f9b87c8b6bda67469d7b047e693c5ea3de27b272d30d39564082217c3aa294daf5de1428b3a1182e7dad9935e192d64c7f2ebb1357cb662ee6a67981f5ba73c8494c693d3280f47ae3ebf5fc6b9f52b044ec52bbaca15a82b169adfaa89c511df1be347829286846af2d797714bf866cd5aa44a557fa7f8cb40851fe39f3c468f8db78e867c917e0288625e271007a690f206cf443c7e52dc29710bfa4505912dc4930b7923d29bb304e13a26aa80e18403a4266c87e304b3a87a58a0f1864da113110236d371f63f4f9cb2724679bec457ed715f797ed6ab4c76de8da2ce6849858bed5d6c108b18ef2e75b5c22384286c8767c66f028cdd7b0043d6931eb12a0847ad660dfc030ded0e0acafd3d38214897c58cbfefefd8d1c07c18520082f9c63c45ff57aab3132caaaa5b94e289dc2b6f49e6d2d6a44bde58d6462424932d146d9aec4c30c0ac0e7ab0e986952f75e73e1e3d78e7e898c688a1f412a4c61f559fc8467f66bb5a7af8562e9415025dd8c70673d762bac81e4100a9b85d031a46d66df941f51c88468a820660170e032252811ce5625307e9a0
+InA = 115dd662e4a18f069d8ef08e92843a45f73aecf6409390a45b46d41b5f92c6a85fe601c90487263afa39a28fa153054098a02fe501abb9abff56ac36378cb6cec3f2a962f6a8072601481922bde99367e2f0b4330ed70bd8c24295c31c9965b5917ed0ed48250f42468f22bb92ed3961b6e248848e2a2565def2a0657c8b49d027a5457d1f824fd23bfaa9a813eac634dc31025fe16bc067e640da864cf22621b422ee506a37343e41a23a56b2025d325d6129ecbd47f984fe0950c45c5d2a1bda0048b8fb91984b66d4930d667ad8f67d0ad2484ecabb3e4a6a4426d225846449457e21cb429bffe97c9510822a8bad238bde2a453e193b9a6b5d1dc8ab36d9ee36633298b6754e710b52afcf043ce9b9ccc2e902ffd6617442e902e4e529afccfa91b70984921c4809e39d9eee9d5be346988ee2da5cf9863864504fcda0ac8e9dea662555476647a8232564fd46ae420463137d947915d8d3c1207a6b5a0fdb9531a559169730a9fd7e6b70ce388673cd80586d10a4a7a08d1504cb4ad8ed3c3a14b667aaf477b4155c8afe75775299cd7388f57dcc590c0ae8cdbc0e95bdc9c9f5bf0e82dbc2726cb9a60be67395febcf3ec47802ad5b0eee7a1734d016fc41bba9056568cb5ae15c71a82acf6a6f9d53e643196ed6d680a4c8e5dea93cb80e03f6203c4f1e9ad1dd2e93968eb2959095d8c91c36c8e7e98a3ea23441f1a4b9a5a3ba24592a69d386bc8a516cb4cd0498905506ed6d26c68b7ba6291a584468b708f8731d13e8634dc2ea7663825aa6e7d27370866950ae18737049cd84f0763499223b2473ed72a5207b60aadeacc68a9d3934a55c8be32b2d44f253e0daf2c6cbf6a777e4784c34de26eb2ab1656210235813817a8793108317c05a569f1b74e2f47cccf691858b28c535d873f507e65a9b9e1a6da65532af12028ddabed18dc4bb8882f8c848c9bb23c7f99d893b9375e6b14dc71f834747cca9e081198c90e589d738895fd06eb1a9674b9b939b5e7b42a2c0d1167e13c13c8e27cec18c13506de7205917d59da589c04c1f45ab5c0b3a80120f86c7021dba9bf4a05c65ad1d27e153e2e44706caac82d99a8cf40e6c59cfeb7ec07e0db3ba0163e2ed5b05c60764562d60500a69a59831178a616b1f35ebd15876062f9437f8a8e78247d0cdc41f658672f4ea16a087c0a415402792838598eae4895ab79b1c4c69d5b144c4e82eabb748eec724454c3f6f0c935349a7d51d8e5a86a50ff5146e6a4c22ce0456f6d0ad50d0778d500326ad33bf7933d62cfd334316bf182ce109b51615679c02e0165d5f8ae54976cd837661324e4765792e4681e2ef7d134859424958ca5d6e1220fc00620df4295680b84574e985e1ba7e08b752805374332867659a273d91ed99dc5774ec798c06f8298ccab90d53bc950bbea2e25492e8c67deb3874bc4368b079d2a73499fc3c51c16c6b8b704967a71b795034d1e2da91cdcb246149c270095999e6be233d86538e0e572e3ef7190d58256a188f88628c81600337a563d4179c1d9a18744cea2279551af93aa278bb88a5ffe45e3e99a492ac86bd5c8957a7c018dcfb9ff56489084af406151658fde5f0b9ca6145098c9c1edfd08f7e0e3d4c162a58628a7d23009a4e4560723a694332fca49b902396b98d014bbabd262f21b780b4a056a878b55a8921293bedf45602eed1b4b4d80717492a9bcd9b8e99ef152bfe21c205b719a1d4d54a7df9579cfaa999404a13178efb236029d52e0a261a1973c5efd966b2219847b14065856272dac65c2d2614406740aa5f9145c859a7be9d6a48b8689b2ced1959b5c8d4b18f735268e70c65b4664481801916400d2b5d0739544bbd178d12cd2d718ea06f9649feee0850fb1584d1e18568e26833bc93d53261a488d261c295d2963b706d992e01eb2d78c5e548b2d483e410ccbf74ab5aad7d0e2637e0d3660fe3115cd5cea046f4778b426d5e079f9c7f09ac448a6d5bed819ec5b8bc525e504b8079650b10ca4036557b4f91c870fa56c200f220216125739e52ebac76f85abc64e1aeb5f3dbb250e50f69756a708cdac4aef845ae31c9a03091ff6d3123a65878e08dc1cb768a6f404ef9d52da0c2ce1f52431cde64e3432982b744710550c023d699b376054445df81699d411648e74cc7506b1506df3aceb7ba80aac6b21379062346c77d5cf68dbbc12e316f823ce2c33cc24c6e22afd7ed62134d744155cc9d007e010026c1e91121292a27efcc5d0213f1996458994b02e886966e97ff8a84d106c09f84aab516cb1d0f7ca443126c30dc0062598a3a21d32cb2fea35fe51a7a83892be55ca24971ac4863b0f4d557cd9a3420ea71b1f1a61de8912d2d6d59f23c425f9ac6077e86e50c333da23c7da73a5e47a804016819bf1c2a75f6c33522a505a1a9a5e313c901214cf33533ec8a6d2218900f2856b858b9bb49255236e090a90c49c84f32bc5f123d6072517e0ea091e6a04117c60639d2ada6f5d0bcd8d5729efa7394bfc85
+InNoiseSP = 55a7fa5b24024bb5e137b7da38998792e82320c91e6723ad06f209329f90c23707823be1ed092b16c44df82eded72c0819d42c484afa9575a11219e6a366a1eb9e6f659f458fb0432c888f6142be6f2f437ec4426b6d4f4103fe6428460e825d252b314fca0b32fd48d6afad55d842a0201aa9bb5ac664188ad119773d23f9cdce67850849cb42c76a2ac292a6dadcb60c5da1fd155419dedd9ef5d854cbd5910679ed5a51c5319f40dc5392de54b6a8d0ebd9b0c16ac8896d346572388c55c7fb9f3d6212eb51218b3c03644d6140a680270b51da06526c7ef4eb09c1043eb526a732e6509c0c0f6841da48467637ce94f88bf8adf841015aeeba5d979a783345f2652ae261d5fbf8957b0f20ab22f28d1edc65662adc042703deeb5d953855570937fdc226f76b444b05b85fdbaf615bcec839d0252021d7960923915d59a2ac6d1bcd8752c548aab3094fa27058bc5707bdc7a84488194c1c81e598bc2fd736eae2f945c3d07e6b9346203a48ebd7f79d4f8b172b74b79178f89bc766bcf63466571aa7548e66cf58ce2a0ec5a56dce10d006bd91c183e2a9ffe42f0e9496f713c905d0e4fe923cc8a2a85afb303a0d1bc429c68b209d9aa9a6aee3be4a8507e462b307ed443fcbb17a3172b4754a499a7384583a006897dc507589007bb20db84987d9d039b698af3392bf24b74e3ec59113f446cd972f4624465b81a5b6000dd89f88a694e941e7d2443f57137e78bdd66241801f8a90cca01148b3ab32dc057227f098936ea884a4b187889657cb9e81e1332caa24a806eacba46ade6022fbb82119f9d46b175c5a7493d59cf33bd5a4bdd0d1078e1c2348e4bde86b4071f6055f78c5109dd662c9fb9e98ad888e88a51e447b2f4a8ea5cbf8182083d7f2fbb93e248d39560e7a3e530ec72a4f8d4bd30fca930e4d7d4fc90ad4f5702bc9df4702521a2ae7a3dc701cf816bb5d24c0d17a56248a6ada95398d12ad80daee4806788c45017f500903c671b8305da3a5589ea5be3a5b6ce4435bc79215a82e23e2c4de860cc519701a94348eccbfc6473643d4093febd70e6fad8207f1367492e9de62b5e0722f76e914e4b670123c8ca5e61ac8a41e547dd22db952e4a71554fd304f1f45f841c71291c02054ea49b285360131d9914aa98cc3ed091ce0653aecd03653d6a4e393b190f1dc5d794214a294668415c99c5af27881acc54d6786aaa1bd2bb218c13a0d6c89a49689a983dae0a8655b6e64d66069b4b6c19623e34474b6bd0a020bde5a39314b5b91ead5852d9cc6e3bd45a4aa079f4199b293c9aaab8c41c1546c736b8f5b24b7a6a859410ea58f569e4edf3c953398bd490b32c9856c0b1891e17aa06e1c6788d6803496b50fd969b80f2152fb4263a1e3583d33e0aed9421d93c322c5869c341a4c109c092f7e2d5f544d344f688ddce65e3ad5a5bbccac709b963d02de9d8b6339923d0822eef1fff5b3a9957beb3a342c230c37b1e01938819bc2b70cacbebfe2493040969610a556711fac49dbe52cd71af4042a4431f574c43e846b1d7604c598d6632bd2962a3f48dd55da79956c1c4ca1c8ca8e79aaebc10723ed045b1c24249c36ab4cd0b6372ba9389769bc55f1c497ebd6b4ebf4e1b8528fde631ff4080d2eb3669d827c4864f7e96cad88b76cce3b02712bcf61d7194c31032b9a42018128bfa5af3d026146356f89987882e547d56301861f7c41574c984a0f5d637b0bd870205a2e3c1ae94605d5599650f50a9c8885db20c5273db062a1c3149826446d12d0d491ed95052c14433b7e1c52701ec8c2f555b79664e37550001148e241521af5b0d454e05100523ec28d8d57191c9e32c5365b6f7d8e750cd2c7aa461f8d97da4b75d94665a2c9566fc9a99064cbc6f012a04b8001a732f21ec5547160d37068005157f5d6696e16e198b1b1a1a450da9357a2f15baa12904695c4b202d5ecd8ab2990c7db893662ec6499461c043ae168b1dd1178cc78badd882aeba252229989de627954be86d685f567a459b624a9f3054553318b7ef7702656a8e13fe9a4feb50a91a15298908df8e89230f4c95b3f578710159007d49589e1428e1eef972f070b3c96efebfa87e0a942490a85131d5c4764968d0269cc6c6fceb5b897b2a2c7d9c9f8f4081bb1187da9db021dc8d8c87a02d45af155bb947fe6a381a9761641545e30539e92408170c7e5a5a3f3f0637a284d222988038e219aab72a036daabc785a7e57deb1360b789f0762acd555a5ab179970ccdf7483aa8d6681801f8524816ed00485e5e48a6a427f9368a60ca15da55b27d0c2c50bb4a312a1ac16dbc23c8e22fba9dd8d83bdc64d84dd2f7b8960feeede0c32fc59d442dfde25d1220898bddc2455ab1e10373e73c861efad11619d456152020e3653c56b275cf34f4775cbcf94047a4b9387ef3d8155af9303e868ec8926afa50418702c336c404a30d27062c4d31f8891022383e4951430a2bb1e99497ab54e1ba8871bf51b6c5eb208d88a437c5167929
+InNoiseEP = e1ead35290020eee908a1b102075c6c1e10b14684aef1e9591aa260b30d6e6d5fae61c8c2bb209e08ebc6de547c940657372687067629d50291347748fec50c4ee0793d4b8b6e515a20743580d856b42173784adc45101e4251e45b866423920191826cb512a87b5867b036e7962c0b842cb14188ec918194c5a68bfed117656752061202710141456c6acafb810bf0a0808beef8af48495e837509ee324e75191dd20b8e96bae2382daf1098f719090c15a46e55d1c8f2a4126e182881162d8529b60877ae1a2851b00330ec0d1062ead8f33d7188159d2741b6e5a71c7590353e7d293f204296fe050872c184f17eb1e682510633857cba3e1b57d21ca19867a9e18766601c434b1a74567c78767e57db9ddbe074db07f0d0ed00b68a26b2c6e2037b8e008b10db3d0145db460981d00aee53626ee575297901f780e4704b0ae258692c61805cd269a9fb5cb45fd8e864003975c05875ac08ff4dd34a51964ed366ccc4b58d60ebbd571600f5b2b3249508d77d96723fcfa90c30d19e04d51b9ff9332764e592595eb2a9bd8d4219a57ff803a6a04eba2c8e047896c82e24e3151b9b17def1f461d96529d58bdd456926b64165404faf20b461b7e9b3196b92042188144a9121414ef26395416725bd157c25e642b967e684116942fc1d59886ed30fb12f42b927e5e8a8a08d169301a08c794b9f29614062256f9ee300a1001ceb3e52291a98ab0487bc942510115706c9008735823d8a452af404ca1db9e4c72b6580e90351d639bd48e352c4d6e880dee2b45a11e9a0606da45751485d004e09c0e91e9951c3aefc9aed888f2a8db79acb117a3d81cfd812ec3c1f1f07c01e0a979c30b45b42ee7c979ab75b76816c38a19b581739cda62ca5e01344c0471c5c60d6be6dc22ec441add572d83940582842828b946b43857022f03c70425f169c825fdd24a9e11afdb75e6827216f7367a848d195d57761c106536d29f8a553055fba6b0c33d0198b05f52982bfc4e54205c4a56b69b5f4dec2594f18bafc89560ccb056ab2c5cd36f891cd0dfd0a9172dbd272f899934448a9cd32e8b4a483c84250c472f2e923cabd6a7a1cd73f41fe3640be880095afeea601c4b85e1d63ba4d5a6e88af2208c8c91e392f6b41aa1bea021f9845bea432b8d7440c4991a250e072a5ab50d2bd664a0af38967269f6173d1e8679840f064b4eba7f402a20462d44446bb093903482d506579459843b054e579986627803e1854ad5ed3de36e23d5dd14911c558255cfdd59c5e822113a248504e70537471a442dd4e2d11871aed58ee8060dac6073ef4f804cec2e7696be4155e1733c5ae969106455a2d02f02fbf06e4558eb33e3fa397562171862c55b4cad80080b86b0a98e6398dc1573699d5ef3bd369b8ca69178844c9ae7641951489506a1cfdec940fda8245fb2d83a19b105ef5079d8ce424a0cc2f964ca5b1aae8a5a7a7a647fd0d2279f8ebbb446d502346140ad14f7b5fce594f8d043b2968673af0f3e60543c783dc39157600120229b4f7259aa8d27080efae4d64aff5306d4eb02184b4ee30b36fa501f91ede3aca2483b0b62d4e6013e4fd47bc2bad8a02eedaa88b7bd2c4041168bff45092b27f8f8ceba2286cdacf5d6a55ebb2bd0d589a4a63e499719eda537a8ec0350afc27dad5c67d3de7d2f1497bf90f8e917424bc0b36a21647f1f507a38cd2435a7ab1aa14151c96ff40bb23065b322fb83464c79008055d24c6abf10d54c909995018992c3ebecde40c21ab7b8ea55b37fc57e11af58bdf2c5bfc071203d6daf4449de620a7148e2a9454c4537e86640e4dd4a44e4e2c049f9a19e68c8aa2322a6bc5df7606f727df98f9398099546011751ca49643ff3d4a6aaeab00702493a70dd345b72d6f459034e84041418348178bc710bd0e411107e5dcf4390deca0ebb081ab0c886117fa191388db4793f01ae4b8e0c8edc973a50621aa51d4f06e5f0c2a269eff52049300344d32f78e2029b7c1c3297d4984a180870c5b636954413e73bb6aebcbfc027375b4550faa37062969da41be13148bf8c08bd802ee5fd8963a847598522b977fb9c20db45905b840f6981a62403de15c5b7395999b450b967fdc124ea6f0ec840b488e51f68f9f53da25415be43ea864923ee3b40441a674721e435087e4b8ec108a2b7547f02089f718112b363a92f0207ed3d56c2b465e11d0f99a4c3d9d93463fa6beb4198547365d4d1429e5d81438ec8f316947ddba05bfb6c213871c4a2f101c04e9d81c531b89aa822ddb2c84f9c3b465130e2418e6069cb0698ae7954cac5498154402237d7d4908b587509664a549c458d5e564cf134e44235db3b60edec168585c638a78003c6ee44f67366b7476d8ae3dcd24389e51bc0354c8066ddd830971aa538929d7925ae6c83c65807309ca2628e159641dc669b1e652359801aa3257d2109aa35f825c55b274e362e0005b1b5b0799f53d01a46d57ca74d93696b0a23370a56282a3c2c51eaaaf80e5e22
+InNoiseEPP = 00b001600000c0fd2f001000000002c000100000c000c000f0ff02000100000c0008000040ffebff02c0feeffe1b000800ffefff0b00f8bf0100010000fcbf00f0ff0b0000c00380ffebff0a00fc6f0010000c00feaf000000f4bfff6f0000001300fe2f001c0000c0fb6f000000ffbf0140000000080002c0feebfffabf0030002000f4bf00f0fe1b00f4bf0070002000040000b0ff6b001000ff6f000000fbbf03c001500000c0014001300000c00200000c000700fe2f00fcff060000f0ff0b0004000200fffbff0e0003c00030000000feeffefbff060001c0ff3b00040007c0ffdbff0e0000f00110000c000240000000efbffc2f00ecff0e00014000e0ff0e00fd2f00f0ff12000240ff0b00ffbf0300010000fbbf0280fe0b0003c0020000ecff0a0000c000f0ff0a000040ffcbff0600ffafff3b0000000200001c000800fdefff0b000300ff6f003000fcbf0080ff5b0000c0ff6f0030000c00064000d0ff0a00ff6f0020000400feaf0010000000fa2f000c0007000030014000040003c000f0ff02c0ffef002000f8bf04c0002000f4bf030001e0ff0a0000c0ff8b0008000200000c000c0000b0004000fcbf01c0fffbfffebf00c0ff0b0004000030002c000400fe2f000c00fcbffe2f0000000c0001c00030000400fd2f000c0000000080ff3b00f8bf0480001000f4bfff6f00f0fffabf01400000000400ffafff3b0000c004c0ffebff1e0002c000000000c0ff2f0120000c00fb2f0020000400ff2f00d0ffeebffe2ffffbfffabf02c0ff0b000800010000100000000280000000030004c00000000c0005c0008000f4bf01c000000000000030000c00ffbf02c000e0ff020000c0ff0b000400040000d0ff0600064000400000000100fffbff0e00ffaffffbfffabf038000e0ff02c001c0ff0b0014000280ff2b000000fd2f001c000000ffef0000001b0002c0fffbff0200feaf00100008000040ff5b000000ffafff2b00f8bf0180001000f4bf048000e0ff02000030000000f4bf0500002000fcbf08c0ff0b000c0002800000000400fdef00200000c00380ff0b001300ff6f00d0fffebfff2f01e0fffebf007000300004000000000c000b00fbef01500008000140fffbfffebf0100ffcbff06000980ff9bfffabf00f0fffbff02c000400000000300fdef00a0ff02000480ff2b000000fdafff5b0000c00300001c000800010000e0ff060000b0006000080005c0fe2b0000000580ff1b000800ff2f004000fcbf0200000000e8bf01c000d0ff060004400100000400018000600000c00200005000100002c0000000ffbf0030000000fcbfffef000000fcbf00c0ff3b000400fb2f004c000c0005400060001800024000b0ff0200ff2f001c00fcbf000001f0ff0a0002000030000400fd2f00f0ff120000f0ff0b00f4bf0140000000fcbffeefff0b0000c0024000e0ff0a000070012000ecbffeaf0020000400fe2f000c000400ff6f003000000003c000000003000480ff0b00040000c0ff3b0000000070ff4b0000000040ff5b000800fbaf00d0fff2bf00b000200004000180ff1b00f8bf00c00000000300fe6fff2b000400018000200000000280ff1b0000c0fd6fff0b0000c0ff2f010000fcbf003000bcff02c0004000a0ff0a00ffef00f0ff02000180ff4b00f4bfffaf00f0ff120000b000c0fffebf0180fffbff02c0fe2f00300018000500001c000000fdafffdbfffabf00300010000400fdeffffbfffebffdaf0020000400fb2f00000000c00100000c000c000000000c00fcbf0080000000f4bf0400012000f8bf04c000000007000080000000f8bf0030ff2b000000fd2f00f0ff02c000b0fefbff0600064000300000000040005000fcbf04c0ffdbfff6bf024000b0ff02c0fdafff0b000400fd6f00000000c00100000000f7bffe6f00000003000180ffebff120002c0fedbfff6bf0340002000080000000000000800ff2f01100004000030006c000400020000300004000480000000f8bf03c0feebfffebf007000e0fff6bffcafff2b000c00fcefff4b000c000140000000040001c00020000800fe2f00fcff0a0000f0001000fcbffeefff5b00000000b000000018000030003c0000c000300020001000ff2f000c00000000000000000f000280014000000001c0ffebfff2bf02c0ff0b00100002400020000800fe2f002c0000c001c0ffcbff02c00500ff5b00000002800010000800003000300004000180010000ffbf0070000000030004c000f0ff060004c000a0ff06000040ffdbff12000240ff6b00080002c0ff7b000800024000b0fff6bffdaf0000002300090000fcff02c0000001e0ff1600000001500000c0fe2fffebfff2bf0700000000ffbf00c0ffdbff0e00ff6fff3b00fcbf02c0ffebfff6bf0180fffbfff2bf00b0ff0b00fcbf00300010000800fdefff3b0018000080fffbff0600003000100004000480ff3b001000034000f0ff0e000140ff3b00fcbfff6f0090fffebf
+InRand = 20435d4bcb09f5a9b8188f056742ae4a0dbca938870e3bff327efb7662656001
+OutPK = b5a0956984e94dcb5756f9ff394f581586c933aa95db66c5fbf68c15d5687bf954d27ba22042d62f8a6311571a81210da078cc70f7d411bf9c2e2c829c280e6ace7022f0de83c98cdf92a1f816a585080bb7fd5ace54adf4dd60b17fc35d3742beba77a10729f9c175954515474a1d7b1e0b06013cf2092d113461c99921ba2d84658c22713f530723f44642af1f8c87fb807a89d10c050635216259050061aa8f3c2d0e102e8e6d99024ac72120593f90d4982dbe5d130d004491ae8ae95afc12ba5059fe1d288a21f178854b50744a069617c70eca46447a189dfec5e7399dd90aac0a639485540bd060f96c877750aa13a118663f186b4a96e890d9d40cdaef268571012fd93fe26f689211a15eda78feafd1b97cc01590190589433d651cc73400bebe3b65aac4858d501e24a7535481ac5b996ad77a5ea814f16082705236299484b3d7dc8606de6e95445be4594e6030d945906b5660dc35fd753735a44a0874dead0ac44e23649c51f62daa013bfc33029ae994678da2ba46cac2a0bd283f4f48e7e9a619e9cc5214d2ea08e893c2e3cb9c11b287dc7835dc237a58c504139a9a49d11ff96c7d8215d3599abee62c54c407ebddde4f3c6b669ace8abaa61fe23b0ea98bae9e5599563c5263cd4125657c83f0d9a2c243158a7c2c6680a8a88add6bceb8e5e00f850394e69d3d47369dc9e1aa86b000d27f4a78c28c624506e8dcfe43a957497310711c54d3f8f22ce12e840a32d146715fc4c4039116a86ee7eb0af30db5adf7999c955a6a0067cc6c33fb8e3ab5124b18687464fc12177b69e66cae866fa291385b8ad872730294958e035a2a5fd9489acd5509a0005eab82dd177bb81af96c03b78d5e94692fb2357ff1bd2b663933b7d9646bc9eb4545fe970a886c54a9915a6c6083d47f42311d841874394963bf0459ebdb354785ae2babb696318ceceb28cd358ef9d389036cf5abfa814cb6abe1508261c9787dc44a9b4038b2a2585a125fb25bc46a179a8b47f1f841137b873101306a2556831bc110493d1c40c688227202c49ff290a0002ff0e56b859b3e3f46e0b34a2c44b19f17275a93257b7ad01640423c80431408813e3d6e5f94e7f18b1c22506c1c1bf4812a9b860b01b248805a03fd588ec54b1adb78a6c4f1af0c547c1b1f8257f6a28c11ea3972f860ff6a1ebd94202d1daaba1077512bf5f5ffb22c63dfbcf2ccba0d836c7042c330028b537364da743f5fec8b646c1974652e8e165c384da662acf28e51089a94857a024a5e6ea27f45abd0115b67d72460405e8ee1b00116ed26bf9c68092eaa8510c45f4015e920f6890a8b3a1aa64ddb820a2e5a6bee9eb399ca7d42a8e8ad79b9a1214e27f90b1f494c77a620f8158e0b6353999446c9b0401ef9910dce28ac6d6a6413fe8398c6dc7615ac85e4d470d1ed394d0bc1273b07f50bbfee2c136c2cbacf6e9e770fc02c9e96d9f39d601aa854e1b7f18a1b260f201055fc844d800af062b163ae176a5b72728acf030139c11d9e075f6cb0c362b00883fa9083f20649e28b75b7aead19e4b347a1e6111300da4487796df4dfdd16e242ac114d37f71485bbcd50f7047a92074265c450c78e016013ed25256a632b646a0840817083c5676182eabc595766d6f7b123c49acbdabbbe5ddb29fc83422432acc397241aeda878c6b6fab3210037d3b9f4a00d2e6753d5583961226bd16910590a7cc3993ff61156097f5bcffb4fae741f878572735296f904292bb9dd834a80fc0422bc002255f09377ce2dde000f23a4b1114de2aa9053a2495048cd9300b07ae6520ad9f2226ac813cbe77e8587d5436bba996042b41a6926a1e26019a34f88f8b285e2a2dc60061a45fb87d7892b745a4766f05a3eaa973e51fff6b7759aadea8254a94d12d7114ee265e96e108060c1fc82b0fea73ac320c9236d5768a979991d2f43b7d2bbe10f9361b959785958ead416b0433e90e757852865a292816d451a2a6a2dd8c87fd828f28de91f8a70cdf9164da8c74a939d6897db47449fe0b7bed448fbbd4d7a12444ae0e865b3f5af4ab8163db69a422c74f3ca71f8996c9758940e5edef168f9867a1f860c64e38508d0256b047c40181d4cd1f68fbdbbe44340fb944a67f310e15aeb8d6a3b168afa78ed81ca536d63b498a679556fa9fe5981c65c5b0566460009a557a7442da41e4cf5d7e1749ac929c84733ce73ca5e7393fd58bb07df14f861420a3aa3f6a1238d46b39742b490a54c8fa67cba2c26bbb655ecc2b2be47aad966b09b6580916f4a9ac3fe6d4e1bbb6161b81f0850bcaaf23c21a7239184b6f9198f9c16a1bd555165e25791e6951ec22acd97bcd10ded28ed67cac18a2f4164318c21088255bc3ad7612364cbaee41cfab0d8282033a8e0b18256c004eb0e6c5aec15854c456146591065664a928816fbfb213a5e4b4477148a2487aa5368b4dede4b54dad78566d42276558149302089bb23af8271e00090a33d29c090692da678a428442f35d8f0e6dbf40f
+OutRec = 00000000000000038000200000000380002000080003400020000c000140003000000000400020000c0003c000000004000300003000040002000030000c00014000300004000280003000080002c00030000800038000200004000240002000000002c000000000000300003000000001c000000000000300001000000000c0002000080002800030000c000200001000040001c00000000c0002000000000c0003400030000c0001c00030000c00010000100008000340000000000002c0000000080000000020000c0002000010000400004000200004000200002000040002400000000800018000100000000240003000080000000030000c000380001000000001c00000000c000200002000040000400020000c0002c000000004000240002000080001400020000400038000200004000200000000040002c000100000000380000000000000000030000400004000200000000180003000040002c00000000c000380003000040001800010000c0000c0002000040003000020000c000080001000000002c00010000c0003000020000000024000300004000300002000080001800010000c0001000000000c0000c00000000c000340002000080001000030000c0002c000100008000280002000040001800020000c0000400010000800020000100008000280000000080000800000000c00008000200000000340000000080002000030000c00014000000004000340000000000003c000200000000040003000080002800010000c0000c00000000c000280002000000001400030000c000080001000080003400030000c0000c00000000400018000300000000080001000080003400030000c00018000200008000280003000040000c0001000040001c000000008000280000000080002800000000c000080002000000000c0002000040002000030000c000280001000040002800030000c000080000000080003800010000c0000c0000000000003000030000c000280001000000000400010000000000000100000000300000000080000000000000c00038000000000000200001000080000c000200004000380002000080003000020000c0003c00020000400038000000004000300001000000000000030000c0001c0003000040003000010000400024000300000000340003000040003c0002000040001c0002000080003c0000000040002c0000000080002400020000c0000c0000000000001c00010000c000200002000040000800010000800038000300004000240002000040000400020000000038000100004000080003000000002c00010000400000000200000000380001000080003c0000000000003c00020000800014000300004000200001000000000000020000c0000c0001000000003000010000c0001c0001000040001400010000c000240003000000000c00010000000014000300008000240001000040001400010000c0003c0003000000000000000000c0002c000100000000280002000040002400000000c0002c0001000040003400030000c0003400000000c000140002000000000c00020000000004000200008000180000000080000c00020000000000000300004000340000000000002800020000c000100001000040002800020000c0003000020000400000000200004000000000000000001400000000c0003c0003000000003800020000c0003c000200000000380002000040001c0000000040003c00020000c0002c0001000080003c0000000000000c0002000000000c0002000000001c000200008000380003000000003400000000c000140003000040000c00030000800014000200004000240001000080000c00030000c000100003000040001400020000c000180002000040003400000000c0003c0000000080003c00010000c0002000020000000030000000004000300003000000001c00000000800004000200000000200000000040000400000000c00028000300008000140003000040003c00030000c000240003000000003c00010000c0000400030000c0002c000200004000040000000040003800000000c0003c00010000c0002c0003000040001c00020000c0001c0000000080002000020000c00030000300004000380002000000000400030000c000140000000040001c0002000040000400000000c0001000010000c0003000000000400018000200004000240002000000000800020000c000240001000040002400020000400028000100008000080001000080003c00020000c0003800010000c0003800020000800000000200008000340002000000003800020000c0000c000200000000040000000040000800030000c000340002000040000000000000c0003400030000000018000100000000140001000000001000030000c00000000200004000300000000000003000000000800020000200000000000003000080003000030000000
+Key = ad539b834982f525e844fbdf56fd496ca25cac45c6b5718de7f2417b7ff6e3b4
+
+InNoiseS = b25e0026c0f1ac209932a42dac2b2a94f3d3a08078080b8ad39ddd85fe04106bad6c591655d7e6dc9936362a1442a112bfddef1594169d3b929b77663dae11971e713bb5d06882cc5933a4b55be9154f6b2da674e96cbc3ac56145875888931f64669d57c5637dbd469bd3b6504124227e5e86789b7403c7dcda60e2012bc20b28d116209cd9d26be498249ad7a933e63e587d4f8c8c728ef5a0e64fa532c8c56db5d629a803c451d0cabfa7fe185c559da2f73136b79418dd245cdd990fa948a7297591195b1fa8d6b953b3a4dc91692c84bada53c4ec8539394de26358102b509549a1dbaaa0ecd9a614b4e96f17d26ce3507c510d1798d11f218ce41a7900249e0ce70ca624560d0a74a23591f8fa553c1cda6b995c951ad4d965a8fea143523358b3147412422e629de92d64820fdb897a9ee22b5be0190e6ca889bf8b27914c61568bda84651b17a787d700aa1c8d0eed5b669d69b644b1283f61266f6d369a50f446e59c7bb911d1515023e484e7d949a9c6498368fd4291433629adfa93c993f5732e7e12a0cc3f79b94dbd84010f5711103f5d622fcbcbac55d511f8ba02ce47e00a4ec84d10af6684829275598c635defc620f59c5a1ce97399d12de74e9f84b8c25ccc87b4e4b7691d6cc595b40b6e7a2d0ea93773d60edb498ba0dc59906a930557d0529b4d975d68e1d0850f601b490f9a107847e909f8c8573c61927374d9a6bfac48f4ddedaeacc39c3037ecb7d251ebb98231bda7035ec9e2349ac3537496f609016d6a79d394ae43e951abe87fad7d7344148943fdb2b76b56c8e6316315a94eda5a3151a65e7608b80968442e33a7cfb1872ce5e2a1f462bd6d65c8d09a8023e944ccd88050a0c1d488843add51135b4ae73d90aef5143bf2016a668ccbfc6bc5d2275302ccb225a269dc08b26a83d96d16ab68e36eb19da009a87195e9200045d07172984ee427b983ec54af056df6caa32e24b2d579862ce60eac0a13a97da0312edf20d38cf94551a8fd61a78232b387a8b217f98ebb29737793d98a364c78a471dc04b0a3eb48cc9e43b6ae6d3875025d58e2aee7a95e69902f4c8bf34e73ce072572883aa930c1665055a07fec86c229dd9d5d4c8c80d227b278fadca1b00da0885b04c6959b96bedb6a5aeb5322a18cc116f29d3c1574cefd7513945479937a20040402011bacf9ddd010da0c7e19e8a65977d4bd96726951c18bd380c62e2fc861e338ab86587a69e49699c1587fc5737613405c50ca11a365b40ac58eb867f37d9aa61157621fb09737970451286e4e1e9d2cb01a1a0c1187bc491e2e1d75e08c02bcfe5cae870bd073ba05bf2b2f4be0d61a4b461970a5d930b5c9bf111b11252e61d3f4a7bd549ec44c0beff42a9c6273b03cad315907f490808144904388d4760e446e6bdadab21593a363f2db448987425f6963012e2803a4c50500ca9a1f22f9d8b64a4c59545bcba76813b7d41fa40d741ffa0d1ef60a040b4441951245c1b4aad9af707c1815959d0bd0bcceb80cce721b83ad1cdb9c1d5b45ae816403d63794cd285f169ec609791aa24f82d58e04436ec63d4a6086803ba584c83446519207c072d24a6ec7a696625e1cbff76c54468e458190e28e6848a4b6a52ab867fb9c0ec845bd26927d1d8b9f24f7afdc72402e65a1e0de44f45eec51585c594eebfee849f63ddc54ce073081837945a121f92e02f104c386df92b927ad02aba9ccbc2857f2557c7aae8eaf8524cae5620d86a2ebe43e40b108ea592b5450f10fcff13bf068771cc7440e7d6ae66c9af4a3340b6fcabee9ef459d8f8dd4b804717b3ec54ae2246abb7a2bd5647712a1694262eb4886befb8a11d4fd297f512e18b819121f4de1af51036a64fb97a111385d7a218ba794b9ddb7066fce7bb3f1b86590b7eb1b753a29d3bc584ecd300fdb33db715c5a15cc1bc3f2fa49a9abc52815c464af878781e85e15fee92788950005deae6822e084a4c9cbdae862c58f76a6fd25262a3a42004881a7160bf546e066f376163ee028b587014a2b2b269a19dea85c4a7690f3d2065dcb200af1e6714199d7498f3858e61d0d11e8bdaa85288c256cb8bab2982e038a084f459ba0697e7e9159ca0de7b2ecab32a8a0e2b3e26097e321868aedb9318c7972bf6e011e03db452ef8a07ce49d129d12b7e55818e9c4ada4dad00e8722764009ec09082a7870402abd180d050dab6cc5e110a290db641ae620ed5ec58a7f97821e07cb54de427454a5d90d28926af2008525d9f053487aea87972764023751e5cc959a793aa986230814082e8d0747d908027b0e0baf36e8768776a79a7056b438b8a7d3a134da4b698c8dfad12e418b2435f4aeda6cf0a6d4d4906e3d4845606947f87963c7238592e81aca8b03405365408383d120fc3178696d6599ca124fc6014ec6f2e93d274a8e28e3c64a395e433043283d81a91839d8984f642fa1c1043932596fade0cb561e08cffb08af0861535276d622b225d38c755b969c1971e24d600a9a5c
+InPK = b5a0956984e94dcb5756f9ff394f581586c933aa95db66c5fbf68c15d5687bf954d27ba22042d62f8a6311571a81210da078cc70f7d411bf9c2e2c829c280e6ace7022f0de83c98cdf92a1f816a585080bb7fd5ace54adf4dd60b17fc35d3742beba77a10729f9c175954515474a1d7b1e0b06013cf2092d113461c99921ba2d84658c22713f530723f44642af1f8c87fb807a89d10c050635216259050061aa8f3c2d0e102e8e6d99024ac72120593f90d4982dbe5d130d004491ae8ae95afc12ba5059fe1d288a21f178854b50744a069617c70eca46447a189dfec5e7399dd90aac0a639485540bd060f96c877750aa13a118663f186b4a96e890d9d40cdaef268571012fd93fe26f689211a15eda78feafd1b97cc01590190589433d651cc73400bebe3b65aac4858d501e24a7535481ac5b996ad77a5ea814f16082705236299484b3d7dc8606de6e95445be4594e6030d945906b5660dc35fd753735a44a0874dead0ac44e23649c51f62daa013bfc33029ae994678da2ba46cac2a0bd283f4f48e7e9a619e9cc5214d2ea08e893c2e3cb9c11b287dc7835dc237a58c504139a9a49d11ff96c7d8215d3599abee62c54c407ebddde4f3c6b669ace8abaa61fe23b0ea98bae9e5599563c5263cd4125657c83f0d9a2c243158a7c2c6680a8a88add6bceb8e5e00f850394e69d3d47369dc9e1aa86b000d27f4a78c28c624506e8dcfe43a957497310711c54d3f8f22ce12e840a32d146715fc4c4039116a86ee7eb0af30db5adf7999c955a6a0067cc6c33fb8e3ab5124b18687464fc12177b69e66cae866fa291385b8ad872730294958e035a2a5fd9489acd5509a0005eab82dd177bb81af96c03b78d5e94692fb2357ff1bd2b663933b7d9646bc9eb4545fe970a886c54a9915a6c6083d47f42311d841874394963bf0459ebdb354785ae2babb696318ceceb28cd358ef9d389036cf5abfa814cb6abe1508261c9787dc44a9b4038b2a2585a125fb25bc46a179a8b47f1f841137b873101306a2556831bc110493d1c40c688227202c49ff290a0002ff0e56b859b3e3f46e0b34a2c44b19f17275a93257b7ad01640423c80431408813e3d6e5f94e7f18b1c22506c1c1bf4812a9b860b01b248805a03fd588ec54b1adb78a6c4f1af0c547c1b1f8257f6a28c11ea3972f860ff6a1ebd94202d1daaba1077512bf5f5ffb22c63dfbcf2ccba0d836c7042c330028b537364da743f5fec8b646c1974652e8e165c384da662acf28e51089a94857a024a5e6ea27f45abd0115b67d72460405e8ee1b00116ed26bf9c68092eaa8510c45f4015e920f6890a8b3a1aa64ddb820a2e5a6bee9eb399ca7d42a8e8ad79b9a1214e27f90b1f494c77a620f8158e0b6353999446c9b0401ef9910dce28ac6d6a6413fe8398c6dc7615ac85e4d470d1ed394d0bc1273b07f50bbfee2c136c2cbacf6e9e770fc02c9e96d9f39d601aa854e1b7f18a1b260f201055fc844d800af062b163ae176a5b72728acf030139c11d9e075f6cb0c362b00883fa9083f20649e28b75b7aead19e4b347a1e6111300da4487796df4dfdd16e242ac114d37f71485bbcd50f7047a92074265c450c78e016013ed25256a632b646a0840817083c5676182eabc595766d6f7b123c49acbdabbbe5ddb29fc83422432acc397241aeda878c6b6fab3210037d3b9f4a00d2e6753d5583961226bd16910590a7cc3993ff61156097f5bcffb4fae741f878572735296f904292bb9dd834a80fc0422bc002255f09377ce2dde000f23a4b1114de2aa9053a2495048cd9300b07ae6520ad9f2226ac813cbe77e8587d5436bba996042b41a6926a1e26019a34f88f8b285e2a2dc60061a45fb87d7892b745a4766f05a3eaa973e51fff6b7759aadea8254a94d12d7114ee265e96e108060c1fc82b0fea73ac320c9236d5768a979991d2f43b7d2bbe10f9361b959785958ead416b0433e90e757852865a292816d451a2a6a2dd8c87fd828f28de91f8a70cdf9164da8c74a939d6897db47449fe0b7bed448fbbd4d7a12444ae0e865b3f5af4ab8163db69a422c74f3ca71f8996c9758940e5edef168f9867a1f860c64e38508d0256b047c40181d4cd1f68fbdbbe44340fb944a67f310e15aeb8d6a3b168afa78ed81ca536d63b498a679556fa9fe5981c65c5b0566460009a557a7442da41e4cf5d7e1749ac929c84733ce73ca5e7393fd58bb07df14f861420a3aa3f6a1238d46b39742b490a54c8fa67cba2c26bbb655ecc2b2be47aad966b09b6580916f4a9ac3fe6d4e1bbb6161b81f0850bcaaf23c21a7239184b6f9198f9c16a1bd555165e25791e6951ec22acd97bcd10ded28ed67cac18a2f4164318c21088255bc3ad7612364cbaee41cfab0d8282033a8e0b18256c004eb0e6c5aec15854c456146591065664a928816fbfb213a5e4b4477148a2487aa5368b4dede4b54dad78566d42276558149302089bb23af8271e00090a33d29c090692da678a428442f35d8f0e6dbf40f
+InRec = 00000000000000038000200000000380002000080003400020000c000140003000000000400020000c0003c000000004000300003000040002000030000c00014000300004000280003000080002c00030000800038000200004000240002000000002c000000000000300003000000001c000000000000300001000000000c0002000080002800030000c000200001000040001c00000000c0002000000000c0003400030000c0001c00030000c00010000100008000340000000000002c0000000080000000020000c0002000010000400004000200004000200002000040002400000000800018000100000000240003000080000000030000c000380001000000001c00000000c000200002000040000400020000c0002c000000004000240002000080001400020000400038000200004000200000000040002c000100000000380000000000000000030000400004000200000000180003000040002c00000000c000380003000040001800010000c0000c0002000040003000020000c000080001000000002c00010000c0003000020000000024000300004000300002000080001800010000c0001000000000c0000c00000000c000340002000080001000030000c0002c000100008000280002000040001800020000c0000400010000800020000100008000280000000080000800000000c00008000200000000340000000080002000030000c00014000000004000340000000000003c000200000000040003000080002800010000c0000c00000000c000280002000000001400030000c000080001000080003400030000c0000c00000000400018000300000000080001000080003400030000c00018000200008000280003000040000c0001000040001c000000008000280000000080002800000000c000080002000000000c0002000040002000030000c000280001000040002800030000c000080000000080003800010000c0000c0000000000003000030000c000280001000000000400010000000000000100000000300000000080000000000000c00038000000000000200001000080000c000200004000380002000080003000020000c0003c00020000400038000000004000300001000000000000030000c0001c0003000040003000010000400024000300000000340003000040003c0002000040001c0002000080003c0000000040002c0000000080002400020000c0000c0000000000001c00010000c000200002000040000800010000800038000300004000240002000040000400020000000038000100004000080003000000002c00010000400000000200000000380001000080003c0000000000003c00020000800014000300004000200001000000000000020000c0000c0001000000003000010000c0001c0001000040001400010000c000240003000000000c00010000000014000300008000240001000040001400010000c0003c0003000000000000000000c0002c000100000000280002000040002400000000c0002c0001000040003400030000c0003400000000c000140002000000000c00020000000004000200008000180000000080000c00020000000000000300004000340000000000002800020000c000100001000040002800020000c0003000020000400000000200004000000000000000001400000000c0003c0003000000003800020000c0003c000200000000380002000040001c0000000040003c00020000c0002c0001000080003c0000000000000c0002000000000c0002000000001c000200008000380003000000003400000000c000140003000040000c00030000800014000200004000240001000080000c00030000c000100003000040001400020000c000180002000040003400000000c0003c0000000080003c00010000c0002000020000000030000000004000300003000000001c00000000800004000200000000200000000040000400000000c00028000300008000140003000040003c00030000c000240003000000003c00010000c0000400030000c0002c000200004000040000000040003800000000c0003c00010000c0002c0003000040001c00020000c0001c0000000080002000020000c00030000300004000380002000000000400030000c000140000000040001c0002000040000400000000c0001000010000c0003000000000400018000200004000240002000000000800020000c000240001000040002400020000400028000100008000080001000080003c00020000c0003800010000c0003800020000800000000200008000340002000000003800020000c0000c000200000000040000000040000800030000c000340002000040000000000000c0003400030000000018000100000000140001000000001000030000c00000000200004000300000000000003000000000800020000200000000000003000080003000030000000
+Key = ad539b834982f525e844fbdf56fd496ca25cac45c6b5718de7f2417b7ff6e3b4
+
+InRandA = c664383843e8befe514ab2bda15c5aad395931045b4b1c235b7bca75ad04216935f18f64dbf1d22c2e410c50e84468859fa80efbd78b1ab2cfcba7f235b471e5107e3337aa4de117333b3de593075f083a31f22e59964846dd454df9d992372561218a20d1f59a6628c65bf060d7a6956e0bc2e679c6b85b44ee1b07a13faedcde44724d4c98ef58516be8346ea4422008e96d68225fc9e0eebd5d8e149956cd1660c5eaab33387c6c17774073e9516009a007de75172991609901deb2808a3b93a15cae615e19852ab9a8fc415ce6f2ed203fc1f785ef6e77739d44eab1720ada4a7ad43cd96b99ad29b15c80b42f0566315841035e246e6b2358372c5257a4b97c3bf6dce693cf5c3f8acf468ac9ee2903a075eb829d9506e0b21504699f7cd7dbd72cbe0a36cc7e6b47f28a00e5e2a848bd95bbcaa9e14f9a72cc9edd253a61419e24dda294fe0de54df1fb14227b73d7cefad3bcbbcd41e74199797eab8089e7e56990f86eed4a3c8c7f39df1a70aaee6fd429d39854286e52d1bd8a0c2a8403ea58b010f0a175d0a974365a2558446b3522186cc2e0ff1b604caf2b211eb080f507ded619594de87d7f2655a2ea918dc384394a362a5a985c6bb935c14100acca189e84494ba27373ab1e955b228122807d78f1cb4a44b64459c90921308a448a83e14556129464459e9204cb2a90e3295833701d0bb6e700d9b3ed30f9599a31d6e00d871bfdf7e3e5a4e2a4317b233e4cded198074395092d9c9c513fd295e71cf4e0e85e2989eb4a31c752b5791c66842db51e4e8e150b23ad4c5724bff7a4f9061e5ae5c91a69615aa36fe6eb044b374fd2b88d5a532c608c8aacb091d08cea8a67a9028e620d7b41047d7a88d07b39050aaa67703101a492be1cc1f16224a91c9ce75b5e48ac5389aec2c7fd44b3521656a38e849b52d7c1dbd9138f868f78cb405c830a00a60f0e6a814efd8599726fe913b7b4fb36b2a94eacb7d938a1212d5f2aca551a2166f32ae45c2933ed945476014151448f649eca8b1b2b07c181e9153f4d7c78eea1b4ee00c4316fd073a1c9da8bbc2876f256e3d2218a180b2efc04fdfe61d2c417093e483bb0535a6892d52515ddbdeab4544b8b45e945650873f24025a6a65c3fc2202a244c695acd60f555f8b5ba196336daafd899ea09f1a8df918277889118383c12e5a8dda938ba5d0214148a16daaf196b6f12a20d17a6d1b57882b4437d9f0099cc483e731fabd2151e4febef2d55bb881f271a343306345096e8c4658a583cf2682d41ea924504ad0069c1764a5fc2c959a14d93ecadc18635e476b7d639d2bb008fb1e05ab6e454a30143f859f43477e7cc6253df152e2fa42958d67446dc9843c42e294b6196eb17c928811c0f686902d52cc24652e43344a6da135f6b2df84b4a802f703965274f8d5566469d9a146a65978d1d453f581881a52680f3eb9a89677563082bbd312bf53a569dc5c6f1c13279de627120fe804f6305a897fe479b1a5729836db61c9bf9c3cc96b9eb52a4836ce0608be6be33f1d2355bd45be2449d10e4012e3a92e420531056ea532039f8ca2daad8d0051d5b201b262db64e0c60901ae91d22126ce3390692a61b6e13d9a4b6ec44019a08e9241ca5e0d261baa092161eb58fed33f94e91a2bc18b7d1dfddc122de3c8d93bd6619945987c8af349714109a65b3a0f9a946b8843b1677d867aa8c5b38a1324d4a5cffb8d98e94e4d837b16c0a823f04ea17d4f470f7c46cb49d01ba23e66f966f111a525e662a0b69818801b8c4c7398f21954168551580766d4882b0f6586f4ace742d2c07b96dba61dfc8763aac92289e84afb1ae4afa784204e82f2c2461bc073da91c3615ca970f195e37b9e5a11143e1317336b320d349b6402979cc6fa1984a843cbf1435a519ca957fcf45658ddaaa8fc130e1bd3ebe5c8b4164fe6a0b76d6e3f252d170889572babe5085a68fb42bfbf45a9419372a7e926fc604faab4ce138d34a1e6438c649ad282d4077ed990b911656c6192cad9a079764f995179eb3225fc67fa71b008aa1a4821a991133497e28cfe02ca4a87cb087c412a8998d22fb4e807bb51ebfab53a6c9a6602e9cc54f1a8f9c53ef95c5f0879256b1d6fe2aacfd488943a49135d49fc54fd326e5963ccc451583030dadf30086128bee2b74902c58d9c0b4a10bce90a3848b75d4751038dd3ae5827f95a83476e060f38779d151a8deb0517b6a526415447b48f66ce28615a5e3c10f971397375a7c0ac5264266f4b930c34faba354d29f795c9aca392413586218e567d9bdb95209c94322a1d96c9a628f9e97abaaf6332e4e61ed0d904b119086ed021401be72abd6c460d50dd2a7d29ab890528d31070b7d781f2a6912074bd1fde427f60faab61b263c2d52cf6a4092b66e9080617554aeda564da0f5c9586c987541ec2551a7ca0d512bee052be4fef66f750b7c9bda78350ca4812394545041af7d550d7142e579db6d954489301dc4cd9b405f685d69470c325c490b
+InNoiseS = 8159e4d8f3901f0cec2355ebc6511fa4993a1576af5257b606f5b5317703e6b78b4053961048db1d4661b68e7bd70c8a00f758c257fbb81ed7cc2aba5fb098a4a0e8d5b5466ad209a639d99c0408eaef04895cae0cc75a18838a2d4517b927807d1e83ef9dd6f1cc1d42ca623b720581b804bee5b95ea47f8a7440db5073166fc595b314a519c178bb37b14b7b18cbe4d68027c45acb6760ed815f035b7b108c4193e470611f2e413d50e7d0596537cfa86cb93c4400c8240978a9d2325f0d30776de50e351662f2d97c8878468dfa5555618c0e25e237fe268658069153d90ee7552296b0853f2e2787853e2a72accf3dda02b203bca258b231b8b60f5fc0aa02b5680b15d6122e8d8bf5a7c474fb867d4f9840a47a198ba81e0d20b55692c528f0d296aa6ddb0a0e868fe41c00c01ba21900740a9cb3469e0948774fd769215a2b8ce940f37ed2b3e989c1883c548a220b88020dad389e55175989f916e62edf3bfdb9569ba33f8242a19e1c58141966be01e50c9514179644bfe6e2b0352d6d17eb637a498a5fb0d57181eb5400260ca0737e59038053a543cec243ee1da72263a0bd3669d4198b62855aaa315a7254603326e4a209116ba66bf9e7abfd34619adbf4086528fb24ab9780e54690695646eacdbb7e2f111782e11001464c22e74e52ddcba7a653bc822f6aec2ac0b694c9809d21de4e404e086cb67e7164b5018d29c3f64da96c1e7291949028ee6d279fa4314b26dc4515f95cd8436dc61a1c0dd429fe3652c8048c09392999b93b96de1be819e833d40b9f20440132d94b2cd13789473b5b87b41c75a3a5da6d8880013d8246ead4983d33c21e1ca7a6ac45e60c0e536ccd348821ce5bf59829bbc8a6357b5839cbe6d9a47bae4bec2963a023766b7d835f21d25c0bcf43b064400969a5ec08a50d245954e8c433bfc06347df623aaf988b17680fe0709012dbe9f3a071f92919de80f6825284a35929619732367a166b065ec83f9fe53510c0b84714db410b391ea1851d1313af396d8112b1c608300123d8d1c47df0bae78f97ca03f988d3ef3697aa76a11ba72e013d316a83edce235e680d8da0e6009dd019f6652ee8111419ed60586a116a637c9d2fb8ddea48384945337cc9720b63f6f7af590e4410d7a6be0c4bdce16776e0385ae3536a81104920c449298a8a0269c2d731a1b561a4e302358445cd1647abb5e54f96b07982e60a5d6200b9070949e6f974771cb9e3080d4bec507e3b462e8fe72228656201828f98d31b736e6dd0de704501dd3e069e7a070ae89c2e66ffbaae926426ae8d507f469b348f0996fa70274a90aeeb22ae51f11365123d7aa93ba0f309ea161f66cad5ca3795563fdcd0717d8650cfcf49821f5a600444b067f7d9a9619f2da086d1877dea143a77097e6999612155e9544a4c88a682e5014a8086c18a6e88942bc38254ca4c3021641b434623a046138b9166039e46e52bc2654f52cc367917fcaa77c7ddf798e6930f6cff97d8305c5f1323047f826279a115abf31470bb6868a4cc9a1f099246758a9801b769ccf145809e4b0d77abd4997d1bc88d90cd1a26ae6383fbd7d2aa3026ef860f9624ad092839b7d5753f1e266ac02614e1dc23052bf8171268aa8ae6c67105a38a08cfd93d7a0a35f5071104f7820632c760756ec263784676aaf708d6699fe09534dc1865cd11b1a58c190a20d93e33204475daa45f7c11e61900754ba3e93827fe2a2212a0f9839c22924ae69102f1ca4c1e1b8125df684cbd1839541b856d75825dd24a1a615810541348020f56952d89435c006d4aeb365ee11ac9a14709c8a894642265d03022243bcef2826f94227d02a1e6851c98c124162681a5113eddd24b3d889b58c0fe2a396a67b8a952b627c20ce5c0d8d66985261ad13b7658ad0b36ce4e24435809b928dc229dadbefccced1746a616d8d202037160442edde9e0f730542355d9cbe81433ef1e8714276dafb05671149c9a77e69195856e9e315820ab07cf35492a0b8009bb7c913ccf34ab5589ad0d6d7b6840b9673945ba16667a03cfcd70a70d14718c5b228566b54fa15fd1100a7e7406d72c6494e14643030d35a44d93325a07083cad85ac1f51d05542402eed5bf08e4e1046c3b4607ff864e4681581376db62ba977815e28203e066702c911593ead01d6ac4c4a69505e56ce6d59ae149631dcf19c2b0d8f05846a9a33901cf078fa35aa5d3f67d52649769daa605282581dd8f4c0f6156b2208841bd3c1573b42fab7c74db0aa0f4f5927e24765d83a307b644700704bc0bca665e1a8c240a9e4296d0873a6041d18f9633738402682fc23752931c611b3e526f2d43d95a346da7835e0662a68daf4de5e2c7561b1b9e2a836144be8841e157f62e22aa0293cb07183e0b9e4b6bfcf6b7baa91353264ad91795e2458125d441a4fb04ec0245282a18abbb39f6bd6c695b392d7ea22499eb22c5a795223191848d46f5691615b274ef72bf28d4226928947cbdcb02a0bd8e28ed45a
+InNoiseE = fbc6ff58a75dbc6e5cf3845afc27d2e024f3012cb691484ec86571bcd098636a008285835cfb3353942b9ba32e97ef3e1e17d72cb53cc5aaaad50235ee2ebe324a50b911963da9db2df069fa3a9905cdc54f0a8e5defcb69d08447df66b34244b4b879a6a577ff1a473c00b08b6eaabdddce9214d64628b053bd47633e215fc5f28a3bb10c115dabb2561d818b0147f5bfdab802decb987d7cb6c503a55104965b5fded9cb1d8c7faf557edac6c45acacf2f91d7debde409fd0b583060e581e7c50542b528099b192cbcb220254e17959933cdd481f944e99a8f9b0736dc9a661540f2e11c503f3614d873e4929b5668be71c9291f2f4da753747a23fa49c139127202ad96ce29e3a247cb638d97c40967cbd1b1a0e1ce6420135f8907a5485c505c73946046add0ca78491c934fa7851acb4e857110b4dabc1da28382ca03f59d69906941829b2d2d72ce99731f108f78cb3956096911cb5f98e8a8a5a109a2acb22bce952721a6d47844183e0f8ae14b5914de6c82a8b821a96243d9cbb7404308abac47904a0ce596f6250ad299fbe8a546482c7afcdbd745b942814e8d7b5087113d5885ceaab6c1893e166bb447f0079b1746ba97d95de7cfbb09c2f884be4927369cedbf8a4e0140d3e4409324cb5ae536356467bf09ac6e2f2a483451f406093a8943d56cb8302e9f898abeea41490b42b9b6e086d088362105a5bfb7605a22a77c5947ec2bb07cec320523899b3c29aa5e146b064f3e959340f1c51e380e1f94ce13f25c568865ae7a1dbdac6d526db2cfa44a732853a2dfb90df51bb0147e257557901a3474f863e6c100086d3034055b596ac2debc72a9f1376ff536facc099aa88cb2968c5beaa4f48bc099ccd6b5f8b33b047997689c6a1b9bd48a2e893c2c5cda755759840ca8e227f9224c74ab6e881a841528c7f0086bad9af2d82375aae2809cd5b4f665a445c4cd2daa50d146c983ecd2e19d8eaf85d24321163b921869807e255a4da5a5800b51a84a0f7cd447587fb66e3f94f7f08359233676ba7d8e44406e7a593549b2c5a72148256c3490c65af28846eca88c2529018c1d0517557aca9d9a4f35eb35a5345f2e3caa0ec9602ee97dc18c049d1045603543c404b82424dab739a2a42be4c8f4d83442c3ae80b91425d22a4113bee47a61dbe74bdece5d302dc2459be992c840d0d6398068d3c0ce578d997168d81d1302e86c7843d93419483d90e7da02d97536ad0ca827ffc8bc5ad761c4da16bfcd866b103faa4040196c383f11822bc865e6288d27e98759da788a46c827a2f3662a205f98be55422c172c4fc2eaca3a8fa911e7ab157284141365170ea02c0461c7d000c11f781175a54f92694e8716f2089aa85c1df74bf8a547bf991454c13ab7d81dbe02e2cc4ea953d7849d59a649120c1a630c4b57d4f5e67ff67560c93266453e12cf2234ab34a90531c4fd86764d847addbd6921be4ba167534390808d742dd282c88a926fc21ec6e8b059b65e3bc8c4dc738425ef9a0ba10e681b1ed8ffde75b04f955bd3358f741c5be06bd69b222d00615a05006def7e01ef5992bf93209945ae13d6ee6f44a1c16340181688724e357669d6b04f61bad76742268c389a36cebbe609294729e4e364147c6204d2e7f6b1954a92172a10cbca1f47e0dce19e356d4a0521830f504058c6d035b471bbfd9bd3db70a87f621b59ea115e5d1b028cbb66288b49cd48b567611602931697117c94c65f51a07e700bcfd60ed54ebe18ef0a72f2e9fc293da7f332c709996d66d8e4b785a2e6199c08368d2e028e023686a933f6dbbea656990886e8eb9931bcbc16da0950498c0dfb43655689ee3794013a69e0ca65c3a5e1c4923db71ce7b6693048a9fecd8895ca310fa1d93fcac2093169c7e458714a2eb1ae319241c88b491b75f4ac567f40211eb68dd6adca53a368ef2e6c0b89570821ba5d9805bcc1fd141a8385e7884e65af2852f9411f01105068fd41605572f0cac986688a42bfa5c7472fc17c220e594b7194698cc87b4788c831df1afab81952295a631449c7696b66aa35e82c34466a5625b1d3584492ac15a05cd2abe26fcd647c526a226743fa7aec6e5ed90255aff582be19b80730550fb8e3a8218d924c5dda52b5f7cd6ba049192244b43c01eb98f7650e3fb99581892b50601282c2b085ec858c1fc90b0c7eb1a72557bace0eb536a42f3310a29e08cd84b3993895decad42bc1701fe8629464de55037d60e2d3f0c57fb98dc1f5533ab708a950020bae7aba840d7a7cc1bea74c37da9d3e767951d4c9a405a1410f1334c3bc058ca75052c17110b2e025f7323a66add069f0539d0b9edbf548d7c110de42c4b4ee194dd76ddc2b2dd6a577437f927cd6a80e57acc6e752beb1c9e6f39529966397deeaa7e956f39f9de62ec00bd2129eb6bae6877824e11565bd6c8f0773796de4acab01ce8acce5a44ed9a74827088e8990cdaa56c264751581d37555befd6e2db56df5a43943ed285186161729dc14804082
+OutPK = 3821857b649a3bd42b7258662d639824e8b22c7661d9e7be58001824e56bf942c51a7aa4cf2e064a7e0938df4aa774554d9023d7887196425a41d0ca79b166690154c32dc59e580675dba22a63a76894a216f96241a7391a9a3db565e906077c040ee29297a2cac16159cf7e0822c097ce2c47556eb807e109fa6adf886b3222213a159507d209d494aa2042d2af1ac8933cb573e26819f05c36468e33f0eab26466c0fb0735cdb97f6270f80dce78721c73c4bb840cc6c8475b1c8d83792498dac8353c30eb41e5bc1419fb8a4f11384431e0c4ab597a3d68b6eed71ba66a21d8d714d77d7e4c7d1fd994ae407d38c3f631631a1ee7a3872b4da184cc02f5f947d2ae8d96740a1f2840eeabb1e0890e42828a0da52b15679de6a7f519b5b6d60e808b1819861c0cb87401a47d4f27475510d9b67be9de19814c0c6cedf2b254190147ee115b97454401de4a0621fe5f4e544a29f2962a814c2991099207042552388145837886ff93c9ca76cca1ecb05eac9a45cc5f4861909596ad55d9d5d627972cb35b1b9941e6281bf29cf9a9f1cc2298605914ad858daaada41a9a0dbd16985abc8b22c064690ca2ed022ed847bd1daa98a76876368277c82921ce15a1916d1ba1b05a86a8cf6600041567049d51a9c416a5314b13a8ce753eba167ff497d08209509bf4206cad3f852a03c54d31d5dd5e37d13e96666ba9f3ebdd8bd7c5e851f73cbac90258a90bb91bde8e7d003b0c2b2786c966d6706e4adb3a81e4b1803c99a00bd94d45cb0c4307ecb9a42f9dae935a226e84472ad76ac59fbc5dfd662c7e917000d7a901d427490bb0f0555d59e39c4864a0c62a2dee0dc2b6a856695c8e90a34981e38d2239da79a853c979e7bc5146fc23ce8b945e15290f525932f409cd97ee41d4a0ba6bca6485e95d476fabebb320ea2b639a16d8cbdc6a55d72557f7509f9a26ad3053165b7ae23a652796270fa783d417e07376d41966ec36102b6bb175c2cd20ea5abf92366468c74e0196afce0f23579bb4575c235651ff10ab3a4f38813bc200de7e262e11ca31e8854f85c858bbb98fc16f60e730d2b817489699768a7dff031036894c39ed99c2818cfc85ac51e1617b8e7bfd9f7540c0e938798bc539b1509a10e2bae74d0a5f998e2c83a53a3b8c94bf65b96c98c4eff216d1f93593654844b6f0af158d032bbe86196541d6d466c2887cc874d2cb75bc06a68585dd0a96d4de25c21ff64d7c0c20716032981a23acbbfc6980fb6a8b53475d644640efa7eaf65a830e95f97bb97ada61b2a14bfa13f7dc4c4a26b53541c53f51c9319f272e36e4b9d855bb09277aea1693c6e82c27bd85509751af4a21b0595c2ab164b79176d2269cf7903e996ccdd4724148a0ebc47531d04881ef562769420596891717778968107c9a63c86d491ac5dd885b672908e7820a419118633896d67a3274455f16acf436bc838a2cfcc5dd8503a982101488457d63043cf83bb273abdf0daafc5d966029fdc9235a9df74d024aebf08b4a9aee68f2b16185ceeb73a4394f015263771fda7979d947e84c616f380a87935ab90f0d9afcd79a6aad65efe6893ca21016915ef66b26aac818ba75a9985045d8ab749bd4a6ac8e2512febc2827e18965e37c9467982f0ab6a57cf70056981d4810f7e21158ada05fe354c5f3b1926802a7a3f88c411c2d01d9e56c2263bc64cf3805f0767b5f199b3d6535d3002b244ee2b0974acaebe9aa155ea9ee9b021d473f8cc545e45e227e8ebd5b4542b527ea5b4262a04f8169ba78586a830cc649d2b78e6f726da1b8e56db1f5e01a48bc3a379967a9772796450fe5f3863da94cef9671f4f56a3461239c3579ce7d2d6194cb84504a7a9cfe00f96e29d1ab69313bf95c021801f0507c1801081484682a15b241f8e7f345517ded639390958b448869ebb50087482f26fb8c7d7aca0f6720ae0d59f21e22e1ea66af51800fe717f444d9ea224216460a2e92f09aa3298879d388d8f2540dcfe5e617190e26fa6e87b9c91685a1131a5a14323bcde0be36d78510872b79a17745733311236875c827ca094f0ac6e5917d9bd6d0e3cd2194cf79e39be41b8dacf804447a44c6862571f0f496d9c8a8e549619b2b2f8af4d116a195cc1a34571a41a62fa09adec4744cdbe505f289047302a820a1a03ea9a2b6e9793d8a9b90b962b96e205d684161918194e1c085db26d812908ec429c199527d2b7d03e6b40fdf551cb70aa0a7a224b3c01bbe95762ce941d0ac72a1c0394acc994b1424985bbd6d64ec119cc535a00c229f37f7c21e32e92b9d71eeb88a118015b454ae4a0bc23bc8daf5b589628dd23dbca40966e0a7cc1463758abbd7ebb260da1dbe944df451fb5cd5f1437467d408509b5d5e726f265d93801155f282f73998f653e643c4493d70125f2c006b402eae4629efa69aac7ba8e8dc04b67009da88f6d8dbf148874cf4733845895ae13fa21746aa899b138b30b034d5e733380f66301dd129e030d43197c1b7f373d126
+
+InPK = 3821857b649a3bd42b7258662d639824e8b22c7661d9e7be58001824e56bf942c51a7aa4cf2e064a7e0938df4aa774554d9023d7887196425a41d0ca79b166690154c32dc59e580675dba22a63a76894a216f96241a7391a9a3db565e906077c040ee29297a2cac16159cf7e0822c097ce2c47556eb807e109fa6adf886b3222213a159507d209d494aa2042d2af1ac8933cb573e26819f05c36468e33f0eab26466c0fb0735cdb97f6270f80dce78721c73c4bb840cc6c8475b1c8d83792498dac8353c30eb41e5bc1419fb8a4f11384431e0c4ab597a3d68b6eed71ba66a21d8d714d77d7e4c7d1fd994ae407d38c3f631631a1ee7a3872b4da184cc02f5f947d2ae8d96740a1f2840eeabb1e0890e42828a0da52b15679de6a7f519b5b6d60e808b1819861c0cb87401a47d4f27475510d9b67be9de19814c0c6cedf2b254190147ee115b97454401de4a0621fe5f4e544a29f2962a814c2991099207042552388145837886ff93c9ca76cca1ecb05eac9a45cc5f4861909596ad55d9d5d627972cb35b1b9941e6281bf29cf9a9f1cc2298605914ad858daaada41a9a0dbd16985abc8b22c064690ca2ed022ed847bd1daa98a76876368277c82921ce15a1916d1ba1b05a86a8cf6600041567049d51a9c416a5314b13a8ce753eba167ff497d08209509bf4206cad3f852a03c54d31d5dd5e37d13e96666ba9f3ebdd8bd7c5e851f73cbac90258a90bb91bde8e7d003b0c2b2786c966d6706e4adb3a81e4b1803c99a00bd94d45cb0c4307ecb9a42f9dae935a226e84472ad76ac59fbc5dfd662c7e917000d7a901d427490bb0f0555d59e39c4864a0c62a2dee0dc2b6a856695c8e90a34981e38d2239da79a853c979e7bc5146fc23ce8b945e15290f525932f409cd97ee41d4a0ba6bca6485e95d476fabebb320ea2b639a16d8cbdc6a55d72557f7509f9a26ad3053165b7ae23a652796270fa783d417e07376d41966ec36102b6bb175c2cd20ea5abf92366468c74e0196afce0f23579bb4575c235651ff10ab3a4f38813bc200de7e262e11ca31e8854f85c858bbb98fc16f60e730d2b817489699768a7dff031036894c39ed99c2818cfc85ac51e1617b8e7bfd9f7540c0e938798bc539b1509a10e2bae74d0a5f998e2c83a53a3b8c94bf65b96c98c4eff216d1f93593654844b6f0af158d032bbe86196541d6d466c2887cc874d2cb75bc06a68585dd0a96d4de25c21ff64d7c0c20716032981a23acbbfc6980fb6a8b53475d644640efa7eaf65a830e95f97bb97ada61b2a14bfa13f7dc4c4a26b53541c53f51c9319f272e36e4b9d855bb09277aea1693c6e82c27bd85509751af4a21b0595c2ab164b79176d2269cf7903e996ccdd4724148a0ebc47531d04881ef562769420596891717778968107c9a63c86d491ac5dd885b672908e7820a419118633896d67a3274455f16acf436bc838a2cfcc5dd8503a982101488457d63043cf83bb273abdf0daafc5d966029fdc9235a9df74d024aebf08b4a9aee68f2b16185ceeb73a4394f015263771fda7979d947e84c616f380a87935ab90f0d9afcd79a6aad65efe6893ca21016915ef66b26aac818ba75a9985045d8ab749bd4a6ac8e2512febc2827e18965e37c9467982f0ab6a57cf70056981d4810f7e21158ada05fe354c5f3b1926802a7a3f88c411c2d01d9e56c2263bc64cf3805f0767b5f199b3d6535d3002b244ee2b0974acaebe9aa155ea9ee9b021d473f8cc545e45e227e8ebd5b4542b527ea5b4262a04f8169ba78586a830cc649d2b78e6f726da1b8e56db1f5e01a48bc3a379967a9772796450fe5f3863da94cef9671f4f56a3461239c3579ce7d2d6194cb84504a7a9cfe00f96e29d1ab69313bf95c021801f0507c1801081484682a15b241f8e7f345517ded639390958b448869ebb50087482f26fb8c7d7aca0f6720ae0d59f21e22e1ea66af51800fe717f444d9ea224216460a2e92f09aa3298879d388d8f2540dcfe5e617190e26fa6e87b9c91685a1131a5a14323bcde0be36d78510872b79a17745733311236875c827ca094f0ac6e5917d9bd6d0e3cd2194cf79e39be41b8dacf804447a44c6862571f0f496d9c8a8e549619b2b2f8af4d116a195cc1a34571a41a62fa09adec4744cdbe505f289047302a820a1a03ea9a2b6e9793d8a9b90b962b96e205d684161918194e1c085db26d812908ec429c199527d2b7d03e6b40fdf551cb70aa0a7a224b3c01bbe95762ce941d0ac72a1c0394acc994b1424985bbd6d64ec119cc535a00c229f37f7c21e32e92b9d71eeb88a118015b454ae4a0bc23bc8daf5b589628dd23dbca40966e0a7cc1463758abbd7ebb260da1dbe944df451fb5cd5f1437467d408509b5d5e726f265d93801155f282f73998f653e643c4493d70125f2c006b402eae4629efa69aac7ba8e8dc04b67009da88f6d8dbf148874cf4733845895ae13fa21746aa899b138b30b034d5e733380f66301dd129e030d43197c1b7f373d126
+InA = c664383843e8befe514ab2bda15c5aad395931045b4b1c235b7bca75ad04216935f18f64dbf1d22c2e410c50e84468859fa80efbd78b1ab2cfcba7f235b471e5107e3337aa4de117333b3de593075f083a31f22e59964846dd454df9d992372561218a20d1f59a6628c65bf060d7a6956e0bc2e679c6b85b44ee1b07a13faedcde44724d4c98ef58516be8346ea4422008e96d68225fc9e0eebd5d8e149956cd1660c5eaab33387c6c17774073e9516009a007de75172991609901deb2808a3b93a15cae615e19852ab9a8fc415ce6f2ed203fc1f785ef6e77739d44eab1720ada4a7ad43cd96b99ad29b15c80b42f0566315841035e246e6b2358372c5257a4b97c3bf6dce693cf5c3f8acf468ac9ee2903a075eb829d9506e0b21504699f7cd7dbd72cbe0a36cc7e6b47f28a00e5e2a848bd95bbcaa9e14f9a72cc9edd253a61419e24dda294fe0de54df1fb14227b73d7cefad3bcbbcd41e74199797eab8089e7e56990f86eed4a3c8c7f39df1a70aaee6fd429d39854286e52d1bd8a0c2a8403ea58b010f0a175d0a974365a2558446b3522186cc2e0ff1b604caf2b211eb080f507ded619594de87d7f2655a2ea918dc384394a362a5a985c6bb935c14100acca189e84494ba27373ab1e955b228122807d78f1cb4a44b64459c90921308a448a83e14556129464459e9204cb2a90e3295833701d0bb6e700d9b3ed30f9599a31d6e00d871bfdf7e3e5a4e2a4317b233e4cded198074395092d9c9c513fd295e71cf4e0e85e2989eb4a31c752b5791c66842db51e4e8e150b23ad4c5724bff7a4f9061e5ae5c91a69615aa36fe6eb044b374fd2b88d5a532c608c8aacb091d08cea8a67a9028e620d7b41047d7a88d07b39050aaa67703101a492be1cc1f16224a91c9ce75b5e48ac5389aec2c7fd44b3521656a38e849b52d7c1dbd9138f868f78cb405c830a00a60f0e6a814efd8599726fe913b7b4fb36b2a94eacb7d938a1212d5f2aca551a2166f32ae45c2933ed945476014151448f649eca8b1b2b07c181e9153f4d7c78eea1b4ee00c4316fd073a1c9da8bbc2876f256e3d2218a180b2efc04fdfe61d2c417093e483bb0535a6892d52515ddbdeab4544b8b45e945650873f24025a6a65c3fc2202a244c695acd60f555f8b5ba196336daafd899ea09f1a8df918277889118383c12e5a8dda938ba5d0214148a16daaf196b6f12a20d17a6d1b57882b4437d9f0099cc483e731fabd2151e4febef2d55bb881f271a343306345096e8c4658a583cf2682d41ea924504ad0069c1764a5fc2c959a14d93ecadc18635e476b7d639d2bb008fb1e05ab6e454a30143f859f43477e7cc6253df152e2fa42958d67446dc9843c42e294b6196eb17c928811c0f686902d52cc24652e43344a6da135f6b2df84b4a802f703965274f8d5566469d9a146a65978d1d453f581881a52680f3eb9a89677563082bbd312bf53a569dc5c6f1c13279de627120fe804f6305a897fe479b1a5729836db61c9bf9c3cc96b9eb52a4836ce0608be6be33f1d2355bd45be2449d10e4012e3a92e420531056ea532039f8ca2daad8d0051d5b201b262db64e0c60901ae91d22126ce3390692a61b6e13d9a4b6ec44019a08e9241ca5e0d261baa092161eb58fed33f94e91a2bc18b7d1dfddc122de3c8d93bd6619945987c8af349714109a65b3a0f9a946b8843b1677d867aa8c5b38a1324d4a5cffb8d98e94e4d837b16c0a823f04ea17d4f470f7c46cb49d01ba23e66f966f111a525e662a0b69818801b8c4c7398f21954168551580766d4882b0f6586f4ace742d2c07b96dba61dfc8763aac92289e84afb1ae4afa784204e82f2c2461bc073da91c3615ca970f195e37b9e5a11143e1317336b320d349b6402979cc6fa1984a843cbf1435a519ca957fcf45658ddaaa8fc130e1bd3ebe5c8b4164fe6a0b76d6e3f252d170889572babe5085a68fb42bfbf45a9419372a7e926fc604faab4ce138d34a1e6438c649ad282d4077ed990b911656c6192cad9a079764f995179eb3225fc67fa71b008aa1a4821a991133497e28cfe02ca4a87cb087c412a8998d22fb4e807bb51ebfab53a6c9a6602e9cc54f1a8f9c53ef95c5f0879256b1d6fe2aacfd488943a49135d49fc54fd326e5963ccc451583030dadf30086128bee2b74902c58d9c0b4a10bce90a3848b75d4751038dd3ae5827f95a83476e060f38779d151a8deb0517b6a526415447b48f66ce28615a5e3c10f971397375a7c0ac5264266f4b930c34faba354d29f795c9aca392413586218e567d9bdb95209c94322a1d96c9a628f9e97abaaf6332e4e61ed0d904b119086ed021401be72abd6c460d50dd2a7d29ab890528d31070b7d781f2a6912074bd1fde427f60faab61b263c2d52cf6a4092b66e9080617554aeda564da0f5c9586c987541ec2551a7ca0d512bee052be4fef66f750b7c9bda78350ca4812394545041af7d550d7142e579db6d954489301dc4cd9b405f685d69470c325c490b
+InNoiseSP = bf801ef9346a2d2160cff456a246c4c10e277bca9ded456ed94a385883620cd8f78430cf918cf9706c333cad91fbe1c05d81a06ac855224d03a613c2f1512478dc66c53b8a049a12cbe518224bfed226643c21155a5f28d9d97d78e70b0cc0af0622b3e8889bd8093442237a74f4dc1877a56594fdf081326fc3ab5b6a8fd0441f9331b64ed648688641198f2c84d16886c45f8856b5b04a245aff2a2e45cc095d9e24ca9922a6a4a28b507793dc8b7b84ebe0626eaec7e2f0c6a5c547e7938925cd1d373ea9979a3d2236a8e13ebb91d481955697157c76a2644081f7e96e587224ddd09fad2c3349ea6a6406155aa5425a88ce6199c78ac65e6a7d1a093ef0b27003e69df3a7094149cf4aeeb6e836b55a170437506a63090deb09cb4090252caf5701350c62eef2a4283287c644227903ee9626262da3ba6a35be090868fe580081de6a470458004e663d00169d71fd4b8ec0d6c603a094ec4524941f36027494310d5a585d30845dc22b0993b8b0fbea878d5e007343e980341c70d0f2553f0e2358d64872a9b91af5109c080fa05304406571856be3ec83d0181b030b7be53d869c1065156a10c5a4fc9bd07b63400dbb0c8568b5d84139e597060ce99f8962f356123e398e819d2881309a28a94faaf8caa79666a9943eb912b1473988a91564d79bb704006e2269201b68f91c00b3816d427b524e8d882da57b4193a954aca5c88438b82e575513700883c57cc5ce78b7bcddfed635718e1cd5b02545c532802054886d486ffddec2930110042a27420a0cce66d1dffaa1f7e47c0f53d3f4c8fe469410ea2831d9131f0049fb88d5016a691273eb9c9784e8ce08b23155b7c2a230c5e5a33f6f31e3c291219b993d6822cd5b822c9f506cd6398708e68339c49fa61ecae19cce241190bd54b2092ea8476e9637447f812a8de17b6a0e94df0a97a9ca83206e08c9aa05076823aa5be46e24cb05bc6aa11eb09a633b295aaa47e862e642b98e917e4e7ab5734e3b8063632b933c362e52b3e64f0020338cdf36fe1a173094ea5ac75e2086082a5b315d8f559e30598430ac27e6ac51b854079a8bc06bb5582e2ce348f1041845376f23d8a7b55441aa8c898ca60ebf57c097acd405df4f34b08a7439a9518d31dc78bc1c53be8b20d53b686b94717cdd76dace26bb25424eae6d47e7ae558be4488fd0b348bf4e42c4756a129bb62db6d7b25949df425653bc59a691db2a8bdc4e291aca98316bce06b0df63a0a6ed93c69b7a1510486318ad3dea55f44a4595a268b87a2ed4cdb00543a4bb6614bf4a54450921d2a9c474a0263885554924f119d7030a899859473a441276de02b71dd1f07b407d90ad27280b3fad4203656b466f5832724903e7db4c3a794e43a4f4416f7a9bbec811aa0f798952a4bc484e65222ca76c1471d1a36261b7dca89e5ab26415d1952568826ace34067713c2d15d991c1a5639139319a11d5ff2b6eea8229d6134b23ca6acd2569ea467b58fb58694c247d901c76c76f0c11a4b131336fb8bed2440cdb7a18d145b74aca0441f2d3151d09692e02c3fe06ad9e3bca57f3407513207b4436b4ffc795e0da3e75d78e13b295efbe0776b61ce1e3b95d130f3720cbd024a829410528c8066450bf47fec2b4b8116e067800c7753741175994e957082b52ac2a0f1570955a85fc608906291013a20eb7449769e6b267dd5553e1344dd46a700b60b0316ac1a6dfa4dbb64c449e5fd5e3a093c2570da1d9e4163b9cbd249180046221cae2d365725d7e2a8330641a4d0b7fa2d696aeed0c95416b0a3a274958c43155f2110463da5da99abc19ba40338e59f530c220d2867b0f69d4e41d6c63324c88805bacf3b2ab38209045561b3a79098bd8ea1e6e16cae5fef9ff4202e01030f68c517f93cd6c8128b84b68d8f9b74d2594e649f59f727709db60d52b16100c0349d841958fc38019918de3896484befe67181cc6627eccb7ea18710463d240a555f7c6be493bd2592653a4e8cdc47c8302ea9cc908257cc874c28a550fac6618b92985258614f0eac9c9443929d58e6a165e5c8837fe43646933cb04c9efd0407c97a2baf9d71bd9c7a5b4504f22c728056216a54f19419d8e72555e378b8db136ee0502c07c4a15ea72b1862f7a40230cc9a69cb6a34e6fd727393e8a9a85d88540f16a6c3f36d9d2aa647c9c10c745feb0e638fca46cc650974b91585decb5cd983e222ce109852dba42bd5dcffc7fda94f80d9147598d864a7e26e920608069bdf963a377499e4869fb151d07a5c0c8f388f3aaf6447540b92f9606cc62fb0e5b457820b2ad2644e99ad937799a4dc584ddab30a2db62b0f9cde26a78a739e629d43c3cd4ede87418a2348a8c64413e800e9ba3c45fa2642b6bc824cda7b0ffc6053a7b878516286adc754d05cde3e6526973390e8346fdf4a74d64575726e1172c6a89210958819b5464a1f0b01a8adbc4243a4a3999cc9b750949e38d84870cd9c93f88b2d167ec185796a6e19d042da81fcc42d
+InNoiseEP = e09827a03ea2aae6aa6f42fc006f2f530c4820199d449c5316776d8d511421d603bd6b3ed5476500e481226b0ad6176a77811e346bd57cbbc404b4107239ac64ee63b5e89c53b8193bd9af8075b20d2fd49da9af0c01e908575e14f7144ad4454852812da1e5503897690469baee995969095e858096afdfd54fa4ec2aa9a21ac9a7ee4c1d0207932ac7104f61886b94a8e1617757f487e040976959afd026768a8d4d734adb3d35422363268fe98756a6fff5c29d3e8d434845de66a9b2692bfb19ba7d49c50f078c789bbae5ebc3dea9920d5fef37462a3d194b8f07ff186870047b5bc9ca9f658494db39ed5bfa820836789203615f0a61412e079ec76b671f9a79738944bb466d982101b296cf6e158d46432003a69ad89fd4c23f727fbc40fc62a69041b66b124a9f2978d451120ad11d1a81996bf2c8363ea0670244fee51c5218e800ac8838588979ca380c94698ba210dee9226f1fd49663bc422f646b04bd12b0d82cd212ad281c819f61a1cf1ca44e1508c8b67d9e6f2275879bbd2060c6f611cf952939561091109e2cc781723be2dc7a2c064429f4d6234596149466e02839043a7068f0aba01ee4b4d49da084402826de309a5313567b78e6041ec0ffab92ae74af93a260bf824b6da2f560a36842d92979469e2c660b4298b86b264fb18165f36eb60f95a036e1c7ae93028b90f49ec00fccc3de4678c25558d14238a9dea4e4498af7ba82785756a5610f4c920f1a5235e4c00b635ac80567216d444683d214a597e59bc7e4827514a62fc30b18c97f325a04208d059c2f1283f3273cb2bda28f666cf009ecad95b3d76d6ab0acbc3a1fd8bc5a8bfd87829e21000ec3564379710e6d6c0bde5e7c89d977e6fc393323d1a2db32d68866c4e5545bb6463f9c47528de934ecd18995562e6a5f50649b5b1473225d87ab7e16bbb0c6e149611213269272c9f0f2a7681776da3c0e6a1285bd43d930ba9e9f77e7601a16185895fa9df01937c11ab210b222c288d994d2f23a681ec270f41110648e46b414ec9ea62fe3ba0969796e9122d34d8099c06991a444720ca3ef7fb034c452fb6fe520954d5d806705e84b706ba685f4e0d199af682220b3b5ca72d322b55371e4b17185b5b5abe913cf2521f90cc548acde5c39efbd428362bde206a44b46a9ac756f2e00954ec212098e8bfdcdcea08d393532220c4899a9631b815bfb94da8dce69ffb02ef4b4a61840c771dab530d479f6730e2a94cc52f12d9caddce3eb48eca0555628b6320ead812ddd4e28845d755169ba0548289780533fa6e8cc4396570af40e412f36930ea994e8b459c78be17a7caa4454dc5097fe6ee1e66587337240710e1d6058a4147de4a9e122e5ad84d7741a700472b6025d5911c28cb30989024b88a379193123fe25041edc933072c17da306d911c34ea31b485a85b62c9ccb948695c3795a35d50af9a926465212e76332104a04c468264ad679e6cb7924bd421eeb8c034987916696d5b345e69e24e59f9645a016039dc13f97be926f1b4ed25a5d4489ddce30f77df143846e6b66f2a2843b6f28d466a8026fa70db095095f1862c6a3042561a6671a16bbd6bd0e9651227d502ea5cf014a52fc6e22a0fe7af8b075d94cb5611aaeb97b6a51f42b782ff661d6646db15cdca511e2dfb0b76ba0acb77f209d42e1ace29b4eb1bd98b3d98b9c44c903261b480815ce0f4db8c7f0306adeeec79ebe4b331e3a116a91b8d6d26738c9d2b8864338b9837e536153fc482f958a208f4dd05c8ebe9d802434441a474acdb437a00d644e2509d6a00a23d4d259fb4ba220cc61506007285ec30ffb13465558ef8f1d24b62d317060ef05dc3e89efca5de506fd614dc4fe4593e5816ed4cf21e49a5892230b6b292673440c46f245066ec205b3e8528daa705939919d721537e330dbcdf5b2c347b6018c3a540258bbd781e134e10ece44e92d563faf542826b55f45d4b417254cba552b4e59d0a61ae12d8044e785268d6e9020c1095ce501e4370be80d2f92a274bbdd3d5459396a0c6815896cfc93bd3a55610882424c5632a722b6f4b26a2dde918ca20e385f4a27c2d97e75a6d752944a3598bba362f88b213e95cd08b412fd8c505956a8b1a37a0b99147534ffbe2a63a52d6234103af39081e72f1a113c8b4bdb36a0717600284b189237abe7b5145315329262fe596019be860182da0516575ae68a845d85a698ae584a5c846cf826c8a90fb20e74174592861c3a22a9c0873e3c6e4da84720fe0d537edc72cad841286fc21a43005c943fe6bec35484813bdc88455f7102869adb43195c9ee995d267a8ed58081d0a50f8746f6e0c3349427a1be61ab060c3d47a9a6d192036c10d0c538fe0268474f18550e41fad727a9b4b0016ed4f3ee04d02f4cd1d3f934c7b590566e7e849f37d329b7f5dd3abbd1a9dec5c90b415545ee1123693694438c46564a02082533c22c594191d04469b21c7316a9913893a861f2d4ff009fae3c2968ff8c82214d2ba4f
+InNoiseEPP = 024000700000c00300ff7b00f8bf003001b0ff0a00feefff2b0004000100000000ffbf04c0ffebff16000300010000fbbffcefff5b00fcbf01400000000400038000c0fff6bffeefff1b000c00fdaf00000008000000ff2b00f8bffd2fff2b0000000200000c00f0bf00c0ff4b0000c004400020000400feaffe1b00fcbfff2f001c00f8bfffaf00c0fffabf0040ff0b00ffbfff6fff0b0003000140ff6b000c0000b000c0ff0600fe2f01f0ffeebfff6ffffbff0a000600ff3b00fcbfffaffe0b000000feafff2b000800fd6f00f0fff6bf0340003000140000c0fefbfff6bf007000100010000040ff4b00f0bffe2f013000f0bf03c00000000c00ffaf002000fcbfff2f00bcff0a00ffef004000fcbf0000004000f8bffeefffdbff0200ffaf00300000c00300002c002000004001a0fffebf000000000008000240ff2b00fcbf02c000d0ff0200fd2fff0b00f4bf028000100004000000000000ebbffeefff0b000800ffaf00b0ff0a00fdeffe2b00f4bf01c0ffcbfff6bf00b0feebff16000140ffbbff1200004000000000c0fe6fffbbff0a00003000ecff02c0048000600000c0050000b0fffabf02400100000c0000f0ff3b0004000340014000f4bf0080005000f4bf0180004000f4bf030000ccff1200007000f0fffebffb2f00b0ff0a00fcef00e0fffebf000000e0ff0a00018000b0ff02c00030ff0b0004000080ffebff0600fdefff0b0000c0fe2f010000f0bf0440001000f4bffc6f001000180001000010000000fc2f00000008000040ffebff0a00ff6fff4b00f8bf00800010000c00003000f0fffebf000001000004000030001000f0bffdefff1b00fcbffaafff2b00fcbf00b000a0fff6bf0040ffebff0600028000100000c0fdeffe1b00e8bf000000ecff02c007c00000000400fe2f00fcfffebf00b0001000f4bf00f0ff0b000b0005c000000017000740003000fcbf00f0ff0b00f0bf04c0ff1b000c00010002f0ff020003400000000400ffaf0010001400ff2f01d0ff0e0004400010000000fe2f01500000000200000000f8bfff2f001c00100002c0ff0b001b00008000e0ff0e00ff2f02f0ff02c00030001c0000c00400000000fcbf0480000000f8bfffef0020001000024000e0ff020003c0ff3b00fcbf0030004000080000b0ffebfff6bf010000000007000400002000fcbf04c00010000800030000b0ff06000480ff2b000c00fdaf00d0ff0600fdef003000040001c000f0ff0e00fd2f00b0fffebf078000300000000180ff2b0008000580ff0b001300ffaf00e0ff02c0000000e0ff0a00fd2f0010001800fcef004000f0bf00c0fe1b000c000180006000fcbf0200000c00fbbffe6f010000fcbf0200002000fcbfffefff2b0000c002000000000700feef002000fcbf010000200008000100003000000003c000000017000380ff0b000800faefff0b000b000140ff0b0013000380ff0b00fbbf078000200004000180003000000005c0ff0b00ffbf030000ccff0200024000d0ff02c000b0fffbff0a000040003000fcbfffafff0b0003000280ff1b00f8bfffefff4b00040004400000001f00ffaf00f0ff02c00100000000f7bf018000f0ff0e00ff2f00fcff06000200005c000000fd6fff5b00f8bffc2f00e0fff6bffcaffe4b0000c00030001c000c00fdaf001000fcbf0180fe0b000000ffaf001000f8bf0200000c000400feefffebff0600fbefff4b000400ff6f0020000000010000ecfffebf0080ff0b00f3bf01000000001400fd2f00e0ffeebffdefff1b00000001400030001000fe2f00100000c002800020000c0000f000700004000240004000f4bf01c000100000000030000c00070001c0ff0b00ffbf010000fcff0600ffefff5b000000ff6f00000007000100002000f8bffeefffdbff1a00034000100000c000c0001000fcbf0030000c00fcbffe6f00f0ff02c00030001c00fcbf0000010000170000c0ff0b00ffbf0180ff3b000000ff2f000000ffbf0000000c00f0bffcef00e0fff6bf0380ffdbfffabffe2f006c001400014000000000c00040ff0b000c0000c0ff0b0000c0018000000000c0fcef004000fcbf030000f0ff0a0001800040000000ffaf0030001000fe6f00400000c000c000400000c00180000000070000800000001300fe2f010000e8bf024001300008000240ff3b00f8bf0040000000fbbffb2f010000f8bf0030001000f8bfff6f002000f0bf03000010000000fdaf00000000c0feafff5b0000c005c0ff0b0003c001400060000800ffef00000008000100004c0000000000003c000800fdaf001000fcbfff6fff1b000c00010000100000c0030000bcff0e00030000d0ff0600003000fcff1a00003000100000c000800000000000fd2f00e0ff0200ff6f001000f8bf0400011000f0bf0030010000f0bf03c000f0ff02c00080ff1b000c00ff2f003c00fcbffe2f00e0ff0a00007000f0ff0e00feaf002000000000b00030000000ff2f00300000c0
+InRand = ef9e8c91a774589ed24f16603ee706cf863ae430553ca47912e090bcc2dad2ad
+OutPK = 0868b7095909139920f60b50f0b56c14e0287116363521de40ce41b87cef52e34f4d15f1a4a7c4fe1681d3ea5094cd440f3c4749e928b212d8285b6545c60cb7ab1bcb591a8ad10f97c5f2c58d5ea8b5cb51fc4dbe14b6d12021b73581c697bac19d68129d09d10d310f4b09d4627544a06690161e7cb15da077898fe207225d4830c8657150a76bc1e9168b1fe0e929b77a7a91de7129b965a92f2d80c15f0074b4eaa8283bb5a7eda3c8a74a1aa396e629c02a8d98fe9fdd05f4a623d74e8211013241ae5f55a633aa65b6ed68c1fd41482262a856e75c75c81b7a271b191d47894d5987f86b23426b1368ae9c789cd298a6acb7cd4749f2a1fa3fa448db83e819976e6a9d99db028538a2234884d905099de876c108945c25780586319e1060d5f3986e00951017433599bd26cca7db8ec030bc1a565a59c61fc8a8c5f7ae087bab08c2e1222eac08d81ee84111153553abc5ae389bc9226e47854857395ed7920e8e01622ddf9393580a20877de6385c8d730ca612fe9889eae42067f97182dd2cd059b3f26c9d6384ea97603e01df8f172a5e2017956a81aae0272acbaed1efda1472a621f86c4e4133628293bdd603b20d64f251b649ff12766550a64377dc90c40d7c062d636550da144730060d4b185a1a75057cf6f2b159739306a20b6a5e4714ebb86248037216eeb3d1ac0440667330a0690bf8eb84f0f5fc5f8c2fa71427a42a225ae873cf3142c19b0350677e7106e4e8f7b8da96eb533a2afb565a33161aa66725a12f44d62bf5e17545a330596a357b65e06c51dd7150dbe9d85503b29a10e367911dd139bb65b62221893255cb6ea73f7067e3965207313d91f7a582b8fd9c84fdcdadb4b9111ce100b604ed3d88b1a86ec23b9c197d07ec69e2dc30756c2d95ef3d2e508d2991739cb5dca7759a4b8605c46a83464d86310c5ef6a6a240086c82ee1a8b1da31dc455d81ab8456e0916e9bc2a2668c10795e7206f2fcfda97387944f3a7c0a0c9c174d5892e7876943753c56cf951c8b8d11d223ae1ba55e3816d04795a95d81d1e92d0b8761f1443a6f25c2444987ed832fe4278e0fb81de4e03106e0b806dcd93190d00b92c2ca924ed230a6b7d23ca1f8dd8590c49ef050486da624dd42b49214a3a60098f934894d54eee870f820ad11d78834813d6fa2b69928b85a83e625e8a20a166cd40d85d8866980987d7002919d5111b5904837013c5795e1b3520cfc82940dd3f00e6d4538323760376a85fe44bfd5022d15646aeda06ae162743ae5a083d6403a917d783ccf9295890940599bc2faac9a4d468f9a0c6db7973d09525639cf0c1ae048a3a869be9316248728f7fb1c630424ccd7668daac8be6977f04069c034fa3c96a40c424648a4da0b808bb499435c4b3215c98d18ecbe09e201b9d1f473882343407b56605da6a1ec1745b45f1829f49eb80be284e589ab8b3df8d26528d8095895497bfdd0eb01a2ea4c59fd7fb2fa426f08ce8c252fc4875c38f8744a4385fc96cb1d8fcaa8ad26e217a92903865f259b5cc3840a95ff889656d031793528b9a2a46d7c58654d13a29c8fd99e38e7e81c1a0e926f8695d806eb6ad3866efa02046f2b00bade0d913ab1a8916da87d03d8a0dbd17f7b9d7692c6c5aecca2f05ac90a62b15497147181e1b1018d222ed4347d03bbd6f39d4b0caacf938632499d7c759b7eb6882467c4ea23e67624652ac414f92ea63295590b34930bb2b36744b0f54f434b863ceea67c171fd539616fee3471059c3d39f96c2ca04c5111c1870628a296fe66222ce84a2e69950961ca42ea8731230091db6492aa244f27729dc11c188753581cc8b25577e908d450df494febabf66c3885a7a159198c1010d569d0a952d494b4c729209b52a7020b1b6e06a81d8c43e9b216cc6c4291a7ced1c77313c423321bc20192c632a5f84e8819abc37848638f58a132122420ab2885d8742bbe2ece1795b1e5cb9cfd627e89256231cf185504619759a3e9ed5047d5bdc1208aa617af35c84a1272f986b9afa2331825ae107fa394a9a243039162e44014ca8d3c65b660cd51a73b4b217e97569b87836ba41d3da04b09f6e45ffa211122f266d43e8a0a66e41e1dd0082458261891d538094640080f9c60612a251d46cba88b8438ccc4228b6622ff81de322a3a20fb3d7179629a023d69c3a96cd5174818e037ab55e71bb1616e4412e5579d0dc941429a50c0fc37be6712a53e6010b5c8a94cc961604d05a847e8083703be17c0d133850c1f62ee765bd3ba64a0097223240a0dc7555d3db7274f65bc6a54b23477903f8a89d4a6ee534fb5af5783c381bb0a72ad76f25849b021d8871348d7d4fc9b61c68bc50c94e08562e165d4572482938656417d371568c6a3dca7007c26a5ef190e957df8e93f56c6be4da00b7a2e675a8a0094855188ae3ddee2bfb821673b389b8b32cfd83d88c986ae7e6f5f5b23e2dd62fc743e2063e8d3051862a54ef89ed823fdd14fbe3fce7182c27d90f981035d15f
+OutRec = 01c00020000c0002c00000000c0001800010000c0001800010000400010000000008000180000000080002800030000c0003000020000400024000200008000140002000000000c0001000040001c000000000000380001000040000800000000c0002800000000c00014000100000000380002000000003400020000000024000200008000200000000000001000030000c0000c00010000400018000000008000000000000040001c0002000040003000020000000000000100008000280001000040000800010000c000180000000040003400020000400024000200008000340000000000003c00030000800010000000004000200001000080003000000000c00008000000004000140003000080001400030000c000100001000000002000030000c0002000000000400014000300000000180003000040001c000300008000380003000080000c0001000000002c00030000c000040002000080001800000000c00028000000008000000000000000000400000000c000100000000000001400010000c0003c00000000400010000100000000240000000040000c00020000c0000c00000000c0000c0002000080003000010000800034000300008000040002000000000000010000c0001c000200000000380000000080003800030000c0003c000000008000380002000080002800010000800018000300000000080001000000002c000000000000200000000080002c0001000080000000000000c00020000100004000200002000040002800030000c0000000000000c0000c00000000c0001c0000000080002c000200000000300003000040002c0002000000000c00030000c0000400010000000000000100004000100000000080003800000000400010000300008000080002000080000800020000c0001800010000c0001000010000c0003000000000c0002c000200008000380002000080000c000300008000280002000040002400030000400030000100004000180003000000002800000000c000300001000000001800030000c0003c000300008000240003000000002800010000c0002800030000c00024000100004000080000000040002c0000000000002800030000c0001800030000000000000200008000040002000080003c00000000c0000c0001000080002c00020000c0001000000000800034000300008000200000000000000c000300004000080001000000001c00020000c000340002000040003c00030000800038000100000000140001000040003800030000c00010000300000000300000000080003000030000c0003000030000c0000c00010000c000200000000080003800010000c000180002000000001000030000000020000000000000180002000000002800000000800028000200004000180001000000000c0003000000002c0001000000000000020000000010000300000000140001000040002400000000c000300001000080003c000100004000140003000080000000030000400024000300008000040001000000001c00030000400024000100008000140003000040003800030000c000200002000040001c0002000080001000030000c000000003000040002400010000800018000100000000380002000080001000020000c00008000000000000380002000000001c0000000080003c0001000000003c0003000000000000010000400038000000000000280002000080001c0000000080003800000000c00030000100000000080003000000000c00030000400014000300004000200001000040000800000000c0001000000000400000000000004000100000000040000000020000c0000400030000c0003800030000c0002c0000000080001c000300000000240002000080001c0000000000000000030000c00028000200000000200003000040003800020000c0003800030000800038000300004000140000000040000c00010000c0002400010000000020000300004000080002000000003c0003000000002000010000c0003000000000000018000000004000240000000000001000010000c000080000000000000c00010000c00014000100004000200000000040000c0000000080003c000200000000200001000000001400000000400028000000008000340001000080000000010000c000040003000040001800000000c0001c00020000c0000400000000c0001c00030000c000140001000040002400020000c00000000000004000180001000000002800000000c000080002000040003000030000c000280000000080000800030000c0003c0001000080002c0002000080000c0002000040000800000000400008000000000000300000000080000400030000c0001400010000000014000200004000340002000040000c0002000000000c0003000000002000000000800008000300004000100001000080000c0002000000003000000000c00
+Key = 3c763fff910725780ae666ae0d91de15edb5faca0b8c2e22c318014401f91cff
+
+InNoiseS = 8159e4d8f3901f0cec2355ebc6511fa4993a1576af5257b606f5b5317703e6b78b4053961048db1d4661b68e7bd70c8a00f758c257fbb81ed7cc2aba5fb098a4a0e8d5b5466ad209a639d99c0408eaef04895cae0cc75a18838a2d4517b927807d1e83ef9dd6f1cc1d42ca623b720581b804bee5b95ea47f8a7440db5073166fc595b314a519c178bb37b14b7b18cbe4d68027c45acb6760ed815f035b7b108c4193e470611f2e413d50e7d0596537cfa86cb93c4400c8240978a9d2325f0d30776de50e351662f2d97c8878468dfa5555618c0e25e237fe268658069153d90ee7552296b0853f2e2787853e2a72accf3dda02b203bca258b231b8b60f5fc0aa02b5680b15d6122e8d8bf5a7c474fb867d4f9840a47a198ba81e0d20b55692c528f0d296aa6ddb0a0e868fe41c00c01ba21900740a9cb3469e0948774fd769215a2b8ce940f37ed2b3e989c1883c548a220b88020dad389e55175989f916e62edf3bfdb9569ba33f8242a19e1c58141966be01e50c9514179644bfe6e2b0352d6d17eb637a498a5fb0d57181eb5400260ca0737e59038053a543cec243ee1da72263a0bd3669d4198b62855aaa315a7254603326e4a209116ba66bf9e7abfd34619adbf4086528fb24ab9780e54690695646eacdbb7e2f111782e11001464c22e74e52ddcba7a653bc822f6aec2ac0b694c9809d21de4e404e086cb67e7164b5018d29c3f64da96c1e7291949028ee6d279fa4314b26dc4515f95cd8436dc61a1c0dd429fe3652c8048c09392999b93b96de1be819e833d40b9f20440132d94b2cd13789473b5b87b41c75a3a5da6d8880013d8246ead4983d33c21e1ca7a6ac45e60c0e536ccd348821ce5bf59829bbc8a6357b5839cbe6d9a47bae4bec2963a023766b7d835f21d25c0bcf43b064400969a5ec08a50d245954e8c433bfc06347df623aaf988b17680fe0709012dbe9f3a071f92919de80f6825284a35929619732367a166b065ec83f9fe53510c0b84714db410b391ea1851d1313af396d8112b1c608300123d8d1c47df0bae78f97ca03f988d3ef3697aa76a11ba72e013d316a83edce235e680d8da0e6009dd019f6652ee8111419ed60586a116a637c9d2fb8ddea48384945337cc9720b63f6f7af590e4410d7a6be0c4bdce16776e0385ae3536a81104920c449298a8a0269c2d731a1b561a4e302358445cd1647abb5e54f96b07982e60a5d6200b9070949e6f974771cb9e3080d4bec507e3b462e8fe72228656201828f98d31b736e6dd0de704501dd3e069e7a070ae89c2e66ffbaae926426ae8d507f469b348f0996fa70274a90aeeb22ae51f11365123d7aa93ba0f309ea161f66cad5ca3795563fdcd0717d8650cfcf49821f5a600444b067f7d9a9619f2da086d1877dea143a77097e6999612155e9544a4c88a682e5014a8086c18a6e88942bc38254ca4c3021641b434623a046138b9166039e46e52bc2654f52cc367917fcaa77c7ddf798e6930f6cff97d8305c5f1323047f826279a115abf31470bb6868a4cc9a1f099246758a9801b769ccf145809e4b0d77abd4997d1bc88d90cd1a26ae6383fbd7d2aa3026ef860f9624ad092839b7d5753f1e266ac02614e1dc23052bf8171268aa8ae6c67105a38a08cfd93d7a0a35f5071104f7820632c760756ec263784676aaf708d6699fe09534dc1865cd11b1a58c190a20d93e33204475daa45f7c11e61900754ba3e93827fe2a2212a0f9839c22924ae69102f1ca4c1e1b8125df684cbd1839541b856d75825dd24a1a615810541348020f56952d89435c006d4aeb365ee11ac9a14709c8a894642265d03022243bcef2826f94227d02a1e6851c98c124162681a5113eddd24b3d889b58c0fe2a396a67b8a952b627c20ce5c0d8d66985261ad13b7658ad0b36ce4e24435809b928dc229dadbefccced1746a616d8d202037160442edde9e0f730542355d9cbe81433ef1e8714276dafb05671149c9a77e69195856e9e315820ab07cf35492a0b8009bb7c913ccf34ab5589ad0d6d7b6840b9673945ba16667a03cfcd70a70d14718c5b228566b54fa15fd1100a7e7406d72c6494e14643030d35a44d93325a07083cad85ac1f51d05542402eed5bf08e4e1046c3b4607ff864e4681581376db62ba977815e28203e066702c911593ead01d6ac4c4a69505e56ce6d59ae149631dcf19c2b0d8f05846a9a33901cf078fa35aa5d3f67d52649769daa605282581dd8f4c0f6156b2208841bd3c1573b42fab7c74db0aa0f4f5927e24765d83a307b644700704bc0bca665e1a8c240a9e4296d0873a6041d18f9633738402682fc23752931c611b3e526f2d43d95a346da7835e0662a68daf4de5e2c7561b1b9e2a836144be8841e157f62e22aa0293cb07183e0b9e4b6bfcf6b7baa91353264ad91795e2458125d441a4fb04ec0245282a18abbb39f6bd6c695b392d7ea22499eb22c5a795223191848d46f5691615b274ef72bf28d4226928947cbdcb02a0bd8e28ed45a
+InPK = 0868b7095909139920f60b50f0b56c14e0287116363521de40ce41b87cef52e34f4d15f1a4a7c4fe1681d3ea5094cd440f3c4749e928b212d8285b6545c60cb7ab1bcb591a8ad10f97c5f2c58d5ea8b5cb51fc4dbe14b6d12021b73581c697bac19d68129d09d10d310f4b09d4627544a06690161e7cb15da077898fe207225d4830c8657150a76bc1e9168b1fe0e929b77a7a91de7129b965a92f2d80c15f0074b4eaa8283bb5a7eda3c8a74a1aa396e629c02a8d98fe9fdd05f4a623d74e8211013241ae5f55a633aa65b6ed68c1fd41482262a856e75c75c81b7a271b191d47894d5987f86b23426b1368ae9c789cd298a6acb7cd4749f2a1fa3fa448db83e819976e6a9d99db028538a2234884d905099de876c108945c25780586319e1060d5f3986e00951017433599bd26cca7db8ec030bc1a565a59c61fc8a8c5f7ae087bab08c2e1222eac08d81ee84111153553abc5ae389bc9226e47854857395ed7920e8e01622ddf9393580a20877de6385c8d730ca612fe9889eae42067f97182dd2cd059b3f26c9d6384ea97603e01df8f172a5e2017956a81aae0272acbaed1efda1472a621f86c4e4133628293bdd603b20d64f251b649ff12766550a64377dc90c40d7c062d636550da144730060d4b185a1a75057cf6f2b159739306a20b6a5e4714ebb86248037216eeb3d1ac0440667330a0690bf8eb84f0f5fc5f8c2fa71427a42a225ae873cf3142c19b0350677e7106e4e8f7b8da96eb533a2afb565a33161aa66725a12f44d62bf5e17545a330596a357b65e06c51dd7150dbe9d85503b29a10e367911dd139bb65b62221893255cb6ea73f7067e3965207313d91f7a582b8fd9c84fdcdadb4b9111ce100b604ed3d88b1a86ec23b9c197d07ec69e2dc30756c2d95ef3d2e508d2991739cb5dca7759a4b8605c46a83464d86310c5ef6a6a240086c82ee1a8b1da31dc455d81ab8456e0916e9bc2a2668c10795e7206f2fcfda97387944f3a7c0a0c9c174d5892e7876943753c56cf951c8b8d11d223ae1ba55e3816d04795a95d81d1e92d0b8761f1443a6f25c2444987ed832fe4278e0fb81de4e03106e0b806dcd93190d00b92c2ca924ed230a6b7d23ca1f8dd8590c49ef050486da624dd42b49214a3a60098f934894d54eee870f820ad11d78834813d6fa2b69928b85a83e625e8a20a166cd40d85d8866980987d7002919d5111b5904837013c5795e1b3520cfc82940dd3f00e6d4538323760376a85fe44bfd5022d15646aeda06ae162743ae5a083d6403a917d783ccf9295890940599bc2faac9a4d468f9a0c6db7973d09525639cf0c1ae048a3a869be9316248728f7fb1c630424ccd7668daac8be6977f04069c034fa3c96a40c424648a4da0b808bb499435c4b3215c98d18ecbe09e201b9d1f473882343407b56605da6a1ec1745b45f1829f49eb80be284e589ab8b3df8d26528d8095895497bfdd0eb01a2ea4c59fd7fb2fa426f08ce8c252fc4875c38f8744a4385fc96cb1d8fcaa8ad26e217a92903865f259b5cc3840a95ff889656d031793528b9a2a46d7c58654d13a29c8fd99e38e7e81c1a0e926f8695d806eb6ad3866efa02046f2b00bade0d913ab1a8916da87d03d8a0dbd17f7b9d7692c6c5aecca2f05ac90a62b15497147181e1b1018d222ed4347d03bbd6f39d4b0caacf938632499d7c759b7eb6882467c4ea23e67624652ac414f92ea63295590b34930bb2b36744b0f54f434b863ceea67c171fd539616fee3471059c3d39f96c2ca04c5111c1870628a296fe66222ce84a2e69950961ca42ea8731230091db6492aa244f27729dc11c188753581cc8b25577e908d450df494febabf66c3885a7a159198c1010d569d0a952d494b4c729209b52a7020b1b6e06a81d8c43e9b216cc6c4291a7ced1c77313c423321bc20192c632a5f84e8819abc37848638f58a132122420ab2885d8742bbe2ece1795b1e5cb9cfd627e89256231cf185504619759a3e9ed5047d5bdc1208aa617af35c84a1272f986b9afa2331825ae107fa394a9a243039162e44014ca8d3c65b660cd51a73b4b217e97569b87836ba41d3da04b09f6e45ffa211122f266d43e8a0a66e41e1dd0082458261891d538094640080f9c60612a251d46cba88b8438ccc4228b6622ff81de322a3a20fb3d7179629a023d69c3a96cd5174818e037ab55e71bb1616e4412e5579d0dc941429a50c0fc37be6712a53e6010b5c8a94cc961604d05a847e8083703be17c0d133850c1f62ee765bd3ba64a0097223240a0dc7555d3db7274f65bc6a54b23477903f8a89d4a6ee534fb5af5783c381bb0a72ad76f25849b021d8871348d7d4fc9b61c68bc50c94e08562e165d4572482938656417d371568c6a3dca7007c26a5ef190e957df8e93f56c6be4da00b7a2e675a8a0094855188ae3ddee2bfb821673b389b8b32cfd83d88c986ae7e6f5f5b23e2dd62fc743e2063e8d3051862a54ef89ed823fdd14fbe3fce7182c27d90f981035d15f
+InRec = 01c00020000c0002c00000000c0001800010000c0001800010000400010000000008000180000000080002800030000c0003000020000400024000200008000140002000000000c0001000040001c000000000000380001000040000800000000c0002800000000c00014000100000000380002000000003400020000000024000200008000200000000000001000030000c0000c00010000400018000000008000000000000040001c0002000040003000020000000000000100008000280001000040000800010000c000180000000040003400020000400024000200008000340000000000003c00030000800010000000004000200001000080003000000000c00008000000004000140003000080001400030000c000100001000000002000030000c0002000000000400014000300000000180003000040001c000300008000380003000080000c0001000000002c00030000c000040002000080001800000000c00028000000008000000000000000000400000000c000100000000000001400010000c0003c00000000400010000100000000240000000040000c00020000c0000c00000000c0000c0002000080003000010000800034000300008000040002000000000000010000c0001c000200000000380000000080003800030000c0003c000000008000380002000080002800010000800018000300000000080001000000002c000000000000200000000080002c0001000080000000000000c00020000100004000200002000040002800030000c0000000000000c0000c00000000c0001c0000000080002c000200000000300003000040002c0002000000000c00030000c0000400010000000000000100004000100000000080003800000000400010000300008000080002000080000800020000c0001800010000c0001000010000c0003000000000c0002c000200008000380002000080000c000300008000280002000040002400030000400030000100004000180003000000002800000000c000300001000000001800030000c0003c000300008000240003000000002800010000c0002800030000c00024000100004000080000000040002c0000000000002800030000c0001800030000000000000200008000040002000080003c00000000c0000c0001000080002c00020000c0001000000000800034000300008000200000000000000c000300004000080001000000001c00020000c000340002000040003c00030000800038000100000000140001000040003800030000c00010000300000000300000000080003000030000c0003000030000c0000c00010000c000200000000080003800010000c000180002000000001000030000000020000000000000180002000000002800000000800028000200004000180001000000000c0003000000002c0001000000000000020000000010000300000000140001000040002400000000c000300001000080003c000100004000140003000080000000030000400024000300008000040001000000001c00030000400024000100008000140003000040003800030000c000200002000040001c0002000080001000030000c000000003000040002400010000800018000100000000380002000080001000020000c00008000000000000380002000000001c0000000080003c0001000000003c0003000000000000010000400038000000000000280002000080001c0000000080003800000000c00030000100000000080003000000000c00030000400014000300004000200001000040000800000000c0001000000000400000000000004000100000000040000000020000c0000400030000c0003800030000c0002c0000000080001c000300000000240002000080001c0000000000000000030000c00028000200000000200003000040003800020000c0003800030000800038000300004000140000000040000c00010000c0002400010000000020000300004000080002000000003c0003000000002000010000c0003000000000000018000000004000240000000000001000010000c000080000000000000c00010000c00014000100004000200000000040000c0000000080003c000200000000200001000000001400000000400028000000008000340001000080000000010000c000040003000040001800000000c0001c00020000c0000400000000c0001c00030000c000140001000040002400020000c00000000000004000180001000000002800000000c000080002000040003000030000c000280000000080000800030000c0003c0001000080002c0002000080000c0002000040000800000000400008000000000000300000000080000400030000c0001400010000000014000200004000340002000040000c0002000000000c0003000000002000000000800008000300004000100001000080000c0002000000003000000000c00
+Key = 3c763fff910725780ae666ae0d91de15edb5faca0b8c2e22c318014401f91cff
+
+InRandA = 8f0774d3d5350bd8a9f1567b6408f044018892f845055d532885b240f813fb98007cb69e66716bc3e67f69d0331b21c613b367dc3263689b62a8b38b02846af41cfd8633f41b89aa2a7353c587b0d6b38a4ae8ad3ae2b279f340239296bea34b2d6a7048e1e38f98a6f3087ec2e24d9ff11a21e1f7b0a01e00e6b1a85c8e9c9fd986177153f76cc8b317ad0df744cf8498f01b7284038a91106217e511a0cf20ae7c5203e0ef196a552877585ad94c3b277e8b5edda7e2cef1455a066e09ef92365f19bc4c86044486498f6f4783667f72431283644708d03b534dda98dcae6692404fd49cad29b10115790de190f7eba815d93265deaf17f7d14651f8962e28394cae148d86181f7a81a941d1161198ab9d0fafa01525a043d6def1321ead41007fcafbd1243a1629d31e06776b1ba67a1bce2cd4cab1062b59877fda306afa8103adc95780c7f44b1b25ad03ff3d669d2ed2c42b46bb739ec5920d3e1f046469bb5c8d178c6caa73b82857ea5934fb87fa994c08a88291c2937129ade1e8f1a40899df5952ccbb2506b9e57796a26e999e2bdc966ec2aa89c4b8a85c15493a198472a857602f16cca5189ba0a303c6163a63d0ab94634512172c8d56a7917644937ab8a3a889f113a9738beea09f8fb2581e410689a626d38b2a3747d6873748b0a21040c8942e0e289f42dd812c298a23a5e7724f3d33cd619b82830e411d10fb9474dd95162587ab0d81a28d698e1a79e50b624f236b713a703bc46ff245387dab0a6bb77b0e0e885e7851aeac70e84b6d4a2e24bb6f9900d803d513b3ece9da5d495a18d09f61c3b50876967213f2cd4784044e5bdb3544fbacb01242e86ab0e4407bedd1ba31de696414b8a03ed07e227282602f9175a73e3698d696b54616b46b7caa419b870487f2e0869bb311398fd548897aba4a8a04f8d9b0152fb414c9a8333a48e1d7a19a7c864df42dde180148441b274beaeab25cd7cdaa5b3e369e0c16d1c8f20e2b8e5b1e70298e98651dd965a7a858a31f87c4da9a6c1dc20e025ad45f1f6b6372d99866b700d047d845b4b40563d039463bdd4e3e3150ec4aee0646b635aebf064ca1e9e7ee2483e46028a316989adf7483c160a08dafbec43aec6da81950a057fa20eb8046b9cccdb42c5e1938e7766c6f653948d1102cb399c5856fd91e1f667b27d205378f1aac0b83356cef589e5282300d8becfb547c7c297eef0c97335e1bc08869b78d46b6771f060b99b837dbee82b874a871cfe16937a4f07ed01815d7aec5664e13e755bd2c3df61f43f7c2b26e6610f5a9cc44a84c21015a06b0a72c22c68aa4493ea6d62f6258ad8e5ae1effa32c87f49431fd34c76995f85bccaa0e091231bafe3b614a0a881b1cb959427dbd3afcb9e4294c6e35d67902a908c1c42936aa258ca60953a9f4928f0820ac9f00d3c72c07bc11fc0954c0a72abbc3962a287da87ac994c1acd4aba4eda401c232c3a125814af2e2e031699bc0d600fa651f2484b1d7db75e22089492c574536e774a15c8fbb8d59164cda93b4066448da3fabbb7f52b8d6536cbea0474d6987f05c9e0296420c50a33ea26be27dc309b9201d5aa518586f58139136d7084585a3b2dc3c6b9895a0f5e468a4b00b0b52d98c147c641a2a334e45abc4289cc7f572324ba5be314e3e655b7a6ce63eb6ae217fb6f67cbb308350e91c48a88eaac9b24e8b35c05566d0e4cb2c1965b6a14593a1d245295aef119a57549fc4e86086badb512ac46d93ad0984fab1e8112a44a60a16e7051e00c630bf79042ad61a45147b1d7a531aa1c9c9bcd708ca12273daae848d990ecc46c228ad162e69302db52a008aea235a10af962b6b052c683b0d814fc74d4807076b8a0050789fa7569604a34d20b4e67aa5e6956e018b6e8696fe28f9649aa210a54d9c8c7c92b2be83a514a1a6f4e03bc721b180099cb14ccd66e812f97205a7310867f5348e6841309e8a7c2bc8fda8e336589d900813046aa96513a35086bd04745b4390ec2c7d981cf752df3578954751dab2265820ece73745903bb681cbe68f52a9f4cc7c30fab8380094df1296a1af88296b55b2a9b90dcf6a0aa8525f005706908b119727004570fb446a5a634ee44cd28e39cb53f707c9992baa7437f74d938e280a0bf29a40080da8d4798141d6a79c33d83e9b6fa01286d3920a1f5450b1c46241370533b1c5701e4f6a4ad720b208d7e567badc725c1e1364b597f03c4ddc1cc33d251d99a69ed744e04e5a2b8471c0a892fb67e0f75368b35f59c6aa6e9557f15b2cf78120a8575ffde653c968949254113406b4e5220833200c81c3013cea24983bffdeecb0f294041de340975c329b3dc9bda0a9562e436efc1b6c707c522a23c99e602bf103a01b5ea4029319f696a2919ab5c2c29a5caa59750c8ff424288e160cc02286f2ab68ac098ae8e59225c8b3579880486cab2da4e87d10348f5d97bdf20b791b60fb43d06bd88c3153c17819feef2c19df7d2cfbd4b74adcc61c370a16fa1521a2
+InNoiseS = ad9613f7d3f429e39015b40b84b0b0913ec1c4208c24d4573b1e0e5cafc5c2e3304c02271d1271721028f125e6974c6682ea15cea2cbe61cd1c03c601a708e42401ffa6cdd6bdfc925b9e520914a5902162f2db0a42b389894122e95cb5b12393642bee7c43bcd394449a938e73625985fdd9897355e13a453e073ad38438fe88738627c7ca3d39137a8e996630b1644a01c0cc5ad8c7ba79da067693c0080bdbdfdae94a5622587c4635fe319f439c7805e194b785fb65e1ce58d446e131206b025d98e60d10a1afd716e79103863382943964362c19c48a3aed93dc2aedaa4945a3d47a07ab585c3b7e377edaaaf8e4552ec3cb049c66ce1a7d9157dd552a63008a3ea6ff2026505788d18bc0152181b7b52489864aa4306eece17e366a9a822ad131059258665298515f1b8b691e490c9190ae668b2e6aaae8633a9f4b1d1a1015009c041b50e55d0d3d19066108e58c3ba80a67213c851687b6494a5235c7810718aafd10b7295810284592327f2b37ab9cd89eb80b3a81130e3a604796d95b3eede92df111d7264d21483851394002743f9fe13555f35250499421f189a1b35a92a2d2eec18e03183042e9ee7eaa15b9c11f17b4009a33960f9d628a225ab1c2557f8dca1dec00f82f80437c10a09f04fc52d3b8f95c19d86603bd6be368d427f274d3946d0221240cf76f4c4bc86b963f1e72fdeaa7eda6a382b41ba210eec4bd0a48b509dae0455a4381e6ec80226920a7fc67649fe4017b26407624bfa60da5c47559f248793eb4dd1216665201762d3b64c4782cf1175ff2eab9c6868b764d27a8f29e882beee78db130563114a1c8d91f125748ea0cfe56361aaa55b8d45a507754d332b8e9c252728b103aa48c1d5243c8e9e9a9879564f61cc2bb8cb3cbec982167859524f78cb374bb95056505acd22f0cd2890162931d4a200ba1e8490bbae0b521ff93bbaad8ed3ae62d7246205f92f8a7180492c398f76674a1a77d4278ee4117c11f001b016dc057c4d8665e7b4608e7726c727356e550a6974a4a0fa77b6677a40ff5683712a24d066d00ec14b2f926ea6b0b01b16f21a28b1ac2bdd05f4c162cfc844fb3ee4a4125e84e0876a77ee05457931550cc6c96673cf9d4526959719170815bbacdc97bd629924ea99731238be3f1c27a6594182f1944af3235ca8ab8ca067a1c6628401b4cbd665457e01c5fb6fed88191b24aa65e08c4bad30e5f9a00857aaaa0092302090ce66cbe192089896a524074c4a4e007e8a466a741417d7fb4f6d85e5c656c9540075258e3e1a0b1483ab482ffab6be449be33d0369c637cc526ee663395e092051e278e6aafc2e70f707017e85ab8dfb292a0d20eab39a773a429cce65a7f236612f633d79032c88b58af02651a1a05166032a446457fc2c8a548a60af745b8d762a1118feed7308af00023ea521d4a78a1e4d44cd266aac096985cea3f490989413b3e9cc047cd2c1e7599a5e3e1f081c647185bd819a9d7764a991e0a04db7a375102c5201527c9d23b242ac5ba5022ce8afcc78fd3a722bdc69119158898e405e68724889d6ee56831c1a2f39e507949a998e9fc3f2640c861841ac7e5ab261b563922109416ea2c4e071b4a1e46bc6c87e03b4e58bce9d7cf40a78407e1f44076f8166dc809a933e219e084126c1acf8a8e14e570383a454d0043c46b5e940540ff1f9de329321dcf7b179ca7adbc33693dab233d5d809332a5047839e50b1f20540d3d4a1908dfc64869937ab9b1a4bb21334826b449db24bc0a85a26183e870c39ce0e38372ca844aad89cec673713461468958b7e24fe3873670eea199eac685e9e64732e6070efe23ab892ce3e8b2053f0c1edbe5a46b1d6d839818047b0f2ae2150b909a3e6cc818c9819e3a156980fc2aebb172ac57a8ced24854550532249d827e798a83916bb4271aa841c5e76b49be014651b59f151092193cce682e1ad237818746724819862494633a96217581bba6a8e1652829ff0c128111d5d5d6b5bdc707ccf09e479e561aede184ac631cc7f19de60babf183cd6e6e3d3a80cbb398c58a147767da3d5e500c956083ced0699a850aada1f7b558c86778d9bc21b02ed4f6beee8a6267244a0e87687713161195d94f6dbf55adb0eb95967880a94c358579d945c521f19f361d68818a8cdf01f0a0f4a125e96f3a984d68c8a8a43cc8bd9c64a1231de791e2754928c088a519921888d460d7c5548e7c9f88e360c8da46e7616865897aa4ef14eaceb7753103103c343358398a4530d8fc86a851999f4e49b30e365044eea61a4e47524198d0f9600d8b10155ee33da006469ab55782c46416195f2a153c264ff1d7dbbb72ca01c26d11aeaa642fedadaf88a7478acca65515ab881719c8200d8559e3a63b96155016b625cb61a09209ab89cb08b444da5e6990f466e286047a72b1073c67d9e5cef88ea2e2c3713f6b1c1bababb9a1f2698e814c6ef0f4bd8aa72a7daeb41364c77831f06720b34574dc550b6b44a3550613c51db1823
+InNoiseE = 9869ddb12180ad1e6aa807a9709803867f57b8a8b7c9d6b2e6d3218f739e565a4064af20a14ea9914a36065df3804c15440d150c66588d6f3ed35828220d005ad2d8e630645a79142b3209fcb6431ffaa1ae85b26d4567abb2b44a096fae483f10173203f31ac4ce5faf521674f1065690a40af86e351e15c2f1f3640c3755cc8bf520548b0ad6b345ea8e0103a46b44c6ae053a82f998ff7c05bd09221793f90ed50d2f7328146755ab9e5943ad8b8f5d59723bb9911cd0734b5bb472bd84b43941cc04715aad31caf56c6e984c13783406fde0d5e00f7c4f1613f882a8bc4c4a145997798994920d20a350fc121051811888e25cca1a1439126008c967985956f59a7f039e68a0363b60113a559de89bfb1e68962a3887de89489b7b6d528b057e707a3da71a5aac31c372afecc9ee8bfc927648147d204b2c824fa36861029e41c9646ee30fe93a1357063045f997d09fa28bec090783def063cead9366d44ed6861537c2da5e28cf0a34024e4201738cb751c8595708a918f0aa79d6276e98c1608e89cf4e9b9a829b49c2341cd0cc81686bc551cb17133ae5185baf1abc2b37fc7cd4ab8cf04d562e29a18355521e1f126c75b625123f180791b65d94bc5f4696e5c06d1e7a49be544dd95e16c978c1ccd63f8aad341ba5b940bde106f49efea1a3e9ee481d0a163bc8b844eeb55eb7ceaf3162686aaa68d1c76b5a461adcb26af2ec714302bde25f203a20068dc3efc8158bab930559e56d4b0ed4d2a5fa6d1d677e262b9164ba9859a97e157ddc190a504f123d56907218a641840b6f55c1fa998693c1412671520a22c02690f9790d2d6c6b67f5e984da48b6e1e91e7affe38a765bd079b4d67a10da7a08ada86166a9aea80e43687a96003de5da1d1bb0018b9712f7b8a2c4af88617f449b4e3ae01f9006ded20f1142afe80f5d4c184d5989c6e24efe182c3258717e0204cd2893e043ffd1d34b59a2445fe56334e7fcb7709de3a41180be7026ab7a08b10179841aba0d34abc35ba1c0edd03e4e857c3a33956d77536718f5124c74814d0bb625a45e52b72336118a0fa382b608a6afcf8c12a8f0ea51a49ba947abbe7b0bef4a729d85364341dc20b46c7a4463cf3a22a928316f5137becc5f33ca2adae99664da189817093d3f58e55f18ddea199ece4fe21c0d8fba94ed76773d2ce5816c60b2e6b5815be238f54d838aecb992ece036de9dc5e74d892fad970a04cf5452fcd9a0c879a8254da8eac188e52dd48e2f983aec7284223139a30a7673d43cb9df86632f1b49bb2d462c20170d0957d52ae1d1a9f1261293492cfca0b671251e1f7977c72c8304090c70a61d7b01e493caa8e112ee4132a26cfa64c8aafe82061916640a69024c8d0da42b5627f8219dc74c537545e9747a9dac3ba160067aec5e45338a2a3e2e8b70d34d9fc596b3183dca4f0ca786d1483e9e17c59ff7f6d42ce8a94c45178d120ba8f5aa458e875b4434f0755985931d5835084478a8a8ed0184b14c5416ee76edb8a6b1822972092118f8c4c371c7ae318023dc536246315ce18d5abf3eb09eed7187bef9396683baf8238a22d1173dc7f2dc7fcd8cab87749c0dbcd1996415ce4fb055b9422c7c75d91aec088865935d235b2b679965839c24d85930499d274175e7954ccb6a69056714088798a2fbe6968ac3676433f50daef6d3ad46f3395d5d89ae455f0d36445b9577f40a9f26417812fdb80d10e1868b4d82225eaefb00081e3bd7d93019af46741b48c6ea1fa056fea89f43339580bc231db095a690e6c3823822d902df97603bcbec34da2e26b2426141ce1db0b9b8b4144a9244151d154683e9f222750e8049537c5262806ba0c41ac836bc13ed6b9c5350da5645994683a81a2fa4a18058d8a60a088cc8134b2809350650945d6478ad641a74db4e7f35ee0b0babd4247dd2b54a24d14a192cadcb898aa9d15672f9815609d2edb5f569b6295e6f17c0dd78bfe54538b7914a5f97ea71da80629311093499c4598d086d4c3471ae0f7d25bbd124653aea4c9be210a025db8d3753691502b0ea01e438b6aa041fc58a0c3c9d5c9819724e61b90cc74804be187555a59ae633e297ff507e96f96ca4c0421cc6b27454ad22859729dc7f4dc47fd132aa75b0d614a0defa674952c0308ad6b3479807f5afbc39ed451ee2b611441d4565a2b25cadd64e950552464775891e57db8842f9cc4a83b8bd526363bbb040fc61291886dbe8892fa71b20c6a29a1b159d8e695836ac8c97eed70448210e253b5a30651929108c1670f4afa10eb8d6252692566ea48a6e01b298c322d824cc59591d4813328da15447e90af6706a5ac42aead65931041c88b984697f11810d0365d0a6e42b2a617a0485b68da600811abe1d806fd975d13e5fe53a28f51250ba0606e3ab3dbdf49894c9c0310957d2819b08864a566866503a8db04f597be9cd721256c74ab736f0868548ad967eee6b0c4c19e03f66bf02d561071b399c54ec5e454664a5d12b2032121aab
+OutPK = a92b2c41ce706bed456c92ba9c5532a9e3d4105a9c5053f9db372c429e2a4f37f86976be5123d98b2803e72f8e68e0cc468ceb4ed04c500a900a8017417ea51ac97cf2bc1d4b11dad2836c7c0f2f12f0c2b3ee6b78e12a313c406f90420b32ffb4ac56cf9c4b83ec5848a3d289dc783983a0165585d20141cf3ab2ca4193c4c903c033ce7feac95ad34ec9815a4b0662087c97b78305e551ea630195d74b2bcd824908b843de40415c9d580445f009c98b6cb56f091343a7a18b2fccafb362730bf1f139a94012c7216c825899655b4002654e53444be1426d5f85e4410ee16861cc9787a34d3667c19af379aa657683af1691d22dd42e2bfb377e88992c68f1ed90bcbae6a0fbb8c13b9ce2e9830d414b67537ed737b6af1844a7d93d71000402935082f00d168dbd9966be19e128d01b61fe3e86c5d05b7b09a3de0afd7017942df88da1d551213eb36ec1fb97447789691d904786321300049bac6867d6db9dca622aaf1722f706b822a0470b1769efe5bb7ca755b1c29678bf5492d7556029ff5814e8bb10b56ace0fb075c47f1a600a40b8b1bb4d6c822048e50faa67fa152df251039e2f72da3180546ed88420889d63a58e498a18a79f9ae325f4da9f4a195c61e67c671c54b4121eaa3e75428c32c19980e329b978e32981c9a36413612cb50e0c95b31ba10826108db55c1d975f4269d41eb534e80af196630146192150a3af2d627ed05310a4b110bbc230c26735bb14e7328423ce25158813fbe84d82510a5ab536182e1b4d46d361613920e96397b5425a7de3342b80b0b34ece5aa63f79370d29f54203559ecacf3aa27f40a33a1b26462bfc3e621b7e903b3e04b1648fd6ae5881061e02c0ab610930e7b2a89ef22e3042b6168a647d17c49ef4eada957c136886fe4d1319611a7131eaadbfe6b420b999128f8998464e5627a0a288c3f3e5457ceedf656904984c2f043104b9822ee939884fa043da0e4aa8d81596151143993208a47318cae39eba6bffe5a5940536052e497695258820a2dd836a680d4ad550e49bea5c229a2736080a0ca2ad69bc94e66c016c51b1b906bea269c8b9523c94154992d6698894a06143dbb5adfc9c881010b7490178a3854e09aec566d35a71d4f920740149fc36564a028dc4d2985f2648a2990b699f0505ebd0a13a7c696960e542842db439cbe1ca2409dc605def6d7064f563f8e51f4270911ff8806273d9b2ac3549b0c638f99e60c5c9d8a20d294796bb147e6695af4be69331597ad656c0f4474e7a73cac1808e3c0552148474eb250e4cd87a463716173366ede98e0e6ff7514cd6cad20ad72b7cf9507d16f828d505da4103f3c8a94555e8bf18df0a3423ea1cd945f29373144b062f489877d84c0d9f0335bb1c8bdfc5f008c4366d8a2cc1a860715c4113fe48ca9e368d952a4496ada4096cf7b621793bfc2ea4a728b256ad0f2386cb5a0598250c510b8551ae5fe4cb9ed868a7eb3d4bd4cd686e64072a5852743816bd12eba1b7bca8fdd90b0633f302fc534cba923b469e854179a852835cf771cd3d23a5a0d5b4904e46ccbc1b39f6728da4f702a21638d46d566532395366211c3a88bc226acb54e1b8a936fd67cd94e6e620f5ec0e2abd2803a25f72106b7d84dfec6077a012371f0d66a2ec6e92e08f8407b41c95e3a7736887ee32e3ca43e4c9ee1f1c59b433768c01e58f22242ed5455ac8fb6522d262c9d26817f10a59b3cf32d2e57a539181d41778e41cd445a811876249448059536965b39c953dc737d632fe18a1cb2d2641e3dd0b67c6e40ec002f005dcfc0634698a818cb2bcfca6f2058b0a1c37e5083231fa9d3ce886306bfd9e2fe01a919c69ad0fa134b399ca86b4a93772d30e305d426819d7f984e10b05aa3fa49612c65e89f0310d4d60c5f67b8672ffea26c76f8a31639bf56360396e11c17cd192a01d7e579ef0b9b2797780efe1487e7bb66bd1f60bce073b212bb1429a246761366900eab92662ddc2fd99786a45a688db084118deca7586663dc50c45d8f6e88560a01cb5e55c289d5e3095d71a75b74975a3757d680092e29abcbc3d19454ff07bd5b92221b087c4154f21c39280acb0a5268345b541fda116ad9f87ecee717089802aa2ae6f0ae4371a08ae7bc2cf30309ece8659ee2c59fea41411c34c19340d05a89711d2776648a695e8e63ae20bb7e3eb4bf2047a3d8528f9baa9050b818ca2fdca1b5c035a87f7be3e34d5aec5d8363f0554fbe8aaad471c441a5346a5879bd968b6fbcd7cec07a133a00cbe5be0de2a213cb785a5f728ceeaa4b85d6fa8421c7539a0ec430a9982bc9554808162282543d2b6a5c9374c41b086eff61a5a05843a5cc884888b6475b0729193d5be8679f60ac14468381f5e41a7917d2628b0740f9934815b58195f6ff3c0bfd0013324b18a98796ada66d2457af69c51669fd9fa5e3d2180b0c915b4bedbd18de7fe04aac453d1e875ad83d325ed74a578857bd60758885193814092abf83088ea5ccc83753532
+
+InPK = a92b2c41ce706bed456c92ba9c5532a9e3d4105a9c5053f9db372c429e2a4f37f86976be5123d98b2803e72f8e68e0cc468ceb4ed04c500a900a8017417ea51ac97cf2bc1d4b11dad2836c7c0f2f12f0c2b3ee6b78e12a313c406f90420b32ffb4ac56cf9c4b83ec5848a3d289dc783983a0165585d20141cf3ab2ca4193c4c903c033ce7feac95ad34ec9815a4b0662087c97b78305e551ea630195d74b2bcd824908b843de40415c9d580445f009c98b6cb56f091343a7a18b2fccafb362730bf1f139a94012c7216c825899655b4002654e53444be1426d5f85e4410ee16861cc9787a34d3667c19af379aa657683af1691d22dd42e2bfb377e88992c68f1ed90bcbae6a0fbb8c13b9ce2e9830d414b67537ed737b6af1844a7d93d71000402935082f00d168dbd9966be19e128d01b61fe3e86c5d05b7b09a3de0afd7017942df88da1d551213eb36ec1fb97447789691d904786321300049bac6867d6db9dca622aaf1722f706b822a0470b1769efe5bb7ca755b1c29678bf5492d7556029ff5814e8bb10b56ace0fb075c47f1a600a40b8b1bb4d6c822048e50faa67fa152df251039e2f72da3180546ed88420889d63a58e498a18a79f9ae325f4da9f4a195c61e67c671c54b4121eaa3e75428c32c19980e329b978e32981c9a36413612cb50e0c95b31ba10826108db55c1d975f4269d41eb534e80af196630146192150a3af2d627ed05310a4b110bbc230c26735bb14e7328423ce25158813fbe84d82510a5ab536182e1b4d46d361613920e96397b5425a7de3342b80b0b34ece5aa63f79370d29f54203559ecacf3aa27f40a33a1b26462bfc3e621b7e903b3e04b1648fd6ae5881061e02c0ab610930e7b2a89ef22e3042b6168a647d17c49ef4eada957c136886fe4d1319611a7131eaadbfe6b420b999128f8998464e5627a0a288c3f3e5457ceedf656904984c2f043104b9822ee939884fa043da0e4aa8d81596151143993208a47318cae39eba6bffe5a5940536052e497695258820a2dd836a680d4ad550e49bea5c229a2736080a0ca2ad69bc94e66c016c51b1b906bea269c8b9523c94154992d6698894a06143dbb5adfc9c881010b7490178a3854e09aec566d35a71d4f920740149fc36564a028dc4d2985f2648a2990b699f0505ebd0a13a7c696960e542842db439cbe1ca2409dc605def6d7064f563f8e51f4270911ff8806273d9b2ac3549b0c638f99e60c5c9d8a20d294796bb147e6695af4be69331597ad656c0f4474e7a73cac1808e3c0552148474eb250e4cd87a463716173366ede98e0e6ff7514cd6cad20ad72b7cf9507d16f828d505da4103f3c8a94555e8bf18df0a3423ea1cd945f29373144b062f489877d84c0d9f0335bb1c8bdfc5f008c4366d8a2cc1a860715c4113fe48ca9e368d952a4496ada4096cf7b621793bfc2ea4a728b256ad0f2386cb5a0598250c510b8551ae5fe4cb9ed868a7eb3d4bd4cd686e64072a5852743816bd12eba1b7bca8fdd90b0633f302fc534cba923b469e854179a852835cf771cd3d23a5a0d5b4904e46ccbc1b39f6728da4f702a21638d46d566532395366211c3a88bc226acb54e1b8a936fd67cd94e6e620f5ec0e2abd2803a25f72106b7d84dfec6077a012371f0d66a2ec6e92e08f8407b41c95e3a7736887ee32e3ca43e4c9ee1f1c59b433768c01e58f22242ed5455ac8fb6522d262c9d26817f10a59b3cf32d2e57a539181d41778e41cd445a811876249448059536965b39c953dc737d632fe18a1cb2d2641e3dd0b67c6e40ec002f005dcfc0634698a818cb2bcfca6f2058b0a1c37e5083231fa9d3ce886306bfd9e2fe01a919c69ad0fa134b399ca86b4a93772d30e305d426819d7f984e10b05aa3fa49612c65e89f0310d4d60c5f67b8672ffea26c76f8a31639bf56360396e11c17cd192a01d7e579ef0b9b2797780efe1487e7bb66bd1f60bce073b212bb1429a246761366900eab92662ddc2fd99786a45a688db084118deca7586663dc50c45d8f6e88560a01cb5e55c289d5e3095d71a75b74975a3757d680092e29abcbc3d19454ff07bd5b92221b087c4154f21c39280acb0a5268345b541fda116ad9f87ecee717089802aa2ae6f0ae4371a08ae7bc2cf30309ece8659ee2c59fea41411c34c19340d05a89711d2776648a695e8e63ae20bb7e3eb4bf2047a3d8528f9baa9050b818ca2fdca1b5c035a87f7be3e34d5aec5d8363f0554fbe8aaad471c441a5346a5879bd968b6fbcd7cec07a133a00cbe5be0de2a213cb785a5f728ceeaa4b85d6fa8421c7539a0ec430a9982bc9554808162282543d2b6a5c9374c41b086eff61a5a05843a5cc884888b6475b0729193d5be8679f60ac14468381f5e41a7917d2628b0740f9934815b58195f6ff3c0bfd0013324b18a98796ada66d2457af69c51669fd9fa5e3d2180b0c915b4bedbd18de7fe04aac453d1e875ad83d325ed74a578857bd60758885193814092abf83088ea5ccc83753532
+InA = 8f0774d3d5350bd8a9f1567b6408f044018892f845055d532885b240f813fb98007cb69e66716bc3e67f69d0331b21c613b367dc3263689b62a8b38b02846af41cfd8633f41b89aa2a7353c587b0d6b38a4ae8ad3ae2b279f340239296bea34b2d6a7048e1e38f98a6f3087ec2e24d9ff11a21e1f7b0a01e00e6b1a85c8e9c9fd986177153f76cc8b317ad0df744cf8498f01b7284038a91106217e511a0cf20ae7c5203e0ef196a552877585ad94c3b277e8b5edda7e2cef1455a066e09ef92365f19bc4c86044486498f6f4783667f72431283644708d03b534dda98dcae6692404fd49cad29b10115790de190f7eba815d93265deaf17f7d14651f8962e28394cae148d86181f7a81a941d1161198ab9d0fafa01525a043d6def1321ead41007fcafbd1243a1629d31e06776b1ba67a1bce2cd4cab1062b59877fda306afa8103adc95780c7f44b1b25ad03ff3d669d2ed2c42b46bb739ec5920d3e1f046469bb5c8d178c6caa73b82857ea5934fb87fa994c08a88291c2937129ade1e8f1a40899df5952ccbb2506b9e57796a26e999e2bdc966ec2aa89c4b8a85c15493a198472a857602f16cca5189ba0a303c6163a63d0ab94634512172c8d56a7917644937ab8a3a889f113a9738beea09f8fb2581e410689a626d38b2a3747d6873748b0a21040c8942e0e289f42dd812c298a23a5e7724f3d33cd619b82830e411d10fb9474dd95162587ab0d81a28d698e1a79e50b624f236b713a703bc46ff245387dab0a6bb77b0e0e885e7851aeac70e84b6d4a2e24bb6f9900d803d513b3ece9da5d495a18d09f61c3b50876967213f2cd4784044e5bdb3544fbacb01242e86ab0e4407bedd1ba31de696414b8a03ed07e227282602f9175a73e3698d696b54616b46b7caa419b870487f2e0869bb311398fd548897aba4a8a04f8d9b0152fb414c9a8333a48e1d7a19a7c864df42dde180148441b274beaeab25cd7cdaa5b3e369e0c16d1c8f20e2b8e5b1e70298e98651dd965a7a858a31f87c4da9a6c1dc20e025ad45f1f6b6372d99866b700d047d845b4b40563d039463bdd4e3e3150ec4aee0646b635aebf064ca1e9e7ee2483e46028a316989adf7483c160a08dafbec43aec6da81950a057fa20eb8046b9cccdb42c5e1938e7766c6f653948d1102cb399c5856fd91e1f667b27d205378f1aac0b83356cef589e5282300d8becfb547c7c297eef0c97335e1bc08869b78d46b6771f060b99b837dbee82b874a871cfe16937a4f07ed01815d7aec5664e13e755bd2c3df61f43f7c2b26e6610f5a9cc44a84c21015a06b0a72c22c68aa4493ea6d62f6258ad8e5ae1effa32c87f49431fd34c76995f85bccaa0e091231bafe3b614a0a881b1cb959427dbd3afcb9e4294c6e35d67902a908c1c42936aa258ca60953a9f4928f0820ac9f00d3c72c07bc11fc0954c0a72abbc3962a287da87ac994c1acd4aba4eda401c232c3a125814af2e2e031699bc0d600fa651f2484b1d7db75e22089492c574536e774a15c8fbb8d59164cda93b4066448da3fabbb7f52b8d6536cbea0474d6987f05c9e0296420c50a33ea26be27dc309b9201d5aa518586f58139136d7084585a3b2dc3c6b9895a0f5e468a4b00b0b52d98c147c641a2a334e45abc4289cc7f572324ba5be314e3e655b7a6ce63eb6ae217fb6f67cbb308350e91c48a88eaac9b24e8b35c05566d0e4cb2c1965b6a14593a1d245295aef119a57549fc4e86086badb512ac46d93ad0984fab1e8112a44a60a16e7051e00c630bf79042ad61a45147b1d7a531aa1c9c9bcd708ca12273daae848d990ecc46c228ad162e69302db52a008aea235a10af962b6b052c683b0d814fc74d4807076b8a0050789fa7569604a34d20b4e67aa5e6956e018b6e8696fe28f9649aa210a54d9c8c7c92b2be83a514a1a6f4e03bc721b180099cb14ccd66e812f97205a7310867f5348e6841309e8a7c2bc8fda8e336589d900813046aa96513a35086bd04745b4390ec2c7d981cf752df3578954751dab2265820ece73745903bb681cbe68f52a9f4cc7c30fab8380094df1296a1af88296b55b2a9b90dcf6a0aa8525f005706908b119727004570fb446a5a634ee44cd28e39cb53f707c9992baa7437f74d938e280a0bf29a40080da8d4798141d6a79c33d83e9b6fa01286d3920a1f5450b1c46241370533b1c5701e4f6a4ad720b208d7e567badc725c1e1364b597f03c4ddc1cc33d251d99a69ed744e04e5a2b8471c0a892fb67e0f75368b35f59c6aa6e9557f15b2cf78120a8575ffde653c968949254113406b4e5220833200c81c3013cea24983bffdeecb0f294041de340975c329b3dc9bda0a9562e436efc1b6c707c522a23c99e602bf103a01b5ea4029319f696a2919ab5c2c29a5caa59750c8ff424288e160cc02286f2ab68ac098ae8e59225c8b3579880486cab2da4e87d10348f5d97bdf20b791b60fb43d06bd88c3153c17819feef2c19df7d2cfbd4b74adcc61c370a16fa1521a2
+InNoiseSP = 564eafbabe3db55e87c419bd7551d0e63228ed9c3ae2a107b7c02614350eec332d19a20f061f879a646868168999eaa9124a058f1565f8710c9669248a15abbe8b7631eb697ceb9b28a4d4359de12975c82ce417e389bde1f08a7960539e066d0288f8989d420a72040e44774bd64a3f0d0ed777e265520c1f75931eee184991718af619b1b62935f43e149068133458595e87e0a273a171714a1b9e8672fa8245fe8b430aa068ac93ef4da0ee1d88b65b3823f7721fca56771082dd0be86f4e811b7c4a838af224aae56534cacc3446d94450002f48ad0e7c4c543810677a937962ab26a2280f14548f7aff393e1a42ae25b56c941a4e7cb600d14dffe440a1d2de6a9027ed965521abd5dc26e25d729dc598e3c14924a29a2335e9fb3533de04e738edbd67096e0198635d3ca644a828753551c622a56acbe272ba06f4b409d922b68782d681d0501cca4f11351674cd6f2a2b06b116c994a123acc65e92e8c96406d8255f538768cb4e74af43a6e8db3da540da3c865a8d2c14e86852c3c97c26af554a609e184b1fe391861c73e4eda477288c1ca94043058236a668989d28a37629f18ce3754c5100a2ce34048e989e950ca7ab50c18e648a45d0e17a9ab49bd087fc46a8b597d32567a5013c58f4d826589c81c5464b29011e508e8b3ab65658105a5c8931deb5dea38e0190b62b6325523a59f287786fc6560421af46098cd686285103499b050729079154fc3a3250a6e859a1b3da1e291d86fc21db496df445d601c3b44a3346a1413280857ca1ff37d75aac5ddb99030a0998de562b4b580905b691f6f481d004be0bd233d28d36f253d1f65b05ab0e631fb661cd5fcc080e76a65160c02ddab089b6400dad4b2a8dc8763c15b9da59c640aaa31f168c657892e2e4816c5c51e80efef47f020991836306c40666e456c441548923ea44097171a19e199f8d2274887c4b54e4808ec5059ed786668245045e2baa1323927b5e5f8749fc668459a4daca0d8e81245b1eea482161dd8f488a6c8d71b88c2d23c0583fee4b3be04cfa344ced844afb41551013c73434fa739d4f0a82249080ed522f51663509585b6174905d5f0042635bfdc912a2e8f5a6efe99d67ea4d965c245507a560b3f2669817d4ee17019aa3d8171bd6304998531b0d706c1537cc2c1e0800e2b3558a12c7d019997a551009d3b12ac9dfb4b61aa2453609d787fb1a7f499db013b44089bbca19885602ae5c5d534aa1013e776b3bd76e9091fdcdc68bf15c65aaa284090dcd7ae9a634c8638a4d64688e7b144c2570858c17039d3b2d82c21714349bed5f1a5701511e79cae191e195a7b09a32999d2cb4291c4eda7c1da06d0985dad801a779613458756ebeaaa695f143d1a50da35e6b69b48f466cb479eed6154ca6ab5a5288932a8f8f62ab46238b97e0a14af0232144a0fa09444ac98b9700ebc819e86fc8b0092cbe5f2b0f60d6a1288f431d606c698d65c465850375634e01b6ab7b369d122874528136bdf8825ce63a621f056c12f00904a108400281da06824b8c5c1468f4931a2988db371620c7ebec23790c997beff4c90fa128459c8da6ddc802b10529b6305c6c41ca245abc65266e44bc4952e2adc7caa35607b20a8f1d0ee26c607e48224f22e36d68af19054115fc23741a28257a5d1ca88450867d7e752aea190357697beb4bcbc3f736924659d680681dac4d08cb8478ba76bdceb99a4864ad8b488b87d238bfd02c529834be431642371199d479b0c5f83b4f8196cc109d3a7292315fdb896992b52287cb61b8d28cadf1a44e58d0d25d5b0f084a3052408b4adf15acf6aacd47021431d9450c1dde8b98820fdd2c78aac94d0e36ef16328c3470552fa558c02693fe1c3135c4457e252f412a18ba0aec007a772de963762ce3a01b04bcf52e85b9cf121931c945ea126910001909f5e5e6b5c61aa7f456c5338a5999a33f216b1dafe3f0b74a74936274d9318906b56d609b295d0a73a10b05642272d954bc3370bd5a85a63962b551039bcbb4866b8e77e1dbb7fa336937eb9e18f6d5c0a5a927aea4b0bc7e736425e79772a0b90292f7495e48986f0543f5b97bca41b405a3fa47c8de3f2791adad8bdb4958da55351e91e6c4703d8d4f843435a9ce2d3dcb47278cc8826e567a314f94ab0a836570e7697d822857aa584818c822b858955e8aa0a51ae64a56865dce4e65032e76e0c0f610836d7d1752169bd59eba0fd206850ead8cb71afb671ad4304991b0f3e0640432e6b1251bfa9f5a9f71672d21790093d902c39d33081405d2156e1fd78ca1b3765c67d0c0e4aad4ad6731e7b6c79ea0456e6e7c1b8709405a21f0c9b5f83c405bf3d1631e8c6093b7591e71244b9212d980c4084ba73a4451f43089c8de175f344fb6691a06f54a5d2cc0b4d43321b0728b8e98bc5d0470104f06ad4819e1492fc922c42575a99ae6588e89c1ef6825e4582f4b6e548b1532236e10345b63e4743c078ec1c5ce0aca1125cb42841968654d9277c521
+InNoiseEP = 1a6aea83846d6c49937f55ec5c228e41f5b31ca182ebc7bc872b9a54c55b088a774624ffe5c5e8b635272509daa7d4a43d628154aa1d9c9c62e7b2d5762d1436ac97db5cbe8afc18bc34c77d0713e553420c5c9811cdfe131c66b9e0867b39ea85a45b420b533342a13d4b94d11aaa02c9596067e1b25ae6193224e5be04538787423996bc6b55c1232da1724ad9b8d6056070e64bf1fac64db54e0ba7532fcd62cd82260afba990b6a7b40b03949fc7ea51082b8143c9e1800893b24544419ca26b228c64690b3550a825e196d2b348fe6ed39c814a9bd67c51519b7905b28f69915d292ff8ae3aca98e85c466cac501e3b9be5478a6c29e3d7bcbb7626cce6e7b29296c727255d90389a9f8e86995142cf54104a60d1a7f46cddb342366e06ac308afbb9ab76a8ff2624725a9bac3f7808217547093a73f8e698834338330bbe06b3cc60621795573167e6eb0f7d38e85b34c73d96900c85b414598463051397b506c8afa6ac54126906265bedaf23654dbca883935b0ae2093b2a4579e01917ca8ddac676622a04a3a605ceadb8f8dad6c7c33e6c9ec5fa89f1ae7279056c4504a039cba1b1e1efc5506951be573131054a688fd80ab0186bdd53c98d2116d61a9eb85b6a03c34ca4265196762f0f82cb7504896aedfd4191b96ac2a8f372a14d820d644773c204799417f67b34f522a6228f562ab84262564e12c2f875d464d05757ba3058416d21bf06627e59b3e5339e45fb436ee8131e82314ef3e2ec7920dbca8ba0a8d249ea5ad75443792ea550f63068c551db89d007306155ea80b016e8342b23660e950671107fd2faa8fc91631eea713a79a017895a70161be146d2d0517091bc3c208a06eb4131b44eabfcb3cd2a1df482d2a81c896551815b11f38d420edbebf5335c25aadd4e552fed05135b2fb24c4655b76006d097dca7e545385a9f9096b64802b1ccb9ae515c7f7bc6385327d5fdd8a29595c1855387c60d1a920888656551b52e91a335ecfc5c3d4dda55270a0ead48aaf03092514f8ba4f6eae5a4c1d38287d235a9cb50cb24518a972eda50d29fb085b98c25f950741bdc6a3853792d27a5a389a1bdc65772832a43eddd12524db26136eaaf5c9aceb1a25993cd42e249e7644979c0afab6a08047766b365bc3e5af585d7241b125ca81aefd9c1ec9ead98b9f0634825590eb132fd10af74d386e227000006efc1b8951862d242205005f5513cca5e17acb614e282bde9ee4c0fd66c76a8cb0d68a9cb49276c9459696c1c230fc2b5d7dafa9789298981df45b1712675d46967f6f4c01fdec92702d3886ff58027fa6d32a99bdf1c86dbdc17945fac98e3c19beac43c959c3967295c0a7a0df1b2362be267623434749ba64abea437529876b7a27073fa1478a037d46b4c0f0b082bd4b1650099550d494d88658199bfe969acd45122731d9f2d4500372ae9183fa547ab22ba4cfdd70634d729a868d78642826e04551936c092c4194ad59ca070ca195c662dd7bef43141e09659dc7db71e1d15f07176b3a258efd331199ee0693645e4348785c55908df197aed0fa0a2d8b0aab351505afada2fc456fa7aa4c23ba6805e7439707ac0167bf61c017fc88866d334bc4259c014daddd423a6004291a9883d889187da474dad31552d1a82412fc2dac9246932b5cd4f2724db50aabbe5146388985156248e92ab9e9807d796d590b40ac3543035191c4597001fa8c1fa1d66a8be47126998b611bcf8298a9fa686c9e42b4407ff20002a97306ebf90f14bd294867425d5dae0c15cc1af3b86cc3057c4a2665634525a1b1753b2ebeda0f9d95052d02d05818d44f3fcf98508e9c40afa08ab4e453cb34991431219f6c2e8b5161acae5fa88497f414701b9f8f06ab8cb49db7a628c47af05215ef6b349cede61083ab3ec22184987547a085cc620cf086dc2dd3f2bf2484fd09af93ae304b06c274e839dbc5e23cb6fc23cdc4a021e9ba355006202e890e0d4d03b15e501969168a0e04fc87526bda835ac44f04875434fe8bf9b63c52fc15406d0a6fe466cc6fd640b5241bd91f8eba1c401745ff085393e09e4ae80b4e95d33f2ea5832bac91f135757b28e0267f283b71430216ca52c2793d776bfa89e5ad10cd808fb898bd90f6e21d36680e74b7a747653301a5f14a24b17abb120b28a9f5331c96c1e89bc1192e9786c336a2fe654be988080d4a1e5800053066bf260fa391a1def437243ce6be582bf022724152f41f5fa2eb53352a07aea4f8c4cb7a0d9f3d3215d6aa3b971ce846228d757f3933a30cae35f88c125a80bedea69b44940a59d1080c78285b1d73954a3b63403d4ad7649bcbbed067622eeaaac36937570fe15021cd893d7d6929adcc5f378bca09daf2b2de09d4a1f9f83d556f9122895628bc41d25bef96e7835ad21bbdae1bfc775821dc66e0494f6f86c5b6bd2317ec53533c238a4fcb81d33875530128192eb8fd63755d92b74d03da66ded0dd5c617050e2554fce83f3a492954ad2125837dba00
+InNoiseEPP = 000000ecff0600fd6fffcbff02c00300ff5b00f8bf058000000000c00180000000f8bf04c000e0fff6bf018000400000c0fcefff4b00040005c0ff0b000300010001400004000180001000080004800020000000fe6f00400000000100005c00f8bf0300fffbff0200fe6f0020000000feaf003000f4bf0000006000f8bf00000030000000ff6fff0b00ffbffe2f00500014000070ff1b00080001000010000000fe2f001c00f0bffdefff0b000000fc2f00e0ff0e00010001f0ff0e000070003000f8bffbefff0b000f00fe6f003000140003c0fe2b00f0bf0080fe1b00100002c0ff1b001400010000fcff0200ff6fff4b00fcbf00b0000000f4bf02400010000800feaf00100000c000f000300000000040000000f7bf014000f0fffebf0600010000f8bf007000e0ff02c0fdafff3b00f8bffe2f000000f8bf0100000c00f4bf05400170ff0a00010000f0ff0200003000000000c0ff2f003c00f4bf028001100000c00300001c000800064000f0ff0a0000f0ff2b00f8bfffef006000000001c0ff0b00f3bf034000f0fff6bf0000ff0b000f0002000000000b000200002c0000c000c0fe0b00000000b0000000fcbf0240ffebff0e0003c0ff3b0004000000000c000000fd6f003000fcbf03400020001000ffeffffbff0a00ff6f00f0ffeebf048000000000c003c0ff2b0000c00440004000fcbf0000002c0000000400002000040000300110000000ff6f000000ffbf0340001000fcbffb2f002c000000fe2f002c000400010000e0fff6bf03c0003000fcbf010000e0ffeebf030001000000000070ffebff0e0001000000000300007000e0ffeebf04c0ff1b000c0004400030000400ffefff0b000c00fd6f0000000c00038000200010000040ff0b00f0bffe6f00400010000100001c00f8bf0680ff0b000c0000400000000c00fe6f001000080003c0fe0b00ecbf01c0001000f8bffc2f00fcff02c000c0ffebff02c00240ff3b001000ff2f0020000400fb2f01d0ffeebf024000300000c001c0ff0b0003c00480fffbff020005c001400000c00600000c0003c0ffef0030000000fc6f00d0ff06000200000c0008000000001c00f8bf050000000003c0ff2f01e0fffabf0140ff1b0000c003c000000003c0f8afff3b000c0003c0002000000002c00030001000fe2f00500000c007c0001000040001c0ff1b000400ff2f000000f8bf0440fffbfffabf030001b0fffebffeef005000f0bf00f0ff1b00ecbf010000d0fff6bf000000ecff0a0000f0ff1b00f8bf0030000c00080005c000200000c0060001d0fffebf05c0ff0b000700fd2fff0b000b000440ff5b001800008000e0fffabf0280004000fcbf000000200000c00380fe2b0004000040fffbff0e00003001500004000340003000140002c0ff0b00ffbf01c0ff2b000400030000fcff020000c0fffbff02c0fd2f00ccff0200044000e0ff0a000180ff1b00000000400030001400fe2f01e0ff0e00fd2fff5b0004000180002000f4bf0380000000f4bf028000c0ff0a00008001f0ff0600fd2f002000000000c0ff0b0000c0003000fcff0600ff2f000c00080003400000000c000030fe0b001700ffafff0b0010000680003000040000b00000000300004001e0ff0a00fd6fff0b00ffbf00300020001800ffafff2b00f8bf0440011000080000b0003000f8bf00f000200008000300ff0b0003c001800010000c00004000400000c0f92f000c00fcbf0200000c00f8bf05c0ffcbff0a00fdef0010001000024000000003c0010000dcfffabf00c0ff0b0008000280004000f8bf068000e0ff0600fa2fff3b00fcbffe6f01f0ffeebf0070ff2b0000c00100001000fcbf014000f0fffebf0180ff5b000800018000f0fffabffc2f00200000c001c0001000fcbf0340ff3b00fcbfffaf00000003c0fe2f0050000400feafff1b00f8bf01800140000c00ffefff2b0000c000f0ffdbfffabf0280ff0b00140002c0fffbfff6bf05c0feebff0a000340ff1b0000c0ffefffbbff0200ff2f000c00fbbf008000100000c002c0ff0b0000c00200001000f8bffc2f000c000b00ff2f011000f0bf03c000300000c0fe6f00e0fffabf02400160000c00010000dcff12000240ff1b0000c0ffeffe9b00f0bff7ef00000000000500003000f8bf0040ff3b000400fc6f01e0ff020002c00000000000fdeffffbfff6bf02000100000b00024000100024000070001000fcbf003000400000c000b0000000f8bfff2f0000001000fe2f000000fbbff9efff0b0000c0ff2f00d0fff2bf007000f0ff12000040001000f8bf003000e0ff0600feefff4b00f0bf000001f0ff02c0ff6fffebff0e00fe2f000000080000b00000000000fe2f01c0ff0600038000f0ff0a00014000f0ff06000140ff0b0003c00200008000f4bf030000c0ff060002c0ff2b00f4bf0070ffbbfff6bf0100013000080002c000200004000300003c0000c0fe6f011000080000c000e0fffabfff2f003c00fcbf
+InRand = 3bec20e879d06c7784727a350e65ef4a75df10bcda185de1bb4e4a6cf8d8e450
+OutPK = e2637b35a9e43a2dd25333c125bb90407b7b1c0c62da269efb13008434d8a8f00d212f6198d693bff0062482ba182ce12bc16520d6928e1d6ea54a870adc94396e2f633e6a9b024f8878c64c8af86164fb6561717369db940d96a1844a7511105271785a8600fd062ec6e3f853a584ae238e7fc948ec1e3b2d1e24a679696eeb4a6a8e3d00f3d4428043b81ca050d3fbd03c036b400cfac70ab179eff09232c40f374991bb53d53f8c930e241105b31391fa9a65142d0c95b88b06c12c7c195296f91246e7687034eca81ad9ad1d47fb6c006e2245ebe8a179531c446976fe39028a06a1cb6451590f9c883095b9d543e36b26bd1506dff3f7eb9d79cad5fc62b106820f544e3688f09a386c07f0a7428892a80620f3da33bf19e595ac79af5d58cbb8b404862fd7b020e564a2551df746dc880b67098df5732eaf139adf20f41c8323c71e378f2e35544c12f446dd8536445a993f25a028cc3e106c9410dd5ed2f5f1acb1a02683e21c357153c056d2e755731f1aaab7dddd9a75cc159377ceb26404efd25e6676d946ec388a4268a1ee7317f209861adf78126f025363c9254839f465fa5e9e37098ebd8f633a16ee310a9d512fa922dd25dce24b364ba24388a72d40eaf13e4f535b61d8007690483f6aef6d4d07819744091a0e4d4a3ac85ae52f04d530a937aa0c81a858d7b38e7a4281ab20f186890d00bf89590650a5de283a37fcb2a4cf85c658f4148e24b478d8225f7e0ee0135c9e39568a6f8292261d856b9c3878741a11ac9d39aba68cb714292943e44aaf5d9d00385425d9407f99ba6a7743e9abd162c80895ea9585b611a205c90b9ad2188e6a903f5554c4a35996ce266a49371a4fecc68660be35055fe1f8797a5f6dce81e22414764c44d3ad981b2eaf415169855ebf5a4de3904227789c58d2a34a0c75e50668dfce4e4f2083a693a607370d5e9a79f943609f94663cf47c94e14d5708346b33ab51c2ba91729206356491cd57afd8ceaa0d3634d3257de0367110f05ab7253c2130989b9575887e0cdc6459f9e3b61d72416242e6c2b02a8ffdb98e5c5f6da8e7a13a7c7276ce47e4bfba1cdb000ce070a4bbed57413ac7e5110682fe86b0c5791644be6a7ce6169bca6bfab174b734918da1ce4a399a903be6e2621ca4a86f90e0bc4750819be2cc4603c4aef178c3865c9d858ce8a1f0519927fff327b906cc6e39a4238da0c7061fc4c8240388acd039ae8e7d68990fc78f5061362361c1e1869762ce6a066d24aa9ea4f8468112072fd16f8b314c5fb4c032725fb6729812fe9bdae138b81aba3abdee0ff740fd9a5ffa18205b3307e53607e90e7894767249ae95a436ab71798e1b5b48f4117cd56e3cf3a4912dae43bc63598a78f2ece536cbbd4320248df43cb845169a01981ffeaa2e9dfc17a8aea8675cecd86e7e9a544bddf12af07652cc4b2610a81871e66eb18e643fcd267f99b731ac72258ac4aa66a41a1cead40450876115119133449e2e5535b008957b7b6682e8271a9b0a4c0b4849fc1fc023ec529d2d6614d7e860587925b1eadb3311c90aa1597af55af1beb62dc01867dc0b932deac47adc91d7c1d93968041a1edeb5808a8883f65f7d11be85b8435d23482c081a9d6d3c5693a05aa66186620ed46a23118552673c96820a6f3bb3aa9e51c3a07b26d497c458ad219c5588f461311bc8bd702309f9b44aba430d81559989140f95982e871d502e02c3f161399a367910d526a2063b4892b507c0611d2d8d6d901e318e92eda13ac2c14ab3a9b13129552c00d529b975c1a8bfa0b55bc77488424af77b97d63fd78d9998158e51a297d286156a99fb6a5112ade63a1085228ba7de53c7c3f2d2131e510002ef92cf4c5a45570e07abc094ba294661e4cd96fcc3a7a1bfba84d1a2c8d0b2d550e20fd963d3100fa9d890540cacbc2babd97b6fc4052dc4d4abf77e6c5b8860219ed1276c9d5c566704a9d19e6f54d8ea63315079839a042ff0520797ce78b1489982c26aaa463b20a2e9ac710cb08473e79bd134416357e5216e3e644b71d7182a121dc0f1463e22aa44cc4f6246016cd1af2d119e82abbeae7b6b1ec05e38acee10fe422051c448a14a71a23a98594258d9525544fe697d752477c5e01922706d2ad10351262a32cf86859a180d7f2185bfe370663c9fe81b419ce52560d9552b95f013b31068eb447c528a48c53bff387575d61dda46a9499a0b0d59602591c31a7a20add950142d82719b08b460270b207e9a03ed91eee0e5d8203e5fa061f8e624195f05a4e4e9376e9dfba02d14325ec2beea8fc0b21e67e8b671d8a194e1f9367ada2c9a096c9a6d5c83ba9344d2d2026138ac89940a946090171fd868310fc29f2996b1046910a754243db02fbec676109fa25de969f3e626646209d38b9b3bf909c4d249e5ae2dbc119d779270aa3e332fa2124506a80f41c323a7e1a9486a0cf5002d1ae09f798670d981dfa07a313a254222a131163d596443ca8697e4990e0fd3c301e45874
+OutRec = 00c00020000400038000100004000300000000080000c000000008000080002000000001800020000800008000000000000040002000040003c00010000c000280002000000000c000000008000200003000080003000010000000020000000004000240002000040002c00020000c00018000100008000340001000040002400030000000034000000000000200002000080000c000000004000000000000080002000000000400034000300004000240003000000001400000000800030000100000000080003000080003c0003000040000c0002000040001c0001000040003400020000c000140003000040001000010000c000000002000040003c0001000000002c000000004000300001000040000c0003000080000c0000000080001800020000000030000100008000340001000040003c00000000c0003c0002000040000c0000000080003c0000000080002800020000400008000000000000300002000000001c0002000040002c00010000400024000300000000380000000040000c00010000c0002c000000000000300003000000000400020000c000040000000040002000000000c0001800010000c000040002000080003400010000c0002800020000c000080002000000002c00010000400018000300004000280000000080003c0003000040002800010000800030000100004000140003000080001000020000c0003c00020000c00020000000004000280000000040000c0000000000002400020000c0001c00020000c0001c0000000080001400010000c00008000000004000280000000040000000020000c0000c0001000040000000020000c0001c00010000000004000300004000280001000040002c00030000800038000000000000280000000080000000030000c000380002000000003400000000c0000c0002000040003800020000c000080002000080000c0002000040001c00010000c0000c00010000400014000300004000000001000080000400000000c0000c000000008000180001000000002000010000c0001c00020000000018000100004000080003000080003c0001000040001c0003000080003c00010000c000140002000040002c00000000800030000300000000000000000040003c000100004000040001000040003c00020000400028000200004000300002000000002c000100004000100003000080001000010000000024000300004000040001000000000c000100000000300001000080001400020000c0002800030000c0002c000000008000180000000080002c00010000c000380003000080003800020000c000040002000000000400030000c000240001000000003800000000c000180001000040000c0002000000000000020000800024000000008000140002000000002400010000400024000100008000180003000080003800000000400014000000000000100000000000001c0002000040003c00030000c000140001000040001800010000c00000000000000000300002000000000400030000c000080002000080003c00020000c000300000000080003c000000000000300003000040000000030000000004000100008000380002000000000c0002000040003800010000c00000000200004000080001000080000800020000c000040001000080003000030000c0002c00020000c0000800010000c000280000000000002c000000000000000001000000002c00020000c000140003000040003c000200008000200000000040000400020000c0001c0002000000003000010000400018000100008000080003000000000c00010000c0002000030000c000000001000080002400010000800000000300000000180002000080002800030000400008000100000000280001000080003000000000c0003000030000c00030000200004000380000000000001800000000c00004000300004000040002000080003c00020000c000040003000000000800000000800014000300000000180001000040002000020000c0002c0000000080000800000000800010000000000000100000000000003c00020000c00030000200000000140003000040001800000000c0002c000200000000040002000040001800030000c000340001000000000800030000400008000300004000340001000080000000030000c0000000030000c0000000030000c0000c000100008000040002000080003c000200004000140001000040002c0001000000000c0000000000003c000200008000280000000080002c0002000040001c00020000c000000003000040002400020000c000240002000000003c000300000000300003000040000000020000400018000100000000380001000000002c000000008000300003000040002c000300000000340001000080001c0001000000003800000000c0001c0002000040000c000100000000040002000080003000030000c00
+Key = 489c79a48135995c4c8cdeec1777a79cc68bfa7b167f21a25e92945b4004634f
+
+InNoiseS = ad9613f7d3f429e39015b40b84b0b0913ec1c4208c24d4573b1e0e5cafc5c2e3304c02271d1271721028f125e6974c6682ea15cea2cbe61cd1c03c601a708e42401ffa6cdd6bdfc925b9e520914a5902162f2db0a42b389894122e95cb5b12393642bee7c43bcd394449a938e73625985fdd9897355e13a453e073ad38438fe88738627c7ca3d39137a8e996630b1644a01c0cc5ad8c7ba79da067693c0080bdbdfdae94a5622587c4635fe319f439c7805e194b785fb65e1ce58d446e131206b025d98e60d10a1afd716e79103863382943964362c19c48a3aed93dc2aedaa4945a3d47a07ab585c3b7e377edaaaf8e4552ec3cb049c66ce1a7d9157dd552a63008a3ea6ff2026505788d18bc0152181b7b52489864aa4306eece17e366a9a822ad131059258665298515f1b8b691e490c9190ae668b2e6aaae8633a9f4b1d1a1015009c041b50e55d0d3d19066108e58c3ba80a67213c851687b6494a5235c7810718aafd10b7295810284592327f2b37ab9cd89eb80b3a81130e3a604796d95b3eede92df111d7264d21483851394002743f9fe13555f35250499421f189a1b35a92a2d2eec18e03183042e9ee7eaa15b9c11f17b4009a33960f9d628a225ab1c2557f8dca1dec00f82f80437c10a09f04fc52d3b8f95c19d86603bd6be368d427f274d3946d0221240cf76f4c4bc86b963f1e72fdeaa7eda6a382b41ba210eec4bd0a48b509dae0455a4381e6ec80226920a7fc67649fe4017b26407624bfa60da5c47559f248793eb4dd1216665201762d3b64c4782cf1175ff2eab9c6868b764d27a8f29e882beee78db130563114a1c8d91f125748ea0cfe56361aaa55b8d45a507754d332b8e9c252728b103aa48c1d5243c8e9e9a9879564f61cc2bb8cb3cbec982167859524f78cb374bb95056505acd22f0cd2890162931d4a200ba1e8490bbae0b521ff93bbaad8ed3ae62d7246205f92f8a7180492c398f76674a1a77d4278ee4117c11f001b016dc057c4d8665e7b4608e7726c727356e550a6974a4a0fa77b6677a40ff5683712a24d066d00ec14b2f926ea6b0b01b16f21a28b1ac2bdd05f4c162cfc844fb3ee4a4125e84e0876a77ee05457931550cc6c96673cf9d4526959719170815bbacdc97bd629924ea99731238be3f1c27a6594182f1944af3235ca8ab8ca067a1c6628401b4cbd665457e01c5fb6fed88191b24aa65e08c4bad30e5f9a00857aaaa0092302090ce66cbe192089896a524074c4a4e007e8a466a741417d7fb4f6d85e5c656c9540075258e3e1a0b1483ab482ffab6be449be33d0369c637cc526ee663395e092051e278e6aafc2e70f707017e85ab8dfb292a0d20eab39a773a429cce65a7f236612f633d79032c88b58af02651a1a05166032a446457fc2c8a548a60af745b8d762a1118feed7308af00023ea521d4a78a1e4d44cd266aac096985cea3f490989413b3e9cc047cd2c1e7599a5e3e1f081c647185bd819a9d7764a991e0a04db7a375102c5201527c9d23b242ac5ba5022ce8afcc78fd3a722bdc69119158898e405e68724889d6ee56831c1a2f39e507949a998e9fc3f2640c861841ac7e5ab261b563922109416ea2c4e071b4a1e46bc6c87e03b4e58bce9d7cf40a78407e1f44076f8166dc809a933e219e084126c1acf8a8e14e570383a454d0043c46b5e940540ff1f9de329321dcf7b179ca7adbc33693dab233d5d809332a5047839e50b1f20540d3d4a1908dfc64869937ab9b1a4bb21334826b449db24bc0a85a26183e870c39ce0e38372ca844aad89cec673713461468958b7e24fe3873670eea199eac685e9e64732e6070efe23ab892ce3e8b2053f0c1edbe5a46b1d6d839818047b0f2ae2150b909a3e6cc818c9819e3a156980fc2aebb172ac57a8ced24854550532249d827e798a83916bb4271aa841c5e76b49be014651b59f151092193cce682e1ad237818746724819862494633a96217581bba6a8e1652829ff0c128111d5d5d6b5bdc707ccf09e479e561aede184ac631cc7f19de60babf183cd6e6e3d3a80cbb398c58a147767da3d5e500c956083ced0699a850aada1f7b558c86778d9bc21b02ed4f6beee8a6267244a0e87687713161195d94f6dbf55adb0eb95967880a94c358579d945c521f19f361d68818a8cdf01f0a0f4a125e96f3a984d68c8a8a43cc8bd9c64a1231de791e2754928c088a519921888d460d7c5548e7c9f88e360c8da46e7616865897aa4ef14eaceb7753103103c343358398a4530d8fc86a851999f4e49b30e365044eea61a4e47524198d0f9600d8b10155ee33da006469ab55782c46416195f2a153c264ff1d7dbbb72ca01c26d11aeaa642fedadaf88a7478acca65515ab881719c8200d8559e3a63b96155016b625cb61a09209ab89cb08b444da5e6990f466e286047a72b1073c67d9e5cef88ea2e2c3713f6b1c1bababb9a1f2698e814c6ef0f4bd8aa72a7daeb41364c77831f06720b34574dc550b6b44a3550613c51db1823
+InPK = e2637b35a9e43a2dd25333c125bb90407b7b1c0c62da269efb13008434d8a8f00d212f6198d693bff0062482ba182ce12bc16520d6928e1d6ea54a870adc94396e2f633e6a9b024f8878c64c8af86164fb6561717369db940d96a1844a7511105271785a8600fd062ec6e3f853a584ae238e7fc948ec1e3b2d1e24a679696eeb4a6a8e3d00f3d4428043b81ca050d3fbd03c036b400cfac70ab179eff09232c40f374991bb53d53f8c930e241105b31391fa9a65142d0c95b88b06c12c7c195296f91246e7687034eca81ad9ad1d47fb6c006e2245ebe8a179531c446976fe39028a06a1cb6451590f9c883095b9d543e36b26bd1506dff3f7eb9d79cad5fc62b106820f544e3688f09a386c07f0a7428892a80620f3da33bf19e595ac79af5d58cbb8b404862fd7b020e564a2551df746dc880b67098df5732eaf139adf20f41c8323c71e378f2e35544c12f446dd8536445a993f25a028cc3e106c9410dd5ed2f5f1acb1a02683e21c357153c056d2e755731f1aaab7dddd9a75cc159377ceb26404efd25e6676d946ec388a4268a1ee7317f209861adf78126f025363c9254839f465fa5e9e37098ebd8f633a16ee310a9d512fa922dd25dce24b364ba24388a72d40eaf13e4f535b61d8007690483f6aef6d4d07819744091a0e4d4a3ac85ae52f04d530a937aa0c81a858d7b38e7a4281ab20f186890d00bf89590650a5de283a37fcb2a4cf85c658f4148e24b478d8225f7e0ee0135c9e39568a6f8292261d856b9c3878741a11ac9d39aba68cb714292943e44aaf5d9d00385425d9407f99ba6a7743e9abd162c80895ea9585b611a205c90b9ad2188e6a903f5554c4a35996ce266a49371a4fecc68660be35055fe1f8797a5f6dce81e22414764c44d3ad981b2eaf415169855ebf5a4de3904227789c58d2a34a0c75e50668dfce4e4f2083a693a607370d5e9a79f943609f94663cf47c94e14d5708346b33ab51c2ba91729206356491cd57afd8ceaa0d3634d3257de0367110f05ab7253c2130989b9575887e0cdc6459f9e3b61d72416242e6c2b02a8ffdb98e5c5f6da8e7a13a7c7276ce47e4bfba1cdb000ce070a4bbed57413ac7e5110682fe86b0c5791644be6a7ce6169bca6bfab174b734918da1ce4a399a903be6e2621ca4a86f90e0bc4750819be2cc4603c4aef178c3865c9d858ce8a1f0519927fff327b906cc6e39a4238da0c7061fc4c8240388acd039ae8e7d68990fc78f5061362361c1e1869762ce6a066d24aa9ea4f8468112072fd16f8b314c5fb4c032725fb6729812fe9bdae138b81aba3abdee0ff740fd9a5ffa18205b3307e53607e90e7894767249ae95a436ab71798e1b5b48f4117cd56e3cf3a4912dae43bc63598a78f2ece536cbbd4320248df43cb845169a01981ffeaa2e9dfc17a8aea8675cecd86e7e9a544bddf12af07652cc4b2610a81871e66eb18e643fcd267f99b731ac72258ac4aa66a41a1cead40450876115119133449e2e5535b008957b7b6682e8271a9b0a4c0b4849fc1fc023ec529d2d6614d7e860587925b1eadb3311c90aa1597af55af1beb62dc01867dc0b932deac47adc91d7c1d93968041a1edeb5808a8883f65f7d11be85b8435d23482c081a9d6d3c5693a05aa66186620ed46a23118552673c96820a6f3bb3aa9e51c3a07b26d497c458ad219c5588f461311bc8bd702309f9b44aba430d81559989140f95982e871d502e02c3f161399a367910d526a2063b4892b507c0611d2d8d6d901e318e92eda13ac2c14ab3a9b13129552c00d529b975c1a8bfa0b55bc77488424af77b97d63fd78d9998158e51a297d286156a99fb6a5112ade63a1085228ba7de53c7c3f2d2131e510002ef92cf4c5a45570e07abc094ba294661e4cd96fcc3a7a1bfba84d1a2c8d0b2d550e20fd963d3100fa9d890540cacbc2babd97b6fc4052dc4d4abf77e6c5b8860219ed1276c9d5c566704a9d19e6f54d8ea63315079839a042ff0520797ce78b1489982c26aaa463b20a2e9ac710cb08473e79bd134416357e5216e3e644b71d7182a121dc0f1463e22aa44cc4f6246016cd1af2d119e82abbeae7b6b1ec05e38acee10fe422051c448a14a71a23a98594258d9525544fe697d752477c5e01922706d2ad10351262a32cf86859a180d7f2185bfe370663c9fe81b419ce52560d9552b95f013b31068eb447c528a48c53bff387575d61dda46a9499a0b0d59602591c31a7a20add950142d82719b08b460270b207e9a03ed91eee0e5d8203e5fa061f8e624195f05a4e4e9376e9dfba02d14325ec2beea8fc0b21e67e8b671d8a194e1f9367ada2c9a096c9a6d5c83ba9344d2d2026138ac89940a946090171fd868310fc29f2996b1046910a754243db02fbec676109fa25de969f3e626646209d38b9b3bf909c4d249e5ae2dbc119d779270aa3e332fa2124506a80f41c323a7e1a9486a0cf5002d1ae09f798670d981dfa07a313a254222a131163d596443ca8697e4990e0fd3c301e45874
+InRec = 00c00020000400038000100004000300000000080000c000000008000080002000000001800020000800008000000000000040002000040003c00010000c000280002000000000c000000008000200003000080003000010000000020000000004000240002000040002c00020000c00018000100008000340001000040002400030000000034000000000000200002000080000c000000004000000000000080002000000000400034000300004000240003000000001400000000800030000100000000080003000080003c0003000040000c0002000040001c0001000040003400020000c000140003000040001000010000c000000002000040003c0001000000002c000000004000300001000040000c0003000080000c0000000080001800020000000030000100008000340001000040003c00000000c0003c0002000040000c0000000080003c0000000080002800020000400008000000000000300002000000001c0002000040002c00010000400024000300000000380000000040000c00010000c0002c000000000000300003000000000400020000c000040000000040002000000000c0001800010000c000040002000080003400010000c0002800020000c000080002000000002c00010000400018000300004000280000000080003c0003000040002800010000800030000100004000140003000080001000020000c0003c00020000c00020000000004000280000000040000c0000000000002400020000c0001c00020000c0001c0000000080001400010000c00008000000004000280000000040000000020000c0000c0001000040000000020000c0001c00010000000004000300004000280001000040002c00030000800038000000000000280000000080000000030000c000380002000000003400000000c0000c0002000040003800020000c000080002000080000c0002000040001c00010000c0000c00010000400014000300004000000001000080000400000000c0000c000000008000180001000000002000010000c0001c00020000000018000100004000080003000080003c0001000040001c0003000080003c00010000c000140002000040002c00000000800030000300000000000000000040003c000100004000040001000040003c00020000400028000200004000300002000000002c000100004000100003000080001000010000000024000300004000040001000000000c000100000000300001000080001400020000c0002800030000c0002c000000008000180000000080002c00010000c000380003000080003800020000c000040002000000000400030000c000240001000000003800000000c000180001000040000c0002000000000000020000800024000000008000140002000000002400010000400024000100008000180003000080003800000000400014000000000000100000000000001c0002000040003c00030000c000140001000040001800010000c00000000000000000300002000000000400030000c000080002000080003c00020000c000300000000080003c000000000000300003000040000000030000000004000100008000380002000000000c0002000040003800010000c00000000200004000080001000080000800020000c000040001000080003000030000c0002c00020000c0000800010000c000280000000000002c000000000000000001000000002c00020000c000140003000040003c000200008000200000000040000400020000c0001c0002000000003000010000400018000100008000080003000000000c00010000c0002000030000c000000001000080002400010000800000000300000000180002000080002800030000400008000100000000280001000080003000000000c0003000030000c00030000200004000380000000000001800000000c00004000300004000040002000080003c00020000c000040003000000000800000000800014000300000000180001000040002000020000c0002c0000000080000800000000800010000000000000100000000000003c00020000c00030000200000000140003000040001800000000c0002c000200000000040002000040001800030000c000340001000000000800030000400008000300004000340001000080000000030000c0000000030000c0000000030000c0000c000100008000040002000080003c000200004000140001000040002c0001000000000c0000000000003c000200008000280000000080002c0002000040001c00020000c000000003000040002400020000c000240002000000003c000300000000300003000040000000020000400018000100000000380001000000002c000000008000300003000040002c000300000000340001000080001c0001000000003800000000c0001c0002000040000c000100000000040002000080003000030000c00
+Key = 489c79a48135995c4c8cdeec1777a79cc68bfa7b167f21a25e92945b4004634f
+
+InRandA = ad8ebc02b5c27c85c559292964696c2e3fd406142102d74d8ae9168131abea968da1715a08c67bed550789ccc9254e404045d18a89d4a4696996a5c5cd3c87cfc3d6e92985973d6cce02ac765bec09fdda16a4776c9d92c75431121d1bd453b8f296dac1a5538555823b20f2999cc416e62eaa90a3a2788d877d03b69e399dd089c7778e4d70d831e48a9c371218478612a2ac8eae6c8278956b1aecb7874da681a14d5e5a95325400ec0341f5d58767d0a7798b56bd0882d35b1fee076529fa8a13d523a965093178959a169de1643611870f869069de2c1fd5cdc514dcf626d5da45e7a34c9ee648295890e01f222fd56089a99c6c1b9ca947da5ea7ca32c7a0faa26bcf6ec390ea439defe6a19daa689cc1a1467cfe75d9285931bbc42f2f21d802cea83fc1aa27500762242c6e624b396c499dda561aff040d6f265760ab4d38256352b682682f90c1f9aa1046838ceb916061a550370d3830c0317be41a8f8853406a8f86bda08f79b3e31f8c7746da1c62553ba706020ede82e632a9061de666a8b00bb10e5f44762a99e47ce09c68d080b931e5c34d4be3dd8a7b56cfd762f610c2a183293268258082873779f418d30c7f3517e192236ac6e208c994ec9acd82c52e85f4642c0626763484afe6160eec7f8e2fa317a1ad89ee65c7dbc18481fcc42a757f3d7b48a90cb211703f1ce112e4822057f385b497b0325d94594b66997e5dbf972b9a70b010da0363b79fad6841d4ba8513bcb728ae50e3bc7550630272a5e189715fc198b331de8931af19a0bbfc9e24947b97c0e2a751aca732389252ee53d1093bc175ec0035013ff2431e4fe3da60cc769c0bb515aed63601e824097e750c3587b0569b2897754676917c7e154aefb5f829b60e71c8beb534be1f39a0cf36520a727204339796bd24885dcae7c5e18ba40ea3c553e193a10f6255734a6bc6e171f9898969d8cac08721bea50ad906b4f8803c695e76c21a46b285a728d3f054bede0b1b72630a5eae82838714b26cf35fc0027fe685b1c7d4d422d606cd23bb68e3fa052b5504b243313892e4d1123dc6661a4bd899cf44584e975b14a8dd6bc4c6e1a7b0ba1ee7c6cef815e561abee2052462e6ce46c92b0bc1c717e0aabb1f09c21272df5f6ad650d0e0528f04c652a6a4b6ae784fabd51a3395622dc274d486c36b47d4be3dd454324e12fabed0f6761c9712d1ea6e9224653348202e4000013d4ea0300b6c9bf6a40ef32c1429c28a24a7b8d14b36a15c17896ea0ca047e7c6609177c2ead50053224a5ea12dfb23095b905fd88b422646e991d1049b9a358f88ba76ba3a24a4018594546c1bd5d4f5ab19daa88cc33f46c6194e3217b00b500290ca88c2200a3493ba935949a9cdab90c477b6717ea0df127097e73d4fb30e659b6722853d468261be609e085735fb15e075414cded0e0825ffaa99b0bcef49fded751d6ddd63d37a4f787b74094b356ffa3b5d920749990700a8c24ac573ac9e87d71d02fdf7362d830ac8080f7212a206754fa3bbd1ea21b5195d53ca67769ce53c4451020190b58c821aa5ab9909d69303419501ed9098fe11c048fa1c48d816334468370e5fc21a02b0bc94d158dbde43f9694f441da613252853c7f8010ca98720eaef82599dbf5a84b3a125eb42ee22af05a495077499d43ac084283d2813b6c0fc268e0b432d7ed59fa06187320ef49a7501aa648d1faa1b1a5e0ee2eca253629ba167a670d86587652fcca51482f704cfe71455a192bce7af801994885a34a3a5a063c79860cb0a846b75caf889ac9221e41d8828a783d9510d830a4a5ca7074ed5804e4d60f24eb90a609f07bbdda2df9f7fa5daddb1547582a6b5a41ed2350d0a4875dcc09c8f929e951f231626c3bb245f498c8899bfa9ea610522190b92ac091260e1489089bc244cd57c75b3813d35d84d48d120531ba07c9aefa76733a93c140a737223e7d58e57705425554b52be20ae22c4e69496e5364c906f78f3687c7f0840eaf70b233557bdea0f896b5ac60b9494fb370aeb69d2698f3aff06ebd85ca19fba82873a364b6671d887faf673586f551890b116bfb0541bb55da875cbcaa0751e05b73f20940a26036c1c2710283fe44bfa8b295528212a5b20b750e787465325adcc469189ec8200e866b6961ce8c8c985d18f6bca78540457bd3c12a4de82fca18dda3e6a1ea38336a0d9d5f52eb52e831afd7d7f23a2c06295cabd38bd558c30763669b89289c97d8dba5ac005fdc87534b32488eefeb5148262202db92989c543b2e82105159cda8f1948513b2a28b7fda1c78ae6c1269021a991b91a20ad698e3105cb58e2ecfab1a8d61a904e63244596acfc89f8b146d966915237bd0ad0e79e2cf029d8087d25056b94a699f6684cc83646257f4d033c7d3a6b565aae8626706a80fde6e595b7215dee1e689d7865f5b6b6fb5a3ac90d84584d15e862d2c0ea9ab60e642c76592e7dae26972dfe414e8e5873c6c6f504ec27f4a91b7b5c4aeb900e1b5e927ac57
+InNoiseS = a80b21a95b81047b96b9897ff01246dd9b46644d94e9eebb652d84bdd21a45815d7441a863e381121a78479f8a8b8ca9b3ecc170c8ac0630b1dfddba2f28a373471cd809001b36ea92e6291e26da1bbd469d526e734f45d78f2c6a840533b955fc36330f3dc865d5b7f9d6110b2b3d0eb92f34b9ae344ba06f04888b3202c111ad4749a2b923aa08528eb6a18a08719b06f4793b0dd2c958da1072628fb9a410bb7c5c2165806a718ba1ca0850c191230083928062776069b14562d513e843be94ad3e476b4c4239c1d52fbf900d2b00cd3725dd5111c238ba16a61314c59e706885aca57fc505fb9dfbe6ce6829bed6cc76e9e1b9af52805a737e4baf2f37ebde562ea01172fa29c27e4ed7244308aa253fe11481c90c9e762b11a880a6590d58ed1139f62ba60313f4afed1af24c4764d79c89fda62d06fcfc6258e88979f2559ca0612f845a5c3d6f6c5df309ac8701cdc21a648996f8d8ee97c8d894382b3994b3357afc5d06682e1095f25c1b59be76a316159cb623f04b494deb258f043f216eba710e6e9b0ee772432a45456602942a39f1912ad530b1d3d0b740e3f581cf368acd463a23d3960066d92de33b0a4a398ff673a719b52e15ac91b6a51aa8144826fd657bb517cb65e6e9782dd30a324022bbd7cd8995a5346b32970e0ae9be9aa79c91826b2c0e3c87c0030f72149ca9a77a53e946402e291097158db12aad04dd0c839aad8c220f207214cc87a07d057a81e37ba81956ab24554f657c124e10683af65fea15531d03f1190ebd0b0278089cbe01b019b194ae6ab8c9ef2c8b82e16ff1a4aad24060a62c5e89a3676d12fc1f1be3ee0e552c15808104919e64000a92d55800486af24632c4b02e631a5572d5751385aa95d4b5bbb71ebd56548a6f4a2cbcc33485803c90187b227ab2829e732b8ad15dddc3450317a966201f4ec3d70a3d04ec9a79a2c98ef2a3a693672ead1f0d20e42008726320f1ebfe162ba0c611b03a32a0c96260443ddeab0b04ff9381f14363a6a4e90b45a356907196cfb845c9e68c91d79d2e74510f5a2556605b0505142b0549416e3f8125c65afdad22a88620a05fe34a998a46ba57043488c2102e23da22f18e680be20702d326f8a86ae4bd36d4ed4e0f53fcd9d59c0cdf6a5cc80325730a513631991e420daa12b9f191af12c9b429298157754cb7ead1be85f8e826371954bb15946fa6620c8d63c235e37e7cad391829c258446522e30a60f0fa7a41c05eb0b2fc8faadad38ba6f66ec70fc62b2e9976a1d56bb2b4b26df54621f26b75af4f5030a668e211d58d36688e10a692c541648214a9e8037b86ec2009f2e6c5c0bb4c7e91db3123818441c8675d672c6e7cfde43d359d39280a974bc3e128407ca6e2883f21bc612777508f32202f2558b5b6224b6d4da3e83c9d3c582f7ab1bba82c904a1a75d6668ab0ef8893f4c24e305063e6d85471ced2d74a383451ab027613cdd094476538f9d03a55be9e4d0788099a3a4acebac42865ae436e55df2123a9425270cb113818640b614f8c487966904043d531b58c44b50571b1d45e04b56c4a0332a180f0006e3da108a08978159282670ea10b4e4e89155285bcd0a4d90827f845683b5fd708f0aa9541af9e304b2eb87c4b06f5d483789a621b5431384d7615cff8579ca2a44f157c6882ca6d15e213a9c8fd85015bf8d55cd21d274c442b66146d8dc456718599a68e8d57869e5a4ca4d00b17996c2bc483e476421205e6095594f5a8908ec8c6a28c118b9f2e7794c9920b928f4a5729ec983f2c5ae4846c91d50ddf2b90f6513949187900aa8ef828d4640012006dccfc7a225168165fb32b3a9d535de29310fa669aba94faf0c37d3a32463ec711a474e1cb20c15b36845ee4f6785d6d1a0eebc771d68d282ae2326933211fb91ea08b045113feef903bcaca3ffb6898f61815890b5a09cbe2413e5394f3d65a42250fae3930ddcd1d3d63bd26714640b8ae4cfaa3e57f0fc446ebc6e0822e6aeca4625252591568e1f80aa29a0679e65d9c6d19ea2f388c1a1a526db1f674f22ceba41412fa247b192eae509ff831be8fe55433a69570a7b3b24f1c8d5b443dcadd25a11f0cc432136c79bea5fb5a83de240c64d0717a7299ce499496e71e675c20428834445aa99db4124df4615865b68555c8ae0b021a824324a81ce0be995352b84490ad84413608c12dc2a00e4a53bfdbbb403a2847dde4616a38ed47cc8b04334cde0269ef0120fdfdaf795eec3b98924cce1a3924198e36dd6bc638faa01d76559942508e400c5f10573fb98187a562ca966dbe1b900043fcec5d826d17eb4a1e3790875d05baba956e6cb9d88b211bba112217d01ca67e83e3f6ae187e82e7c3398c127f894bedc489f15f6faad152f2364ebc8d63009e241d01c2db7bb9d6487de303a3915c4699c3e9857d8403671d2a774fe618d34b75560e78285f11938a343d8f5663a396c642b8dbe44aea56325af044979be3cd37b0e3b69436f45d5e95883a47e09d
+InNoiseE = e845be9491fc0c92163b96787e80bf0ed9e369841d76e3ba6a87548fb7999f66d78d96a7d296e777fe3db2c3033a11064940d950d84f01b3d0aa612119d18c200225f7d554a8f2c6c921c28e67efd92587a0f084578f5c36473500110ca8a7415c55750a75f004823d1be0cb09c6488918ea38c3778c212e2cd80081d95950c3fdc1cc61b24b5c8991069867d196189ae04c893be3fb51aa794abfe72b33e48ea2af932238e8747b37459854e30c84d223faf5cbf0707ae958483b3553b3a64aea4afc97eb296078007e2bb480c911795eb3b9954a83fad85d3e254f434c3d1075e9180b32fe7b2e083c0bf3469cb2e8ac6aa7c59645c9d0b3e2fc78ad19014546a545d7ad64d0ef2804eb9a8495c4805de8049c278ef24260019b0a1dd159055ac289067820e2c8cfa1cb7dae90e57072d0d14921ec2ed8ac2080011f004a95405c5167023b0e6e9ed2d0b15b9a0008a6aebd9677feb4cf0d6f904dc0bbea8ed11601c8bea66581c006e5a37ead62954e7ab810537f06e7d1210c80ff844fac9127c1b2005d6541dbe821e490e008524671a653709c249db99940567c4292166bb508984120ea44ae4180f62365d97b3c0153defc41099a5715a4f564d1454c172e3b8366a9a8b5415c99a9d60f6f1fe5a06dc58b7782dfe727f2a8932d45f5289e00ec07f6932d4a44f1e32bc446e9a69e611bc43b3015059f9093172583a0e2e18a2a557fe8690eb852cd1adbd1eee0e8ddbf9de930658830262e97ac30ec3893945485040d5caea90c3510b924a9ca842f281260217e2aa49a7155849c13b4963c708b71ddca47f9849f3347088519a8437462f348e7745aeb40d2eb36a2224617d85220f18f2c99d4c54ccc94a1dc1e47988d95470c925204014b201065b67c785d2a6f23289ad89df746ed90b1fc5a23c8be594a3943726d3c47efe0b087255413ceed195fc6bfb32fd8640b995f382e2d945645a05682ad74cf9ac2e3e9f13b958f89a541edb34558363bcea95315d9e7184dea7f45c081e1ed74869685685148164e4aac81747fe43be7d550a06885552416dabae41c23c89c4791e2110b07a3b4007e2a2d2d6dab709c384d09a92f91316225ccc46980c5ccbfc19bbc91f2d818f557f2f133780367a4954bd1fc8121e86ef57f1a59419d20162baa6b465d61231f5806dfcc373b8510b8d5cee65a09f932c54c71212c3570306baef65a6e8fc0e533b10e0abaea9105b3e5daabcfc82c92550c535c646e84aa4ea50ba7d8c78f419f61d4f6d39f1daf4deb0a48136c6a5d65f2f8db6612ec654795241c0543a1bcc69f8a65dfae5c76fbfd8c105a056b5a5d7e586087719301ab6804f2d5c1988a420b45f58a3e717725ec36e9c1877bd515c814da53822022763808907f4c56384131babc125b1562884a538cdff5e0c137e61b45a40de995c75929a8b1beaed8817ad64b15a823c1f6345d0924958310f0690c576a459d572b32496cdc53fa424cbed92b97a4214974ee10daa7c19d065299ac11151a4eeae4dc6771f642b0661c999ecc96ca58a0c7e80a621c6b32d1046c38ab9368269cc61a48e532171606213c5d5bf4ff6a19b4d36bf85b12b830903ca70576b276195356c03caba1cac6d67e923e2b498371985664cd0a15b46f1253822c3870927202f08dfee8d315255d09548ab2b698c8d19d9b2166a496c4431b5e99644620ac7663ba3cefad032bcafd77559d4fabdc71451c17f074ee12ad13443698a06c758c44afb28190245b221874073a7cef2d8ae46d5d2c2ce6b7fbd0c92978c1dd6a2bba3da1655bdb9cc09c0a24d1c4822c3ece61708414f996500ccbf0790e2f7f9fdd739c7aa7ae1d0be4ba4181910f9d339b6a1667c115b6b6884dc48642f551fc7822ae8ad55d6cbc32141762f1da6ff967720622daaea75455e6a999535763a17248426532511e33d21e00e784258a5079bdef43068acd352e7a43b269ceed99ec518db908ce55dad54c46277da381efd1a147854b492aa31b45f86f06476d6b2cb078cb8d137ab94c3d419dc5b67b6051286c9feb1a9a24d92d8ac781dc7c7b458e30bbce43a18ff2c4343560af9109cbdf69b49ed0ce24304015fb0adb88c2e18f0e50cb662f5635f6240ea0ed7598f4ef07da6d21ba975e9411e39968d5461977104c283ac14bba833cb5c112c699c25064bb22f0fb80e11cebcc0e70a90556e61399049e5880f2946e3425a4330d8ca8aae68d2a4160f1436d0a37aa91c3651bb4bef354f82404061884e6dd55ec591bea4a52b0e4096dd04514d4cbd891495da4d113381a756171d00e8d74d42d2a06001812fc63a10731918076ac892573fd17eb263dc0490615a0412bb38e793eaa146758b4c08af825936732f0bb8616151574c2294b51bb9a1bca28fbc7e29b30e5852c910f40c3d920feadca804e7569e827747ae976b6e5852a76bddea504cdc5bbdbaa06de2aa78686f0973845bb7563fc3c03eb6fa93162b40f56d50bab801068dfc3934c4e7a2c6708c9057e72
+OutPK = e092dbf9d91122e3c8c9b32888abf88fd30616761cccd9e4668c8224c749b688e8f27683d5ca9bac627d876c0ce8f16a872b190b69e839729d51b3a44742af8690498480044eea562de9fc9223254b1ee4c0047763914a21c93876e8e4a8650834774d4eef5b18207f114cbd124c158bae99d658a3718ad0dfd85504815a88808662264a90282e6fb9ca32863d5c789172ca65201704d2e94042aeef4e461ad19d9561abbbc09ca279aab71548b219dbe15199131c7eb9427ef9874297dcc51d83dc1d57d0a9260350a250f9d9db527e4e903605d1bbec04329288f081099218f715a8e808c9a222ea9582b6268a9909a357f4dc1d6d5504253adca14110e416f5aca00dc6ad747dda9ea34085854589062f557434adc9656b1357c550e972c86005e41bf08ac8817c4910813bb44ec3ea63a9b7acd1882695640fdcdcfb943baaa0d36a874b329c43d0dc82e7e6b1ae662abe23cd8eb1382c3241f2614894842a43e969805159846a38715cd98244260c604618eaefc89548648fc6d5f23f28246c033d5b3110267e0b25f6adf9432156bd6b9dc42bfec7d32b5b1abd80d19ebbf2388ad3af0a55e92da8a311434ba38a8b3d6252a7165596c7825e027ea95f619df33413619dfaabbc4b7119a9a8227044cd7827046b2e846ccc12bbe58af4d17d7957a50b6830c55898d220a3cf6500fb1d901380452209401837aa9c6c3403d71be9fdb43f5f64a5b866aeeb559833e604a463623dc9b5ad397ac8e3e7b0520ac610743a6e39a4a0a01006bcfd59e38bed07c7360f772c45d1dedcaf18e2593566d646292911bbdd7d632f57565712765fe90b0109ea3db99da61bb5422ea15767082184e221bbdb09e4094e58ecefff20560866c6eba6d1d7443180417ac8690223e9dd8c99a18e442e80cb4ba0e0746f644f34996200d8db57e6d7927485ae487166665880263cb1bcf28e1c4977864ec9a0b4089e5871be09e6a89879ee48aa415bc0a1d914a1faac0c91ae3889554e594842b696b293700651c0836e081dd250b00e5229a2ea113907b1a0a39b5a4555f66b4baa5dcd1bb02ff997c75d40c98a13474a1892ec463cd2eea565ea9c26bb987613c5aafed27dd42eaa212a09a01a322b80ff568f9d16c3ac70c5b958b7cf16fb9a17313858257133290a5873d34b6957da55b8a5bee2ff2a4ed363b12076c033746bc5b06a9e9c41dcb5780d0d29c0e73ad4fede7582256c1b3bfebc96245af8f97d6655c0606b1bf1bb8a27c719c41117402f9f93fea5bc0be6bdb14b96657de399a1c1206da328342b2d78aff767d7116690b16e0a9385b131a08e6230d0306c0bcb8e1685c1f857ab98d6f865080719058915beec43539640245cc6bd478719c39a691099c225b966ad7a838c4c08beb4033649288bae74978eeb06a6e36486ef1766955e3c31a525f377fc6abd050a52ea3e383ab792606827f276a8a736887a9cafb80314107e9c53d45be5ca85ba0c582683328839a2dfeb044abf56a32d22a4146b7aa7c7c926404b12645b10dc4013058dde023068f60501d9ecb8234a08992220d923110187f26b7b946c683b0d9c64f6ca397f019199ea72e9a13e5552b9b16c95e66eed44149e980c10a46b76c9451f8665f69a716d7bd5fe1858e677509a1c6ba3a8662aab81e42d7e39652d5c09141e8d3899c3c15a4c41bc1489d407ac1e949d37b1d359b58b6870c87e5c885880d8bbf2889b634ef21379f8b9f9a68344d74c4b9c5ea4cb7be919ae0354a189064c0a013c2b655264140c3dc43a812c762d00db994a5cb9a98a35cc582bfaca1c134636a76393ace07e46167aaf6ea22d783a6c8d0d891b5669b711ea92a701a660a2e7e1a368a1f65bd63a8cc2041fa30a09108c002d198d10cf5d821628a8cbf6ca45ebdd5a0018f8a832684fcb91b66f03c0bf56b9413c405457f563347f2928ef63bd3c6531e6ec412ba914ce9f2e576c913af8eabac93492ac2aae23233481afa0594a02cd6a3d7451eb67ebaa17972b6c749e765ebf9b1fc8953eb8b912ba2a6aed334726fe3b37741a26499f62928e0b460da43586923487e2648beb2c2ca59e64a987100590284ab2d25ca215c8ea765d31372d63547627221dfe697fd2db5e6226ac8ce52d0155604ad8c07c799a56e828bad21400421a92da4d455172c2ee099aa13f32a0fb02eac95487624c888eba13210434f52505bbd3672e83b2157cd2890a80e42475bd6e4c590ca9af555dad24e2694dd86d923a62fe313adc2757e195a2f7a8a268d1014a0bdc5bd4b6fd2c0a6a77468ea8bab7d0abb339ad9d186e172a2dc40d2b12cc631c0ab30017ac54f61ab239efdf257ac26ac6e3205ad84e5bc9876c284326430baab511444460dd08fbc5cd7c5ca2e14185c8148552aa1bbaa319b2cc937f17aef4711862e944f2a9360c5d5a96c6baa7f40bee36ee65119c243e78a08eba11e48c109f652844af9837056635649169f2bb3d9dc209543907a6ae6d837f68d670bc7b0626b86b0ab7
+
+InPK = e092dbf9d91122e3c8c9b32888abf88fd30616761cccd9e4668c8224c749b688e8f27683d5ca9bac627d876c0ce8f16a872b190b69e839729d51b3a44742af8690498480044eea562de9fc9223254b1ee4c0047763914a21c93876e8e4a8650834774d4eef5b18207f114cbd124c158bae99d658a3718ad0dfd85504815a88808662264a90282e6fb9ca32863d5c789172ca65201704d2e94042aeef4e461ad19d9561abbbc09ca279aab71548b219dbe15199131c7eb9427ef9874297dcc51d83dc1d57d0a9260350a250f9d9db527e4e903605d1bbec04329288f081099218f715a8e808c9a222ea9582b6268a9909a357f4dc1d6d5504253adca14110e416f5aca00dc6ad747dda9ea34085854589062f557434adc9656b1357c550e972c86005e41bf08ac8817c4910813bb44ec3ea63a9b7acd1882695640fdcdcfb943baaa0d36a874b329c43d0dc82e7e6b1ae662abe23cd8eb1382c3241f2614894842a43e969805159846a38715cd98244260c604618eaefc89548648fc6d5f23f28246c033d5b3110267e0b25f6adf9432156bd6b9dc42bfec7d32b5b1abd80d19ebbf2388ad3af0a55e92da8a311434ba38a8b3d6252a7165596c7825e027ea95f619df33413619dfaabbc4b7119a9a8227044cd7827046b2e846ccc12bbe58af4d17d7957a50b6830c55898d220a3cf6500fb1d901380452209401837aa9c6c3403d71be9fdb43f5f64a5b866aeeb559833e604a463623dc9b5ad397ac8e3e7b0520ac610743a6e39a4a0a01006bcfd59e38bed07c7360f772c45d1dedcaf18e2593566d646292911bbdd7d632f57565712765fe90b0109ea3db99da61bb5422ea15767082184e221bbdb09e4094e58ecefff20560866c6eba6d1d7443180417ac8690223e9dd8c99a18e442e80cb4ba0e0746f644f34996200d8db57e6d7927485ae487166665880263cb1bcf28e1c4977864ec9a0b4089e5871be09e6a89879ee48aa415bc0a1d914a1faac0c91ae3889554e594842b696b293700651c0836e081dd250b00e5229a2ea113907b1a0a39b5a4555f66b4baa5dcd1bb02ff997c75d40c98a13474a1892ec463cd2eea565ea9c26bb987613c5aafed27dd42eaa212a09a01a322b80ff568f9d16c3ac70c5b958b7cf16fb9a17313858257133290a5873d34b6957da55b8a5bee2ff2a4ed363b12076c033746bc5b06a9e9c41dcb5780d0d29c0e73ad4fede7582256c1b3bfebc96245af8f97d6655c0606b1bf1bb8a27c719c41117402f9f93fea5bc0be6bdb14b96657de399a1c1206da328342b2d78aff767d7116690b16e0a9385b131a08e6230d0306c0bcb8e1685c1f857ab98d6f865080719058915beec43539640245cc6bd478719c39a691099c225b966ad7a838c4c08beb4033649288bae74978eeb06a6e36486ef1766955e3c31a525f377fc6abd050a52ea3e383ab792606827f276a8a736887a9cafb80314107e9c53d45be5ca85ba0c582683328839a2dfeb044abf56a32d22a4146b7aa7c7c926404b12645b10dc4013058dde023068f60501d9ecb8234a08992220d923110187f26b7b946c683b0d9c64f6ca397f019199ea72e9a13e5552b9b16c95e66eed44149e980c10a46b76c9451f8665f69a716d7bd5fe1858e677509a1c6ba3a8662aab81e42d7e39652d5c09141e8d3899c3c15a4c41bc1489d407ac1e949d37b1d359b58b6870c87e5c885880d8bbf2889b634ef21379f8b9f9a68344d74c4b9c5ea4cb7be919ae0354a189064c0a013c2b655264140c3dc43a812c762d00db994a5cb9a98a35cc582bfaca1c134636a76393ace07e46167aaf6ea22d783a6c8d0d891b5669b711ea92a701a660a2e7e1a368a1f65bd63a8cc2041fa30a09108c002d198d10cf5d821628a8cbf6ca45ebdd5a0018f8a832684fcb91b66f03c0bf56b9413c405457f563347f2928ef63bd3c6531e6ec412ba914ce9f2e576c913af8eabac93492ac2aae23233481afa0594a02cd6a3d7451eb67ebaa17972b6c749e765ebf9b1fc8953eb8b912ba2a6aed334726fe3b37741a26499f62928e0b460da43586923487e2648beb2c2ca59e64a987100590284ab2d25ca215c8ea765d31372d63547627221dfe697fd2db5e6226ac8ce52d0155604ad8c07c799a56e828bad21400421a92da4d455172c2ee099aa13f32a0fb02eac95487624c888eba13210434f52505bbd3672e83b2157cd2890a80e42475bd6e4c590ca9af555dad24e2694dd86d923a62fe313adc2757e195a2f7a8a268d1014a0bdc5bd4b6fd2c0a6a77468ea8bab7d0abb339ad9d186e172a2dc40d2b12cc631c0ab30017ac54f61ab239efdf257ac26ac6e3205ad84e5bc9876c284326430baab511444460dd08fbc5cd7c5ca2e14185c8148552aa1bbaa319b2cc937f17aef4711862e944f2a9360c5d5a96c6baa7f40bee36ee65119c243e78a08eba11e48c109f652844af9837056635649169f2bb3d9dc209543907a6ae6d837f68d670bc7b0626b86b0ab7
+InA = ad8ebc02b5c27c85c559292964696c2e3fd406142102d74d8ae9168131abea968da1715a08c67bed550789ccc9254e404045d18a89d4a4696996a5c5cd3c87cfc3d6e92985973d6cce02ac765bec09fdda16a4776c9d92c75431121d1bd453b8f296dac1a5538555823b20f2999cc416e62eaa90a3a2788d877d03b69e399dd089c7778e4d70d831e48a9c371218478612a2ac8eae6c8278956b1aecb7874da681a14d5e5a95325400ec0341f5d58767d0a7798b56bd0882d35b1fee076529fa8a13d523a965093178959a169de1643611870f869069de2c1fd5cdc514dcf626d5da45e7a34c9ee648295890e01f222fd56089a99c6c1b9ca947da5ea7ca32c7a0faa26bcf6ec390ea439defe6a19daa689cc1a1467cfe75d9285931bbc42f2f21d802cea83fc1aa27500762242c6e624b396c499dda561aff040d6f265760ab4d38256352b682682f90c1f9aa1046838ceb916061a550370d3830c0317be41a8f8853406a8f86bda08f79b3e31f8c7746da1c62553ba706020ede82e632a9061de666a8b00bb10e5f44762a99e47ce09c68d080b931e5c34d4be3dd8a7b56cfd762f610c2a183293268258082873779f418d30c7f3517e192236ac6e208c994ec9acd82c52e85f4642c0626763484afe6160eec7f8e2fa317a1ad89ee65c7dbc18481fcc42a757f3d7b48a90cb211703f1ce112e4822057f385b497b0325d94594b66997e5dbf972b9a70b010da0363b79fad6841d4ba8513bcb728ae50e3bc7550630272a5e189715fc198b331de8931af19a0bbfc9e24947b97c0e2a751aca732389252ee53d1093bc175ec0035013ff2431e4fe3da60cc769c0bb515aed63601e824097e750c3587b0569b2897754676917c7e154aefb5f829b60e71c8beb534be1f39a0cf36520a727204339796bd24885dcae7c5e18ba40ea3c553e193a10f6255734a6bc6e171f9898969d8cac08721bea50ad906b4f8803c695e76c21a46b285a728d3f054bede0b1b72630a5eae82838714b26cf35fc0027fe685b1c7d4d422d606cd23bb68e3fa052b5504b243313892e4d1123dc6661a4bd899cf44584e975b14a8dd6bc4c6e1a7b0ba1ee7c6cef815e561abee2052462e6ce46c92b0bc1c717e0aabb1f09c21272df5f6ad650d0e0528f04c652a6a4b6ae784fabd51a3395622dc274d486c36b47d4be3dd454324e12fabed0f6761c9712d1ea6e9224653348202e4000013d4ea0300b6c9bf6a40ef32c1429c28a24a7b8d14b36a15c17896ea0ca047e7c6609177c2ead50053224a5ea12dfb23095b905fd88b422646e991d1049b9a358f88ba76ba3a24a4018594546c1bd5d4f5ab19daa88cc33f46c6194e3217b00b500290ca88c2200a3493ba935949a9cdab90c477b6717ea0df127097e73d4fb30e659b6722853d468261be609e085735fb15e075414cded0e0825ffaa99b0bcef49fded751d6ddd63d37a4f787b74094b356ffa3b5d920749990700a8c24ac573ac9e87d71d02fdf7362d830ac8080f7212a206754fa3bbd1ea21b5195d53ca67769ce53c4451020190b58c821aa5ab9909d69303419501ed9098fe11c048fa1c48d816334468370e5fc21a02b0bc94d158dbde43f9694f441da613252853c7f8010ca98720eaef82599dbf5a84b3a125eb42ee22af05a495077499d43ac084283d2813b6c0fc268e0b432d7ed59fa06187320ef49a7501aa648d1faa1b1a5e0ee2eca253629ba167a670d86587652fcca51482f704cfe71455a192bce7af801994885a34a3a5a063c79860cb0a846b75caf889ac9221e41d8828a783d9510d830a4a5ca7074ed5804e4d60f24eb90a609f07bbdda2df9f7fa5daddb1547582a6b5a41ed2350d0a4875dcc09c8f929e951f231626c3bb245f498c8899bfa9ea610522190b92ac091260e1489089bc244cd57c75b3813d35d84d48d120531ba07c9aefa76733a93c140a737223e7d58e57705425554b52be20ae22c4e69496e5364c906f78f3687c7f0840eaf70b233557bdea0f896b5ac60b9494fb370aeb69d2698f3aff06ebd85ca19fba82873a364b6671d887faf673586f551890b116bfb0541bb55da875cbcaa0751e05b73f20940a26036c1c2710283fe44bfa8b295528212a5b20b750e787465325adcc469189ec8200e866b6961ce8c8c985d18f6bca78540457bd3c12a4de82fca18dda3e6a1ea38336a0d9d5f52eb52e831afd7d7f23a2c06295cabd38bd558c30763669b89289c97d8dba5ac005fdc87534b32488eefeb5148262202db92989c543b2e82105159cda8f1948513b2a28b7fda1c78ae6c1269021a991b91a20ad698e3105cb58e2ecfab1a8d61a904e63244596acfc89f8b146d966915237bd0ad0e79e2cf029d8087d25056b94a699f6684cc83646257f4d033c7d3a6b565aae8626706a80fde6e595b7215dee1e689d7865f5b6b6fb5a3ac90d84584d15e862d2c0ea9ab60e642c76592e7dae26972dfe414e8e5873c6c6f504ec27f4a91b7b5c4aeb900e1b5e927ac57
+InNoiseSP = c64beb7608609174039c0966eabff18173e661d90f131e87d7ae8150e5c34773303d482a1d83f58ef58037e0ccf1aee263b3a8e0a6f4716019c3b735a4e5b4cc111c0b34623a65929394a9357224214776813e93fc1a03586de232a7a8327ad2be35a6542eb9adf031dd2a829662c2923fc25588de898075e4bd2b30921ccbd0f13280263a689fd4fb107aa35883f8212c858ff08aaabb6b0259154a0c479f40aca2a06d6319dd7ee75d6fc048f5ad6425c966cf1c6afd97108164c8828dcaf86a96264e85123180d4d97e4a43d620d9325b359a1788db6c5ce4cd08252d4c0f9d491132cd217da62ddea308ac8e992cbecb7a66bbe0909abbaff2a96f925b79f6516ec5c78ed3da0154030cbdc7ebe50286540ba84c54bdeb2b98f2693d69d9de9434a18529071a0ff96784288fd43550233ebeef51fef54e115ff5994b87072e2583806773d33969c8eb3bdba29a9012464278236159b542bd30aa8c6718ca3320ab6534455dbba143b937b24cd702c8d4963854bd819f9dabcc000005fdada7bed44a94263843ad6c08f7c05c09829c72510bc432411eff258c7d783e1676fbc7757321c6d845fbcd7ae609516427588cb4935438b76a64ea41f5f92ba167a25737748af281bb2b3646659e10db4896a4865013126cf70ac85098a305627a102cb4fb4d9d044498019604ef082f21393b588d81dd1a97a5640e171dad9dac6ded9874e52ad1eaaff519be29a35dc6cb60b044f5caca27eb9c96842758f2500592fe1f408be9381afbd95b557b3022b9ab00e2060498a00f5332802522ce41c6918a3a177b088d321b082b09eee19acd1986b9cd4f0802f6ba146e6c3859d0af7fd3a51a1d6c680717e3b1cf6e9b42e2a3c9094140f90289594e40a196112793e53a7a749fba7539061aef4518db2c9c981ee249d12f205e9f241949e18171e81c26700694bb3bcc474949f504ed98fcc827cc8e9b628b56ede94c3cca63e75c32aa9b41a5526c0160818c9c18ab484d49e015facce589c340ebe41d6c4f9ad66353f5ba6182d838cbf554a8ad4bb41b7396362d9dda2530a1411c15ba4a61cd473d0269e0d76e9c5aa8b3902c169269220c0076d215b61424237bbe46cad136d6c0dd220d42ab265402698b1d821a52512dc7c6ab0162b402ff8ab1b33da922abc61840ac76aa3505136789e07d461a08715acd713eaf3f42f40c46f848da48d15cbc2adeb705b29c33ebd330f8384501845eada58c80654a2cea07a3d558e4e80d81b8f943840b73a3272479132a2557efa6566b0688996f0e44ea171a52582a5abbd49fc12d35536bcc137283457de9d53a87295568cd5e6338a7b5b3664c26d18c99024b6143a5bd657f96e2320869305498864e34e9c046e6968705bbf530ddc73bb954817a0161b3ca898046b7eb97270ee48fb348a34b39d14933e18a47df80362ed4fc0227848fa448d061a11d97b62123f2985133d15a59f3d4dc83cb19cca11000320324c4a4bb008c965a2344237418ad00a5f2dfb609d89f89998e7e9470031bf0b06197198bdfdac9498f528ca7f817318e569816caab2944414113e3f89a08200e4921dd3ab85b5782b569d610b41f7a61d6ee2406e3e8e06429bc3329765592f04fe1403fc0fff88eb28f6587b2b6b1e5cc8cf1d44983c70e4de001500cb71807a8ea9a00f9cae380c8087fe8ef6ab157d90ccf234b206cee3823c3f775365c972422d0597bd01ae9e1596bdfde7c53617bb3964904c4897fb8f07515b525f3558715498168f12081b698a91e7b9ab7b80bcd2f1998a92255a4af3d4a169e73d3d255f2ec392c1a671aabd8be28c21207c636b12e49da9bf1978aa74723be73ca9872222e0ad439f681946e484867565f8ca48656e8f618bfda386209d9046a1c886b9414bd11e93178d0cc1944d84b987882884ce8271b5532332980c0d5ca8c629b8b81f65f62aa5545dba61e215249a7f2457c06965004db62e31e1344a7fe3557105580e6c181a184079090b535a7e15b04c434277b896cc98c7e56d2a8492b4f25a91c211d4a0bb219fb087453ede72402a17386c1c96770821f6fd4dfb97ce84ffe1d15133b765c7f6fc4b83d85b54704cb3bbd4a3221e990d3cd521874894993487a6fa25463645b89b8aa5aea272c9e2e0e2e14f010080f2e4f6fc6b771a7e7c377e91ce8896105e1e990714dba41a925479aa37bef79a963969bc16ce49888b8513e209820e3116dc786c39b8f5a4861ed4482f45e3c481fae6b6ce4a0fb9dffc76dd0b3b5046bcbaea678821655c434055106cdb3d2a88830d91345837bad1ac5cb230af6995aea644cd87e38a60393d024122d1a8ded1de4cff1050e98040866dda4c89278d02aa6bf5c4e31f442505e07a2889189c45994c5ced0de41b58baf44f2a7fc34259909fbc2f8b93356bf362086ac2493ff40ab3652fd9a890aad5c6c77a20052d5398649056cfbac185018cdb0c7779ab5898ce65b5af261921668d3b6b9186f6692db8c4658f29dc3050d7115
+InNoiseEP = 56580d3581e9b313853d989e7aa880543520dd4d79ba81873613024324416364673aa8eba9f6428ae800496d492547f1205d67af20d2c6bfa4029bcb560d421ac9ede485fc9ce525aa141fec013ad09b7b5ef14b24ce5761f2065850a6d9c7df2843d610a00b98e52ba54cffbaf24c0596609c3314d1823891ada417b425612df3ba99e287f65cb7e1727cb510c92932bd35511dcf81f9b4bc81bde3a2b98d7e3d7a847c64172c7f1fd6a5d214ceaeb955581613b6a9d28743569bbd711920a768a0bc31be652460497cbc8dea2eb7e54d09045b0f89f36c0d111c83c7b46406fd9e15f3aec966bf6fe6836d4e1c0f41ea729358621c2ed3078b26936096f73a67964af5e77ba41be07238d6a062c6582d01ca41ca6e8a75079d1497119466550d25d06e84a979adcf30ccfebb158482baa111026ad314fbed709f4f21d0700a2e82ca94837a314891dd573e64d53a8bc49a0d796fa1b4c7c640b9739c7d53518d74adba7f6c1428353bb628f79d1ca195f4aacf97a242ecc6336e0bcc89a29d133c55d9778be105e4cbef773495342a425858ac2a6094dcf4eb78f139bd0cf0918246b8e89281b9bf5949c452dbb45a199dfda959c369b2bdfb2221aa0f5d7f4fef8087c6d04bd56f34b8f98297c31feb281cd0b0bed525f32552079e274395f3a16fec6dfb16fb7558be4169bbfda64131e8e93318f69d974f841964d4278f53e910e7f29287073eb7d939217dae13b10be11f4ad27dbb505198a9c2402bf3649b95c06e78bda95e504a6a91b6ee77d7ef3a51d07552d692a920df1a5450a67839c39a8a636b89b14b6d7d8b6685343ad2539856bbd50a24b0134f56d5b6c28069efe7089d2085a799e7232a4c4e1785d58aed5d489b6ec2c49ecc2b5069ea29acb82d3bd61b77dedc67cfa2b1964d8080e850f47bd4a9052d043eca734487d526c2453c6d3722a01810a9a20c73903d9a4e51663683b17a44952dbe00185584a19f8a85ba56a0dc6f6c2eb21407de982ea6c24bb4ba99941cdf846c2822e51d88d7b88a6005c642d607c67eed65b63466b050e2810a2777de0cc055f100c69c7219cafe07936618f54f981414394387ad95b1adeda3d94edf6a2e4db35cd0b463b17aa7a1973f5a225226a4ec2fe6ba4c504aed32cb4abd79374ae045fe6a4c7bdbc0aac0b8bda624dc6099de66aa007c4938e0b4995466f8c3fe7cfc051c64a33d8c8f15eb7520dc0c35dafab4fc8c595859f000bf44717c9be42448eca6da25e0b702eebf6b272f41dab06106093fb0bb8e27b584e6c86df28616df637820f905b623503bd14523948545290c616fb30213e7da511965a2c81576f4a0786474a3c508a68fa92168b8847c6e6755c8fa8c5c9368381104545667d1221ab59a13dd9dbef2b0ed36736e90515c2175e408956a9085a3950b7da321668b97525580013970f415bde3225534b82f2549e0dc339e117bb1d9815683593bb18d2c70d387be2a723097ca6e30ebd3322d18d85c1241aa1e361828b7cf8c229ca177103d8c1c028bcc84b80a1013af478198dea250cd20f86d46b6e95f52b8e2b40eade14c7974a563d6a85e86dc69396629d58aa7b6110d6da33a7b80555ba543e25c987754522dc96db560e24b72e9dbb260522cf1e6da744c5faf989bf14e128647457ad26622d3aae638dfd49fe69938421e8a9d9f1ea0c8720d7e6a9da7893c7440fd920edabe4d06419598e2cb2e59606dedc006bcaa2acbac9a2ecd3065697b92059c299aace3f02cd51675049f820b4a4e7480026bc71e19e388df193a0ab98be36a353198a7ae0ccbf928e67eb427137bf6ce25fbec0be741fc4912a22c68875d37f2ac823008393461a684b643c0b39ac0f6eb35b9a2ae0953cbe1163a93e7a104acb035cbc00ab86ba09b8bc1738766602579e15ac21f94686095d0c82b945330834f633b655e54984948f7dc6e79a67e52c195726e2fe6d518e2615c9a43a3aa2e69ac2e8c67f1615db3070f671a08a0d2250c755cdc9b97901e6b36f68d9ce34526330a17f98bb2a12c9d09aa4846c0affea228a6705b5115667c231acb86e62541876d51e2622840181d18d99d8a7dea535aba4e62a8d432280db67ecdc4c5b997c079c2214718d0dd7e5fabfdb4ab9815340c66c0fada45c6041e4bea112db4627ce4a08a5b3e42e9ea8c390ee84636807b4d4b4759958951fe3cf64608d7b7799a42600a934c75b85843e436cb4a70440025a5ab594128190ff96c1c82d555bdf543b82d3bd88fd078b4b2c51b6171c3e90587169938535c48c5dbea13b6b187341f2f61d835b99e4fe3799e5a3af787cfa68f2a100c6f38c120ad73f36d95da3de1184103b0f192d80cce06b3d36b20acd5dd116a1ac2b383aa4f37876e107a4dc01842e12d2d6b819a8139bd7b9214b3e7dc44039652bb8acdb12f0fff23f7ac445804281959ac1529d63cab2ab6170c220c6983da677348ef13c3840835d6b14361798f598cc2ca4b16ac76affe8017a63e6225ed4a5cd05b
+InNoiseEPP = ffafff1b000c00008000f0fffebf00b0000000fcbf028000c0fffabffd2f011000fcbf0000002000f0bf00f001100000c0ffaf00e0ff0a00ff2f0100001000ff6fff1b00f8bf00b000d0fff2bf01c00130000400ff2f00f0fffebfff6f01000000c000000040000c000500ff1b0010000070011000ecbf0240ff2b000c00000000fcff0e0000000010000c00038000500000c00040001000e8bf00b0ff3b0004000040ff0b000b0001800010000400000001000000c0003001100000c00200001000000000f0ffdbff1600fdafff0b00040000c000f0ff02c000b000200000000080ff1b0008000340fffbff02c0ff6fffdbff0600ffefffdbff06000030ff2b00f8bf00c0ff0b00ecbf02c0006000f4bf01c0000000030001c000000004000480ffdbff020000f0002000f4bf014000f0ff020000f0ff3b000000fe6f0030000c000280ff0b000700fe6f0000000b000040ff1b00fcbf0180ff0b000700fd6f002000fcbf00c000a0fffebf0040fffbff0a00ff2f004c00f4bf01c0ff1b00e8bffc2fffdbff0600fe6f01f0ff0600ff6fff5b0000c0ffefff0b00f8bf00c000c0ff1a000100ff6b00fcbf0300005c00f8bffe2f00dcfffabf0040000000ecbf01c0fffbfffebf018000300008000280000000ffbffe6f00f0ff0200fcefff0b0000c00300004c00fcbf0180ffebff020000b000f0ff120002c00010000800ff6f000000030004c0ff2b0000000380ff4b0000c00030000000f0bf010000fcffeebf01800000000f00ff2f000c0000000030ff0b000f0000b0ffdbff120004c0ff1b00100003400010001000fe6f001000f4bf00b0000000fcbf058000b0fffabf003000b0ff1200028000f0ff160002800000000f000300000000ffbf014001c0fffabf0080003000fcbf0100ff1b0008000440ffebff1200ffefff5b00f0bf0400ff0b001300024000f0ff02c0040000dcfffebf0480ff0b000300feefffebff0a0001800030000400fe2f001c001400024000f0ff0a00028000000003c0024000e0ff02c0feefffebff2a00fdeffe1b000400000000d0fffebfff2f00fcff02c0014000000003c00380000000f4bf03c0ff3b000c00fe6f00e0ffeebf03c0001000080000b00010000400ff2f002c000000007000d0ff02c000b000e0ff02c0feaffffbff0600fd2fff2b000400feaf0000000f0000b0007000fcbf0200000c000b00040001200004000400000c00f8bffc2f002c000c00028000300000c0fe2f0130000c00fc2f0000000b000300005c00040002c0ff0b0004000030002000100000b0ffdbff06000200002c00f4bf04c0000000030002c000d0ff020002c000100000c000b0fe2b00000000300000000c0000f0ffcbff0e0004800020001c000180ff4b0000000100000c0000c0feeffe0b001300ff6f01000003c00480feebfff2bf050001200000c0ffaf00000003c0ffeffe5b00080005c0ffebff1600010000dcff02c0020000fcff0a000440ff2b000000fc6fff0b00f4bf03000030000400010000100000c004800000001700feefffebff020000c0ff0b001800feefffdbfff6bffe6f002000100000b000d0fff6bffe2f01300000c0028000d0ff0200014000100018000070ffcbfff2bf0140ff0b000f00ffaf00e0fffebf0080ffcbff02000030001000f8bf0040000000ffbf008000000000000240002000f0bffe2fff0b000300fd6f00000000c0064000000000c0fe6f012000fcbf03c0fffbfffebf0380001000fcbf0070fffbff02c0fe2f0000000c00fe6f00c0ff0a00fd2f0010000800fe6f000000ecbf010000f0ff0a0002c00030000c00fdaf00200018000780ff1b000400010000400000c0018000f0fffabf0030001c000c0000f0ff4b00ecbf0280002000f4bf00700050000c0000000000000700f72f006000f8bf008000f0ff020000f00010000c000040ff2b0000c001c0ff1b000000014001200004000240fffbff0a00024001f0ff0e00fe2f000000f8bf003000ecff1600008000f0ff1200ff2f002000fcbffe2f00d0ff020002000000000f0000300000000400ffef01200004000030003c0004000030015000f8bf05c000c0fffebf05c000f0ff0a00014000f0ff0a000400003000140000f0ff8b000400fcef00000004000030003c00040002c0ff0b00e8bf0100013000180004c0ff0b00100000000050000000fe6f01f0ff1a000540002000080001400100000b0002800010000000fd2f0020000c000000003c00f0bffe2f003c000c00ffafff3b00000000c0ff3b00000000400040000c000000000c00ffbf000001e0ff12000300016000f4bf038000100000000080ff6b000400024000e0ff0a00fdefff1b00f8bfff2f00d0ff02000100000000e4bf00400000000400fd2f00a0fff6bf00800030000400007001e0ff02c0000000000003000070ff5b000400feeffffbff0600fb2f002c000000003001e0fffabfff2f00f0ff0a000030003000100000b0008000fcbf
+InRand = 13d4f1b473932b85d8bbbdbd30713aa4a25482ae0f02e3351794769e68e70eec
+OutPK = 8b2d5c9aec7477f2e384b6a9ca064b029f6386ca3b760412859c647f78c364b158d8659c65d692b506bb4e56dc233189bdf703ed9134a47873cc14793f05aba55fc63ac34e9d02d3ef2a706e1a916a8816d8d07deacb2777e2b4a545e7e00575fd7a664b9d626bb05acda02a55e9a96afe54ecd6f95037edd2599089167b10e46d68f25258799ac46b166d9f9313d0e8f2d0a8a2a533b68ae1ac9c2c467071712f192bc809f2dc45b76e214a5d41222ec4b9d5a062a0b600beb70270aea1254cd8f79d4b034a357ba19527ea02bdd1a1ea09d04eabf568103cc4ca8be72e697a3e8ba131de02396c0a5b033742af982872ea3ade03f8a22059fad59fb21ee9529c544ccd9f7d64172c9d2515d6379b2e0df9903ec839e1b1db192a3a1ce5b1d94a17f748698a91cbf39306ad24e34ad329d3a8500cdf8bbbde0e2cc26b3d673e8a2159d0bbc0e5fd38f08e22063178b41a0d890b95761fec6bd5f64ebc260c6909008efa02f7477c199d32265e552f057606b13d84cab4de42094a94961226f58021cb1f34a95d1206a00ce286fea7c5aa40b1b93d9345ed1723b6747b1de69823ddb4409ba4391bbd24494011a4a590d20b151237a9e7a23b4be8865207514c666fec394406b367685880baa020eb5c6a556c614312aae258018d23b46f2c120cc43a8c9b082b755ab1fddd90b8eb4526eec8c43768255cac2c2d161a605ea90e2fa100a82448cbe04104e839ea617f93730064e0a963c3d9d60f4b214b29a1aa38380a5409e29878b788cad64db263449a69616d959bc8de606b40228dd38eef97797c80374b749a818a5ef40560a14701a4f4e45d1be415591ec47950aa9d6ca84af8986c9516a55be012a02d93a6a43604f9ed58a7dee509aab6576ed9a4217cd1bbf291864630723eb0d25c3ae7f123dbd12d92061a30de10a4e5a9dca35303e1379c0976295851f67e5237cae4e4566c8481382e2f212f580adfe4e18822d0a6bfd69a355eb670324e35559f95a9fe87222708d80a2b9269b075d660c020c2ba5c29acd809eacb71e266d627b7a322a9675203c4583e61a9b6c99ed6fd299e9992f60189be642d0b4bbb0e710153db50b4931c6849a4bc81608629a2ef850218f00d944be1ebe4f1520760c09119f47d751e85425eb89e309a45233fd290ae53f052074164b0cc35705f805b611aec389a209a122cb412d1d214f11314e427a931b5a82f49dcc4ac0ca5d347f49146bd6b35c62dac7689abd60be0399f045692484289491644ba9963510ef2c352a0e43c9589c6b52215c43ffde82ad868768c2da59727ffc45623eba8e3397995382471a60f4a82985987c110eab255cdf4319c88481e87f1fa80feac75eb0b626b71064d98a4c9324a7f9a58b023053c7849ee95ea7fe2510a8648b5894298d15776a49d35580122737a97968711ac9e98011c453b27f47b383e6ea46532678101374a2e6a8778e6b3f82212c240050a22cd40554aaea30b04eaf1b7748e7d51da4a1be83070eccab4c2f621fb93983b98940c9926b7d7f51a58ffa6d4a7350c0879546c4025013f952d9c410ecd7532d30b393b9a2bebac155468f48add16b6ca79e887bd5c7e12fafc71fbdda1cbbb8196b56dd3f168ce5e15dce5f0400030582e716056f85c5b10c2c5f4bd20cc6f64e2ccf179ee5f7864164c642d6356e6a89c17c91a0312f24859324b7d01f419691e03b4c198859a6b6b355964fc839c26f101ca88719b004492eed28a9d9c956519c053e0acc0350c2c3c82d4a438ec40033390fde66f2ab7672a16f8d7ae841d641754bde0466fa84b6822dca7766c7468205c12679594812d190153e54784665052961cdfc3fb3510084f44ea8bc47d00d8c7a0683a4178ab5e8725196334488ea9927448fae93ac72a548648d481122f166c8cc902161fadbb2325c2c4d89c79196a8648c49504b7036037337d85788b156865e674f6deed11965102ffe2041a7d9a55ddd53fe368828f6540fe79a4c4a1762dee08cb39524f89c0eb0a2a9246df1f85c1318c259f6dd6ae4dbcf39e91c9e3ba23f809bfd69d81bd749ddcb2bef679bfc6c118853e9736c9b5c808b4b31d254b444cd98c5ba104e6d368007c67e541ff42489d962c5462b5aa325f73e5e34c19998be96a705c78bb47a678f1a57819a887c2f3d64a37e997790f060f91c8b2760348802b1f854731003d21ab8f907c91861cc962804cb80c5cc26dd5802572c1594ce16cc57df24063289c582510cf5ff810ec43c2e0701b2a080d891992372c1979dd8420223cf04cbc401233d1a42fe646f8b2f2c98cd49cb813ab08b729a44f314c35884582d920e564659cef80aa61387029822713c2e868765a2917d8060c0115f5b6df3147548812216fd152ef4520c64e6a89de4a88946f7592dfc9ea20c61c8c1aaff83391fa71e6ce5e06f67e47e702663b137c275f5904e0c21c3b080723b75b492dcd03fc768ea633011ab3d3c4987cc5d96a92c22250b41ccf15beb885cca27352b4d9af
+OutRec = 00c0003000000002c00030000c0002c0003000000002000030000c000100002000040001400030000800008000200008000340002000080001800030000400004000300000000240003000000000c000300000000300001000040000000010000000004000000008000280000000040002000000000400024000200000000080002000080003c00000000800024000100004000080003000080002c00030000c0003800010000c0003c0001000080001c0002000040003c00030000c00010000300004000300001000000000800010000c000140003000080001400010000c0002000020000c0001400000000c00014000100004000100001000000003800010000000020000200004000380000000080003400000000000030000100000000140001000040000000010000400028000000008000100001000000001c0003000000001c0002000000002400020000c0001c0003000000001400020000c000080000000000000800030000800020000200004000240003000040003c0001000000002c00030000800000000200008000180002000000003c0003000040002800020000c00008000100000000200003000080000400020000c000380001000000001800010000c000180001000000000000000000c00000000200008000240003000080001400020000400010000300008000280001000000003000000000c0002000000000c0003c000000008000040001000080001c000000008000080002000000001c0003000040002c0001000040003000010000c0000c000000004000300002000080002c00020000c000200002000080000800000000c000180001000000003c000300000000340000000080000c00020000c000080001000040001c00010000c0000c0003000080003000030000c00034000000008000040000000000002800000000c0000000030000c0003c00000000c00018000100004000340002000040000c00000000c000000002000040003c00030000c0002c0002000040000c0003000040000c0000000040002c000200004000100003000080002c0001000080003400020000800014000300008000240002000000003800010000000038000000004000340001000000002000000000c000380001000080003000000000c0001c0003000040003400020000800028000000008000000003000080000800000000c000340002000080002800030000800020000200004000080000000040002000030000c000240003000040000c0001000040001400000000c0002c0003000000001c000000008000000001000080003800000000c00028000000008000100001000080000c00020000c0003c0003000000002000010000800020000300004000040001000080002c000300004000100001000040000c000200004000140003000080001c000200008000180000000040002c0002000040001c00020000c0002800010000000008000200004000100000000000002c000100004000340003000040001c0002000040003800000000000034000200008000140000000080001800000000c0000800010000c0000800020000c0000800010000c000340001000000002000000000800038000300008000040003000000003800030000800034000200008000180002000000002400000000c0002c0002000000001000030000800018000200000000200002000040000c0003000040002800020000c000080000000080000800010000c0001000010000c000200000000000001800000000c0001400010000c0003800000000800018000200000000100003000040000c0001000040002c0000000000001000010000c0001800000000c000300002000040003c00030000400020000000008000000000000000002c00030000c000200002000000001c000100000000240003000080000c00010000000028000100000000280003000080002c000100004000300000000040002800020000c00000000200004000100000000080001800030000400000000100004000240002000000000c0001000040001c0003000000002c0003000080001c0000000040003c00030000400034000200008000180000000040002800030000c0002800020000c0000800010000c00004000300000000380002000080001400020000800010000300004000200001000000002400030000c00008000200000000000000000000001800000000c0002c0002000040001c000200004000100002000000003c00030000c000380000000000000c00020000c0000800020000c0000400030000400038000100000000140001000040001c00020000800008000300004000080000000000003c0000000040003800020000c000040003000040002800030000c0001000010000c000100002000080002000030000c0003800020000c000340001000080002c0002000040002800030000400018000000008000000002000000000c00000000000
+Key = d5492977f04342e5c73557bce8b5f5c5a6c2e2acba0c8addcb834e797642aba6
+
+InNoiseS = a80b21a95b81047b96b9897ff01246dd9b46644d94e9eebb652d84bdd21a45815d7441a863e381121a78479f8a8b8ca9b3ecc170c8ac0630b1dfddba2f28a373471cd809001b36ea92e6291e26da1bbd469d526e734f45d78f2c6a840533b955fc36330f3dc865d5b7f9d6110b2b3d0eb92f34b9ae344ba06f04888b3202c111ad4749a2b923aa08528eb6a18a08719b06f4793b0dd2c958da1072628fb9a410bb7c5c2165806a718ba1ca0850c191230083928062776069b14562d513e843be94ad3e476b4c4239c1d52fbf900d2b00cd3725dd5111c238ba16a61314c59e706885aca57fc505fb9dfbe6ce6829bed6cc76e9e1b9af52805a737e4baf2f37ebde562ea01172fa29c27e4ed7244308aa253fe11481c90c9e762b11a880a6590d58ed1139f62ba60313f4afed1af24c4764d79c89fda62d06fcfc6258e88979f2559ca0612f845a5c3d6f6c5df309ac8701cdc21a648996f8d8ee97c8d894382b3994b3357afc5d06682e1095f25c1b59be76a316159cb623f04b494deb258f043f216eba710e6e9b0ee772432a45456602942a39f1912ad530b1d3d0b740e3f581cf368acd463a23d3960066d92de33b0a4a398ff673a719b52e15ac91b6a51aa8144826fd657bb517cb65e6e9782dd30a324022bbd7cd8995a5346b32970e0ae9be9aa79c91826b2c0e3c87c0030f72149ca9a77a53e946402e291097158db12aad04dd0c839aad8c220f207214cc87a07d057a81e37ba81956ab24554f657c124e10683af65fea15531d03f1190ebd0b0278089cbe01b019b194ae6ab8c9ef2c8b82e16ff1a4aad24060a62c5e89a3676d12fc1f1be3ee0e552c15808104919e64000a92d55800486af24632c4b02e631a5572d5751385aa95d4b5bbb71ebd56548a6f4a2cbcc33485803c90187b227ab2829e732b8ad15dddc3450317a966201f4ec3d70a3d04ec9a79a2c98ef2a3a693672ead1f0d20e42008726320f1ebfe162ba0c611b03a32a0c96260443ddeab0b04ff9381f14363a6a4e90b45a356907196cfb845c9e68c91d79d2e74510f5a2556605b0505142b0549416e3f8125c65afdad22a88620a05fe34a998a46ba57043488c2102e23da22f18e680be20702d326f8a86ae4bd36d4ed4e0f53fcd9d59c0cdf6a5cc80325730a513631991e420daa12b9f191af12c9b429298157754cb7ead1be85f8e826371954bb15946fa6620c8d63c235e37e7cad391829c258446522e30a60f0fa7a41c05eb0b2fc8faadad38ba6f66ec70fc62b2e9976a1d56bb2b4b26df54621f26b75af4f5030a668e211d58d36688e10a692c541648214a9e8037b86ec2009f2e6c5c0bb4c7e91db3123818441c8675d672c6e7cfde43d359d39280a974bc3e128407ca6e2883f21bc612777508f32202f2558b5b6224b6d4da3e83c9d3c582f7ab1bba82c904a1a75d6668ab0ef8893f4c24e305063e6d85471ced2d74a383451ab027613cdd094476538f9d03a55be9e4d0788099a3a4acebac42865ae436e55df2123a9425270cb113818640b614f8c487966904043d531b58c44b50571b1d45e04b56c4a0332a180f0006e3da108a08978159282670ea10b4e4e89155285bcd0a4d90827f845683b5fd708f0aa9541af9e304b2eb87c4b06f5d483789a621b5431384d7615cff8579ca2a44f157c6882ca6d15e213a9c8fd85015bf8d55cd21d274c442b66146d8dc456718599a68e8d57869e5a4ca4d00b17996c2bc483e476421205e6095594f5a8908ec8c6a28c118b9f2e7794c9920b928f4a5729ec983f2c5ae4846c91d50ddf2b90f6513949187900aa8ef828d4640012006dccfc7a225168165fb32b3a9d535de29310fa669aba94faf0c37d3a32463ec711a474e1cb20c15b36845ee4f6785d6d1a0eebc771d68d282ae2326933211fb91ea08b045113feef903bcaca3ffb6898f61815890b5a09cbe2413e5394f3d65a42250fae3930ddcd1d3d63bd26714640b8ae4cfaa3e57f0fc446ebc6e0822e6aeca4625252591568e1f80aa29a0679e65d9c6d19ea2f388c1a1a526db1f674f22ceba41412fa247b192eae509ff831be8fe55433a69570a7b3b24f1c8d5b443dcadd25a11f0cc432136c79bea5fb5a83de240c64d0717a7299ce499496e71e675c20428834445aa99db4124df4615865b68555c8ae0b021a824324a81ce0be995352b84490ad84413608c12dc2a00e4a53bfdbbb403a2847dde4616a38ed47cc8b04334cde0269ef0120fdfdaf795eec3b98924cce1a3924198e36dd6bc638faa01d76559942508e400c5f10573fb98187a562ca966dbe1b900043fcec5d826d17eb4a1e3790875d05baba956e6cb9d88b211bba112217d01ca67e83e3f6ae187e82e7c3398c127f894bedc489f15f6faad152f2364ebc8d63009e241d01c2db7bb9d6487de303a3915c4699c3e9857d8403671d2a774fe618d34b75560e78285f11938a343d8f5663a396c642b8dbe44aea56325af044979be3cd37b0e3b69436f45d5e95883a47e09d
+InPK = 8b2d5c9aec7477f2e384b6a9ca064b029f6386ca3b760412859c647f78c364b158d8659c65d692b506bb4e56dc233189bdf703ed9134a47873cc14793f05aba55fc63ac34e9d02d3ef2a706e1a916a8816d8d07deacb2777e2b4a545e7e00575fd7a664b9d626bb05acda02a55e9a96afe54ecd6f95037edd2599089167b10e46d68f25258799ac46b166d9f9313d0e8f2d0a8a2a533b68ae1ac9c2c467071712f192bc809f2dc45b76e214a5d41222ec4b9d5a062a0b600beb70270aea1254cd8f79d4b034a357ba19527ea02bdd1a1ea09d04eabf568103cc4ca8be72e697a3e8ba131de02396c0a5b033742af982872ea3ade03f8a22059fad59fb21ee9529c544ccd9f7d64172c9d2515d6379b2e0df9903ec839e1b1db192a3a1ce5b1d94a17f748698a91cbf39306ad24e34ad329d3a8500cdf8bbbde0e2cc26b3d673e8a2159d0bbc0e5fd38f08e22063178b41a0d890b95761fec6bd5f64ebc260c6909008efa02f7477c199d32265e552f057606b13d84cab4de42094a94961226f58021cb1f34a95d1206a00ce286fea7c5aa40b1b93d9345ed1723b6747b1de69823ddb4409ba4391bbd24494011a4a590d20b151237a9e7a23b4be8865207514c666fec394406b367685880baa020eb5c6a556c614312aae258018d23b46f2c120cc43a8c9b082b755ab1fddd90b8eb4526eec8c43768255cac2c2d161a605ea90e2fa100a82448cbe04104e839ea617f93730064e0a963c3d9d60f4b214b29a1aa38380a5409e29878b788cad64db263449a69616d959bc8de606b40228dd38eef97797c80374b749a818a5ef40560a14701a4f4e45d1be415591ec47950aa9d6ca84af8986c9516a55be012a02d93a6a43604f9ed58a7dee509aab6576ed9a4217cd1bbf291864630723eb0d25c3ae7f123dbd12d92061a30de10a4e5a9dca35303e1379c0976295851f67e5237cae4e4566c8481382e2f212f580adfe4e18822d0a6bfd69a355eb670324e35559f95a9fe87222708d80a2b9269b075d660c020c2ba5c29acd809eacb71e266d627b7a322a9675203c4583e61a9b6c99ed6fd299e9992f60189be642d0b4bbb0e710153db50b4931c6849a4bc81608629a2ef850218f00d944be1ebe4f1520760c09119f47d751e85425eb89e309a45233fd290ae53f052074164b0cc35705f805b611aec389a209a122cb412d1d214f11314e427a931b5a82f49dcc4ac0ca5d347f49146bd6b35c62dac7689abd60be0399f045692484289491644ba9963510ef2c352a0e43c9589c6b52215c43ffde82ad868768c2da59727ffc45623eba8e3397995382471a60f4a82985987c110eab255cdf4319c88481e87f1fa80feac75eb0b626b71064d98a4c9324a7f9a58b023053c7849ee95ea7fe2510a8648b5894298d15776a49d35580122737a97968711ac9e98011c453b27f47b383e6ea46532678101374a2e6a8778e6b3f82212c240050a22cd40554aaea30b04eaf1b7748e7d51da4a1be83070eccab4c2f621fb93983b98940c9926b7d7f51a58ffa6d4a7350c0879546c4025013f952d9c410ecd7532d30b393b9a2bebac155468f48add16b6ca79e887bd5c7e12fafc71fbdda1cbbb8196b56dd3f168ce5e15dce5f0400030582e716056f85c5b10c2c5f4bd20cc6f64e2ccf179ee5f7864164c642d6356e6a89c17c91a0312f24859324b7d01f419691e03b4c198859a6b6b355964fc839c26f101ca88719b004492eed28a9d9c956519c053e0acc0350c2c3c82d4a438ec40033390fde66f2ab7672a16f8d7ae841d641754bde0466fa84b6822dca7766c7468205c12679594812d190153e54784665052961cdfc3fb3510084f44ea8bc47d00d8c7a0683a4178ab5e8725196334488ea9927448fae93ac72a548648d481122f166c8cc902161fadbb2325c2c4d89c79196a8648c49504b7036037337d85788b156865e674f6deed11965102ffe2041a7d9a55ddd53fe368828f6540fe79a4c4a1762dee08cb39524f89c0eb0a2a9246df1f85c1318c259f6dd6ae4dbcf39e91c9e3ba23f809bfd69d81bd749ddcb2bef679bfc6c118853e9736c9b5c808b4b31d254b444cd98c5ba104e6d368007c67e541ff42489d962c5462b5aa325f73e5e34c19998be96a705c78bb47a678f1a57819a887c2f3d64a37e997790f060f91c8b2760348802b1f854731003d21ab8f907c91861cc962804cb80c5cc26dd5802572c1594ce16cc57df24063289c582510cf5ff810ec43c2e0701b2a080d891992372c1979dd8420223cf04cbc401233d1a42fe646f8b2f2c98cd49cb813ab08b729a44f314c35884582d920e564659cef80aa61387029822713c2e868765a2917d8060c0115f5b6df3147548812216fd152ef4520c64e6a89de4a88946f7592dfc9ea20c61c8c1aaff83391fa71e6ce5e06f67e47e702663b137c275f5904e0c21c3b080723b75b492dcd03fc768ea633011ab3d3c4987cc5d96a92c22250b41ccf15beb885cca27352b4d9af
+InRec = 00c0003000000002c00030000c0002c0003000000002000030000c000100002000040001400030000800008000200008000340002000080001800030000400004000300000000240003000000000c000300000000300001000040000000010000000004000000008000280000000040002000000000400024000200000000080002000080003c00000000800024000100004000080003000080002c00030000c0003800010000c0003c0001000080001c0002000040003c00030000c00010000300004000300001000000000800010000c000140003000080001400010000c0002000020000c0001400000000c00014000100004000100001000000003800010000000020000200004000380000000080003400000000000030000100000000140001000040000000010000400028000000008000100001000000001c0003000000001c0002000000002400020000c0001c0003000000001400020000c000080000000000000800030000800020000200004000240003000040003c0001000000002c00030000800000000200008000180002000000003c0003000040002800020000c00008000100000000200003000080000400020000c000380001000000001800010000c000180001000000000000000000c00000000200008000240003000080001400020000400010000300008000280001000000003000000000c0002000000000c0003c000000008000040001000080001c000000008000080002000000001c0003000040002c0001000040003000010000c0000c000000004000300002000080002c00020000c000200002000080000800000000c000180001000000003c000300000000340000000080000c00020000c000080001000040001c00010000c0000c0003000080003000030000c00034000000008000040000000000002800000000c0000000030000c0003c00000000c00018000100004000340002000040000c00000000c000000002000040003c00030000c0002c0002000040000c0003000040000c0000000040002c000200004000100003000080002c0001000080003400020000800014000300008000240002000000003800010000000038000000004000340001000000002000000000c000380001000080003000000000c0001c0003000040003400020000800028000000008000000003000080000800000000c000340002000080002800030000800020000200004000080000000040002000030000c000240003000040000c0001000040001400000000c0002c0003000000001c000000008000000001000080003800000000c00028000000008000100001000080000c00020000c0003c0003000000002000010000800020000300004000040001000080002c000300004000100001000040000c000200004000140003000080001c000200008000180000000040002c0002000040001c00020000c0002800010000000008000200004000100000000000002c000100004000340003000040001c0002000040003800000000000034000200008000140000000080001800000000c0000800010000c0000800020000c0000800010000c000340001000000002000000000800038000300008000040003000000003800030000800034000200008000180002000000002400000000c0002c0002000000001000030000800018000200000000200002000040000c0003000040002800020000c000080000000080000800010000c0001000010000c000200000000000001800000000c0001400010000c0003800000000800018000200000000100003000040000c0001000040002c0000000000001000010000c0001800000000c000300002000040003c00030000400020000000008000000000000000002c00030000c000200002000000001c000100000000240003000080000c00010000000028000100000000280003000080002c000100004000300000000040002800020000c00000000200004000100000000080001800030000400000000100004000240002000000000c0001000040001c0003000000002c0003000080001c0000000040003c00030000400034000200008000180000000040002800030000c0002800020000c0000800010000c00004000300000000380002000080001400020000800010000300004000200001000000002400030000c00008000200000000000000000000001800000000c0002c0002000040001c000200004000100002000000003c00030000c000380000000000000c00020000c0000800020000c0000400030000400038000100000000140001000040001c00020000800008000300004000080000000000003c0000000040003800020000c000040003000040002800030000c0001000010000c000100002000080002000030000c0003800020000c000340001000080002c0002000040002800030000400018000000008000000002000000000c00000000000
+Key = d5492977f04342e5c73557bce8b5f5c5a6c2e2acba0c8addcb834e797642aba6
+
+InRandA = 38cbf333053e5c9addf8537386b88da15212a3a81ae15340f63f194faf1e9f38addc51fbaa72470e210d2a97f8c3b988456389d8a0c6928cbaa2a752d3e2a12d23d2297ec417b003776169d5b5b1198ce1bc10206f9ecd915d680b71ab0f14a87560400c8927492278ce10d6e3470912ce1bb275cc098356dcd9a3eee19704825332c146bf7dea0e02b3ec22802fd706da3283bb2bb7aa58d17c09264bd8dba2776fa9ab08ceed27aed39091d4ca732cdb4880ad35b201d0ad8bacd61015d07fe1ad3c9b1865237a2f28afc16b19311d61b3ba2021a8cf1d733f61e3609986aa006ba6053d1023b1e5a0b797900cfa59a1d5b40ea466830170f5187a4cde089752f4467b9ed487ea85335b4f7b2a8400133867ada22db09d79d478d0c9a88977450154bf74201e020c2458b29ec3d25b94d0992e4f89cca758806aaadee52a592913358da200d006373458cda6dc399d2ee4c765771a6d7e69057345de1047611125a9a04df955e71b3a026a5d5dcda81035470227e5188090462e64a806b9d539c0a13eeb9635710d516a10219683c459cb397b467e0fee4bb4d4d060d001093b93297889499c3698428107aa58f76c012d0e95d046c2066b94211f005e22a3b5ce5968a8c47a3fa9ead41e925356820b85b12ca6d588525184ac72ed2de5aa24e2716e944dd20d3e22e0c10b163c007aba809f95c6e4a1e6ddf0f4515e1094ecf7426a747f1eaada64b6ed1df26ff3e474a1586a090247558e050d162e1583151cd9694cf650208d6d600663ddda518fd7438036064c925bf5a5398e4f18aa90193100ae980b3c2633f43da46f1cc8a19c4bbc6b4cc0c595aef60254cb96350e105100715b084c100a5553337d107cd78444b1865814968303aaf28ed9c2edd7effcb51ad73146bf4c8900a15ff60449a19566b8ca6b4a75a76432c397d81e2eabec411ac409118cfe53b25041d146a5f8e579bcd59978a5afce582a519331a325b4beee5b4361199bfac6362bc76506138ab9fb38b11e01d6b78a2266b2f00a329a84a1873bdf85fae39d29a714c0d891d9b31647d052cee828a902b268eb593aeac0c45a55cea69c429040c5a1763c0f52e394fea300108476968529c24cff510ce92b97a6d2620946bb3c8ffbb518da2bfeeb69e7cf41b1d39859b24824b3f792f8758fcd295f21c3d6e428281a42a740f67896454ce5d50d8e984c9e0309828d303bcb2117c31a10a6cc7917c8ba803c464d06a2ca146d6e9e666045b78dd385356be976bc0963528b7a4a42524942a63921245a7a089ef92aba50afdaa629ab470fab604ccc39869ec496ec2c2a102e1871a54c5378880f05c380304067392629d5a1f121674ae09870e082ff29a1269a22ee9316e889071f21199a36e522baa3bd43e81186d217b57a022045c98f2a6b9a32b9dc9ea276256900feab7ad036c53202297667fdb681e996cbaad03269a01bfafa74d00f6525f5534fd09800155f74cca1206ae6e6d0e058a0538c5d8480aeb1ef4c755b21222ec265de576316b63829ed37cca935e154f10580c05c1d8f51fae6d6643c43a9d546b07deb89afd9780c5e7adc0baa69383f6b235b11e87a3660531039e89e00496a9be8088609318e166a5d0272aa473617359840e79c95faf9cd9f08547780cfe594d1aff6352b7d6b1468148bff2c6c37d6660aeae9b482543337b6453f531e805b60526fdd59e5c1ccb83568d65db81c1dabf5afa573a7190cd42ca1e459c145a5430b61eab011e28f965befb658b8844ff7b056777bd80dd2097245120f8e5887750d386b28302a7279d700d3772eee28f3cb68e562328a3226437b55b9b2be81d1318f5413ef15de9a48fa50a344b6c51399afca6a82301cf6417213014a5a45bbcd90771ac2397e19e117d5a92d0b94c71965a001159feb9325b961ae725b0b14722aa3a906b6b076e9bea80d7af7c6211c28ee6a44bd4c2eb6cfa93a332489572968512818799687422122a59ed981f6a3bfcd928f9f0372bf64b4db4cd315597642d80c7683f85839fd57eaf80b1542f84383e04e313b91a6a8798a729e02a530a621b5912a1ccecbfe7da8e067a543526130e06537ca89a20399e775a2e34ea739eaf8e04180043f01d74a9a954a4a5b6b87cfd43fa4e1a9e36beca135e56c23f2756791cf1814d76537074575e18c09a5f68187c34d2223358f59c220b5229f69f948156e9f658d74d59292542ed226b8cd4e074ee6d0d0d1c64a6a9c1765e90c67f35bbbe342120d40826b174a2dbc4566309368500010d289e942d421bee3e7c5b90471ff8b9be754c168ccd5ef640e56bfb2d1628146dc0a752305f41316859ecd263052a90c149cf2003fdc791a1b4e555cdd163eec93346742b8199eb17ad1c66e342b991a1cf950225532e5ddfc8cb0e36d3b390d253a193b955a124e01606f51e14e20c0435a36924a2e79c5ddf12d126c73925a38c4f8109d09737459fc6826ef12653648a52665f80664b147ed544653d3ea09259b511be4c406
+InNoiseS = e5c40729d83a968c6b2ae550c89b6623e77579f20c14a6c8cb4a623b25da3973ebcc3ee5c92a34345957fe5d10827aa05da022a399332066a2679d11e83e00ac2be799dd3eb7a6a4c787e0b59fee900013ac050b77cbda453cfd06f0de51694b12433fdc32e37b16a8e60d2bb02b82101415850212b676144ba4145ef47731af3419fc8e406be62485d7da056868435a8d1c6a52823fd4ff3db975998aba564e7274525407c318a604a4dc1360c6629f9d425963019f2c9d05901e38abfe87ee51e4ea7c8591ed84c2f991669d6454d9a1b3b88e41e9309e94ae8fa797a91e27fe08f09a2c5e826b44b1626bc88098a4280428c05ffd8129939d450bd912784ae55e13c7044bc593e9406c1ec0c262ca59f225f60a812217471d1fe6af221c98a43135f6f6696441f17b698d92702f811702ce6b292e852723d21f996675c77070427be30a70f8fc8decd69c94c19c88949ac9b528261a26a77016952a7e91a17ea81be4bc4f2175d8b44835472e1606d0059a2e4d86c236cd9c8f27dbe9fb71800b5c335862f62acd0bafa1aa6821d585f7e6b4a5718a6020d4c53244b99d80718a09621766b7c158ec90cb0dee4bd630a9264a49db854e3af0126b17359842956ccca27e498df495c220ab9968000c021a7386464f958f8691a66c671186a26e484f04e5b29112e409ca5f366091d69ab4492f52e8f6abc7e561394ae89ce050bb776b7c3cb3c334f9e5da9fd3af1de2c68a2109ec683976a24cc4255139854c4a482fb8555e926c41a96db8f62e62f4ae6783bb48622a8e2373c7b0252dc40d52b1713fc48b0058b1f65a0fcdb3b8d07c3febd7eed55021773a58e9017aa90934c55f5799d53ee4aef2a8cb2209c01fafba6151be916bfdf15862204c52adcb0da5231408ebca29c02029cfda60baaa5aa09da3f3719495fa8de6107a6d175b030d8b84fe4e5b4f17032f447a20695f349ce8124d868eb7f2dab6820e6f78115c53514ab631f6fdbf3dda71b1be890d12e5bab273a9312d2afe739b6d75c6cb6446c445aa0198eb862748b55de2054acdf0b54bcd323ac6f27f4de55e197f7517210142e179d81afc501a88661d89be6c7e6aabac160704a5b6c1a2b829d9ce7486418fb92cfa23bd1ca3872a0b67b5ce4c62caf1a495bc442c11cdc87c06563d1a73941ae907d46ab77059166dcd11d12221582bb49ae411debc6cf36a9ed3c68643f7c18af0cf107e7dcabbb221cc201023a6156bdabde7e9b12bb3c64e0f0fb92a87ba2c59dcb8a5d48d97fce343ca0e69210b1c4d01897141ec4627e3a7ce05f848c4c558e0af50c36c053b100df69532935f46293a29c5c8e24ca3ade25bb427712f80263b736a6403cdd4c47b284d22ba4575d066d202d498e86b6ec23dba8abe87f63e375b0b6f41601c68fa1717e67b654ca21bfb428a0ca7ff5d2f51447e05a56e550a6790657cc3b0c736922fbd21b491694a64a11f45108b605f06d8610686f2912f504ff07f313edae07930cf8a4e838b0606a402625c494205fdfa764a5a9416853548364b0cd84ede3d3ae49af22f4d01ab127d31264eb8ace5d2554e55290a2d8979aaf88a761582f5c92a19f49ee4f573d54a61a638703e7535eb465bb6fa3a3dc1ab5cc452d826ef6e818a65aa842f1b60c57a50354fdaf1518f4d94c088ba049a5d963105f2a8e0819da6192361240474c5cebb328cdd907a2e04b0bda41c720c23c808a0918e68fb83dfe12684a4bb471eba7a0d48e75847a44042220ab85b412048da1981306d52fc659f34876d16ae89ccba803080b9268a8597bd57f00dcf79012cbb0528f9830864a129a5495879d228ef86fbc5bb39ac450b196a97200a1829323781d64fe981fac57b5228719adff3ee5e72c29af5a18d3e9635574cb65b4086e92fbb1ba62d36a823139955d186f65d05b6bc3915e5061416f548837d532a453aa50eb26848121a3a33aa5604c678249c676c8ec973c558f2e8ef88fb105998550da3cffa09838974403e8610370b2937bae154f488bf31c816890c83b878cacc577f2a3bb256687025c17cd595614fd2e803d1fa2891634772badad84549a71a5ae83e3b9418f6f9f6ca6d4a67d9c30459554bee4ce413091e1ca092a507d9685fd259d5f0b991d0645dc6d48a1fdf8171aa8e4c210c6cef97360155acdbe19b55f000012120e221240964cdf57b09900791e02f5168ddb798e3bdb1056298ef04d00a1bde9653e073e8899530db2d30961a4a58d604ae055ab56e5a0dcb77403a41e51b5d0895c675900c9e253d1851e9d87eabf235056bade602f0952ca1c8dbd5cd4a59324d5dfbc309518b5769926a4c7863c51db8a4249c6fce71c92b9c086c217f987a38569d923b01f58c3c3682a5a796223f89c7262160226cd8ef3730ec71a43d4e3053b2f15b855e3934501a90bfc6bdd9c3d6003ce2d86076215ce2db3e31bdd2266c8693e020129d63576617d3c68127608b7566b4be9dab7576b4167fa0cf610180153207d5ff9413488a
+InNoiseE = 4dc2a8f9bbe84e148d7f5389a5a28bd7162a43aa2e3e9415d944324e10c17885d0856af0495cd531401e6a970a82d439943b6ac520527c2afc2f3cbaf4d401178b9293111d76a14bb1407ad61e05284f6398020ed2db4ab42da6abeda9b9f631646ff2eb2300566814e225332580cc4c5068eb176509703d188e39354211a6944e125510006b972fd12f218efc8f231200c1062e91e2137931a125d2cb4b4e0d129a275cc766351d7961a589d0c254a19651073a456a80e6e59b28b00a5413eeb9e9e57bee93528388749cd2c08b89e5d1674a07e7247db53ea14b804413769be256ad01de518192c0310621a26bc3809313c6c877972eb9b9dfd91e27eb33468fcc99c14012f02ee67ee6479886fe208eb41b47d03d9d3ca3138cf08f2a8d9064a1c94e69ae5d8fcc5181bca3556bebe67c09ab47c7237532c03ebace244854b25160d455ab00b93bf3ae4822118c9c49aa42e149523a2c6badf8feed721ad8d6e56b26a0ae6836ca486e00a24c522a9544140701519b5cbc4392c72cf60b6c5e85d44ae4ec2077971640f98b361fd09e16252ff02dab937308995c8a9905674761b408a38c5fa6671583844bfcc5e0041088450940f37d5880999636a1fc8fba69264bc79482d7a4b2fa33467af8048924870e6e97d660b58d01b85d501f2566d5150aa74e8b7b0009a14449714d4e34de2fd7a87541033d1da5b7aba57081edc539837600239a870669f52c45de3585b0710f2a645d1807c1b9526df45445857f78018067f65d051a5fb96413e68792647b8a94664c5a2430f4b4e280898af079c1b95a456695d21ada7540af9490b109507d48b563bea1bd214988d7241a18af9f85901f4974b45afe7520ea31bf97c958bba2022012d755ce8cbb1701e7d730fd48396165309321bb2f5c45c9c8a153d22366849304277050dc31751d21db9fe6831c8506769eaa5a6bf4a1ab0adb72b5a003d75187d906e679b06dd63118a91b37a3748544c97eafd44fe5c90abec58acf93762471ed6b51a330082e624655d7846273aa9a78f667e47d4fef8c0981dd28896ad516288c85dc476493951a4cffdf8aa3ad706a4e6425b3870a5b27ce517afb1aa8808092bbd7161dea9048c9015ab10956f4c0b9e88b6c86325b5e6571df5d3c382dd94acc55ea65caf452bd1e3188c694b66924e793ccac23625cc7033cc2a05991d8bbc71e5c7c851871e2da8af241e198c3285946ad1603dc7990ac0f436a1bf2a8ee009c53c8e51eff9b3fe69ec4a5bc661a98a82074bf41df22362a32aa5318a800563bf40b413740dc1e3c0b20f160d804bb147ec55f5e09c8c834b78a8120e80c95aa84de5c0459f702257c6ccfdbfcb7a996970a0a3ed0e72ea11e09006a47a8424a494b383602ad9daad233befaa70598d717d52cb7e44be1b3ee4e948727cf071a8cb05e54a766f54e7935aaef25138db4d4468d627f9a090a689a7329718de61efa323645850685ac742e0e7c725ad006fb4a7209d2d1b7845e344b2ad9c29bf979bdea397a8d3097474a20c2fbea8071665942536a6e1f2c3bd7b54400259b3210147610c12047d705cc700e1f2d7879fd63fe69c554b64e02c76a4a1ba4c23b7f4e6fb000ce894598f27049f9ba0c257d5160e27b214ae151da0479f1069df0f4814b4b8636d84c0c963d2887a41cc867209c9fb219bc9847c10277f80d32ae40f88060f651739c77689f3640f1eba789a254b0cd191b61c82a48afaaeca4194031e8417cc0144d63c4643616dd2215a980236a02c60f8e67d7a42104949d4a1ee8d38cb88cc7a6b5674904984a894966e673578105ab3274bc3b015ea992bc6a70994e845669858b0062c0d1fa6defeb64c3d3e0a8a49e4de6892980c85a901295a479475438fa59b103dd1bd083d79c193a89db61d18c7d2d8410d5b63d3c04a85464713e8a856abb17aa1c935e5e2c791b0d5b2899bb2088127fe8907b7390f1bda841f48632561bbc2ddda6f2d01fac069629f63e1ef0821b388bf86966e178d82b3e9776a47bc011091c5e02dc12617c681be877584129617781a1d2a8b8a905e124a63620ad74cee68bc0d1a0cb4bda493f353c93200c533bfc6c94800e88895e045e09f6d4faf662468143955858768150746aed404d3e81a1f34ef9083761b1393b008ad524a789cf5da8862cb66b70a800f7a84e7aaef13456483d4ab8e8045659615237e6183f4be799583822b84bd397e7cc9af9865b70443679f52638910e7d3ff5c7497af2054e31927032e47d9b3a5fbae4613853061fecf8cc3d46a04aae5277021b0f2a4ca2cb6027b66d8d17d8089780a39794126d75744b7294775ca0675e3a32f7b62ff0ea6b033c0e3f77e39bea7e58b86ea0b973923295a7262b71657973f69133470da2565f4c4f3b24ec9bd5c309eab95db467a355e8cd385fd79fb766cd9d97a182438a56e11b08a3a57bf1aaad1a465d3557ba552a0168d4744d6020020b312eed92496e5a4502650c1529b5d549b98f59dd7831033e41
+OutPK = 41aa3310e40a5c72e000a825b951fb43b8145b3d240dc83aa705fd81a585a0b20894b09bc641244008319d5674e3dc916955877d153c3d9c04236e5513c67d535c704922ea0af16cb02423e0a8831104f6533c98264fce6a59d055ddc55e15a986667cc8c46397e6831850ed94d3416c320ea1502570936b9ae649d3c22a6fe104c30c15a6a915c6416ed891016435c8db948cf796a99189fa8c1601d1ca03b93bf80bd47591342c6dd9d976c77ea631e2b0d15eb569f2d9cee48d4a4e7de8c17a5f8046dc83f83916fd6ced61cbb6a2058ff22e685465e5ba97cab0312f5669d458f7d596f2171582afea02549766d1aa09aa39054080b174574875ad0579da369079565ba413a360198f92f1fb0a3d3ba467c3c5f1218bb6a9cb94373e1e0d0bddd53d24bd05c101db21b2b41a8b20822829bbfbdd3a93821c6facc547152f71771492a0c2c709aa541b5fb0a43a50d9ac5fa414509d09d448f4f271620c1159026768bc46ea9364e906571fab6976105d34ae22cf895fcc087461706936ee0a0ea4a8f9b73039cf18b874a7582ac3af68855e1e9d8928256158b801ca93178a16ac0f61c6d0234549abfe1dcbb9386084295d43a4324ca3015f575235285e6a97a125fb088706dd0bb7922194631cb0b46fb11e66c5a2f753aa3812d74c62519230019f6f27759dad24ee2365d3ec13809c920abe9c587f817d265e1d1c551fb917e85d53ded3e530afd648f72076aa0f396d41d824dbd46a79ed2e276b0025bc49e4b738cd166ee9134b1366558ed3a3f838b6c55f2bceaa6b6a55ac181d591a5c3d69cd4be59a7ea68de6e00ae238ee37475c0221c2f0785fa3deaad4eda01505480a171095206794330a004c37e9c0a9af38936bab9bcaaef2496eeac863f392295eeb6fd793f681c39a3b255f4e91228ad9d6831039da8b9948da25b830102b8468be17a5a317732bae56f407a575e8448084516202435c22d42b1e08e8b127498f3f50b99e08719e4595ebde9b54a76d7b8e145ec48002623580be54e99c4696bd4c47d28f96079596311f7ee7951513040a9449f2a806a04f20e4d5f25a23c84f40262ab0f144ee2956b66c7c8e2c38ff0c0a29df461080cea94c470f90ed1635340949280ab15e9189e9f054e5a39797a291c724162f64df6a4ad93b3a4f80845411124fdfd61320a4176c04cde3011a770add6870e36c0b8594083ad7d1246a22ba1611f88290138d79f6249fde82a920fe95a0c3d1ac017ebe55205eef41a8c01f5813c05a70c11908a37546e4101af12342c70cd05d03a1ec5404ada42a2d29a9fc8811805e5490a0317e1667dacb21a9a1d2489816cf1e2deaa2c708b5b03216cf2350546c062bc6e6e81b52a851826e9aef2e4ed55d18e99f8401c5db7709f659b808007c847e90754cdd7c55d850b0fb5077fb606867e3e00b9b109552a61cb187c6416e0729a5294f4e6ebf892be54ece31c74507237edc5884987237d5c270f52a86c429781648188479268e9e7eea00d12f6e321857d7948d388de52dc5d52d46b606848ac970c8ad49610ee8785470f71a10cbe3e48bdc497e5123890e1a93586089cd917a14fea4e4de89c885db8bafd2615a6855e1ca1a303443af75ef9d1239c3465bc9e0b2880b89b0d0140e11c147e894fa1096e75c4b863e69ef2f2c45c4087d40dd64130b58739e445bdb141a7f39d7d9a2032da8bbecca12d5aa1a718040834fec212f139d8aa4f50ee5159ff5102c2dad585ce19c8972b215f761e87d71d45174c4486a2c43a4435400b986cf8727c2e2009127405e39b85e1cea1e9841912567200bc6e5b2fc52ac0b895043228292adab063ba81ed049f5e1e0749d3e025ad16459b4ca18db00ce61a164d9d352043a6f44e8e7949c393a0aa56fa0bf8d065e531cf570a09e1f21b6c7ee507d6e803999296576bd243f3410e42adbab7e19a3098ad75967c248dc0359cc68a5d6ac1c6c81fdc1c1f6a00f530aa2facc51b0ab9cf8f408afdd2967dacb586b721a54f7363d87f1af0ed354d017b567ae0b2440279f4c5e237360018194a8e38bba66316d7634f1002d5d245e5019ad08090a35b732e84aae82ef9bb329286661f9e5218da95a6deb6ab7416b551361444ed890866a8cbb70ea59b20c05a64fbc94efed92f72faca2584d779476d41c5427d9119ebd6d94af6ef9306637cc4590f705dc5f28007568c7748d49248483cca2accc1a6bf008668878c853a1d09c247e0c3854dbf55647ae9ba2194c4a111c99e032f830b69b9ef8c45a864c23876343ee870290ff33b8241a4a95808a0fd17217af5803d5f83e676d29914290ad98addc43f77a85fa6623b0a254af2a87ae677c18029049a225681da84ccd9d37ddd50b8a205c7b9f2e140256d278c4c858ffc9fcb5d7c2aa302abeb15c6c227f5a4508dd0579164264fb953487f5a077d9a4ed2fac17dfe22012a327ae89a1e6b99133ef6415ef953056117f9b476d58d6865f577d65576559a03660a912789870fbd42520
+
+InPK = 41aa3310e40a5c72e000a825b951fb43b8145b3d240dc83aa705fd81a585a0b20894b09bc641244008319d5674e3dc916955877d153c3d9c04236e5513c67d535c704922ea0af16cb02423e0a8831104f6533c98264fce6a59d055ddc55e15a986667cc8c46397e6831850ed94d3416c320ea1502570936b9ae649d3c22a6fe104c30c15a6a915c6416ed891016435c8db948cf796a99189fa8c1601d1ca03b93bf80bd47591342c6dd9d976c77ea631e2b0d15eb569f2d9cee48d4a4e7de8c17a5f8046dc83f83916fd6ced61cbb6a2058ff22e685465e5ba97cab0312f5669d458f7d596f2171582afea02549766d1aa09aa39054080b174574875ad0579da369079565ba413a360198f92f1fb0a3d3ba467c3c5f1218bb6a9cb94373e1e0d0bddd53d24bd05c101db21b2b41a8b20822829bbfbdd3a93821c6facc547152f71771492a0c2c709aa541b5fb0a43a50d9ac5fa414509d09d448f4f271620c1159026768bc46ea9364e906571fab6976105d34ae22cf895fcc087461706936ee0a0ea4a8f9b73039cf18b874a7582ac3af68855e1e9d8928256158b801ca93178a16ac0f61c6d0234549abfe1dcbb9386084295d43a4324ca3015f575235285e6a97a125fb088706dd0bb7922194631cb0b46fb11e66c5a2f753aa3812d74c62519230019f6f27759dad24ee2365d3ec13809c920abe9c587f817d265e1d1c551fb917e85d53ded3e530afd648f72076aa0f396d41d824dbd46a79ed2e276b0025bc49e4b738cd166ee9134b1366558ed3a3f838b6c55f2bceaa6b6a55ac181d591a5c3d69cd4be59a7ea68de6e00ae238ee37475c0221c2f0785fa3deaad4eda01505480a171095206794330a004c37e9c0a9af38936bab9bcaaef2496eeac863f392295eeb6fd793f681c39a3b255f4e91228ad9d6831039da8b9948da25b830102b8468be17a5a317732bae56f407a575e8448084516202435c22d42b1e08e8b127498f3f50b99e08719e4595ebde9b54a76d7b8e145ec48002623580be54e99c4696bd4c47d28f96079596311f7ee7951513040a9449f2a806a04f20e4d5f25a23c84f40262ab0f144ee2956b66c7c8e2c38ff0c0a29df461080cea94c470f90ed1635340949280ab15e9189e9f054e5a39797a291c724162f64df6a4ad93b3a4f80845411124fdfd61320a4176c04cde3011a770add6870e36c0b8594083ad7d1246a22ba1611f88290138d79f6249fde82a920fe95a0c3d1ac017ebe55205eef41a8c01f5813c05a70c11908a37546e4101af12342c70cd05d03a1ec5404ada42a2d29a9fc8811805e5490a0317e1667dacb21a9a1d2489816cf1e2deaa2c708b5b03216cf2350546c062bc6e6e81b52a851826e9aef2e4ed55d18e99f8401c5db7709f659b808007c847e90754cdd7c55d850b0fb5077fb606867e3e00b9b109552a61cb187c6416e0729a5294f4e6ebf892be54ece31c74507237edc5884987237d5c270f52a86c429781648188479268e9e7eea00d12f6e321857d7948d388de52dc5d52d46b606848ac970c8ad49610ee8785470f71a10cbe3e48bdc497e5123890e1a93586089cd917a14fea4e4de89c885db8bafd2615a6855e1ca1a303443af75ef9d1239c3465bc9e0b2880b89b0d0140e11c147e894fa1096e75c4b863e69ef2f2c45c4087d40dd64130b58739e445bdb141a7f39d7d9a2032da8bbecca12d5aa1a718040834fec212f139d8aa4f50ee5159ff5102c2dad585ce19c8972b215f761e87d71d45174c4486a2c43a4435400b986cf8727c2e2009127405e39b85e1cea1e9841912567200bc6e5b2fc52ac0b895043228292adab063ba81ed049f5e1e0749d3e025ad16459b4ca18db00ce61a164d9d352043a6f44e8e7949c393a0aa56fa0bf8d065e531cf570a09e1f21b6c7ee507d6e803999296576bd243f3410e42adbab7e19a3098ad75967c248dc0359cc68a5d6ac1c6c81fdc1c1f6a00f530aa2facc51b0ab9cf8f408afdd2967dacb586b721a54f7363d87f1af0ed354d017b567ae0b2440279f4c5e237360018194a8e38bba66316d7634f1002d5d245e5019ad08090a35b732e84aae82ef9bb329286661f9e5218da95a6deb6ab7416b551361444ed890866a8cbb70ea59b20c05a64fbc94efed92f72faca2584d779476d41c5427d9119ebd6d94af6ef9306637cc4590f705dc5f28007568c7748d49248483cca2accc1a6bf008668878c853a1d09c247e0c3854dbf55647ae9ba2194c4a111c99e032f830b69b9ef8c45a864c23876343ee870290ff33b8241a4a95808a0fd17217af5803d5f83e676d29914290ad98addc43f77a85fa6623b0a254af2a87ae677c18029049a225681da84ccd9d37ddd50b8a205c7b9f2e140256d278c4c858ffc9fcb5d7c2aa302abeb15c6c227f5a4508dd0579164264fb953487f5a077d9a4ed2fac17dfe22012a327ae89a1e6b99133ef6415ef953056117f9b476d58d6865f577d65576559a03660a912789870fbd42520
+InA = 38cbf333053e5c9addf8537386b88da15212a3a81ae15340f63f194faf1e9f38addc51fbaa72470e210d2a97f8c3b988456389d8a0c6928cbaa2a752d3e2a12d23d2297ec417b003776169d5b5b1198ce1bc10206f9ecd915d680b71ab0f14a87560400c8927492278ce10d6e3470912ce1bb275cc098356dcd9a3eee19704825332c146bf7dea0e02b3ec22802fd706da3283bb2bb7aa58d17c09264bd8dba2776fa9ab08ceed27aed39091d4ca732cdb4880ad35b201d0ad8bacd61015d07fe1ad3c9b1865237a2f28afc16b19311d61b3ba2021a8cf1d733f61e3609986aa006ba6053d1023b1e5a0b797900cfa59a1d5b40ea466830170f5187a4cde089752f4467b9ed487ea85335b4f7b2a8400133867ada22db09d79d478d0c9a88977450154bf74201e020c2458b29ec3d25b94d0992e4f89cca758806aaadee52a592913358da200d006373458cda6dc399d2ee4c765771a6d7e69057345de1047611125a9a04df955e71b3a026a5d5dcda81035470227e5188090462e64a806b9d539c0a13eeb9635710d516a10219683c459cb397b467e0fee4bb4d4d060d001093b93297889499c3698428107aa58f76c012d0e95d046c2066b94211f005e22a3b5ce5968a8c47a3fa9ead41e925356820b85b12ca6d588525184ac72ed2de5aa24e2716e944dd20d3e22e0c10b163c007aba809f95c6e4a1e6ddf0f4515e1094ecf7426a747f1eaada64b6ed1df26ff3e474a1586a090247558e050d162e1583151cd9694cf650208d6d600663ddda518fd7438036064c925bf5a5398e4f18aa90193100ae980b3c2633f43da46f1cc8a19c4bbc6b4cc0c595aef60254cb96350e105100715b084c100a5553337d107cd78444b1865814968303aaf28ed9c2edd7effcb51ad73146bf4c8900a15ff60449a19566b8ca6b4a75a76432c397d81e2eabec411ac409118cfe53b25041d146a5f8e579bcd59978a5afce582a519331a325b4beee5b4361199bfac6362bc76506138ab9fb38b11e01d6b78a2266b2f00a329a84a1873bdf85fae39d29a714c0d891d9b31647d052cee828a902b268eb593aeac0c45a55cea69c429040c5a1763c0f52e394fea300108476968529c24cff510ce92b97a6d2620946bb3c8ffbb518da2bfeeb69e7cf41b1d39859b24824b3f792f8758fcd295f21c3d6e428281a42a740f67896454ce5d50d8e984c9e0309828d303bcb2117c31a10a6cc7917c8ba803c464d06a2ca146d6e9e666045b78dd385356be976bc0963528b7a4a42524942a63921245a7a089ef92aba50afdaa629ab470fab604ccc39869ec496ec2c2a102e1871a54c5378880f05c380304067392629d5a1f121674ae09870e082ff29a1269a22ee9316e889071f21199a36e522baa3bd43e81186d217b57a022045c98f2a6b9a32b9dc9ea276256900feab7ad036c53202297667fdb681e996cbaad03269a01bfafa74d00f6525f5534fd09800155f74cca1206ae6e6d0e058a0538c5d8480aeb1ef4c755b21222ec265de576316b63829ed37cca935e154f10580c05c1d8f51fae6d6643c43a9d546b07deb89afd9780c5e7adc0baa69383f6b235b11e87a3660531039e89e00496a9be8088609318e166a5d0272aa473617359840e79c95faf9cd9f08547780cfe594d1aff6352b7d6b1468148bff2c6c37d6660aeae9b482543337b6453f531e805b60526fdd59e5c1ccb83568d65db81c1dabf5afa573a7190cd42ca1e459c145a5430b61eab011e28f965befb658b8844ff7b056777bd80dd2097245120f8e5887750d386b28302a7279d700d3772eee28f3cb68e562328a3226437b55b9b2be81d1318f5413ef15de9a48fa50a344b6c51399afca6a82301cf6417213014a5a45bbcd90771ac2397e19e117d5a92d0b94c71965a001159feb9325b961ae725b0b14722aa3a906b6b076e9bea80d7af7c6211c28ee6a44bd4c2eb6cfa93a332489572968512818799687422122a59ed981f6a3bfcd928f9f0372bf64b4db4cd315597642d80c7683f85839fd57eaf80b1542f84383e04e313b91a6a8798a729e02a530a621b5912a1ccecbfe7da8e067a543526130e06537ca89a20399e775a2e34ea739eaf8e04180043f01d74a9a954a4a5b6b87cfd43fa4e1a9e36beca135e56c23f2756791cf1814d76537074575e18c09a5f68187c34d2223358f59c220b5229f69f948156e9f658d74d59292542ed226b8cd4e074ee6d0d0d1c64a6a9c1765e90c67f35bbbe342120d40826b174a2dbc4566309368500010d289e942d421bee3e7c5b90471ff8b9be754c168ccd5ef640e56bfb2d1628146dc0a752305f41316859ecd263052a90c149cf2003fdc791a1b4e555cdd163eec93346742b8199eb17ad1c66e342b991a1cf950225532e5ddfc8cb0e36d3b390d253a193b955a124e01606f51e14e20c0435a36924a2e79c5ddf12d126c73925a38c4f8109d09737459fc6826ef12653648a52665f80664b147ed544653d3ea09259b511be4c406
+InNoiseSP = b1854163d7624f012383189759bdcd5ccb464ded5d1cd229a2c4fd610eeb3a568e2900ed165616cea24d09853c0ab8658ced40e7751bf1b34ae4c9896ab621992aef37b9041446a9a3495cd942ff266a04c5b9039b5f18420f51b976a1b4d9a81095f00d4cb4e87144284df1a470c25637ed724b21da34206369bb7a5624e341449421da69a3464451c53c3893919533443594761c30da94ec8295807b78e8ac8e80d05e745375a37f9027691b2a309e85d255bf7c06178ac212b90d1c3d262d9bf6096c5d806e39ada889afd21db68d6192a318a2fb09288c6180c7c4843129e0eb9db966aa317d127d4318f9a677e95294283452f8612635294a9fa31cb08bca6d4dc0a3e2239c753301080c4a142d08502fe898b288bd6c4230046c103103aa3cf84a65860951a0fbb990394aa03035b6d49342a57a86cf687860ae0f70544aa6f2020ee558e5911161b793d1764fc55aa2098de4bd9a6749ebf2298c9941de469b94891004ae06d944a7902f02093b404b684f5c6bb0b000bb608ff2b7a904f7a266aba00d04e30fb915d40893d411b91a0785a037477f344aa02a57e81ef7b88d49f6c0b589af5152cd16492b5c16a3cf4da893452ea4986347b22cbe8ef48945026ef68f822af9f6e18ca0fe403e12a1ba187412f2e6c4e59f2f0ece65fc4d45ecc7f47a058a5a55e503cb5a485e23573de071388365a81e76f98eb6ec4203f346e0b670d29464974249045239e119f545ed6a6f57c5e80ab15266c2a87d15418ab3588c218477ceb65679020f0a96d9c74be12fc064b999f5ce7a24e36d89b6714d3dd52af3145849689dadf603f2bd7ba3e3127a4c03ff2a90f7f1cc1a4388a05a1b6098a6236b32069c0f5baeb8b2be20125d80e9f3576c5b114968e1d9c43159a281a9c70c87291f6646f3e2a28796898132218890a62cd799fd1bb14b979a0758665c11b7a690dd05dd2e59b68eaaa98ee1fdc6aa111fdd2a11fa1150b99b10ad47ecdd6b2481dd63704c56cfad7ce0e7bc64ea169bf25f799a279bb12969683fe493069b7422bcefa49b12c964700a66dda50e525ba04d4f0be93aabb7e0b1335888a5de0e9c431c4592fb6d2384322c26f005a7a3134ebd48a5b299a94d6d7ae4d37438d55f69101b457a7198cbded8ba2f5d85360b70fb1476bc80281c77f4cc5b9521aa52e01443f09e30a0929a8aa804e9f0f5b52b1fa0c805d604989ed3d9cbb1c66ea8a7e160e9b01d0d642ecae2b91548023e41125e5b96ddccf5f35a89b4c0c79a7259614921b9862b6a91674d49a8353c63f89ad3eae1ea5040d176e28632209f5dd2d37182035d0b34b2d2fd49ab1b3885d55180691ca7c93bca936dd9f7b71c741b7eabcd71eba93b6cc8ed29de343e00a6a7a74ee6913b160bfb8ea4897fa97e2b8f2d27456bf945c121b29ce9e8c4749d14161f65d2dd3db0ca1f2f853975191e48aae5b7e684fc5ab599ed11b83b61cebba59e4ffec46830ea1cb11e2b5075f0ee4b58538048c62241f2d73baa39fc8223268f818660bb9b96fbd42ea50f7e0199760ff07254e913530a112f7c2b35a7177aae3c9f60d8a62238979d2d81988d084045abf10790beba42ee8b07a006098bdb42cac11dea20f5e2981880dadf42f59bce784f130da97c645c2f661b8167f1435ad8eea3e004459e0d5628bca93a390a530b70148fb51ca130dac87f96559146d66010d54820f525d469622cf13a5a8a79f09a1088178e39e794ec756ee15a351651f6fbcc4cb786f6d60e1a53cdc5251631f84e6d6cdbc9e9dd41165e4872f6ee35c0d8b6db7dc48d6384a9287f65461044d08be3c29f2708a57496fe966a659db13d496ce16c39c34aad76ab80a330726078a0a8f9429ddd7e9c91c2b3b1594044a658e3f46e3a079a98222a9a5074883095a6717ab807a4b2b77a4894c5c6e0dfae62abeaaed26ece6b29553413df7448fc4e40b7311e3bb556716ab2c921d36b1ee7e65111c6224c300e466a259309c39de94a7d5a32764c4259add929900845829c91a677fd39355e2d3392d0c547875a43f05f3819c1ed74a86a890cbfa8255645916b62a1dbeaaa5d94e80f54d831240ed266e07bafa0780f92fbb147109e2a65ba6536d01154cee3e577ae2afb8a3a761d9169b5afcd322f85558bd0a762be2821a1290af2c50c194a2a7a85aaaa1d68c11642de8a3c1a22674481782f8847b8330ee4091a281e9bd3e4a5925bcf54f062bc4aa20780497200e61e4bd537e0515fda23246c04943d0d3b59205c501610118ac73c2f33e5b15b3c49123e9e1be9b6b8984b13146497b53b48574b85c376565c5cf8e82d0049f3d591e652947fa027b011fd008042ae6cdb7c6e3e55833ba0b0a00cc1248405b9d90a17cbd022fcb049c0cb6f0516c2a566eb776be7194df155e3ba46f10ec3272f879dc1186d2b8851b938119656c563b95d7977eefb73a1929991c0f413b99049269597796688020ca0a6e401263ab5691934a3d971aac2ce626b811c7c1394237f3c91
+InNoiseEP = f3add13126a14ead16967a55997c47a25801efca5d3a8633f38ca2bacc15aea13b784a90c920c0ffe1519e275f8051fe6133ca42b6184899c44057c686c24b85e2341017714d72231f95105209269c6f5a6b1c6b1fdf0e82048d9b5b8ec355996aab3fdf5ed55e44b64a48d0421dcd69464568827a3206f41aa0f444b2b197025d03b9dea0c442ac07137e85854239248d080f3e9f4aa8ca86039d519d45b2d424e5928ab6b39a6658e65bc119006672daa2cb0c54783e455456c43862d10b050a2d1611144d2b5b21d6668129ade5ef1165ba58e0769c2d31fbd19ad144cc38566e243b83362d51c8d72937e54d10db9e4428f4aea5ab7c22d22d07079698d671a8b7b58edfc181d53fafd1a2c0034d8c2382807b85f82f9a025320ea7d768da0cd03aff8b7eeac270883cd27dc664976e301524aa8eec82da0163cd6b8b456881af113349915c5ae9515156bdb820018c6b895c2357e93e2e1b04acc54c75fae61ce1882666ad3a95f7d10b32c73f2c3f97d215c34a36391059742b597e0c26a30103b8824311500dc416a33c68328266394f16a57b684c21b2b785f77eed6e7c5ad1f7967b6095912455c48cd96b6a2629a82ce277c3a0c2b01bda0e02d6c201b2f249e6d4ecc910fa38c6d703b868a637f050da4c831390bba28b65f39d48824bbe88402c6794a2c4f0b63b713f09c69592cc79b7e5e5b224fb8a38c9c2581e7602e5039b64ca7f468e99e1f6bef7878842d5ba1cbe54c5dae6ca7c360ea8488681bf48850b805bc82a1d07bf630d90188aa68cd1edfc22df57bf1abed5a10f230f1089a40c531a841a15e094615c732a42a4005c3815d2d272711079124400f23e9a7d0f112c5db8f02b958621ce6c7d6010c35249fc5c06e2c505565de5708a6448707c9a5ff0943b709c046fb2d00c5e2b4b356f15bd0ac0d2af0d237134acc6b2ae5b2d9436870aebe62f26415966695a42011bf301775340f52c7567924b9d4e977b8d03121afe7b5f111d9a5a6235f054e8a98078d74d4ac3a6b7d4b5b555e6d870c032b8b4d451898abef511add7a235c6aa3d62e2b40e757b876c6ab7addbee704cd39beaf3164495a95de5083d4a26833f3a75e397c58598a5e484be40544ff1def8329806948c94238f5ae0f38d4f287748ea04e1c74e7b9f45ca426cdc8aeda5c940d7c3940e68926d4523a5c4ab89fefe9109876064fd710bbbb18b24a9f149a8752a28d4284346bf1b76382ca69f000a6d8052200ad202464d44072820c3f012d280b310951d2593624b80485504bdc25749e166d929824057869a763f9849671a349267b8ae75a7fb1274b53f02d3f8f98e822ad1461d35faa83433a1f2e0eb3598ef23f0aa765b6f96506fe50be77dfac62ef0f74d0db752d0ba8a0586eb51d14a83e69ce197318c08562b3727597ceb25b48fc395211c262f618590d834c602b4c6e81c93fe3f8558272e25dc6b184a01f8cb5e5b7d66c102c7a47fe55535067690440b2895bc96b5b73ea2fcca448e9dd389b725e91c379f296e313d6e44c40b575aed932fe628341c066f36dd8053d81776973448191a3f2b5cd8c52e3a3a7416bb234a59ddf2567ae92839e1a7a71aa0d175f0503729a1b6f5fac31ba5552ad481ce0ce8208ea98ea3a33c1acdce0c31a45f06c8c10cd4127f24edc116a13db464a7fa00ee7f92629918db067ed9e4830ccb830a95840971a492a838217dca136c9149c2f245cda65f58c4996d12639049a648ec6845518488e1ae8f4d23c14102b9d90b38b6a1b47d44cc7fb458a230e59c4d42e366414b6577bd567c7ec70d779ed277ea4d3e7107a1fd20d9ff85289bc7d626264544aa8b24dee58a94e12871b23ab97945f630a6125037087924399a8a60c8a66599f82c1c496078ddfa400e0be15a41849d940a8941d10a322d951b98522cb30426092c5419882e06fa0cc41119b3e540e240c506d2898305b5ed2c9324310c530d67a7072870269aa338db4a26551122f62abe129fcb8f0163975cb8636360ca331d966902b9d28dcc016549942310209a7c64f261e998f6421c074007adee784c4beba5fc2f1c5a9a6a40907dea8dde52c3c99b330cede97e06a4421bea9a073808d351d2d411d63d1ca113939ada555263e455c7e19a1d8c9c271fe402191dced3d0c01718607185a329e42216de02d9857da9936f2b2bc47076b6bc92672e831d66060a5bed6b077abe2301a60376418a125e9216888f5e151858c3ee799794ba347f5b26c18608c93f2b09d0e87d8ad03521c1100181581e617a23466ae3496878cadfe5922250226403c0706204161ab546ff9875c910b694c69f0d3c08a901fd3e8f79e052feb55d1010c58d590b8295ec6b09f586577ba1e2f900ed856140020aaa1f8e25945bd91add9b0109a38f75fc886ab422a37e673206c8612861e9093d2f20293c7a42346b05e32d14f177f8586c74492e0f27e04376f88c61f6c27c302f5c5866d96a36c53d709b63aa94e14ba4fc43e5c8edb93cf08a1
+InNoiseEPP = 00b000000003c0ffefffdbff0600038000000000c0ff2f0000000c00010000e0ff0a00038000000000000040ff0b00ecbfff2f012000f0bf000000e0ff12000680ff1b0008000200005c0010000180ff3b0000c0008000c0fffebf01c00060000000fd6f004000080000c0ff0b00fbbf05c0002000080000c0ffdbff0200ffafff2b00000000f0ffcbff06000280ff1b00f4bf00f0ffebff02c0000001d0ff0a0001000050000c0002c0ffebff0e00fe6f0010001800feafff5b0000c002400020000000003000200000c0fb6f0000000f00020000e0fffabf04c0ff1b00f8bf00c0ff1b0000c001c0ff4b000800fdeffffbfffabffdefff1b000c000000013000f0bf0080fffbff0e0000f000e0fffebf0480ff1b00f8bfffaf003000f8bf06c0ffcbfffabfff6fff0b00fcbffc2f00400000c000000130000400020000dcff120001c0ff0b00040002c0011000fcbffbef000000f7bf01c0fffbff0200fd2f000000f7bf03c000e0fffebf0300003c00fcbf003000100000c0fe2f0040000800ffef00f0ff12000200000c0010000240014000080000c0ff1b00f8bf0140ff0b0000c000c0ff2b000c00000000fcff0e000140003000fcbf004000e0ffeebffbeffe0b00ffbffbef000000fcbffe2f00000000c001000140000000fe2f00fcff060001c0ff0b00f8bf0440001000fcbffe6f0030000800003000000007000140001000fcbf028000000000c0010000fcff120001400020000800ffefff0b0007000180ff1b00080004800030000c000580ff2b001000003000fcffe6bf00000030000400ffaf0060000c0000b0ff0b000700020000000000c0048000000008000000000c0003c00240023000f0bf01c0ff0b0000c0ff2f00000017000440ff0b000000014001b0fff6bfffafff0b000800fc2f000c0003c0010000e0fffebf0240ff1b00f8bf028000e0fffebf030000fcff02c003c0ff0b00f3bf0240ff2b001800ffaf00600000c003c0000000fcbf034000d0ff02000080ff0b0003c0fe2f0000000700010001200000c0014001d0ff02c0004000f0ff060000b0ffebff0e00fd6f0040000c00007000000004000030000c0003c0ff6f000000130003000010000400fe2f0100000400004000500000c0fc2f002c0000c00040fe3b00f8bfff2f01f0fff6bf030000dcffeebffe2f00fcff02c000000020000800fd2f000c000f0000f0003000fcbffdef01f0ff02c001c000f0fffebf0540014000040000f0ff1b0004000300002c00fcbffeaf000000070001c0fe4b000c0004c0ff0b0018000100003c0000c00100000c00030000c0ffcbff02000140000000080001c0ffebff02c0034001f0ff1200ff2f003c002000fd6f0000000300fe2f00dcff02c000b00000000400020000c0ff0e00014001100008000030003000100000f0fe0b000400054000f0ff120000b00010000000004000100014000380ff0b00ffbffeafff0b0007000040002000f8bf00b00030001c000340ff4b00000001c0ff4b000c00fd6fff2b001000fd2f00e0ff0a0000c000700000c00000010000ffbfffefffebfff6bf01400040000000fc2f0030000400ff2f000c00ffbf00b000f0fff6bffeefffdbff0a00ffeffe1b0000c0ffafff3b001000fbef0100000f000200ff0b0003c00340002000f4bf0040012000f8bffe6f0000000c000440002000fcbf010000300008000400fffbff0600feaf001000f8bf00400000000c00ffef000000130003400000000f000280fe0b00f8bf060000300000c0034000e0ff0200fc2f000000ebbf00f0000000f7bffeafffbbfffebf06800030000c000040000000fcbf003000400000c0010000f0fffabf007000f0ff1200fc2f000c000700fb2f01100000c0007000d0ff0a000180ff0b0003c0fdaf005000f4bfff2f000c000700fe2f00fcfffabf01c0ff2b000c00ffaf00e0fffebf0240002000080000c000f0ff1a00003001a0fffebffe6f0000001b00fc2f0000000400fdefff2b00000002000110000c000200000c000300fd2f00dcff0e000400ff1b00f4bf0070002000040004c00000000700ffef001000140004c000d0fff6bf01000200000400fd2f002c000400feefff0b00fcbffcefff0b00f8bffe2f006c0008000040ff3b000c00054001e0fffabf0200001c000400ffafff0b0008000070ff0b000f0006c0ff0b0000c0020001d0fffabf0440ff0b0000c000c0ff3b00f0bffbaf00000017000480ffdbff1e0001c000e0fffebf04c0ff0b00ffbffd2f001c00ecbf0000001c0018000300006000fcbf02800050001000feafff8b0000c0ff2f0080000400ffef00000004000380fffbff02c0ff2f00e0fffebffeefff0b001300fcaf00a0ff1a0000800040000c00003001000003c0fdefffebff060002c00010000c00050000f0ff1200007000800000000600001c001000007001000004000600ff1b00040000c0003000080001000000000f00007000100004000000001000f4bf
+InRand = 452a15b4d9006730f0a433be2d84ea6706f03119ad2410292922375e0f7d2c3a
+OutPK = 5696358110206fb1cced54bffe3e0fa80884f17ab7572dfdcb8b290fd99c29c74d007d68410e52899001aa27e511076d11acda1ee6ae52b3489831c4ccb89b331bef19c5beb666d2f331fabc0e96d4bdf5815d394ac99bb410a83e29c731e06c5403b0075cf15ef20edf09ea6a012aaf589a1f670cd6adf8920f177155aa0fd094c101c5487c682f0054152fa2676800673843820172f8cf5c5a64d47a881fd69327cc4885295a5be6c2805acbc8ae6cd8c6512d9e94f84baec76f419e03a272132c3884840610f58fd6bf6893da49d4549d5c0e87d28a417097daa1917c691b08076564d1c6683a139792e6da65d66115026755730d9a61b9f09ab158cece142fcaaf42d56397affd9a7ba6f8c40c7e5cfcd48b81a9b6819ed631c709f28329823c4a64947a3bcd47920dfc949b56eef44c9a256ed225da70d8245c444f98a2c49e3ca97b4632d8155d14aa35c7cc6051c57aab4ca84a24180d3adb15051a49afb9b6982a1a2ce99137297eebe64103fd51646cd6dda0f612268607e1f6aa213adb97a595ec6d9ae20730f6d12da5e6d6273178a294030cc3a4427eb2a1ea9d3a262079eee0165732a8460116d76ad5695dde58dba1edeaad26643a67b3c815970911caa9189244558e20083d1783d7c85aad881bc3cf6194cd8a46a7560c74d1a81df1c84832c91d2e3ad98669b709757d08b534b356b5f0948572bb9023dfdebd5b3650bfa995a4965e3d45e84c06e7f15d731264eebb01c10dd3a7381661cc9491ac3e76efa0ada251c9f79790549f2c9af179b91b44533cc8b2f67f7d6f587bb3c50ab14634ba8d8e41cf4a58a728f55c2b87fe67d7580e8aa3ed249284244f97a693e35d67ac1a00888ee63482107386ccd2023d9465bb7671b3341636a856b8b90aa38b78f25e434c6d8e6b0b20633de72e63750d834ed714e46f7dbc5d14046692850a9846e28518ec0cb78e4fb15bbe3738e852a6d84876fe279266621a5fb2c65453e33e9eaccc95265a668266a6c0ab15bd1a70c525e18f6d0dde6fcb4b23e5b8a8a715e05060209d1f89fa07b1816181f740ded8271e059018408e5a2044df41f81db0376aa2aae69dab1b6e88675aa578f8d5d243e838a231859364cb409fa21042196721a0af53d1a98d82886ade64b64d316204e4d6c7a5389424aafbb787823d87e1f379d5a6622b870e852f7d8db69f14ab905a153dceaf291034841ddadd53129d010b6ff232aca879c11e45c02095394cecc045a70d8ff5c57ad14df16b0c15009476986ffb93abc8848d79801ced33e3f03e37811a545b6808e9461f7a2752afadd6db15e316a599c8979a05c849d54345f83dad8ad8d10bd1f9152dd11884bb981273af1e4ae5386eb33e8f2b7800267eb78f71ab0cbaa05d8fc13be72c5aaa46bf089b5e150b967b17bd242be86df331e7c911a9cefb9b16fc5379c1bb0b8481b3f0ec7ec3fa3a956ac47e1ba69db083055e56c32941e1efd032bd9e413b63737bb99130ec99b1035509ac4611fa27afa265f9e52238e7211e62d5e1faffca7e8c4552967bdca8c32de81545767b5f033a21d1b07131e5ce85f3718f4bd00518f08946854b2e667f7e49291196a5400450ef446eb76ae05f7e1e52f56c49725bc087a3c7288e931d4d87a8ca8e651e7f2ab0b008c56f68a01fceaed626ee5666722603247f6bb2e05038e93c70e1cda82fc2edc3ae5ca6b622ff4af401b7f4c09ca4e9084ceb2812b8ba36435dd1cf55b896250824351149445d6fdce499c6ec94f19bb6abefb2b6fecdb7c90985b93d8c068373210d1ca87d09dc9e78d81b9d2659f939ab1e59b1a58009f468ff887c489fcd4fe3392749554024995aaeed8d57846ce261c0776d540647e8484de8c780e02725a37540f3e216456cc40c5d14e3b412cf5167937bdda6d809727ae5483f715acfd9d422488a1c0bb553ed8a83cedce18e427905c58bb998fdae6fc377f155185d085f83a71364430d275949afa59f4294e7718b56b24a67b114ee7d5c945741c710aa191b202e43ba894917a21fb39266a1995e8e0017007f281bf8781b02918e4b27f6f36d75ddc0c767349a31d35c3af3d8b52c5bdc2e01a29566f1cf94f256206e1d8afdd3f7c20a6a6d98c46b8101fd648484b5fc2f038dd4d8c410a0f3976ad0499873bee69c131d0daaca531a853fb661798967ca011d7251a234d6c94e326217fbc72e364fd02f3f4336e5afe5dd6ee6ad9a411805dec8a2253f778d89d29c2a5d76a544f30818b32d8c5710224090f32a28bac4b2b04920cabb12514ce5d34de66b627ae1a524974c55990a92bd4a01bd0fd7c8fa527b7236421d99b1df396912895da67132bd48158ca540a69c962c222019a1157dc90639743522db4e4a656e307817aa52632add1b026ce82709e50057d81c5bcd3080806ea630a1740bff4cc938b434475028cd1084ea6b2595f77a165a86b327e9c9f2dc0e480934fbaf86951269c5a6c1e9873a16e38033555be2dc4063144d5244e8f3fab06442
+OutRec = 02c00030000c00000000300008000000002000080001000020000c0000c000300000000240001000000003c00030000c00020000300004000000003000000000000020000c000340002000000003400020000c00014000300004000240001000000000c00000000c0001c00030000c000280000000000000400030000800028000000000000300003000080002c0000000040003c00000000c000380002000000000800030000c0000800020000400008000300004000040000000040002c0002000040002000000000c0001c00000000800000000300000000300001000080002c0003000080000800010000000004000000000000000000000040002800010000000020000200008000200003000000003400010000800008000300008000080000000000001800010000c000340003000080000000030000c0002800000000c0003c000100008000300002000080001c00020000c00010000000000000100000000080002400000000c0001000000000c0002c00000000c0003c0001000080003400020000400030000200004000100003000000000c00020000c000380003000040001c0003000080003800010000400028000100000000080000000000003000020000c0000c00030000000000000100008000380002000040002400000000400018000100000000180002000080002c0002000040003000020000c0000c00000000c0000000020000c0001800000000c000300001000000002800030000000038000000004000040000000000002000030000c0003c0003000080001800010000c0002000010000000010000300004000040003000080003000020000c000340001000080003c00030000400014000300008000080000000000001400020000000004000100000000340003000080003000030000800030000300000000140001000040001800000000400008000000008000380003000040000000010000400038000200008000280002000080003800000000c00020000200000000000002000040001c0003000000000c00010000c000180002000040000800010000c000000000000040003c00010000400004000200004000200002000080001c00010000000018000000000000000003000000003c0001000000003c00000000c00008000100004000240002000080002c0003000080003c000100000000080000000000002000020000c00024000300008000180001000000002c00010000800020000300000000040000000040001800010000c000040001000040001c00010000800038000300000000200001000080003c00020000400010000200008000000001000000000400030000c0001c00010000c0000c0002000000002c0001000040001400010000c000240001000040002400010000c0000c0003000000002400010000c0003400030000c00008000000000000000003000000002c00020000000010000300008000200000000040001c00010000c000340000000040003800020000c000380003000080000000000000c000280003000000003c000200008000000002000040001c000300000000040003000040000800000000000024000000000000100002000000003c0001000000002400020000c000180002000000003c0002000000001c00030000800038000000008000240003000000003400000000c0000c0001000000002c00030000c0000400030000c0003000000000c0001c0002000080003c0000000080001000030000000020000100008000300003000000002400030000c000200001000000000000020000c00020000000008000180000000000000c00010000c0000400000000c0003000010000c0000400020000800000000300004000300003000000001800010000c0002c0002000080002800030000400008000100008000080000000000003800010000c0000400010000c0000800000000c0003400030000000000000300000000240001000080002c0002000000002c0001000000001c000000000000240001000080000400000000c0000400030000c0001000020000c00000000100004000280002000000000000020000c000380001000080000000030000000020000300004000080003000000001c0001000080001c00010000c0002c00000000c0003400010000800020000200008000080001000000000c000300000000340002000080003c0001000000003c0003000000003800020000c0001800020000c0001c00000000c0002000030000c0001800000000800014000300000000180002000080002c000300008000040001000040001c00030000c000140003000000003c00020000c000240000000040003c00010000800014000200000000240001000080002c0000000040000800020000c000240003000080002c0002000080002000000000c0002c00020000c0003800010000c00018000000004000080002000040001c00030000c0002800020000c00
+Key = e99667992da3a284dc25fbf5e0856d5877938a2b7e96172e49f5b6cae78afb8a
+
+InNoiseS = e5c40729d83a968c6b2ae550c89b6623e77579f20c14a6c8cb4a623b25da3973ebcc3ee5c92a34345957fe5d10827aa05da022a399332066a2679d11e83e00ac2be799dd3eb7a6a4c787e0b59fee900013ac050b77cbda453cfd06f0de51694b12433fdc32e37b16a8e60d2bb02b82101415850212b676144ba4145ef47731af3419fc8e406be62485d7da056868435a8d1c6a52823fd4ff3db975998aba564e7274525407c318a604a4dc1360c6629f9d425963019f2c9d05901e38abfe87ee51e4ea7c8591ed84c2f991669d6454d9a1b3b88e41e9309e94ae8fa797a91e27fe08f09a2c5e826b44b1626bc88098a4280428c05ffd8129939d450bd912784ae55e13c7044bc593e9406c1ec0c262ca59f225f60a812217471d1fe6af221c98a43135f6f6696441f17b698d92702f811702ce6b292e852723d21f996675c77070427be30a70f8fc8decd69c94c19c88949ac9b528261a26a77016952a7e91a17ea81be4bc4f2175d8b44835472e1606d0059a2e4d86c236cd9c8f27dbe9fb71800b5c335862f62acd0bafa1aa6821d585f7e6b4a5718a6020d4c53244b99d80718a09621766b7c158ec90cb0dee4bd630a9264a49db854e3af0126b17359842956ccca27e498df495c220ab9968000c021a7386464f958f8691a66c671186a26e484f04e5b29112e409ca5f366091d69ab4492f52e8f6abc7e561394ae89ce050bb776b7c3cb3c334f9e5da9fd3af1de2c68a2109ec683976a24cc4255139854c4a482fb8555e926c41a96db8f62e62f4ae6783bb48622a8e2373c7b0252dc40d52b1713fc48b0058b1f65a0fcdb3b8d07c3febd7eed55021773a58e9017aa90934c55f5799d53ee4aef2a8cb2209c01fafba6151be916bfdf15862204c52adcb0da5231408ebca29c02029cfda60baaa5aa09da3f3719495fa8de6107a6d175b030d8b84fe4e5b4f17032f447a20695f349ce8124d868eb7f2dab6820e6f78115c53514ab631f6fdbf3dda71b1be890d12e5bab273a9312d2afe739b6d75c6cb6446c445aa0198eb862748b55de2054acdf0b54bcd323ac6f27f4de55e197f7517210142e179d81afc501a88661d89be6c7e6aabac160704a5b6c1a2b829d9ce7486418fb92cfa23bd1ca3872a0b67b5ce4c62caf1a495bc442c11cdc87c06563d1a73941ae907d46ab77059166dcd11d12221582bb49ae411debc6cf36a9ed3c68643f7c18af0cf107e7dcabbb221cc201023a6156bdabde7e9b12bb3c64e0f0fb92a87ba2c59dcb8a5d48d97fce343ca0e69210b1c4d01897141ec4627e3a7ce05f848c4c558e0af50c36c053b100df69532935f46293a29c5c8e24ca3ade25bb427712f80263b736a6403cdd4c47b284d22ba4575d066d202d498e86b6ec23dba8abe87f63e375b0b6f41601c68fa1717e67b654ca21bfb428a0ca7ff5d2f51447e05a56e550a6790657cc3b0c736922fbd21b491694a64a11f45108b605f06d8610686f2912f504ff07f313edae07930cf8a4e838b0606a402625c494205fdfa764a5a9416853548364b0cd84ede3d3ae49af22f4d01ab127d31264eb8ace5d2554e55290a2d8979aaf88a761582f5c92a19f49ee4f573d54a61a638703e7535eb465bb6fa3a3dc1ab5cc452d826ef6e818a65aa842f1b60c57a50354fdaf1518f4d94c088ba049a5d963105f2a8e0819da6192361240474c5cebb328cdd907a2e04b0bda41c720c23c808a0918e68fb83dfe12684a4bb471eba7a0d48e75847a44042220ab85b412048da1981306d52fc659f34876d16ae89ccba803080b9268a8597bd57f00dcf79012cbb0528f9830864a129a5495879d228ef86fbc5bb39ac450b196a97200a1829323781d64fe981fac57b5228719adff3ee5e72c29af5a18d3e9635574cb65b4086e92fbb1ba62d36a823139955d186f65d05b6bc3915e5061416f548837d532a453aa50eb26848121a3a33aa5604c678249c676c8ec973c558f2e8ef88fb105998550da3cffa09838974403e8610370b2937bae154f488bf31c816890c83b878cacc577f2a3bb256687025c17cd595614fd2e803d1fa2891634772badad84549a71a5ae83e3b9418f6f9f6ca6d4a67d9c30459554bee4ce413091e1ca092a507d9685fd259d5f0b991d0645dc6d48a1fdf8171aa8e4c210c6cef97360155acdbe19b55f000012120e221240964cdf57b09900791e02f5168ddb798e3bdb1056298ef04d00a1bde9653e073e8899530db2d30961a4a58d604ae055ab56e5a0dcb77403a41e51b5d0895c675900c9e253d1851e9d87eabf235056bade602f0952ca1c8dbd5cd4a59324d5dfbc309518b5769926a4c7863c51db8a4249c6fce71c92b9c086c217f987a38569d923b01f58c3c3682a5a796223f89c7262160226cd8ef3730ec71a43d4e3053b2f15b855e3934501a90bfc6bdd9c3d6003ce2d86076215ce2db3e31bdd2266c8693e020129d63576617d3c68127608b7566b4be9dab7576b4167fa0cf610180153207d5ff9413488a
+InPK = 5696358110206fb1cced54bffe3e0fa80884f17ab7572dfdcb8b290fd99c29c74d007d68410e52899001aa27e511076d11acda1ee6ae52b3489831c4ccb89b331bef19c5beb666d2f331fabc0e96d4bdf5815d394ac99bb410a83e29c731e06c5403b0075cf15ef20edf09ea6a012aaf589a1f670cd6adf8920f177155aa0fd094c101c5487c682f0054152fa2676800673843820172f8cf5c5a64d47a881fd69327cc4885295a5be6c2805acbc8ae6cd8c6512d9e94f84baec76f419e03a272132c3884840610f58fd6bf6893da49d4549d5c0e87d28a417097daa1917c691b08076564d1c6683a139792e6da65d66115026755730d9a61b9f09ab158cece142fcaaf42d56397affd9a7ba6f8c40c7e5cfcd48b81a9b6819ed631c709f28329823c4a64947a3bcd47920dfc949b56eef44c9a256ed225da70d8245c444f98a2c49e3ca97b4632d8155d14aa35c7cc6051c57aab4ca84a24180d3adb15051a49afb9b6982a1a2ce99137297eebe64103fd51646cd6dda0f612268607e1f6aa213adb97a595ec6d9ae20730f6d12da5e6d6273178a294030cc3a4427eb2a1ea9d3a262079eee0165732a8460116d76ad5695dde58dba1edeaad26643a67b3c815970911caa9189244558e20083d1783d7c85aad881bc3cf6194cd8a46a7560c74d1a81df1c84832c91d2e3ad98669b709757d08b534b356b5f0948572bb9023dfdebd5b3650bfa995a4965e3d45e84c06e7f15d731264eebb01c10dd3a7381661cc9491ac3e76efa0ada251c9f79790549f2c9af179b91b44533cc8b2f67f7d6f587bb3c50ab14634ba8d8e41cf4a58a728f55c2b87fe67d7580e8aa3ed249284244f97a693e35d67ac1a00888ee63482107386ccd2023d9465bb7671b3341636a856b8b90aa38b78f25e434c6d8e6b0b20633de72e63750d834ed714e46f7dbc5d14046692850a9846e28518ec0cb78e4fb15bbe3738e852a6d84876fe279266621a5fb2c65453e33e9eaccc95265a668266a6c0ab15bd1a70c525e18f6d0dde6fcb4b23e5b8a8a715e05060209d1f89fa07b1816181f740ded8271e059018408e5a2044df41f81db0376aa2aae69dab1b6e88675aa578f8d5d243e838a231859364cb409fa21042196721a0af53d1a98d82886ade64b64d316204e4d6c7a5389424aafbb787823d87e1f379d5a6622b870e852f7d8db69f14ab905a153dceaf291034841ddadd53129d010b6ff232aca879c11e45c02095394cecc045a70d8ff5c57ad14df16b0c15009476986ffb93abc8848d79801ced33e3f03e37811a545b6808e9461f7a2752afadd6db15e316a599c8979a05c849d54345f83dad8ad8d10bd1f9152dd11884bb981273af1e4ae5386eb33e8f2b7800267eb78f71ab0cbaa05d8fc13be72c5aaa46bf089b5e150b967b17bd242be86df331e7c911a9cefb9b16fc5379c1bb0b8481b3f0ec7ec3fa3a956ac47e1ba69db083055e56c32941e1efd032bd9e413b63737bb99130ec99b1035509ac4611fa27afa265f9e52238e7211e62d5e1faffca7e8c4552967bdca8c32de81545767b5f033a21d1b07131e5ce85f3718f4bd00518f08946854b2e667f7e49291196a5400450ef446eb76ae05f7e1e52f56c49725bc087a3c7288e931d4d87a8ca8e651e7f2ab0b008c56f68a01fceaed626ee5666722603247f6bb2e05038e93c70e1cda82fc2edc3ae5ca6b622ff4af401b7f4c09ca4e9084ceb2812b8ba36435dd1cf55b896250824351149445d6fdce499c6ec94f19bb6abefb2b6fecdb7c90985b93d8c068373210d1ca87d09dc9e78d81b9d2659f939ab1e59b1a58009f468ff887c489fcd4fe3392749554024995aaeed8d57846ce261c0776d540647e8484de8c780e02725a37540f3e216456cc40c5d14e3b412cf5167937bdda6d809727ae5483f715acfd9d422488a1c0bb553ed8a83cedce18e427905c58bb998fdae6fc377f155185d085f83a71364430d275949afa59f4294e7718b56b24a67b114ee7d5c945741c710aa191b202e43ba894917a21fb39266a1995e8e0017007f281bf8781b02918e4b27f6f36d75ddc0c767349a31d35c3af3d8b52c5bdc2e01a29566f1cf94f256206e1d8afdd3f7c20a6a6d98c46b8101fd648484b5fc2f038dd4d8c410a0f3976ad0499873bee69c131d0daaca531a853fb661798967ca011d7251a234d6c94e326217fbc72e364fd02f3f4336e5afe5dd6ee6ad9a411805dec8a2253f778d89d29c2a5d76a544f30818b32d8c5710224090f32a28bac4b2b04920cabb12514ce5d34de66b627ae1a524974c55990a92bd4a01bd0fd7c8fa527b7236421d99b1df396912895da67132bd48158ca540a69c962c222019a1157dc90639743522db4e4a656e307817aa52632add1b026ce82709e50057d81c5bcd3080806ea630a1740bff4cc938b434475028cd1084ea6b2595f77a165a86b327e9c9f2dc0e480934fbaf86951269c5a6c1e9873a16e38033555be2dc4063144d5244e8f3fab06442
+InRec = 02c00030000c00000000300008000000002000080001000020000c0000c000300000000240001000000003c00030000c00020000300004000000003000000000000020000c000340002000000003400020000c00014000300004000240001000000000c00000000c0001c00030000c000280000000000000400030000800028000000000000300003000080002c0000000040003c00000000c000380002000000000800030000c0000800020000400008000300004000040000000040002c0002000040002000000000c0001c00000000800000000300000000300001000080002c0003000080000800010000000004000000000000000000000040002800010000000020000200008000200003000000003400010000800008000300008000080000000000001800010000c000340003000080000000030000c0002800000000c0003c000100008000300002000080001c00020000c00010000000000000100000000080002400000000c0001000000000c0002c00000000c0003c0001000080003400020000400030000200004000100003000000000c00020000c000380003000040001c0003000080003800010000400028000100000000080000000000003000020000c0000c00030000000000000100008000380002000040002400000000400018000100000000180002000080002c0002000040003000020000c0000c00000000c0000000020000c0001800000000c000300001000000002800030000000038000000004000040000000000002000030000c0003c0003000080001800010000c0002000010000000010000300004000040003000080003000020000c000340001000080003c00030000400014000300008000080000000000001400020000000004000100000000340003000080003000030000800030000300000000140001000040001800000000400008000000008000380003000040000000010000400038000200008000280002000080003800000000c00020000200000000000002000040001c0003000000000c00010000c000180002000040000800010000c000000000000040003c00010000400004000200004000200002000080001c00010000000018000000000000000003000000003c0001000000003c00000000c00008000100004000240002000080002c0003000080003c000100000000080000000000002000020000c00024000300008000180001000000002c00010000800020000300000000040000000040001800010000c000040001000040001c00010000800038000300000000200001000080003c00020000400010000200008000000001000000000400030000c0001c00010000c0000c0002000000002c0001000040001400010000c000240001000040002400010000c0000c0003000000002400010000c0003400030000c00008000000000000000003000000002c00020000000010000300008000200000000040001c00010000c000340000000040003800020000c000380003000080000000000000c000280003000000003c000200008000000002000040001c000300000000040003000040000800000000000024000000000000100002000000003c0001000000002400020000c000180002000000003c0002000000001c00030000800038000000008000240003000000003400000000c0000c0001000000002c00030000c0000400030000c0003000000000c0001c0002000080003c0000000080001000030000000020000100008000300003000000002400030000c000200001000000000000020000c00020000000008000180000000000000c00010000c0000400000000c0003000010000c0000400020000800000000300004000300003000000001800010000c0002c0002000080002800030000400008000100008000080000000000003800010000c0000400010000c0000800000000c0003400030000000000000300000000240001000080002c0002000000002c0001000000001c000000000000240001000080000400000000c0000400030000c0001000020000c00000000100004000280002000000000000020000c000380001000080000000030000000020000300004000080003000000001c0001000080001c00010000c0002c00000000c0003400010000800020000200008000080001000000000c000300000000340002000080003c0001000000003c0003000000003800020000c0001800020000c0001c00000000c0002000030000c0001800000000800014000300000000180002000080002c000300008000040001000040001c00030000c000140003000000003c00020000c000240000000040003c00010000800014000200000000240001000080002c0000000040000800020000c000240003000080002c0002000080002000000000c0002c00020000c0003800010000c00018000000004000080002000040001c00030000c0002800020000c00
+Key = e99667992da3a284dc25fbf5e0856d5877938a2b7e96172e49f5b6cae78afb8a
+
+InRandA = 466e66f041312a755f555a949a661f8966f440cc2d3a4cfc08ad7aa8f018a681444247d0dd37b2ea61a2496ad9880bdda2c2c41895f83180f79cda5ba1f202f19896aa9ba51443ab58f7349c1fdecef93b444a9996a13d716e709efd1c08e2e2768daf974c78dce6a18b5eb9ba8db402ad955609740425aa21457adfe5802991075b62dd62ee55135b4040a3b32147748e9980e0958736c66c24262cbf55c782646c255712e4665ff80576774d420f4e13892016e6530001df93fab40ed9d48ec5d2d44726cfd0dab47094b8dbf0da6da5b4f7534e680166788daeb2a7bbfa2f746fbab1346d3f3d8f82d859f973661c3c6bb624bc0ed2de274789275110fe50b9e18ca5196040006e9dc78be2bbf1214e6e47e305f54e7d1b51450781123ae01ba6774ca51a7745f2a68aa44e37a8d6444fc03aca490fd4a1aa02908a51e1df4cbc1d0804d70f9d94859b01264cea842da37d86fdf97f5cad10b03aee627545aa02c3d67f07de5f63624e45615308a8e81d55698851ba625188624b032aa52cbbda9c80f715897fc86d300b527d303aeb03b8b18440b21e2b0a682da5740dc9c6863a5a1606ba784e2e47a4ca2fb09bbe641346d533e82c221d6fbbd29a2cba99d71fc153959c62985bb092de06c2253a032ecea0f6d8bb041ac2236a98f51a7220a73c52f4498d153ad99774883f85680410944030fe28204db6ba2a454a0928b10af2b920b3658e92c3bdbcf4681c7993f2aea59c419a979687a3ac2180f94c8e50c293b627c0216f5c0ffa2c310b7d49ccbafa14875e8a94b3ef1e3ad7926db6eafa0e87cf04a8f5101a8407f0d7e731ae31ee23f14c09609c29e254dc781588648e89abe662fc5a79e30cd8559bae7db123b96f4dd878526374311b681c45ddad6d96a520a13d6860e967e3f7daa8718f86f5c4cca4309f5caa4646b2ab5787033553b4149de6d63aa4593a70e6486b0c3d4d02dce897d0e14bfa6bca6b261176ead285e7007905672a76754c8d66414d99781eca5f9e83fe7bc3066c139eb909d3156d79d1860a2650149683e34578f272bd185a93b0418e50844fc04b253504e8e2f7a60a50c1ddeb747c357f249164d437e0bf53a8df2a01a836fa69d85893ea895c449fcb7e6d7c9f24565848b97f529e376885a219640774898a96aaf7c1260b871246949f6874c23e28e2adc98b3d6e4fdf8fc091a45e75ae6503f535f4b1ec20f66b61152f63dd8e3bc512305a0cfe9bd09a5c65910586123a246cd8188764c5895d278ba01f91196f4c68599cf1b3b800c37bd3f813a5ce326b0d9952ac4b6b96bf2468de4837157451177dab8ccb5d90591f5c81f5d8f14855a22ca4e89522fb4ff333756021f4c3b5245738293d8c81d4b0dc7ed618ac14c712350ed97d1685388ec2e14317a2295710418889fc3e214d2d225869509b068ca5032af80db7587b759b4c073069a4c4fe9a8be40f39d500667dc720b90a2ee17785644a6a3be55f51260736ad6525d7e121f413165bb38142621191b7f21feddbb8a658cd013187b246051ccc0cf952f574148317571a6643666d9fdbd4fd7ed343519b791c131c68ee85850595339c962335351744db3738569cb5bd42becb2e3c0c2be214592d1aa3f1d3cefb6b364896cb12096b2491df4381d50ace9f766cf140fb289a5b6415f0fa72548ba6e54a5da261bd2606150ff6003c87af1a687512645cfcd2cb555962d59c5affec340c9460218541228c99df057601824715f38ba0a27f945125fa1322a02c4b0e3388866cb349f4d0a1e423cf20d41055a46be20e0a43d70183de531537c9ec0229a65168027494f19a4ce8ace666298bdbe3db10bafb4661d7c9f8b70ca955d7054a25dbcc6575a18979ce4d42118039a739811429d0e26f4f6b842890e47c6d5178d5f2ccac590157f9b35aa0746b9c9353181a546ecfc92131ba556c1638a9529c6688f037ebf8879e15ebaae9f881c9db1143d7509b2b8ca242ecd245dc0898f847b96bdc4c273908a2149817eb2bb0aa9963e54007767ea0afc0d9508f0e5039cfa257f19cb57898c52737f16958e30101d289a484e7e460643409126c6610a15570648a40a0233cbdf863cd1384299f6cd5b8a88c700bff8faf80bf35874a024db6164195bde038e2f61c4d9e6994643ef0923f6c795a8f2a9d24a2bc391b4046b8cf4bfb40287893254c493a70434b12af36c64d645ccd6e4bd8c003674bf315e512bc9d21b6121ad11a790197bbf5b453f8443449012544e02c5045705c908a89f2d1faf0309d0aae891efc1717aa3f32ce3645138c13125db68d06effa6436359783c4d863702e6b2b4f7eea76a4504a8d01d39ab252d66a64f3b19186fc5918f19d4a36d2415aa943d267cb5d4673990e74fb8ed8484af02df782cd23e302574069aba1a631959be32831c3a27b35a3ff909de8a1fb2a6c852fd05affdff5fa11ce353ba7a3b1e1920bb62c497acdea0243431d8ae65459e867aa8082d44a5ac8fee3bd3d1653dcd0d209a62c
+InNoiseS = 55d10f4b48067ba8ef2308defa0285cec13987c82d528761f851ce55ef4f40659ebd2f0b6deae54a28ad4dcdcff387b99a8e4639c0cf354127186a92f6c0b1735f6d34d89596800646533332b2e5dab016a3d5129f8702223a94003c03774bb2061d322a86d3b7727904414df6c1c03c1a680863a43a09a862e6a0a0921eeb4431188c4c7b79edd293f24c3a1496763936b23d599d922b82663fe59836743d843046d5ac7493a95e36d16b770b7a47bf5b958355fc289c653e4637dda5ea68d40253440eb96f5976ad7a6248a138e760352792267249e91239050d4cf24406a57b2d98025bf104e587632aa7c52f65e781561b0e71c5ce16f371fe50da1f6008f2824828523fd3e2a201050b17a2b474491d49a0b36c8c34eea3adf9339c20a651d6eaa5205f406d933113816e1b5e587ad49a09de8e6e6630f60ed96163833a8560385c1b2124f165ece2ec52af9a9b612f9dc03a81190e5f46eaeda60faa0b7fb79c047207cc4e48078a46a0c49cd1132e9c9a00f85a193551a8cb4b99ec1d6c07491c114969b652935a82bba252326dab5440cca66fe24c37fae468a32b22d21540862f062c148fbda0f0a320ca12ca1ddadc661b19b45c461a38171ad57da96b89c01aba52ce596ebb325a22016e2070d5420284e4a319bf521480181ac4294d28ee037016f03149ad538e632f3c274a162e93b982079fa85aeadabe3cc58671e4bc0a2604243037ae98211789dce0b5a59dd89cfd5b13c56c97d520d73a4c373501f604c3316ce255df320b9494d10d9415faed012ac6b53400bc6d47e3307852697a142c4a9bd28a16b11cf93af3914d31e020ebf4ce8dd41aae69e8885ba641b47352212e69c03f932c5c4d54687a3503288ef5c3d6ab83fcaf2059e9098b59a898c7801683d636e45cbbabd1d8268606c3764111666fc3d57845891126dcd9faa5191a8bdae12466d62cffca274b10129b5d1d5caba36c15bf1e7de911b471a3c7a82ac6ea2374122e2770f8237c0d48e4e25c112b2f6659582567088482c9e9e0789191d226353878202120448980a6b7069bc71bde617452fd2317ed4e989f771032c46ba99724806f5998c8e8fe940b7ea24e6291ea5e550e7e5e2088dcdc6c60dee55496f22c870ca597357d728a9e45c4a8a44c984f48a62281a994098558f77eb70d2b690601a92c8327ea246355744ad97d912ca63d84eab3e0cfa521ce800699989a79269d10fb9285826da751db0dee4c716eeb946b59bf312226e7b95171915ea15615a1bb1c59a749cfb12761edd989e08a79079af4a781dc4f5d6056a91a0661742e5d54673a6c1cec1013739c2e71df299942769ef25da2e5daba26f9addc5155a8608aecbf53254c2d27a588b0823aa54d0bc24a2131b7d842edb3f9666a798f3e86abfe279d86259165bd93e8e7f913ab7d14a94f40cb44062a3d2728e9029ebaa6865be89d7d052c049e7389ca866022c267a3392f689d7ae627fa3b58141ab762d10be4e6d8752edd88cfa3985bfc723023dc380398ee9d9b9b9153a5a06eb295858397fd7719400f80e38aa18fd1eba6c324b15726f4907b84897aa3ff347578a13f1b905a148db1ffe55813c17831f54ab7eb52b44545427a25c17481d0851b590c4e1ea2682139c421108dd2bda3fdbe90549a7548e1463cf0a51e4abe1c171f4866191cba036d270248635c48848fdcb690a0bae3240029cbd688a4d6f150d08d6a0b4e62e2714e57806aad69420c71a72788c996b19b5fd5870506262b878e354056ed2dc4cc6bf00e59acd15ef776d1ee75970799c95a5a32814f8e790d459d32d4ccd807117acf2d2ff290a57342e9b439fbc6163c268953fcc0444d5afd040dc035a3aaa54884c09b4d60f2c0cf2c1e91cb1dd5573e26f3ca8609d3d582c926d429005891c02699e5d3c40eb203a8641fb8bc5dc13a04e832b954cc0003988ab691495e5662e21afb22ba667836bb7f177301005ba3124e4f947f851e5de9b8042b5da3476cbee91e588626da672217be58d146a2f3eb590be8832595455972b5531a87b1e63c20ec19bb9ee925ed1d5c7765b19c199769920d8c4ee6d7eaa3973995011e9e77c6de17662beb49ee169dcda4c6d7091459794277a0418c55926bbbc83a320cc38de4126612a3bd8a55ebfaa897c9e17082319d71b2c964ca7471721e96c69bf84d2ce9149069e119b3f40617d20dc20404dd587aef66972f14369056e2673c5cc54877f69bf8e0ada3527c58c9d8c408ce218c04858e5b94c4ba2e2da592b4c0bfb2c9e0a894e614e7a21baad239149743d1e4b3b068c5c324e1080c09eeca22487b2d807620ff20f64eb1ce404ff82f3926c0673b4341c6a6a1005b08e1fd27578ed1373af6879be3159b2cad27a29eb0c9fab2358258d0a946b1396e6d007995c291cbee1837b4454a13c84aa7b4da799bdebe33b9d2879d11f02008c82a7f9d48c33e5c57a74f731496b62a180db690406a9d8828c13550093aa1a29309ffb96adb68cda1a1089a
+InNoiseE = 121aa624d30404040175048b103de50d947082212327e5c6d7208da2728f9ea5ef151880c98011516e525785943af3824676c562531144a82704add92ba0079da53dc88ed59d32822781a41949f60ee8a8b35822bf8bfbd3ffa47b0a6b46a16926ab810de79289c15659261bb4c91600975658b4239a00406233211799177811256b0f7597ab4b169bb0424a900f76759682b3d542d341e60066ceead0b319e40edf5333177f712c4a9788c11d701f2e1a214aa7d87210a1df861de92d676999e4c68e0ddb41de716c0c569c5e8bc62ece83ebc14266c819573edd33f5c52c286d65dccb8a3444d228800b1dee5d88430fba5a708637abfce060c57cb926dda157092959e01b455e754065dfb97092c965b0e861fb7dd150f811e5c3f77d41c7dc5356f0f497c0e7185702398a8dc5d311c58c02d00d18155e1901ff0afdba08724a5883e7b2a66a4dd2d85cab4e2d8c87856cb93af91cac1337c828565d8950fae2582177bb40c3da5e5034e9478b2789d163d720229b7be153d5575c86cfe58129eb1435cd8e4a816e76e94411077d9d9837463884452e5dc8471c71b5c9fdb859d25734295fe0be0c551fa901a6fc4515e7554cd3bdb87bdfd9f17a41a13396d806751c1138e81995ab879d59aa89df001fd882f5138df1bf62ad4d96ef641c3577e04ea5e372ca3a47d7b8c8829d0f9da89c25a394a0921f4eab11ad0e17e931c1ae1e89ff0c59e025bca569823017f9cd007605d156a2ce6ecadba763dba289f6805261b0d23cd8aa9ae759f066c0130e675ec52f0797e278b0eea94a6ca80729459acb40cf19af581184590eb70b8c1566534de4f5dea35c61a9f62fdc307f282fb33d84ad5718459287853f769ce497d25c3fd55192468524293aa906a6b1f11524804375f8b8324d084f639e5506bd55ab5732669df1159c70b3ae32ee63cb06317e645d241c123a787402491c31afed53e0228dc0e2462e86e70b8afcb0065f13fa8193be55a29a59327788518ec8ff3b15d290366bb93698f82c2bc7d0a2e9c50f042708b403982dad02ac976d9aad53a2a8195b6844da8475772ab8a322ae12c7b81ca7401d5942c8763c00c503e6650a95108ab596b1814c4f919213722a4a36d7045b9f801ad9a872b3913e41ea46d01434f9b90ce4b751e01821bcd883a63a2d9837121ba23b2d72bd8a07e22b14bdb3c616a832940100541936aff634965b5c0b9c9051dda6fd7593f43e266e56469c0a90c0ae5e57f781780b111bdbf2c9667542ac025f6697cec7a215cb765016ec07224b4d58f18350b2aa764368a69ec4913a3534c455f9f2c6a10d6172d61c8a950d8fa6f5c6a5b8885de6a227b5a8708d868a51315ab6c61a16e9786e08cf3c857c415662e32ef97b8e24c5166c9821c02f8be965d890d154f01f5eda8136713607740a122eb26c0910040ae879bc935798da8413538e2ea9568ebdc9504c202f9a8e79098ce92e1e6115c507a06ebc055e64094711c5388695b84858989cc2eb96900a48129198324af985281ce6ea74d0c2a4dd6515a23bf488808344f00f3023905de32185a915fddd9901c0f5724eadc850439e2de4e166a5814c3e40d36686c5115ed5c13d7146c1031182e3175de04cdb54add2ac990600878f858ee4970a89286ab7e04587cfd879a8ca8e2a2dfef6ec088090dd20e0d2624ce5edb6f57f46aed3ec8039196e9c7bcd353acde58e65c26b01a6dc7f03eb1de5c97d8c9c97cf9b5062341800e7fbe0de48686645b79c257feed2253a1ae5a29ea0ba1479da61130159b8494663f56d465b91026cb7ee7a56867688575c0268f0ad053151716b37a9b9dd6fc4e6738132f4e4dbab3ad61ca24bbe49897958bba0d0db6276100c2fce04e04e392cecdd6799126f8f179494f1563eca6a2c930acd95f169b4308e1e62689c14b8e3f27be3c1a772948a77e4d38990a9983a744400bb0aec769b21db0ae10963d80e65619a7a39eb6bf614b64811bc452f67a60d595c6230c802f252cbdc008afb351cc4268137f48031d32ac71b997d9f6eda00f5efe28078a31bc2ca0e77304191f6d7aab3e561a7577aa218a18eba60257a8bddeabdc61164a8111886d89000478f9c1bd005198e5be5ed27078e096fd56030c51c04cc673f09055d3de26119834e668192d045a03cbe054768d05be3a8895205e4713a8e826bdbd6660bac715125f928839981ee898fb4e89d196b213f38643a3b9008d9c8cf4a2f5d43e232d70582c9e9c0767d64068ea447256d9e1ddf9e0e3b831a702acda0d110b522ef157ce9674d119ca14dcaec20371301c89263e42f57d15999165d0f44d11a54564a083059f5633cb9bc4e825f6bef346985ee571b8f1c45ec8a42b9cbf4abf214ce904e4d5e41636f57243a0e7ba4a52aff900cc25eda444e814c9f253fe34532a198dcab228ba12f2b576784f7847a8596fce7caae95eaaba06b74b67c90def7db4fc10d645b28d3bcb40c0606cdbb63a48284405053da8d2d67d68bd3b1aa0a
+OutPK = 9b9c71049e319792e1110bea4da03eda4dd5220d01ddc3c921022e5d3bcf8b70c1218b77d576d564c08f95d6fb271fd09c6c546e764e2d8b36a72e852de90d89683e9b54598fd7dff269431685381ff842643813944c2d502a582162ab35125938acde5ea3b2644c16dfca13360efd8c0dc02284750c8e8255aba28d2411591463186d4c5b63caf21a9972560c848c01d650b256a0b2f312850de681720a35012ad22a1fcb89d491768f9524e74453370075d5a60d6e470cc348f1e20afcddb41bf44c73428f41dbc50e27248f7f11683e0abf5e1bc4e1e1255fe2b22b1bec643389d453a8e58be11205237d8e9578ca9b822c18856f6e2434d39286dfcf2b6280d84f6ee2e1c3fc007394240e448a8429bdac9dd659edab45adc3608b1050c7c521aa19e056599b8c820e740f22dc6e15b520a8f4933f53adec2e9d270100389991eb00f6f6f21676699d72f95ae838dda9f5e43bfc985c554532801122e86527aa690c4567af3eb755f540abc6abf744ce8ae69f83111a4c9a618373c5cbb61ab2a91b028338b1242e7f170196331e0883c66f784c6edbbe8b0aad15572a9116fb3d50759a3695e38d4ae7c61888a15e056ec2057269f5600424256435bc2321ea102a333531a96ddda6584dbd3d171109b2b533d00720754d99b4d7ceecf891823b2a95be9bbf960a2626281b7402a1555ad5933c793757abc611e9ae555e882257a4125882438134c89d17948cdd4174a28178cb0242a55571dcaab8cae3494578ab84124ace3edf82645ac1386c12595debb108951e4d32567fda2e12cdadd810ea61166dc2a83e6d9c4a578c212df229cd0b8ae52e89584b2729359f912a6e6fde14b1e22a78191bb26259912be8ef71edd98d85484e647461ba9c5ac332d8959a42c5088990ba08cc877b0acfc964b31de2e8411a5cd6d94f769c605c8a5a2b25c2e66f42983281e41143c489ce29887e1f7898a4606071bee156bef0d49a455b21f5ca7beaab014fe597ef3d9d514d56110d21b532a5f0401419663d215b81ef7d7f28df3f1b2d401df4d8659381ed4ff8db80e964aa1b7297f0632d2c8b2b822678e1b861ffa9bdb03e013e916a392895c91e890c60d65c3650b1cb8d4adbd014f45ef50617b48a2353db272160a8d48728c2565c6a0511b934a3050a246a200b6e296d694c324376a9569f0df0c525d24ccd9b1a779da422591cefb952dc72f16859a1e6b84db768233b03581853ec7a40bbf98980411ebb5a396352d492b6accc79129b964417e94871c480a08e867c2069d5e03b994084e4c5f3d8c499054c2903d1c0913864b92734008bc396448340b91c5157c325e1a107774dc1779352164508fc721d268393442c7212e8a9da833c851b224ce7975e621dad0a191575571e6d505b91c7423884bb9a464a64e6a40e566a70c8c74940768fca4b62d03023c430826005b7bf9dc6f863f51e98aa713b781d8aa1aa65f73de019be459852850684f963502a82c9619867026064f9543a13dd7af9bd77eeeb25d2566cb2326b2d50805602dc270655d8eca27320d5302bca56ef5eccd4046066a4d9778675e15a01d17363d0c5076f43de1084f551674a6b02a990ae164284e467dc300b5c619bc0a45e411b8e5b15121d1e62668850d4a45ec7a56113e213dd291a40d80d33be8c0cc3fdee08e0a94182b63c4d8de78c1aabd5ab6cd90b18de9e7e5684f8b497a1751867af4218c20ca949fc8506a837fe9e1843b4da6dd690c9cab2e00880089bf0af4914c1ab01241c5e9816efe9275138230327e23a6ef42ce810c088feaa8b2a5d120837bca64b54a57618228b5bad1c02d33591d71790027780113804dc60757991c1150580398a45c1620bb17c620db8ec78d04770b2295086a6a6ea36ab0f44e13f70364a890532f08a8f9a11557093a27ab8ebd5a0bc9dba17a972c4db39aa6580c6b1a4a1208e5af6cb6d246ec44fb463cae807f668eb27c3390a60ad3cb3ca021e06d295ba7bf07483076f73ba44ba81609259aa0d7b48a975b56aad075c06002696da667bd23ccb26045bfadb6121dd810ea05b77195dc66cc5c68de68df80cdd2ea6a699c451c2d6d033c6c4915c8408009494029fa3782442f83d0fa2e8ea536d1a8f4f146344a639609753810a512539eed3673fb60ce58dc07b97d80369021f6738c92ea445c038e0cc89d8596e6268641b2604b3a6635d594b9348e73a6a448840623966fc5497d1cb252714b993ef45a27b3d36f01da8cbface615dc349b0d8d168b1aed283bd38302fc181984734231660e076f7dd56eaa839f398d18c00cf4a418961231a506ec185b862d4c442b2820053831abcb609e13956d9440150e50da9850f95ea8a93bf54984b0c000bc299a721a35ab9882e5c9b7da11265cc36115071a6381145b22246a0d3e851d2d901b79b1b7bb2b8d659bd5b9aeb9288582b69b124e189a914a9645056603db6649cd5ace453c80fbd592a06779567e41532ac0318cf3eb8d3fa154d6197216326aa
+
+InPK = 9b9c71049e319792e1110bea4da03eda4dd5220d01ddc3c921022e5d3bcf8b70c1218b77d576d564c08f95d6fb271fd09c6c546e764e2d8b36a72e852de90d89683e9b54598fd7dff269431685381ff842643813944c2d502a582162ab35125938acde5ea3b2644c16dfca13360efd8c0dc02284750c8e8255aba28d2411591463186d4c5b63caf21a9972560c848c01d650b256a0b2f312850de681720a35012ad22a1fcb89d491768f9524e74453370075d5a60d6e470cc348f1e20afcddb41bf44c73428f41dbc50e27248f7f11683e0abf5e1bc4e1e1255fe2b22b1bec643389d453a8e58be11205237d8e9578ca9b822c18856f6e2434d39286dfcf2b6280d84f6ee2e1c3fc007394240e448a8429bdac9dd659edab45adc3608b1050c7c521aa19e056599b8c820e740f22dc6e15b520a8f4933f53adec2e9d270100389991eb00f6f6f21676699d72f95ae838dda9f5e43bfc985c554532801122e86527aa690c4567af3eb755f540abc6abf744ce8ae69f83111a4c9a618373c5cbb61ab2a91b028338b1242e7f170196331e0883c66f784c6edbbe8b0aad15572a9116fb3d50759a3695e38d4ae7c61888a15e056ec2057269f5600424256435bc2321ea102a333531a96ddda6584dbd3d171109b2b533d00720754d99b4d7ceecf891823b2a95be9bbf960a2626281b7402a1555ad5933c793757abc611e9ae555e882257a4125882438134c89d17948cdd4174a28178cb0242a55571dcaab8cae3494578ab84124ace3edf82645ac1386c12595debb108951e4d32567fda2e12cdadd810ea61166dc2a83e6d9c4a578c212df229cd0b8ae52e89584b2729359f912a6e6fde14b1e22a78191bb26259912be8ef71edd98d85484e647461ba9c5ac332d8959a42c5088990ba08cc877b0acfc964b31de2e8411a5cd6d94f769c605c8a5a2b25c2e66f42983281e41143c489ce29887e1f7898a4606071bee156bef0d49a455b21f5ca7beaab014fe597ef3d9d514d56110d21b532a5f0401419663d215b81ef7d7f28df3f1b2d401df4d8659381ed4ff8db80e964aa1b7297f0632d2c8b2b822678e1b861ffa9bdb03e013e916a392895c91e890c60d65c3650b1cb8d4adbd014f45ef50617b48a2353db272160a8d48728c2565c6a0511b934a3050a246a200b6e296d694c324376a9569f0df0c525d24ccd9b1a779da422591cefb952dc72f16859a1e6b84db768233b03581853ec7a40bbf98980411ebb5a396352d492b6accc79129b964417e94871c480a08e867c2069d5e03b994084e4c5f3d8c499054c2903d1c0913864b92734008bc396448340b91c5157c325e1a107774dc1779352164508fc721d268393442c7212e8a9da833c851b224ce7975e621dad0a191575571e6d505b91c7423884bb9a464a64e6a40e566a70c8c74940768fca4b62d03023c430826005b7bf9dc6f863f51e98aa713b781d8aa1aa65f73de019be459852850684f963502a82c9619867026064f9543a13dd7af9bd77eeeb25d2566cb2326b2d50805602dc270655d8eca27320d5302bca56ef5eccd4046066a4d9778675e15a01d17363d0c5076f43de1084f551674a6b02a990ae164284e467dc300b5c619bc0a45e411b8e5b15121d1e62668850d4a45ec7a56113e213dd291a40d80d33be8c0cc3fdee08e0a94182b63c4d8de78c1aabd5ab6cd90b18de9e7e5684f8b497a1751867af4218c20ca949fc8506a837fe9e1843b4da6dd690c9cab2e00880089bf0af4914c1ab01241c5e9816efe9275138230327e23a6ef42ce810c088feaa8b2a5d120837bca64b54a57618228b5bad1c02d33591d71790027780113804dc60757991c1150580398a45c1620bb17c620db8ec78d04770b2295086a6a6ea36ab0f44e13f70364a890532f08a8f9a11557093a27ab8ebd5a0bc9dba17a972c4db39aa6580c6b1a4a1208e5af6cb6d246ec44fb463cae807f668eb27c3390a60ad3cb3ca021e06d295ba7bf07483076f73ba44ba81609259aa0d7b48a975b56aad075c06002696da667bd23ccb26045bfadb6121dd810ea05b77195dc66cc5c68de68df80cdd2ea6a699c451c2d6d033c6c4915c8408009494029fa3782442f83d0fa2e8ea536d1a8f4f146344a639609753810a512539eed3673fb60ce58dc07b97d80369021f6738c92ea445c038e0cc89d8596e6268641b2604b3a6635d594b9348e73a6a448840623966fc5497d1cb252714b993ef45a27b3d36f01da8cbface615dc349b0d8d168b1aed283bd38302fc181984734231660e076f7dd56eaa839f398d18c00cf4a418961231a506ec185b862d4c442b2820053831abcb609e13956d9440150e50da9850f95ea8a93bf54984b0c000bc299a721a35ab9882e5c9b7da11265cc36115071a6381145b22246a0d3e851d2d901b79b1b7bb2b8d659bd5b9aeb9288582b69b124e189a914a9645056603db6649cd5ace453c80fbd592a06779567e41532ac0318cf3eb8d3fa154d6197216326aa
+InA = 466e66f041312a755f555a949a661f8966f440cc2d3a4cfc08ad7aa8f018a681444247d0dd37b2ea61a2496ad9880bdda2c2c41895f83180f79cda5ba1f202f19896aa9ba51443ab58f7349c1fdecef93b444a9996a13d716e709efd1c08e2e2768daf974c78dce6a18b5eb9ba8db402ad955609740425aa21457adfe5802991075b62dd62ee55135b4040a3b32147748e9980e0958736c66c24262cbf55c782646c255712e4665ff80576774d420f4e13892016e6530001df93fab40ed9d48ec5d2d44726cfd0dab47094b8dbf0da6da5b4f7534e680166788daeb2a7bbfa2f746fbab1346d3f3d8f82d859f973661c3c6bb624bc0ed2de274789275110fe50b9e18ca5196040006e9dc78be2bbf1214e6e47e305f54e7d1b51450781123ae01ba6774ca51a7745f2a68aa44e37a8d6444fc03aca490fd4a1aa02908a51e1df4cbc1d0804d70f9d94859b01264cea842da37d86fdf97f5cad10b03aee627545aa02c3d67f07de5f63624e45615308a8e81d55698851ba625188624b032aa52cbbda9c80f715897fc86d300b527d303aeb03b8b18440b21e2b0a682da5740dc9c6863a5a1606ba784e2e47a4ca2fb09bbe641346d533e82c221d6fbbd29a2cba99d71fc153959c62985bb092de06c2253a032ecea0f6d8bb041ac2236a98f51a7220a73c52f4498d153ad99774883f85680410944030fe28204db6ba2a454a0928b10af2b920b3658e92c3bdbcf4681c7993f2aea59c419a979687a3ac2180f94c8e50c293b627c0216f5c0ffa2c310b7d49ccbafa14875e8a94b3ef1e3ad7926db6eafa0e87cf04a8f5101a8407f0d7e731ae31ee23f14c09609c29e254dc781588648e89abe662fc5a79e30cd8559bae7db123b96f4dd878526374311b681c45ddad6d96a520a13d6860e967e3f7daa8718f86f5c4cca4309f5caa4646b2ab5787033553b4149de6d63aa4593a70e6486b0c3d4d02dce897d0e14bfa6bca6b261176ead285e7007905672a76754c8d66414d99781eca5f9e83fe7bc3066c139eb909d3156d79d1860a2650149683e34578f272bd185a93b0418e50844fc04b253504e8e2f7a60a50c1ddeb747c357f249164d437e0bf53a8df2a01a836fa69d85893ea895c449fcb7e6d7c9f24565848b97f529e376885a219640774898a96aaf7c1260b871246949f6874c23e28e2adc98b3d6e4fdf8fc091a45e75ae6503f535f4b1ec20f66b61152f63dd8e3bc512305a0cfe9bd09a5c65910586123a246cd8188764c5895d278ba01f91196f4c68599cf1b3b800c37bd3f813a5ce326b0d9952ac4b6b96bf2468de4837157451177dab8ccb5d90591f5c81f5d8f14855a22ca4e89522fb4ff333756021f4c3b5245738293d8c81d4b0dc7ed618ac14c712350ed97d1685388ec2e14317a2295710418889fc3e214d2d225869509b068ca5032af80db7587b759b4c073069a4c4fe9a8be40f39d500667dc720b90a2ee17785644a6a3be55f51260736ad6525d7e121f413165bb38142621191b7f21feddbb8a658cd013187b246051ccc0cf952f574148317571a6643666d9fdbd4fd7ed343519b791c131c68ee85850595339c962335351744db3738569cb5bd42becb2e3c0c2be214592d1aa3f1d3cefb6b364896cb12096b2491df4381d50ace9f766cf140fb289a5b6415f0fa72548ba6e54a5da261bd2606150ff6003c87af1a687512645cfcd2cb555962d59c5affec340c9460218541228c99df057601824715f38ba0a27f945125fa1322a02c4b0e3388866cb349f4d0a1e423cf20d41055a46be20e0a43d70183de531537c9ec0229a65168027494f19a4ce8ace666298bdbe3db10bafb4661d7c9f8b70ca955d7054a25dbcc6575a18979ce4d42118039a739811429d0e26f4f6b842890e47c6d5178d5f2ccac590157f9b35aa0746b9c9353181a546ecfc92131ba556c1638a9529c6688f037ebf8879e15ebaae9f881c9db1143d7509b2b8ca242ecd245dc0898f847b96bdc4c273908a2149817eb2bb0aa9963e54007767ea0afc0d9508f0e5039cfa257f19cb57898c52737f16958e30101d289a484e7e460643409126c6610a15570648a40a0233cbdf863cd1384299f6cd5b8a88c700bff8faf80bf35874a024db6164195bde038e2f61c4d9e6994643ef0923f6c795a8f2a9d24a2bc391b4046b8cf4bfb40287893254c493a70434b12af36c64d645ccd6e4bd8c003674bf315e512bc9d21b6121ad11a790197bbf5b453f8443449012544e02c5045705c908a89f2d1faf0309d0aae891efc1717aa3f32ce3645138c13125db68d06effa6436359783c4d863702e6b2b4f7eea76a4504a8d01d39ab252d66a64f3b19186fc5918f19d4a36d2415aa943d267cb5d4673990e74fb8ed8484af02df782cd23e302574069aba1a631959be32831c3a27b35a3ff909de8a1fb2a6c852fd05affdff5fa11ce353ba7a3b1e1920bb62c497acdea0243431d8ae65459e867aa8082d44a5ac8fee3bd3d1653dcd0d209a62c
+InNoiseSP = 6b8addd536524101c8fa265801addd950fd2d7b19fe4a590e175300c2ce7532a467165019fd7d1485a779105b51720b68eccc12c95622861c18d5f3679a2a11466ef66aa258e61cd0f26b2eda7f1400703868030296c41c241cc5fa7aac5212c6e52a8c20013d9017ab951af7513c82d1f8531abb4214cfcd5cd72377563db181ab084f6afca162a866a941a899cad20cc1156078a71c6f6f8662e6424f87b8e84aee1ce925bf273969256645e648ff6286677915257c2d49210a1b04e7b29eaf030ca2dcf8c0d3375096d41a904c36b2d1e9e466c73d319ba3cdd79b8081e1d7c6ee100ce0e7a40496be1f70e338edd9aa2e1ec869c859f1817456ee14fa8118f7832f50ae0e5a73a8964527d421b76678fe346d8f31511576e2c84ea6415d1061ec46455017fe70910d36c76cd131f272ba180ba12a3cb23ae6079892c058a8a0d01a54b398579b1ecc6a1f3960092d114343027695b87eff62bd35691251b935ab332aa00068a89fcf25b14d6e9084044959d830d686fb92ba222d7bb712c0572c8fb99eb4285fec5dbe5d7b83f8582c49928d65466c479831d116482cae805037ebc2200fbdb1519555c125db380ecb068df5efbd33a7df5c257462c3215ae29bf316d72bc7c4834a55332995ec99882efd9666be73afa6f8e0a3f877e9290a89bfe47110284b8299727c05a38b20807a93d17e4c55595e8a392deb58cdc568796b9f6bfd0d34b3af9318ff9955da956b5a555a023f6a68455a794e6d3c86a6c46a70591e598074eebfc87f348bdbb2b21399b3d35b360d429f341238d4ce695a8699f4c6a41a6d865a9475879a1fd9a0ae62808ab242920bcc8011611dc677aa938564b38b3911ae2721f02aa9e8701b686ac17984190ea11ecbd090e2a4b4f11821daf62f570150ba8ee08e0493e33fa0b108a9ba2a897859cc94a6545c34329462df93f84c3b35923cc0fac9442025cb028fc9519b70dde566d2c3f2899690cbf4b6656e979571fd0aa5af21123c753f1d4a4fc24db4ea19623ec2ae5cfb428d9a054c00eb53068bd37baaac8eb18723917ad7737e819a189928e8149ec83f7da1807fb21b5a644c3b4c77479529e4cd112451384275d869be843ce9479723d8a074cc66be5054197b9e0fcb0c2260e7219ce86c79e0f4ecac526ff5a716059e4a9a1f6501bacbf905a3c99af11e7e242de820b82795407da6ab558bbc4d5567e44d24bb8561557f49a675357893dc8d5e873dec55b62dc0fc77bd469274cc425c1a174c54ad6e6f649b38a8b6749101623bb5b2e38d1eaac041ccefa6c1569adca16843b059fc35f9ac600ca1a911940ab15de9c4c144a21e6e452899c9ee173113fdc2d90950ce26d38827c21c83c1e63e61a7a3d3a06e8171660467595b1671829ac485503164894bcf240d2a05576f20a78869a7899ab682c5fcdd067da91a099a64266ea34a513666e89a292bf1fac7ea8377e5b51e398e48ec92622597d03c4054cc94593175a28a1f981990509eabef568e4e65e99437a2b8291a2c60a6f69cfe8c82aad1759fafb716d63675040859821ab1c9be1b36c2265ac80360afa196e22c4a1d945585f4e2996b4e91866cc28ab21555954844d9e714d96a6145778a9e6603abd4ca802778ea5748a4799b7faa04345eff917ca729dd1a1e5bf88b8ddaa7a33aafa6e5eec53c7173222d4ea96a1446a512b6e12564c0048df48fb88549a37d2065904cf88b3804cc20136f98aa69cbca686115f9daed1a622746ee9993fea934ab3d991e2b2081202085e3ff568a098a39e81145a7a15bc48c26a47ae28f0691f33fcf8515345ebe88a09656544f3a17f5d41dfa4926815ecbc10424a9219e62a83584382fa38bdb00623ba0f20379691e17479fc8ebd12607bf3e4adf559e51a1a1d8788d32269187532c29982899e5a83b7c6d929584c91dc5769fa5b1489499b4fc4d5a479ce12d4b4b2067628c37205d19d00874d3181a0981718816202f6ad6c8de8fcdbe270a30bc6f1899c2e8886a1cecbf68e2542a7040abf2c0540a9f823997db447149503f4764706615db6655c0978e31dab5b256995a08f7110086602ac6f204b55799bc8f68616010c86c4a090cbc55b1385f3d231aab8988ee810249c4dc82bb1a96485afd3093976b54588b145a3e4f426bb80c32da5d7099100c54df1360d8001cee5b9d89c19d501fd6b9e7596b20969330a117b5c2d492950841acfec61e20bd8b55222389624716fe859aa2bd21043082b0b8be3991bf7cf8843f4322a6e167b24941e7ede5b5d0442c166d6a00a745d1c8cbcaa56889fccabe0ec2048cf6d30a98e64c5e152676f04b6b50d85ba068ef0c2a148731b0132f50f6e869022c446bad8ac219fb3c96e754092e6b7f64e608bfd0e72e7015dbb3c1f210254745e135bbfe8c46488a05065f7d97a90b61bfe24232aac498494f0f89d1e0293ecc78fec02c78c30a636bcaeb86de704b10cb411292b84027c1ff3460016b4cca36c6bb2fb32e040050b5c368082b9
+InNoiseEP = 3bc5cf207e017b682a21a47b75503aef0fe96bf80d73a0cbe9daf58eabed30e0c9d0a1fa88bb7a42761bb598d4c37bb61386a63344aa8c42a39b13cb87d902a112e264c4fc286e6703a6cf506e8fea14a1b4a4a03ae998b995909d2fca5196d80c176b1a3f351e3d7a56d08f8a235daf03c80a9501c08756ebf040190eb76e8d740223a43f2853f251432825485f97497090b3b9d7191925a28a70d32646b1486743cea5d364c248d3e474e27e8c173218428af32eb5780c4ff9c47c2c455d0b83f01089b3652cb921560f7c646c27e670a359537731cd9e149323df20c199b40b66c06642a256d016b4c56cba3de19be9853a30a39b2959cb215d2eac56d5d4dfb06a4f82da34cf108804d754587ab450f06327b2cd88550feadabb8e9d1404526cf74ea4125a5eb5c790ca16594739d54e946779abb3012689b6066aa2fb7d925b9893e1f067d5512190a349785c58db4c9fd265148d3344f5c1b3aa18928899b33aee55050451fb167811ae8a5c50b9f40400a4c28bba64096f088af54fe4885197b814c2be2dead7c2e7f335916aefd94297b89a384451b7dba87b01cbbc36c37c9bb09661a4f8389043253331e2cd6a41c948d6309284579dbb3648e86f9cde470b8400089a966888663117e9dc21580889bbe9ca2517bdb9b58d5740f0c16a8dbee0a3b1569c8d219808a9a7289b94268a23f1c958d2652877172d917da3d305757d098986f6e2e1e460a3cf4b0094d8a050e998a6b4885ad106f0c98b102b58c5c4b63b5e1d442974bad2f06c9feb0d01219e652e90db2338fe4b4041b665668c5a00a24530b3a181d2089bb6a4b599d2294d2036562f5047d8938715213f90630a58014e174f906ad0a20821a539775efda2b4e25abb4fd0090ddac2774db411ee4d43339c081aba8f62f0e291351b1fb84a3e28191714bf94b3a94aa529eac8b0aab51945d185ffacb823e163b379fda4de57d23046618180e0728367b262ed6da3629fd91e2baf7cb9d4d401d219221527da00a0a0c778621aa7bfa73a2677dc10ec81a1db301884b86e5b0ba0928e4d29e10b11243fd39990a25a416d3ea6d214620645969be0e634544df6c48e1643964e68a338d5633e4b085f2f36d234959b69b309f2f20015dbf9f71bda678b85f186d75999ec1c3da0b6128f1304976b381c3f2c118312af521b0f810ac1d190876c5499aa1e0a0c5e8b9d7975c92d31014177bb310c2a282be0740297629c54f5d1ae79361cc77650c0a6206fca4441a52e780bc22b71dd1dd12f4f5886449a1702cdf04c114c628e0d7975110e85cf045bb820bafa99432033de0408e1b81a12ee4f8a6f043a1d115d145d729b15e63db3d4e5926f76bca550785be6ca3e7b640f77e82c145e8b9717f2b61ba223248202b68772c9de72fdaa4bbcdf4ac433187a85ec03f8588530dfdd3bbbe5a0490294c5900f8080ba647bf3b8c0a6b624da805f4ab6f04981d550f29219eba6051058ba1a87e80839a572f76320a31731002184843259f656904f5dfae05c91c8df4350f425bde08b2b2651feb012df0ae777c842a44103db2c861b45658fe1413c256ae47ba944ca2f139338c44fa80e59445b88cf6156bfed02166df2b3970fa2daf1c9832de4f5a3b3b199108a0d2af2d237751e868a234a5ac36f8959cbda135e63ac98a91884ca6c62c9930507559a7d51ab52bf3da1a95b545cadc34afe4b1add66fe24b531380811a31d9716972c2e2e261b805c0a1ac70d956720902eec010c78c6bab20a2936522f75bb5dc361f494e289ef5ad6c8964d6dc8e883a1b0e036920a5e011c8416e881de84b809854ca0ec128681ba989db383a1827bdf8c61d997196ff8809f68c651aa6c27f0b825ea227ccd9972efc8a79d80a4a5fa0a23f9035298703208be84b5463a703356a7039563c627841786d0417579e69f495510fa6d6ba2e6e5d5de05de899f53d3f9256ce94079722c6d9bd16d220a3e6d399bff03088e38e3db2ac9d8f44fe426d3f5968539392845cbe00d8f6452d5f1c87d9a4745391338127996d52e49b809354fde1bf189297865aa2d712f80ac4ecdfde7c58d51e89cea5bd36690276b056b4569257ea78acbde485924026ed86d741356ad5c575aaa6200a5e18b81cd99332723133dc5a00595cd9a7cce957164b3e6adaca3f6ea7d687e444ef39269e65ec81320c5cd283fa250bf4bc246e5595600568440f73a0ca8a30da4e6ac8af4ccd0705ac62f3865f676ddcd9a85c38757f628bf0655ebfb941897fc62e5007bd145fd66fd78b5ad4bcd1392b826554f0a2c1702b45865fde2e1e7f4fe0f4355cbb20b4a16dd9fc0e88e7c57eaa8a7d56d21b89ee79edad6e82b950066f9c06050689b4505e396530c05940a92cc87d79531169c566960802de5b278168563d05908061df9dcbd31268e4429e008eb9e3a4628a194023d1412334975b4ac35a1e4ebf6197e6150b60ca4370a2e3fa9c897554e5a9fa3cfa5a647695c6ca865e402bc5536c2bd65e9112a
+InNoiseEPP = 00f0003000f4bf0100fffbff0600ff2f003c000000fbaf00d0ff02000440003000fcbf034002000008000180ff2b000400000000ecfffebffd2fffcbff0200048001000000c0fdaf00b0fffabffd2f00dcff02c0020001e0ff0a00010000fcfffebf0040ff0b0000000400003000fcbf0280004000f8bffcef011000fcbf0000000000fbbfff2f00d0ff060000f0ffebff02c000c0ff0b00000002800000001c00034000a0ff0a000100005c00fcbf01c0000000ebbf01c00000000800ffafffcbff02c0010000f0ff02c002c0ff0b00f4bf0480010000fbbf0200001000fcbf020001e0ff12000200000c00ffbf01c0ff2b00f8bf010000c0ffe6bf007000f0fff2bfff6ffe0b00fcbf00700000000b000280ff2b00100002400000000700fcafffebffeabf03400000000c000030002c001400fa2f000c00f4bf04c0ffebff0600fdefff0b0007000540ff4b00f8bf034000000003c00080fefbff020000f00000000700004001f0ff0600fd6f00300000c0fd6f010000ffbf038000000000c0feafff0b0008000030005000fcbf02000000001300fe2f0000000b00040000d0ff0e00014001100008000000ff2b00f4bf0280ff3b00000002000000000400ff6f002000000001c0ff0b0004000200000c0018000300000000f7bf044000c0ff0a00f9ef00f0ff020000c0ffebfffebfffaf000000f3bfff6f00600000000030001c00f0bf0100000c00f4bffb6f0000000b0002c0ffcbff0e000070ffbbff060000f0ff3b0000c0003000000007000240ffebff120005400000000b00fcef000000ffbf01400000000000fe6f000000130001c000f0fff2bf0100001000f8bffc6fff2b00f0bfffefff0b000400ff6f01400000000030000c00fcbf03c0ff0b00f8bfffef003000f4bf000000f0fff6bf0300000000f4bfffefffbbfffebf000001e0ff1e00fd2f000000f8bf0280fffbff060000f000f0fffebf0280003000f4bf01400000000400fd2f00f0fff2bf03c0ff0b000000000000200004000140ff0b00100001400020000400008000d0ff02c002c0ff1b000400f96fff0b0013000180000000fcbffcef002000080004c0ff0b0003c0fe2f000c0008000680ff3b00fcbf0200feabff0e00fc6f00000003c0faef001000040002c0fe0b000f000340000000fcbf03c000f0ff0600014000e0ff0a000240ff0b000b000200003000fcbffd6f00f0fffabf00b000e0ff12000000ffebfff6bf0140000000fbbf003000acff0a0000400060001400020000f0ff160001c0fffbff02c0fcaf0000000b00003000fcfffebffbefff2b0000c0020000f0fff2bf0480ff0b0000c00100000000030002c0fffbff0e000080000000040000b000100000c000c0ffebff06000380ffebff02c00030ff1b000400008000500000c000c000e0ff0e00f96f01d0ff0a00010000e0ff0e00018000f0ff0200040000000007000180ff0b0000c0ff2f0110001c00f9afff0b000c00010000ecfffebf03c000f0ff02c0ff2f00000007000280ff0b000800003000d0fffabf0480ff1b00f0bfffafff1b00fcbf008000b0ff0e0000c0ff0b0007000070ff1b00fcbf0000001c00ecbf0300ff4b0004000070000000fbbf0240003000f8bf0000000c001c0000c0ff0b00fcbf0640ff0b000c00feef00f0ff1a0004c0ffebff0e000070ff0b0004000240000000070000300010000400fceffffbff0600030000dcff0200054001f0ffeebf0280fe2b00fcbf0680001000f4bf004000e0ff02c004c0ff0b0000000300002000f0bffcaf00f0fff6bf050000f0fff6bf0200fffbfff2bf03000000001b000070000000fbbffeeffe4b000400038001d0ff22000000005c001400003000d0fffabf00f000f0ff0a00020000ecfffabf010000f0ffeebf01400120000800ff6f00100014000180fffbff02c000300010000400fdafff2b000000fe2f00ecff0200feaf0020000c000000003c001800003000200000c001400040000800fcef0020000000000000e0ff02000100ffdbfffabfff2f000000fbbf03c0010000fbbf0200013000fcbf020002f0fff6bf0580001000fcbffd2f00bcff160000c000400010000180fffbff0e00ff2f015000fcbf00c0fd2b00f0bffeefffbbfffebf0070ff3b0000000030004c000000fcef004000ecbf00b00000000400038000a0fff6bffcafffdbff1200feaf002000080003000050000c00fe6fff4b0018000180ff4b00080001c000000004000180ffcbff0a000070001000080002800000000700fc2fffcbff0a00fd2f00f0ff06000380000000fcbf0070005000ecbffe2f00dcff1600003000f0ff02c0054000200000c00500000c000b00020000dcff02000300000000000000c000500004000300004c0000c0feaf00100008000540ff1b00f4bf00c0ff2b000c00020001f0fff6bf010001100010000030fefbfffabf00b000f0ff02c003c0ff1b00fcbf00800090ff06000000001c0000c0028000f0ff1600
+InRand = d7e1e1191de63a93fd94e7106466467856449206e281c17eddfe5597c5ba7e7f
+OutPK = 73ae7fcb7366049e5c2060340422d555213529f573740c28c3f7e11191d2ff660c2c9789d31cc5cfc91227e48ec3c9593df0524f9998904f8d04d3a82de231ac4760a3097911c216159909a08c42195a7661ee825a93108560d582792bb5566f900ff301118ae6607d8282bfc340de1ea6463cab4734ad43e15dea831c599fea681947790c8b1ae4f24ed2460b68d371b24c80474ce5a551418bfed86f0349695f6adf26da3e9a65d3913e29f96eb9ad94452847d43b0aae7401d46aa384108de8ef5a906299744b19ed4f62ae99a6bd18580b6d736ac04a5c450901b8c61843470c23d37b964a200863097bce54f9588db1ca487107eed30630b013774770f881e9375e99ab44a978663d1a22b0bc3e38e19e4f1042e07fb8c3340873b60def9f9cd605eeaae4609742139517ce5f4080ca3a2e6b189b4bd26a6700d6bb7bac648774962aa27ec4a9c5c89c9b264d0757497b88825859cfa682db002a9665864b5a1d9154456a1872321e6f596b87a810aa3cc326070a06be443d5894e981bc4552abf2faadbc37a356f8e5f0655847a6d7aa8e8dad54442ce0154546f784609855b677986523c111b54221dc88e7a2519500da5cc2e9b0ab31db066aff6e3c1de5de84f8893ac361f494130943c724dd563070a23c0f7601ee0077f46b38033f2d493d29c27062953d7594f5c2381e5500ed684550b40959ce1a8327b474288e0fb052a0466a11239574f9ad38467541aa1276cee8aee57b3ea1f20d8cb40f74925415e8099c896e6fcefbd8574a47496028db135433d888690a701118bec07ed04150956586ef56a4321aa18a5197553aa37d1ab9a8d4905f4ece940b7442447f21048b562e75e490e46313a21866ae6be3951e3b9d8c56c940a46746d18b285e1c5409e5c6007b8214d8976e101c161e1e9aef577614b08b1db9d4ebcc2c568598e5d35a45394233254a651dd543db9081ee320207e173732e3c3f19707a0db288e2635029be6e7266d37332994c52e3d445573aa088abcb5faaa5de1dc801112649ed8f4e95190091e00482024a96d96e1a0b14d7c3374c2df96689e53409239f03d9e576c9a3da4725d4dbc58aab1f36a4ce346102923a9052f95703ece5208e9cb6580c9006fab2520549ec5ad3ea5ffdd7eca486a016cea353e89918a8aaee98fab66493c5e8ebb6e7b90da7ef7a812ea497541065098b265200ec25c6d4ba175b6b26e3b03c550668bed246ba5d178f260693858897969608116250652146d3819a5535efc598ab217ea94587042c15711590b3c0e746286a8c521806d99329a8f424c9046336578e85ab3e17ab40657a86b6b4dae8de63bb0573280417c7dfda172320613122592ed64572d38d94864884642553190af01aaf4a5439499883ded55a7a2e3e6b7fa06e909b225e48513cc82fcbdb9c497cba02c06eacc707d5b4825ffad9ca16589fe2c7a365de96b7e2d1a3b7a54ccad5f0f0777834a41ef870ae4560158d35e7e12902b26325d5e52d3efdca6290e3b601f581454236c89d1d9ba978760eaa1018fef4482a6111eb871024d81429915ce552852bfb561ed55bf14eae24ddbaa024935aad9523dc8e7b5dc72b933404ae6dcfd97a8cfe27865774966d74b8cf53df557f8d9b8203fc3824dd44658c23e3e39da6d0c9d007960469768352c42a6d8e34940d93582559494ef581b7b2a45c294de684718ea09009aa1778925818d86b0ee8a76bee470246e480b76716497aac21a9173d1657488a24ac57824174372e89e8f369586ee4545a06c2f960ccab7e21237d9b0a44243082201c57d7e69be972b5f899438a54d490e9c255c222f64ebc4165a95822964242cce9773968271d15081e821931ce05f265441a226d8435ac0a163849c945ad9a3e29c64fc4ff8d298f46d683ca240f0fed020189a9835c592d67777e3b0f559be8dbfd377594a28a55a85fa8d415e190616eb0e2a4039f8cb9c6a5c8251709b4fa9992f576fdba9355dd283d6646b90a80b2e7c54d4f69c97366f884571c5548cc7c5671d76ca5664abbf75bcec6ce22554a68c023b98d1387090f91f0318d5092c667e2124da5540aaa5c4e2dd6638d7751940dea57df2726e151e5a2332bd5d608e169ded8e4f2adb9cabe03afa2be4641879a36e40054dc2f5be00405b71ddba7774991678b6e39cd2c7215ce06e1bedc8b004af437b579273303f8637840487002ff38d176274c156617ddf1173653a9967df61a144b61942b6093d18e0a631ab58539824a70b7b181d36339169d3d44f903fea37818983d663baae9401d3edd00e3ed6d704784d2208d584c5d486e723e11691b4ec8cb164c78c4208e24f0d81a33d178a4eae516a0130c499cb56a4ca4d5b1e54c73e28c70394db17539a958a036123157e97612caad06011c57c5e0be2484d17ee7304824845aa2c0142280e5aae956264e71865958c671652c4f440583cc4e9df15235d90b2c8d5298bb9551de2c14cbfa90cbd283d6a4057661582b55da53e2d37134
+OutRec = 0300002000040000c0001000000002c00000000c0002400000000400000000100000000300000000040002000000000c0001c000000004000080001000080002400000000400004000300004000200000000040001c00020000000018000100000000280000000040003c00010000c0001c00000000400038000200004000300002000040000c00000000c000140002000040002000020000c000200002000000002c000300008000040000000080002c00000000c000080001000040000800000000c000140000000080003000000000c00004000000004000140001000040000800000000c0001c00030000c0000c0000000000003000010000400000000300000000080002000080000800000000c0003800010000c0002c0002000000003c0000000080003c00030000400038000000004000300001000080001400000000000024000300008000240002000080002c00030000c00030000000004000000001000040002c0002000000002400010000000030000100008000300002000080000400030000c0003400000000c000100002000000001800020000c000000001000000001c0003000000003c00010000c00028000200000000340000000040000400020000c00018000100004000100000000000001c000200008000240002000040003400010000c0000800010000400018000300004000100002000080000800030000c0000c0003000080000c000200008000300003000000002400020000800014000100008000200000000080003c00010000c000000003000040000c00000000c000200001000080000800030000400010000200004000300000000000000c0000000080000400010000c00030000000004000040001000000003c0003000080003800000000c0003400020000c0001400000000c0000000000000c00030000200004000240000000080003c000300000000300002000040003000010000c0001400010000400020000300000000280000000040000000000000000008000100008000100002000040002c00000000c0003800020000400030000300004000280000000080003000010000c000380003000080000000030000c0002c000300000000080002000000001400020000800034000300008000340001000000000800010000c0001800030000c0002c0001000040003400010000c00018000000008000140002000000000800000000c00010000300008000040002000000002400010000000038000000008000280003000080000000030000c000080001000000002800020000c000380003000040001c0003000080001400020000c00028000100004000380002000040001c0002000080003400020000c000240000000040001400030000000008000100004000100000000080000000010000400020000100004000280000000080001c0000000080002c000200008000140002000040003400000000c00028000300000000000001000000002c00030000c000240001000040003000020000800018000300008000000001000080001400000000c0001400010000c000180003000040000800030000c0000000010000800024000000004000280002000080000800020000c000280001000080000c0002000080000c000000004000280003000000000c00020000400018000300008000380000000040001800030000800004000000008000000002000040002c000100004000240003000080002400030000c0000c0001000040001c0003000080000000000000c000380002000000002c00000000c0001c0000000040003800010000c0002c0000000040002400030000c0002000020000800008000300004000340000000040002c0001000000001000000000c0003000000000c00030000200008000140002000080003400030000c0000000000000400014000000008000380001000080002000030000400014000000004000080002000000002c0002000080003c0000000000000000010000c0003800010000c000040002000040000800010000c00014000200008000080001000040001c00000000c000000002000040002000030000c0003800030000c000380001000000000c000000000000000002000040001000000000400004000300008000140001000040000800020000c0002000010000000010000300008000280003000040003c0002000000000000010000c0003000020000c0000c0001000040000800000000400018000100000000380001000000000c0002000000000c00000000c000140001000080002400000000c0001c000200008000000001000000000c0001000000003c000000008000040001000080000800010000c0001000020000000038000200004000040002000040003c0000000000002800010000c000000002000080003c0003000000000800000000c00010000100008000080003000000002400020000c0002800010000c0000000030000400
+Key = 8b02bb704b579798db6aa226b731347518155ce79f24d9005c79f17d5baa5b1d
+
+InNoiseS = 55d10f4b48067ba8ef2308defa0285cec13987c82d528761f851ce55ef4f40659ebd2f0b6deae54a28ad4dcdcff387b99a8e4639c0cf354127186a92f6c0b1735f6d34d89596800646533332b2e5dab016a3d5129f8702223a94003c03774bb2061d322a86d3b7727904414df6c1c03c1a680863a43a09a862e6a0a0921eeb4431188c4c7b79edd293f24c3a1496763936b23d599d922b82663fe59836743d843046d5ac7493a95e36d16b770b7a47bf5b958355fc289c653e4637dda5ea68d40253440eb96f5976ad7a6248a138e760352792267249e91239050d4cf24406a57b2d98025bf104e587632aa7c52f65e781561b0e71c5ce16f371fe50da1f6008f2824828523fd3e2a201050b17a2b474491d49a0b36c8c34eea3adf9339c20a651d6eaa5205f406d933113816e1b5e587ad49a09de8e6e6630f60ed96163833a8560385c1b2124f165ece2ec52af9a9b612f9dc03a81190e5f46eaeda60faa0b7fb79c047207cc4e48078a46a0c49cd1132e9c9a00f85a193551a8cb4b99ec1d6c07491c114969b652935a82bba252326dab5440cca66fe24c37fae468a32b22d21540862f062c148fbda0f0a320ca12ca1ddadc661b19b45c461a38171ad57da96b89c01aba52ce596ebb325a22016e2070d5420284e4a319bf521480181ac4294d28ee037016f03149ad538e632f3c274a162e93b982079fa85aeadabe3cc58671e4bc0a2604243037ae98211789dce0b5a59dd89cfd5b13c56c97d520d73a4c373501f604c3316ce255df320b9494d10d9415faed012ac6b53400bc6d47e3307852697a142c4a9bd28a16b11cf93af3914d31e020ebf4ce8dd41aae69e8885ba641b47352212e69c03f932c5c4d54687a3503288ef5c3d6ab83fcaf2059e9098b59a898c7801683d636e45cbbabd1d8268606c3764111666fc3d57845891126dcd9faa5191a8bdae12466d62cffca274b10129b5d1d5caba36c15bf1e7de911b471a3c7a82ac6ea2374122e2770f8237c0d48e4e25c112b2f6659582567088482c9e9e0789191d226353878202120448980a6b7069bc71bde617452fd2317ed4e989f771032c46ba99724806f5998c8e8fe940b7ea24e6291ea5e550e7e5e2088dcdc6c60dee55496f22c870ca597357d728a9e45c4a8a44c984f48a62281a994098558f77eb70d2b690601a92c8327ea246355744ad97d912ca63d84eab3e0cfa521ce800699989a79269d10fb9285826da751db0dee4c716eeb946b59bf312226e7b95171915ea15615a1bb1c59a749cfb12761edd989e08a79079af4a781dc4f5d6056a91a0661742e5d54673a6c1cec1013739c2e71df299942769ef25da2e5daba26f9addc5155a8608aecbf53254c2d27a588b0823aa54d0bc24a2131b7d842edb3f9666a798f3e86abfe279d86259165bd93e8e7f913ab7d14a94f40cb44062a3d2728e9029ebaa6865be89d7d052c049e7389ca866022c267a3392f689d7ae627fa3b58141ab762d10be4e6d8752edd88cfa3985bfc723023dc380398ee9d9b9b9153a5a06eb295858397fd7719400f80e38aa18fd1eba6c324b15726f4907b84897aa3ff347578a13f1b905a148db1ffe55813c17831f54ab7eb52b44545427a25c17481d0851b590c4e1ea2682139c421108dd2bda3fdbe90549a7548e1463cf0a51e4abe1c171f4866191cba036d270248635c48848fdcb690a0bae3240029cbd688a4d6f150d08d6a0b4e62e2714e57806aad69420c71a72788c996b19b5fd5870506262b878e354056ed2dc4cc6bf00e59acd15ef776d1ee75970799c95a5a32814f8e790d459d32d4ccd807117acf2d2ff290a57342e9b439fbc6163c268953fcc0444d5afd040dc035a3aaa54884c09b4d60f2c0cf2c1e91cb1dd5573e26f3ca8609d3d582c926d429005891c02699e5d3c40eb203a8641fb8bc5dc13a04e832b954cc0003988ab691495e5662e21afb22ba667836bb7f177301005ba3124e4f947f851e5de9b8042b5da3476cbee91e588626da672217be58d146a2f3eb590be8832595455972b5531a87b1e63c20ec19bb9ee925ed1d5c7765b19c199769920d8c4ee6d7eaa3973995011e9e77c6de17662beb49ee169dcda4c6d7091459794277a0418c55926bbbc83a320cc38de4126612a3bd8a55ebfaa897c9e17082319d71b2c964ca7471721e96c69bf84d2ce9149069e119b3f40617d20dc20404dd587aef66972f14369056e2673c5cc54877f69bf8e0ada3527c58c9d8c408ce218c04858e5b94c4ba2e2da592b4c0bfb2c9e0a894e614e7a21baad239149743d1e4b3b068c5c324e1080c09eeca22487b2d807620ff20f64eb1ce404ff82f3926c0673b4341c6a6a1005b08e1fd27578ed1373af6879be3159b2cad27a29eb0c9fab2358258d0a946b1396e6d007995c291cbee1837b4454a13c84aa7b4da799bdebe33b9d2879d11f02008c82a7f9d48c33e5c57a74f731496b62a180db690406a9d8828c13550093aa1a29309ffb96adb68cda1a1089a
+InPK = 73ae7fcb7366049e5c2060340422d555213529f573740c28c3f7e11191d2ff660c2c9789d31cc5cfc91227e48ec3c9593df0524f9998904f8d04d3a82de231ac4760a3097911c216159909a08c42195a7661ee825a93108560d582792bb5566f900ff301118ae6607d8282bfc340de1ea6463cab4734ad43e15dea831c599fea681947790c8b1ae4f24ed2460b68d371b24c80474ce5a551418bfed86f0349695f6adf26da3e9a65d3913e29f96eb9ad94452847d43b0aae7401d46aa384108de8ef5a906299744b19ed4f62ae99a6bd18580b6d736ac04a5c450901b8c61843470c23d37b964a200863097bce54f9588db1ca487107eed30630b013774770f881e9375e99ab44a978663d1a22b0bc3e38e19e4f1042e07fb8c3340873b60def9f9cd605eeaae4609742139517ce5f4080ca3a2e6b189b4bd26a6700d6bb7bac648774962aa27ec4a9c5c89c9b264d0757497b88825859cfa682db002a9665864b5a1d9154456a1872321e6f596b87a810aa3cc326070a06be443d5894e981bc4552abf2faadbc37a356f8e5f0655847a6d7aa8e8dad54442ce0154546f784609855b677986523c111b54221dc88e7a2519500da5cc2e9b0ab31db066aff6e3c1de5de84f8893ac361f494130943c724dd563070a23c0f7601ee0077f46b38033f2d493d29c27062953d7594f5c2381e5500ed684550b40959ce1a8327b474288e0fb052a0466a11239574f9ad38467541aa1276cee8aee57b3ea1f20d8cb40f74925415e8099c896e6fcefbd8574a47496028db135433d888690a701118bec07ed04150956586ef56a4321aa18a5197553aa37d1ab9a8d4905f4ece940b7442447f21048b562e75e490e46313a21866ae6be3951e3b9d8c56c940a46746d18b285e1c5409e5c6007b8214d8976e101c161e1e9aef577614b08b1db9d4ebcc2c568598e5d35a45394233254a651dd543db9081ee320207e173732e3c3f19707a0db288e2635029be6e7266d37332994c52e3d445573aa088abcb5faaa5de1dc801112649ed8f4e95190091e00482024a96d96e1a0b14d7c3374c2df96689e53409239f03d9e576c9a3da4725d4dbc58aab1f36a4ce346102923a9052f95703ece5208e9cb6580c9006fab2520549ec5ad3ea5ffdd7eca486a016cea353e89918a8aaee98fab66493c5e8ebb6e7b90da7ef7a812ea497541065098b265200ec25c6d4ba175b6b26e3b03c550668bed246ba5d178f260693858897969608116250652146d3819a5535efc598ab217ea94587042c15711590b3c0e746286a8c521806d99329a8f424c9046336578e85ab3e17ab40657a86b6b4dae8de63bb0573280417c7dfda172320613122592ed64572d38d94864884642553190af01aaf4a5439499883ded55a7a2e3e6b7fa06e909b225e48513cc82fcbdb9c497cba02c06eacc707d5b4825ffad9ca16589fe2c7a365de96b7e2d1a3b7a54ccad5f0f0777834a41ef870ae4560158d35e7e12902b26325d5e52d3efdca6290e3b601f581454236c89d1d9ba978760eaa1018fef4482a6111eb871024d81429915ce552852bfb561ed55bf14eae24ddbaa024935aad9523dc8e7b5dc72b933404ae6dcfd97a8cfe27865774966d74b8cf53df557f8d9b8203fc3824dd44658c23e3e39da6d0c9d007960469768352c42a6d8e34940d93582559494ef581b7b2a45c294de684718ea09009aa1778925818d86b0ee8a76bee470246e480b76716497aac21a9173d1657488a24ac57824174372e89e8f369586ee4545a06c2f960ccab7e21237d9b0a44243082201c57d7e69be972b5f899438a54d490e9c255c222f64ebc4165a95822964242cce9773968271d15081e821931ce05f265441a226d8435ac0a163849c945ad9a3e29c64fc4ff8d298f46d683ca240f0fed020189a9835c592d67777e3b0f559be8dbfd377594a28a55a85fa8d415e190616eb0e2a4039f8cb9c6a5c8251709b4fa9992f576fdba9355dd283d6646b90a80b2e7c54d4f69c97366f884571c5548cc7c5671d76ca5664abbf75bcec6ce22554a68c023b98d1387090f91f0318d5092c667e2124da5540aaa5c4e2dd6638d7751940dea57df2726e151e5a2332bd5d608e169ded8e4f2adb9cabe03afa2be4641879a36e40054dc2f5be00405b71ddba7774991678b6e39cd2c7215ce06e1bedc8b004af437b579273303f8637840487002ff38d176274c156617ddf1173653a9967df61a144b61942b6093d18e0a631ab58539824a70b7b181d36339169d3d44f903fea37818983d663baae9401d3edd00e3ed6d704784d2208d584c5d486e723e11691b4ec8cb164c78c4208e24f0d81a33d178a4eae516a0130c499cb56a4ca4d5b1e54c73e28c70394db17539a958a036123157e97612caad06011c57c5e0be2484d17ee7304824845aa2c0142280e5aae956264e71865958c671652c4f440583cc4e9df15235d90b2c8d5298bb9551de2c14cbfa90cbd283d6a4057661582b55da53e2d37134
+InRec = 0300002000040000c0001000000002c00000000c0002400000000400000000100000000300000000040002000000000c0001c000000004000080001000080002400000000400004000300004000200000000040001c00020000000018000100000000280000000040003c00010000c0001c00000000400038000200004000300002000040000c00000000c000140002000040002000020000c000200002000000002c000300008000040000000080002c00000000c000080001000040000800000000c000140000000080003000000000c00004000000004000140001000040000800000000c0001c00030000c0000c0000000000003000010000400000000300000000080002000080000800000000c0003800010000c0002c0002000000003c0000000080003c00030000400038000000004000300001000080001400000000000024000300008000240002000080002c00030000c00030000000004000000001000040002c0002000000002400010000000030000100008000300002000080000400030000c0003400000000c000100002000000001800020000c000000001000000001c0003000000003c00010000c00028000200000000340000000040000400020000c00018000100004000100000000000001c000200008000240002000040003400010000c0000800010000400018000300004000100002000080000800030000c0000c0003000080000c000200008000300003000000002400020000800014000100008000200000000080003c00010000c000000003000040000c00000000c000200001000080000800030000400010000200004000300000000000000c0000000080000400010000c00030000000004000040001000000003c0003000080003800000000c0003400020000c0001400000000c0000000000000c00030000200004000240000000080003c000300000000300002000040003000010000c0001400010000400020000300000000280000000040000000000000000008000100008000100002000040002c00000000c0003800020000400030000300004000280000000080003000010000c000380003000080000000030000c0002c000300000000080002000000001400020000800034000300008000340001000000000800010000c0001800030000c0002c0001000040003400010000c00018000000008000140002000000000800000000c00010000300008000040002000000002400010000000038000000008000280003000080000000030000c000080001000000002800020000c000380003000040001c0003000080001400020000c00028000100004000380002000040001c0002000080003400020000c000240000000040001400030000000008000100004000100000000080000000010000400020000100004000280000000080001c0000000080002c000200008000140002000040003400000000c00028000300000000000001000000002c00030000c000240001000040003000020000800018000300008000000001000080001400000000c0001400010000c000180003000040000800030000c0000000010000800024000000004000280002000080000800020000c000280001000080000c0002000080000c000000004000280003000000000c00020000400018000300008000380000000040001800030000800004000000008000000002000040002c000100004000240003000080002400030000c0000c0001000040001c0003000080000000000000c000380002000000002c00000000c0001c0000000040003800010000c0002c0000000040002400030000c0002000020000800008000300004000340000000040002c0001000000001000000000c0003000000000c00030000200008000140002000080003400030000c0000000000000400014000000008000380001000080002000030000400014000000004000080002000000002c0002000080003c0000000000000000010000c0003800010000c000040002000040000800010000c00014000200008000080001000040001c00000000c000000002000040002000030000c0003800030000c000380001000000000c000000000000000002000040001000000000400004000300008000140001000040000800020000c0002000010000000010000300008000280003000040003c0002000000000000010000c0003000020000c0000c0001000040000800000000400018000100000000380001000000000c0002000000000c00000000c000140001000080002400000000c0001c000200008000000001000000000c0001000000003c000000008000040001000080000800010000c0001000020000000038000200004000040002000040003c0000000000002800010000c000000002000080003c0003000000000800000000c00010000100008000080003000000002400020000c0002800010000c0000000030000400
+Key = 8b02bb704b579798db6aa226b731347518155ce79f24d9005c79f17d5baa5b1d
+
+InRandA = 711d61a7cc0a64ee994d801efabd24af23056beeacddc2a453dbb25eaf9b22d6f8aa0cf11781712ea06ffeeb5adb4a20669ceb8e8a6f7867c55dc376d941a1300e68b06110738e1fdad3c68ab12bafb8d1ed723eb898ee9631304bd843dcd69c6a3d7659a872f74529ad5ab697b285361702dad750299cdd0d3f10cc680b94134898200463ee1d45e688520118cc162872fe87aa6707c57e291d490e6ea92f624f1218212ba4d08818960676831c2d21939bd680be8c0face78192423b97a37f4aad65ab00d24fa7aa8a13ef6987716ce45a2bddea183c5c37a6a64faa3c1a345f0ab811b768454c1b9335c5cd8db96e657a0cd635a20dec30ef425e18957053f28174f9514613a7685fe662c007f61a309959b8a09b22b341630c86f04870af4c57c64e6181f0047d955a2516b546fda3799e8ec3472982a57d249f29e88bb26064541da9a8867975acaf7861ecac944b2e37b8f7194a725342505e9855334d5dc6c7d8ad864856bbcf50490f23fe86e452b04e0134f6665295c26da31211890b9c5ce243713904415f8e71f42c0955e723815f7cad3726fc2a3ca861e7095f7277ceacd9a88f93c71eb70a9bfc600d7d959ad6171be74c29ca4d980676120c59879bb1de4c4369223e15c6f43d8b1e4e94344636f8496cea5f8d696f1414e3c5a20d239c75d0d21289615c2ad12c9654c623b24ae0008a342d8994c0ecaeb211c5079fc81d7ec6b0407d6ab042961d0962b21e23e09f11a39459095e58b8fc30b93618797954e1b87a0c7f42b5e28591c7cdd2a58e6f039a4ad3b054a328dfc0daf276b586dcb4c426c4406aa528f5f4fc497483312b7d3825b30cdfc6756852404c148bd751be30d2143a2ff2383d4fc3419c417c2093766355e938fd43263bfaedbeec42bab1d9f6ad8e90aa58d03c033720aa30d679085b4029a2cc048ae8545b791a253efea4464ad8d6b44b561395679c418e6a6c67cc7147218ebcb4dadd1d5f8d0dabbd79538d81b4d22151550dc8bf729cf63338ea5c4a950e2b50e2cec5a25187ad10a12729ad62806a92035eb49cf04e8fe35f1c40811a9987e0d06145a1eaab26d14a7e0e15bb7d4533e42dd2d430de6faccf2a25e3c132579e1a5103ae457a420b25392687a0d813e1caba3b1e11635198cd672eae29a1cd9d97478e2c6189b50b49ed4e68809d0d15035cfaf6943a378d107044f6674f9d811acad9757be85df6e52c082ada6d2688f2a6e12e4760f24abb3ba8f1d9d9456c94c20b3711dd01349fe70b16711f7209f1921e5d81ba25b3ea9af2bb51ed71215dd41a096bccc14ac4476e8e7702f9fd5d19e40bfb2e5164435534f1d48263afa708469b340d0aae57b1020988fae64c662e017ec68a4f7aa5d1b1ec82edf365b62d130e8ff28f6d511f97b166d1bc607b127f4a612e9bc5445f6055e9192529e528be112757a0abf2bf72544199806944e04b53d8e45a08fd96c9939b9874e9803e56bb16608a47cae265fa023d799646e0775c5c0518825000b872b7d65653424b050905e02ee6a732717f5a12b1117923376d9b8a728c3a24828123903426c0c27f9bbba84c94e18259bf2e0010244df8edf6a938ed1787796c32f4d26878e1cd7b520197c8838144f82c5c3a2e5837a89dcd95f6d29dab9d72ea63d41c9e072b67c61a1f182c2368c4a22fd46699e4a5d6a804eb6e974c8642f5ce9aea803522e66f81b21bc0323ae9410673a43683cdf61befa5b19496758ac456155103a38924f613215271c18892ad96dceb7a32b10a8e1e86d9588c7a5ed9690154f4629909e1d45b545f74a14480de196fe58b332a13acb353cad15bb85d87fb458d6b4f225524d3ad87d0985a049572a0f16ca1f81b5f550a4127cf9c9e9b5e39552042e624ebdc96f35f48647a5c5231889db3ba0ab8835aafd76719892990b2b75931568b9213b96d6da5c01455d5407534ca64f7d0a55a47c44f85e221489e42932d80e5d261d39159176dda667b2d2f8173798ae841e7d92b6c3f450ba9172ed635dace2592ccbc477cd72c2661513701262d84d9bc6611da3c26085d6978000f322d80374f6cef19100625972fd08610cc1810d147b6b7700872a4898718a2919d82d3e9f9d8064069d41301f060321e9b19f7851cb36b0f298e9d5cb4104feb8aca16f096390ada458cf0026fe6c74299e35f97b682d82311eda63b640991e651a940c5be02e74b2f74fe4953d61d09278be62956dff441b45434035ab0c9eb05543d8c6a2a480c0fa419b9e999844f5a5f73d4627104de298e80787542730d57d7ed35236be32271f1d034562b8484bfa101926cbb5098fa18f09a1ad54b0d1556598d9178f602b4deda672ea147e8c4e8a5029003f11a1df1cb6e6f17a24010630c908096a3712ba2b94717448374389aeb08c783adfd9491aaea0746481a6a24757712ed94992aa6e6b715974356d061c1449b1bd11cc64eed6b5c24f86b26a872a49936db6b9487c0cef621fe16a248921386c9dc49bb08c1c15d2061
+InNoiseS = 4ca10ae48258b14d83e48384f858eae4c130e49055a044c78590f0a74f1a19e8edd427300659bb38647c032c5722a6ac4b05413b4a0dc870ac1b16f25099428b9beb83af39887340a1c4bd986241429eb8eec081442a418b10ac60be94b3e34374a63c5582274aba579a03202666b16f3066985a26b96dd8993e242ccd6a3d9a2d287fdc82da9091830d0886b80bf3419ae968c615521160e85676c96af65dd92ea10e2c520bca4b882326b7b2c131d22d922827c5b41f2f33b281b50e8b64d92ac48a9bc4516a39272018ef5b707223b59bc2e07dd6831015b610b04638aa9d949cb7444c7d3162cf3a088b628f73c93d66375540330ac2d871d4b0405421caa142a0a2effcaacfe5173862deb228da286993d7605db54efd98f122b1949be7a15481fbd23d97d588b7bce9bf4da31481296149138c1b5753caaa9428b6d5f3be48c69979d351525c61532bf5535aa7d72850c294021d1614fda36c781f2ddbf10a4f723e578a596092ec312827ae456cac024c5a44bb68e5175025ce6876aeb1951d022426faab5a0db9d0cf369324a153533fb50b5a5866c77c00524265020b769517d7970918f5d2bc5a6e874332186d5f9e7a3a03aa28a39de5078002c0845c9eb74791088a81b185958e014f95d1219d702d2ec39a772fa82a3c91fc873a991d54c3c0babb64a7bdd15e78232d3b71a1c08bd60e68fc2f5e46348c8b948c1f93860eab88d24b736c785f2a4564a6754492c9c988c786166708d2b240d3da4c3280bd5616c0aff7a81a9a560809766df9b27e014c6c8aa45b38a6416046ba4b560804a90b54c3f5b6a2abaa7149968c8567c0ed07f2ee99218ed52b88bd98624c1c91b9ae1b082b6d07c8697eccc6a953cec576a25cdf548866a1cb03ded4fb26a1afa6c9287050459de64af1aac1572b1509643c6407f5d4a944904856c42f770717adbc701ff3c9f44aae6bea40c50bdd9f1f9368b667286732023d89581e7f872639f19766b65305b07babe9003e631b6a32511e690a864a18d45263c9436a0f1245db4405b31e1d9a6598922ae6f09a61975b3ac27225baa60e1bf6f01b8a0c07988dc9167648e9b1891c3e01a5031260cd52d08cce39498835b9122ef5d4f24f34c7623448294c4da985b5904583bd6f7e301bb911abaf1c778ee1567e8f4df0e05479cf1b5854e0cc7083a4463a96f2aa4d28944147464d249057b13c3eb8e0e93466a70194309f3339ef597459e9ffb5b15a1cf080c34713b819f6051d2baa142b9602460ac3451161913bd960202f68092c456b6ab71c638877f008862904b3fa62b815414e745b2309629d4691e894282c37e70ab15a82a7b055157b481552c6ee3ef7510a9d9420216b7f2275ef64dab95cc86b3e8ffbcaf508bafa0251c4755d287041290b4f6675f22d3ff9c31808c29d8281f2aaa7c7dede72a42592c10b20a35cee60b31b9d67f5ea6adaa33fb3dd911816aa142975551ebd40569bb1fd904a504b7ac811234029921b6b06b28a1df7c2a2ed59a4ad3541f8852e815b08c09444809decfd2904a62d991cc3c6659d6d4c6906d482bc03cce05cbb9ae142264aeda128148084c5b93b831458b061dd99e94e60a1ef51199f550156047901792628c6d5ff316c20be6d5ea59030d6abb624f8909e12a02dcf69f039ad45ffda6113f2f6aa9be6bb934122ae2de84b7a0add8488c052aaf50d0e098d58c7be3c80fe946c566c00b4739fd59aa2de006885b0cb9c65231f5fa760af69a929443aa3252aa16497dcb2c1180f42cca1310b99278a956a07cf540561dc0bb9e8b6b2ea25e57ae52d636b6297a2afbcb6d014942a33ce4869829de6f1ba761b60b6b93f993938884076c1e4976848f8bb6c4486daddaae7fb8073749a70f0eec61ff2c9687184c09bd15f849108700efe8a01b72c9c97d40957b4e3d64533411887541a5de6518ab756994bb5ed9f7af8da0bbe6959c57e4a2f346884a6716a847015c5545d69770d56d44500097f83ee4b736df867029d979848b64a2c281b2ac3d140ed854f12fd946b770754139e0e320223dac9823a14c28cbae29dc4af992ced62ab7255f265c533cb54b75f48a8321e7aa8f7939c468d26645c57b4abf0569260229a40611920af1add758474b8d026f41785559ad04faa501a649e44abee8607fc4eb21376552ba48931156485c7916d4d63247a757d4f92cf34322202b94acdc3276d99a2a899d3471475bb268ef9ea9976a949437e30e168301d027518aeec04ffc4294a00a2649b271b334f3cbdf88678b8b8c583f62c0815937667aede6ae53aec271e5d885a84b58d348449aa06649d6a635bd7d392885a0ae60218d8a085a509f5e5ad12e24c4d36b04600c0011623d4a4bd2206fce8d5003234405db3ad7e3d4dd6aa790abc6b1e49e8b26436ae6c9f1010b1e052158639c1582d641d8b3ed0a9c289aa5eae66e66a43048876b14cf4a69489224564c4932adaaf83e2b15e0b2248425d71198849f534d801b24cccb2aa434829
+InNoiseE = 349b8309537882c618c642ab2d78d380e2401039ac30ec4fd00536ad1d6297b1a4cc7dad96c8385cf90ce6e5f6995dfd317a510bcabc18a58ea4959286c491ae000ca949f92137c851e143c574b66e510b66b05ec661bcf0d32a401450af69e8a8943cd8b807ecc8be785a0ce5b840200fdc4c41c7a48fe2625853a59d807452ae647f3d139a97cb66aaf2b4988095d52ecc17e6a9ec41ee8821fbeec2d03d7a2466a0af2a211e718bded74735ac8e6a63cf3b18f16ae61619263bfc065e9c2197b9ca9b48e2ae35ab3c0500684e86c6e55e34a4d37415864578484180f53c500a16b601232987cdd02994ef560a8cc37f205ee27712e4a39104c23ec69e03c98c5c403cd7fc2b3f22aaab0c8dc50e6557995468564c100e37d009228b7a71fc98abe828810d4ec0a208103c1e5068029a86a8abc4e0516a96807de96a38ba7c16bd1e2dcf85d7ec192e9fd9a4822207640d6681b45c4f0d1b1ba2739ea684676278a5259e8166357aa6b90a1cccb8001e7e3aac2eb6c713a9705daf156015ccab64c49509760d4b464462ba92ce812aa10bd195c27e70a6254208929bcf85f5f23f583e1ec4b60123aa3d9217f37a33b19bd120aff2e13ab15a534d669701a9cb650a58958c5900a02938a7fd321cc4d07a0f4dbae65d00d44724377aa3ddd4a37d368995cc85fe5672d68f3c453b286fa5cc0b3322ca8d3eae302b982e732fa369c89ada8d45213ba560797a81d54f437a1e8708856781dbe5a5e013676bdfbebb758d41a500f466aad9ce997fd566a143dd299a5a51494de7aad82a820cc966901b9e0f326bfc01e77e7b78285c60fa0064c0e0cdd5e54112e51667323c9c4f842628c20128489ce13cdbc4366c7ee94791d936adad0c9a290e78931a2adab935664ab195e998108c50490bab1ad28a77f5a6b296dcf51e1b5850d2b9ea41ca1f45d96a1660ba055d1b441cb0702a8460dc1eab7642ee35f352bffd638be7148dae22cb8300e435364fe6481052506a9529426803b236ba6d8497214529a6ca9935ed79bdab27ddf330c20ba1d5b1959e487a206bd6706fd51ae3ecac0a7029485d13c9a84d1a1e0d9446a46d7c046ddef9bb50a5ada49a6f50612906b2423a4a7d30b837e39640b196a6a98175f05ea02c624c57186489be678f7981d5918c8a59ef3618245f45d5182b33ea6f7fa2f92004b043ffc9a4b88bd2b319c5f3a6a1f93511ef5ac6e9c2773beee23533b175cc67b6879840bb002bd6b45055a5f2acb6e8e3ad5cba250510c6758ff59e8257b051a06d8dab789d856165eae7c057c22e6ca3fee36a20052ac6f5f583ce2c1e48b172ca815bf84d263bbeae1d88679df8681661fb0650a48cea4ad15c78355429048a91a847d3c45c72e964db630073049a6ab35a9e1cc15aa979e05e81f8ee5945b289648025f6b86a489bb75023eb0c2a035cd211e5a88a3daa479c53098184359ed6e85a3c3d29e6f6e66e1595b3aa78abccb5485d67a741e211436513dd5ac5047068aa03ab59890bb148d809cd9cbe152996b5e8d266689276105c887b5268ecab7f2e68e045aea8082c0a535f3773ff614527616fc145e8228d2d467144a06077dfeda7ef2284b46dbb7861ba904f19429a15422dd79f4ab4ff912cc8ac83635e611ca79581f487b56c44a2f5f0f84d80f00918802571072e591cf8c81e1b831431117f30ac7e9ae6067f410e03d580231c86141e0ee1a6767a1085c3a1391ede006fdd47d5ea252bd1a723a92abab5a21ea0392f812235c40d7b2930405e2a8278b3e885ed41cb29834d09c7013d06596834bd465856f3e407600c29544380acfaeef9127af55fc150a08b8381a3450913dbc33d5f1591cc27cfa41ed705e8aa8bf872c0a31268fb329a3d14121898d213bca41d43d99c4c37eb0f454e4e8e889f35d12d1818c750ce16adb47eb6e1c006402992490158ba67b87b6cd6d5d787fc953fd66796fb8d1cd6e67d13403c7275d2ec56ce16a0792429f2b374b608ec3cd17a740055c0ef2162cd151d0025b23d6db53249f15409563e49595b26dc3970a8ef890280c64b56de24941208366558d1f601e921335c880af02d52e5ade6757a6019c6648a2a77ba634f802edb4cd0b6cf0f8b4bfbea2cf68b80f4b6f47e070eb87479b1996d1681298ec5445be5ff4761baa0f9a586e4ac2170d75311322c1a30301ee5275bca731052d4688f71774e5a1509f60d163c56fa403aa810f1804cc682cc3b112522917c51613d4fad32f401a02ba0b059c4b1136d07164bf935a3bc14ca8640d597dce54316616a38a41ef7fa54e0178c2111084742bef21667b6d031a5c5a962a23d3229715be995f18438f75168d7a69dac1c8f10b480841c001dbc6997003d0ae87d4424aa3a918b81c17894b16f4697559b2a0ef3ac68e61ea9a788407324235951e2ef01b0a260993c03aad0f00a433c2b40a90518a11eaea4756af408da6181c288fa82bb4c3b625b98096a62d6c15449234a609958e1955dcd40c4d9cb8d14
+OutPK = 5455f1b09dc660942d60b9e13ab27b43086b595a47099a78b526a4bff2e3ed7addf47fc796c09adf480b8a091d09111d0999d632d910c03dc1ea867aa458a99b2c9a4795393bb6a417085fcc776a6c49f32a8185e24c7662ef62bbbb184db4e73670a16279b1c0245d8aeb3c6aa93c6eb588f84bc89847e312c8b419fd8305ea392a32a297c449ccf51ba56ee566a6e91c9cb46417a927bd688bd0223232db5c5f62d85bf3bbbe2ff0021dbace3c8f3a6fd3991e70a277c2390a64b64c43907154d83e779e0358426e090fba1a3676c94652e20a40f3dae5bf08952265706a9fc942991a3cdd56265f48768096287598c820953d90728c57b0fd352be0db8e677824a7f92b6ce9bf68721b2251547f392200c5c2625448b11a8dc7a0bce8b6f0202a24b65d63a6d7479b474543d2d6c42508c8724996c335134553a6df4d88d5ac44560023a009d5561bd0a5d0d65859ffde9070bb2c72b616ea62b1647a0263d1ca4f3e7d35267e00831eb1dbc2f9a7a7793d1d6d575b77e6686b429cb771fa928fd55b0b48d4266f557b49800083f6425f9653f84763dafb03c8dea5aede1f86e69d37148da13afa4d9ffa4a435a23a93aaa1175224ff2667524ec7a8af29c518784e6f43c657fc4a023a2da2773ea7d19cae499cd5f244473aeae2865b92bfc248212e843c73b6879f769f9b5a8757e1fc4f61ba801613e8c004a61516774e159e14ba972b3cd3d77e3f6b4f093aad5906d91f6c4b9f19cb158e05a652135bc11e89735f4b6bc2fbac84ab8c849644d1678522e3109e8ee32d3a1c407e993d311b0baaaba5aeac6ed11a904092044e8028964044b01ef56663b908aa3cb2a5fdb23121aa12d80ced256d5501a57f1c99ca0a69848859b8ee4484dac685a02c2c37a7baa0a0b7029d1190e89dff2e44d2afd5461c2f05a040a0be2e0652e4e9c041e3405112fa12e869069e1081666c509c60186b512b822274d27c9c78f413fcdb48097692115a461daa70688fc093ed22d9663818e238a25a0b2413176e1687f20320c8efa9d2666a2444d88d452717e0608c98c91fab266143bdf5698b8ac22de8576c672da2993cf0bd6c2c638f19ca672603da4355226a71e8465bc51cc96ac7c34a0e91a777962249a6429a6810405828093709d9cbe465c446371188ce0c4caaaf1849210cc5ec084fe2de62773191e3d9c7290caa4952c0a5b2ad4c4b6907c616a55a8c7768d1b9b45b8872819a4e44b47a3c92f86411c14530db4d82a83c0678e22834408d342117fbb414d8979718499231f43a960281ca12974a6b08f2258a7879a3c67d0afaa6451c9167a4c79d04c909fff62d2ab71e116ea58dd41057487db414bea87b925e5ca33c88852a71a104d3600942fac9119a7512054b32d7b57e23554a026d7510996738d844792050519d5a0226812ba5a1998098213008d08d91f14a2b8088b5ccf2a5e003bb84bdaf9e25c4d1692420400c19f21d675f686290df3295ff814de1e3c09d74504696e0ea29d357e76b813d2339880f293419098851b665fab0dee933352274157f944701459b9da26c745fe73729e7757f46c03066e9ab8e1a863a8e1a430ab719c3b0e342617d80859c200b787996abc02b00232728741cb34c4478644846acfda8f393789e259abf7fd67a188c3382c0d8c0c4d427b5e2e8650d440177ea64e3720e6e8a4864c7cacdd65f25c2817c5b2832be22ba31bff60e540318449f9f55451aa04db2215846d9b794b949b09e66f59e84f70f278bce0aaae026e79132c8be008ae6db23869e58b2c9695149bdef339753759043463628c2690d6aa352c1410f0e99ae326fa9b678add542c9e2a44d1a3815aa06c5cd6f5b2c7023db2ad0b6773c4121ced854a27ed579189a014f5fa43d38cddd1b412a8ca6410a32e553d70e5d4d90a84986cd0abcaab4e73f0cdc68b6d7d710d5e9b83ff482f37a8237289255001801aea39a5641da06a21b3ea2dcf057f27c210acd300750a06c991e7633942204897101182fa9f2049286143ba79666f18905719c0e879d7d442e4c7a537c713597308b54f261963f7e8bd82725e5e8837e23253667427753d78db5f22e5891eaf9b459ed8f86516769a44c03a528d45ca13067056225b430bab658496220edbe3a70c5e2daf0736da80076ec85ea08cc259a40f412296861d37af6f38610624291e7f410abaa56edb34c20ecd2bc8c2e4315b3850bae021906498497912a70266259824976aa9eb88049c4b08f6876063dca72331c5a9ba0e2f8e21e0668db8d1c105566e39c51910268e551dbb146a9aacd8a457a91e49022e8eb318d216dcf086454d0a88a5ea27364a27d3b585757ce194255d857d9b12e648661659c4c16a9b954216bda445863828ab799d45b61184b8fad5613199eca1cb59a5c69c4843d8a8dd0ab9c149f6035dc26cd4bdb59fa3b6ee699781cf3b06977d22ae472155bb5e64c96b1241f5e67aaa2b73335032aa34da0ee50a0145e92ad0f2011e57151a3f1a01ee5b
+
+InPK = 5455f1b09dc660942d60b9e13ab27b43086b595a47099a78b526a4bff2e3ed7addf47fc796c09adf480b8a091d09111d0999d632d910c03dc1ea867aa458a99b2c9a4795393bb6a417085fcc776a6c49f32a8185e24c7662ef62bbbb184db4e73670a16279b1c0245d8aeb3c6aa93c6eb588f84bc89847e312c8b419fd8305ea392a32a297c449ccf51ba56ee566a6e91c9cb46417a927bd688bd0223232db5c5f62d85bf3bbbe2ff0021dbace3c8f3a6fd3991e70a277c2390a64b64c43907154d83e779e0358426e090fba1a3676c94652e20a40f3dae5bf08952265706a9fc942991a3cdd56265f48768096287598c820953d90728c57b0fd352be0db8e677824a7f92b6ce9bf68721b2251547f392200c5c2625448b11a8dc7a0bce8b6f0202a24b65d63a6d7479b474543d2d6c42508c8724996c335134553a6df4d88d5ac44560023a009d5561bd0a5d0d65859ffde9070bb2c72b616ea62b1647a0263d1ca4f3e7d35267e00831eb1dbc2f9a7a7793d1d6d575b77e6686b429cb771fa928fd55b0b48d4266f557b49800083f6425f9653f84763dafb03c8dea5aede1f86e69d37148da13afa4d9ffa4a435a23a93aaa1175224ff2667524ec7a8af29c518784e6f43c657fc4a023a2da2773ea7d19cae499cd5f244473aeae2865b92bfc248212e843c73b6879f769f9b5a8757e1fc4f61ba801613e8c004a61516774e159e14ba972b3cd3d77e3f6b4f093aad5906d91f6c4b9f19cb158e05a652135bc11e89735f4b6bc2fbac84ab8c849644d1678522e3109e8ee32d3a1c407e993d311b0baaaba5aeac6ed11a904092044e8028964044b01ef56663b908aa3cb2a5fdb23121aa12d80ced256d5501a57f1c99ca0a69848859b8ee4484dac685a02c2c37a7baa0a0b7029d1190e89dff2e44d2afd5461c2f05a040a0be2e0652e4e9c041e3405112fa12e869069e1081666c509c60186b512b822274d27c9c78f413fcdb48097692115a461daa70688fc093ed22d9663818e238a25a0b2413176e1687f20320c8efa9d2666a2444d88d452717e0608c98c91fab266143bdf5698b8ac22de8576c672da2993cf0bd6c2c638f19ca672603da4355226a71e8465bc51cc96ac7c34a0e91a777962249a6429a6810405828093709d9cbe465c446371188ce0c4caaaf1849210cc5ec084fe2de62773191e3d9c7290caa4952c0a5b2ad4c4b6907c616a55a8c7768d1b9b45b8872819a4e44b47a3c92f86411c14530db4d82a83c0678e22834408d342117fbb414d8979718499231f43a960281ca12974a6b08f2258a7879a3c67d0afaa6451c9167a4c79d04c909fff62d2ab71e116ea58dd41057487db414bea87b925e5ca33c88852a71a104d3600942fac9119a7512054b32d7b57e23554a026d7510996738d844792050519d5a0226812ba5a1998098213008d08d91f14a2b8088b5ccf2a5e003bb84bdaf9e25c4d1692420400c19f21d675f686290df3295ff814de1e3c09d74504696e0ea29d357e76b813d2339880f293419098851b665fab0dee933352274157f944701459b9da26c745fe73729e7757f46c03066e9ab8e1a863a8e1a430ab719c3b0e342617d80859c200b787996abc02b00232728741cb34c4478644846acfda8f393789e259abf7fd67a188c3382c0d8c0c4d427b5e2e8650d440177ea64e3720e6e8a4864c7cacdd65f25c2817c5b2832be22ba31bff60e540318449f9f55451aa04db2215846d9b794b949b09e66f59e84f70f278bce0aaae026e79132c8be008ae6db23869e58b2c9695149bdef339753759043463628c2690d6aa352c1410f0e99ae326fa9b678add542c9e2a44d1a3815aa06c5cd6f5b2c7023db2ad0b6773c4121ced854a27ed579189a014f5fa43d38cddd1b412a8ca6410a32e553d70e5d4d90a84986cd0abcaab4e73f0cdc68b6d7d710d5e9b83ff482f37a8237289255001801aea39a5641da06a21b3ea2dcf057f27c210acd300750a06c991e7633942204897101182fa9f2049286143ba79666f18905719c0e879d7d442e4c7a537c713597308b54f261963f7e8bd82725e5e8837e23253667427753d78db5f22e5891eaf9b459ed8f86516769a44c03a528d45ca13067056225b430bab658496220edbe3a70c5e2daf0736da80076ec85ea08cc259a40f412296861d37af6f38610624291e7f410abaa56edb34c20ecd2bc8c2e4315b3850bae021906498497912a70266259824976aa9eb88049c4b08f6876063dca72331c5a9ba0e2f8e21e0668db8d1c105566e39c51910268e551dbb146a9aacd8a457a91e49022e8eb318d216dcf086454d0a88a5ea27364a27d3b585757ce194255d857d9b12e648661659c4c16a9b954216bda445863828ab799d45b61184b8fad5613199eca1cb59a5c69c4843d8a8dd0ab9c149f6035dc26cd4bdb59fa3b6ee699781cf3b06977d22ae472155bb5e64c96b1241f5e67aaa2b73335032aa34da0ee50a0145e92ad0f2011e57151a3f1a01ee5b
+InA = 711d61a7cc0a64ee994d801efabd24af23056beeacddc2a453dbb25eaf9b22d6f8aa0cf11781712ea06ffeeb5adb4a20669ceb8e8a6f7867c55dc376d941a1300e68b06110738e1fdad3c68ab12bafb8d1ed723eb898ee9631304bd843dcd69c6a3d7659a872f74529ad5ab697b285361702dad750299cdd0d3f10cc680b94134898200463ee1d45e688520118cc162872fe87aa6707c57e291d490e6ea92f624f1218212ba4d08818960676831c2d21939bd680be8c0face78192423b97a37f4aad65ab00d24fa7aa8a13ef6987716ce45a2bddea183c5c37a6a64faa3c1a345f0ab811b768454c1b9335c5cd8db96e657a0cd635a20dec30ef425e18957053f28174f9514613a7685fe662c007f61a309959b8a09b22b341630c86f04870af4c57c64e6181f0047d955a2516b546fda3799e8ec3472982a57d249f29e88bb26064541da9a8867975acaf7861ecac944b2e37b8f7194a725342505e9855334d5dc6c7d8ad864856bbcf50490f23fe86e452b04e0134f6665295c26da31211890b9c5ce243713904415f8e71f42c0955e723815f7cad3726fc2a3ca861e7095f7277ceacd9a88f93c71eb70a9bfc600d7d959ad6171be74c29ca4d980676120c59879bb1de4c4369223e15c6f43d8b1e4e94344636f8496cea5f8d696f1414e3c5a20d239c75d0d21289615c2ad12c9654c623b24ae0008a342d8994c0ecaeb211c5079fc81d7ec6b0407d6ab042961d0962b21e23e09f11a39459095e58b8fc30b93618797954e1b87a0c7f42b5e28591c7cdd2a58e6f039a4ad3b054a328dfc0daf276b586dcb4c426c4406aa528f5f4fc497483312b7d3825b30cdfc6756852404c148bd751be30d2143a2ff2383d4fc3419c417c2093766355e938fd43263bfaedbeec42bab1d9f6ad8e90aa58d03c033720aa30d679085b4029a2cc048ae8545b791a253efea4464ad8d6b44b561395679c418e6a6c67cc7147218ebcb4dadd1d5f8d0dabbd79538d81b4d22151550dc8bf729cf63338ea5c4a950e2b50e2cec5a25187ad10a12729ad62806a92035eb49cf04e8fe35f1c40811a9987e0d06145a1eaab26d14a7e0e15bb7d4533e42dd2d430de6faccf2a25e3c132579e1a5103ae457a420b25392687a0d813e1caba3b1e11635198cd672eae29a1cd9d97478e2c6189b50b49ed4e68809d0d15035cfaf6943a378d107044f6674f9d811acad9757be85df6e52c082ada6d2688f2a6e12e4760f24abb3ba8f1d9d9456c94c20b3711dd01349fe70b16711f7209f1921e5d81ba25b3ea9af2bb51ed71215dd41a096bccc14ac4476e8e7702f9fd5d19e40bfb2e5164435534f1d48263afa708469b340d0aae57b1020988fae64c662e017ec68a4f7aa5d1b1ec82edf365b62d130e8ff28f6d511f97b166d1bc607b127f4a612e9bc5445f6055e9192529e528be112757a0abf2bf72544199806944e04b53d8e45a08fd96c9939b9874e9803e56bb16608a47cae265fa023d799646e0775c5c0518825000b872b7d65653424b050905e02ee6a732717f5a12b1117923376d9b8a728c3a24828123903426c0c27f9bbba84c94e18259bf2e0010244df8edf6a938ed1787796c32f4d26878e1cd7b520197c8838144f82c5c3a2e5837a89dcd95f6d29dab9d72ea63d41c9e072b67c61a1f182c2368c4a22fd46699e4a5d6a804eb6e974c8642f5ce9aea803522e66f81b21bc0323ae9410673a43683cdf61befa5b19496758ac456155103a38924f613215271c18892ad96dceb7a32b10a8e1e86d9588c7a5ed9690154f4629909e1d45b545f74a14480de196fe58b332a13acb353cad15bb85d87fb458d6b4f225524d3ad87d0985a049572a0f16ca1f81b5f550a4127cf9c9e9b5e39552042e624ebdc96f35f48647a5c5231889db3ba0ab8835aafd76719892990b2b75931568b9213b96d6da5c01455d5407534ca64f7d0a55a47c44f85e221489e42932d80e5d261d39159176dda667b2d2f8173798ae841e7d92b6c3f450ba9172ed635dace2592ccbc477cd72c2661513701262d84d9bc6611da3c26085d6978000f322d80374f6cef19100625972fd08610cc1810d147b6b7700872a4898718a2919d82d3e9f9d8064069d41301f060321e9b19f7851cb36b0f298e9d5cb4104feb8aca16f096390ada458cf0026fe6c74299e35f97b682d82311eda63b640991e651a940c5be02e74b2f74fe4953d61d09278be62956dff441b45434035ab0c9eb05543d8c6a2a480c0fa419b9e999844f5a5f73d4627104de298e80787542730d57d7ed35236be32271f1d034562b8484bfa101926cbb5098fa18f09a1ad54b0d1556598d9178f602b4deda672ea147e8c4e8a5029003f11a1df1cb6e6f17a24010630c908096a3712ba2b94717448374389aeb08c783adfd9491aaea0746481a6a24757712ed94992aa6e6b715974356d061c1449b1bd11cc64eed6b5c24f86b26a872a49936db6b9487c0cef621fe16a248921386c9dc49bb08c1c15d2061
+InNoiseSP = ee8182d96f319477ec31a90e7e466e1fe90abbc0abd2e68e952c659794cfdf901eb0a0c481c3f11a46118f4b9a288f1827fadab55868d020e0818994bab98bfc0db557bdfe3fd4a1295a831258e76af1a85c019da0eb2fc25a5e2c9ba98448e865723e41f7f8a802aeb9a66b40ec249a225848777e05b27e8db160d43d43e847c339342db6fd648020f7a4421fed95b1b84868c6acbc953922173c41b0608cfd7b650d2f13c4b91aaf6600910d2a7d87ed8cb8a54c140c6aad638ff58d79046bca4f38b51801f6f6eafc94420dbc35bf019bebdfeda27d580d439505ba08ea976bc0f68112781399635e14d73622d469af175e7e2eef887575b112744683c3c9f37e78974cb7674104277d9e8fb9af78138c01e96af2105de9001f06e2295aa7a24847384ebad216ff597d787624240df403613d7922a7c68dbe6894cf60b669f8a18a0e84846e05303f68c8a9595c7a1c2e6b82fa14b00a2da0b369660286c1a7a86554a39905b01965665acf1f1a7200297b7382e3eb227dad0de70b30b32c9b495450d83b2072daa1d61b699d1d0166246b6efeb266cbd6729b3540fe14d3b272f1152d29fb405b4c2f2f96adb3e90e51ade8656ac8551054d10f05d51155b796d8dabfa4a35ec06dc325c92ed94a7bf28ed0974d54e8c4d9658f7ea4b7150d14716602e28755ce81a9a197379e25a5fccf49969754ad632f803592055c829ace64fef68ef324100bf61c714d82a9246e09bca1431833a3f0a3ecd4f2609488b172128127c12d6e9818e27a45f5918411c339e92c1dd829555afa4a1bc9d80292b7920400d75128ba54461bc3e5687455501fda0db5e990a6cd8e03a1b8ec54238197c22a017986d5a17722c605c42e493b4260676a9af43a6c5964c71ddd975bd8b49b1de569cc6c89b667ad5334ca7bfd1c0b5ae8c85ecbca5c59e9d84b949b31f2a1594eea92436715dca746435c554b386e2a6265a198b50ffc5c6c460f093ea12fa13718fa445dc0e4161ea6ae71673282905d302aa22d9587d10411d577117e947cca55b344f21141a92acd69296ab9b0583968f959365ca7b9f318c2180489e060e8bda8709bfb10ba75924e803d3466d23e7ee20efa1b964da2823a37fc8660900dc287488d1e918b8df40d0e4c3f059a91c00071ddc81b4836ba143fc22159802a655ddb52c7dc6e6f25aec2e48ea69bc7aacc94e7d00af08d09a0556c0a73d06ef464422c47ac23e0060a07b59ca109c54a8bdc094bc380de51908eb0b4314e1d9b8d3e87e8a621a1e7454445b6a23bd0d391d2fe3677aef161cb40129ca9422a379a379b693ae2b49175a71d758b79ca19ebdc8ee0b926a325e8bef852c96811ab1516fc294459de763b950aa4ba1f11b69789b529db2047ab541364c32dca3dbe29a1244cf56b6e8ebd19373acbe80d8cab9ea7889e3cea625df9d05e5f54e7d098dce2ba89ac7ea8fed87ccfd18d0165d53545405a51525ea325c19f4781f885d5a11ad7868876365e8f403451740a06896b558d593a5c46fb7e29b89c8beef2b0baab2595f611809e97535c90766239a287dc031012a44730a0664a195e225b53e9ab1f4882754dc5293e64328acde9901d7e96d3c32254e28d1c7dcda48564762eee27cf66a1c6813fe6237300e24b719d1d928bf979a4a5b0eb570960396c3c925701496c2e9896d3ccb2ce603c36d052bf3819cf19ac825975ea27a15cd48f2b117a2a37705f081ce6f069c577c4457d2054346acfd66650fa1c4f0547fc9ab1d0742769d44aaf6427cd8c689aa14497bc0b2c3a91fdb07560bd31feaa9a2e09bcdbd529afc7e2ab289a28290c07a273f45c7a10e3559a530876a6cfc1374a7183bf172574cb891e278b2541f2a111abcaa3f09e7cbb24ddc9ca99c555d600aee2592e45c148b2eaf1b5ae562988130f090e301f3f09714834d94cfb57defa53bde222c8cfdc6d7de54e67283a9c9387033147ec77844e9f538fd973639b632280411144ea53930d914e649b6048f22860f3d38f466e38301dedb309e64c5b2cc4e8c35d2e8e932290b9b33d4ced0bf87b8d2a558296c92664c0028c2ddaf499800f9c894283630e9694ad26381e5c92b5c860a279c403e602c929f7323aa4689f5244a8545f53187ab5a239b30167932c2e36c72d12ba671864b9c83d68c04a18ba622fa4654abda3e0f1d11f586e6080d6524bd9783f4a3b78a9992eb6972d95cfb14182448ce89aeda8a399a77abaf8bf56d49a6b294e158d19e8f3716211fa19cb0a2f2655f8ce24e4a101a35c68d5aaaa34bc2fcd4ec3354e2fbcc5a9b8d75d559bdabf02807a59c62fa3e960de7e460baaa4bfcd7a0ca088979bb124fbe5beaa28e42230943526a8fe48e5ad8d5132766415ca2f562a6654c3e1c2f3290c83276db375302267ac09fbf9a3f17ffc010c3367018c7b1b72c6fc6a02bf2d04db3e9a0cac601cca96f44104dfccb7024e44f09c0cd4b2e52666ee4e23f74618ac0b84f24f015f38cdfefbefd6567396ee7593f56f
+InNoiseEP = 5a9a43219eac7517ad1b9abd5ca888e84c953f48773e0cec94d4322f4cea9ddb830c1c3de76ea5564a5b3b2ec5476414ac2f4ca4b2b0de57cda2ffa9aaa58feadbeb403f057810881f56b5e60eaca35551eca91f36554610c8cc8cf26b75e939a84803d8ae1584890cd80aa728caad06a12c6f29560c8adda597e43a5d00a2c261fb8a9a37fb816c04331176c48d4670d7085adb9fc3077ed49172a81006448c3bf229724bee26b657eb11957008a653d9156ad76597e4138c866e310d285718a6e3a9bdc6af88b260cc24e9a7dd452fb81f099a464946caa2a4ca2e9b27dc48a2980db3ae7098f55d95d8eb0e19d380f6544119ae0f1430f82dea7490ac46b77cfdae116c4096d0de15c1a3c0fb861011821e80709fcca262a8e4f038c9555f4c17a278881f1e867c9761ea909c9e5aa3769a7e5748b0bb46bd782d212062744243239f1847ad5d9f449b9b80a934b5b702c758ed9a5efe5202caf6192e47aa49686eb612f01944715352b6e7c759802992b3248f7ec4a0d973829131c16a65ac97cca9b731757587a38776ce987d2f5f61f024993e3ae2bd95c7be68a723e590040c14e84ba7518ed54855dab19948f4945fe5771923ea97d0492902b139737a5181b9173a17742e27e626ae6475989bc68aa048339a80a3300879b72a0af0f4ca5f688cc6a6902a790a228356a9f576bcd07de18b583276e4977aa80d60a1acb0e88974a3775f31b76ffc4bcdae32e73f892f7ea1d9c6f21a073dda31a5def64d1e516a4af52c09095d21756dae530c53df12ae9d10916ca672a322893b69389464c0452186bd88897029818d0250df314d2e2efbd803353f431fea4b092854ff6fc5d9444129c49d0313c49e9be0516e414c8420fb2a1c28548d4a0293fce5452d6ff1da977a34a92711ce10480e0581f80fb0609458958f15bf7486447672905552dfce91976a0f261e94bc1eafad76fe7d6547105d0ac5d89d670464367ba27defa406c6f32c0e21166c605ab8ab0f5bf211b78190e9dffb8508f2aeaba52980eec98c3863b8b4fd81b67529132675992c272eb615794156e80a091772442f884d4630aa6e344359fd5694c910faabf198027e1ef5481ecba91245c1ed936a5416ab706796547878703c4126d5267596354fbdc518f95e7396b795de3c7996150a295d82056712e4288f99743c8b740703cd57ca668ad2bd5c50349e438522bd5ba84821a320410b27062fb5f196432955db86a6a88b306d0b71149c9b7d8204ab7cb6bc4299fe5598c490a79d53abd8ce6d71e0948b5dc41f8654f983350c739b1e3cc697a11e5306ca097b75a980d0f37b9044a6c3209570d09ca4cf8a3de989d11bad8abfd9a5254093d4d8482bae4e7125f2a6d27c49ef68aea073a505d86609040cac20029971d33e5c96e76e812114ce83234b56a89f5d8bbbd6a1e4e49a0cd08ee4fb354a6e568bff89d3b234d7e01543de4e60660dbd5b88d9073943e9f3f16aa5da91ad30819d59b94326da3909aaf0e7667394444ffdef93193daa21ed03847771ce981c40438a2ee8b472e494a9307d53254dc7c534dab0bb401a0ba434268022d8a7155e654c14e5709632bc9c2fd507027258edaefe73fd482be81d77b4955d22728819a35d26745edd9672c8a5615c510495abf2bacf93b358c0aa1e13cb6048be4082b95b3cb96015442fcc338677b204f6cacc907f5e8194994dd83b16a592defc349190532fa616ca1f763ddd6a575858f8885351c72aa2857ad30447c0f6564064187387820d3209e7d38329503ff89f54793300489dec9092007940ff8372222b3e1819c7996a13458f69068855b9f63dec8f9501ab0c799b6190e42edd417740d5a40684bb74946a64c3fe0c1e2f9674f6260e200d7b4913647ebedc75c20d023be90ec8d1eea1a9946143c249e572b5eb6119fa9a2ace5c46f281f9760f8f1769e8104c2441b4da0a5971ff22dd6343591977eb524235e4959804c00a39a43bb31a30024ac801a707c1f8e8983cd5e677b62d9e9ff4e0eaca214a5babda03690229ae91209964242d1acfea5416bc19743dd30c9111a660e3ac4627c4718cfa94c737af2468c52f8e47d9055399c52369b2c550ff62af79b75cb8dba1f538b61d72c5dee5a5ccb44a2f8b70190795942c64602482ec6cf8d74f75cadc921f4050c3e7e52b80cfbc6a29d5024e1de18be732b7c3a1d1eabac8603a8019e378f9a3b5128f8890047363d80be37b9464a213dd61202e7160037f57bb991ef214eb163d796957980177bda87897478e3a4d3094ef2e9a587c991fa4055540ef51a66983aeb2857215af46ea6b3a6d580099434bab327fb680d9097e3072a1637233d0559546e09853f8a6b448e814e7ca6a51482bb2958744692e55b43b8bcd9a5ca487589aaa848e955924169c13d0e95e9348c20c982e0d697a9a5b114708463ebd3b48c5dc7502c1bd3c69bfc97da18c60e1c7b4a4a29528a95bf4b258800208cca3c530a3c05eceb838d61cd56c40e4b41c08
+InNoiseEPP = 0080ff1b00fcbf0180000000f8bfffaf00b0ff0a0000300090ff0600ff2f012000f4bf00c0fffbff02000000ffdbff0200ffefffebff0a00ffafff1b00040001c0ff2b00000000400000001400f8effe0b0004000300ff1b00f8bf06c000d0ff0600000000100000c0010000ecfff2bffdef00e0ff02c0fd2f001000f8bf0180ff9b00fcbf0700003000fcbf054000000004000400001c0000c0048000000000000640003000000007400040001000007000f0ff0600ff6fffcbff1200008000200000c00300000c00f3bf07c000100000c0fdef0030000400010000ccff06000030000c00070002c0fffbfffabf0140ff0b000400020000fcffeebfff2f000c00fcbf0300002c00f4bffd6f00b0ff0a00074000c0ff0a0002c0fe0b00f7bf0240001000fcbf00c00020000400030000e0ff0200ff2f00400000c000000100000000020000dcff0e000500000000fcbffc6fff1b000000ff2f0000000c00034000e0ff0e000300ff2b0000000100003c0000000580fe2b0010000380005000f8bf02800000000c00feef00e0ff0e0002c0ff2b000800fd2f0120000400fcefffdbff1200ff2f01e0ff0a000000003c00f8bf0280ffdbff02c0024000f0fffabf0080ff1b000c000100003000fcbf014000d0ff0200003000f0ff0a0000f0ffebff06000240fffbff0200ff2f001000f4bffe2f00ecff06000080ff2b00fcbffbef00e0ffeabf0030000000030000300000000700fe6f0000000300070000f0fffebf040000d0fffebf0000000c000400ff2f00acfff6bf00400100000300feafff0b000400fe6fff3b0014000280000000f4bf0180ff1b000c000300001c00f4bffe6f00d0ff0200fe2f00f0ff02c00240002000040000f000e0fffabffbefff3b0000c0018000000000c00540fffbff0a000400ff2b00080008800010000c00048000100004000340000000f0bf0480002000f4bfffeffe0b000000fcef000000fcbf0000000000080001c0ffdbff060003400010001c00007000e0ff02c00280ff0b000000060000dcfffebf0080fffbff0a00ff2f004000140000300010000400010000bcff0200fe6f003000fcbf0200ff1b00f8bf00b00040000400ff2f000c00180000f00020000800014000000004000180ff0b00fcbffe2f0120001800f76f004000fcbf0100011000100002000100000700fe2f00f0fff2bf018000f0ff0200ffef001000ecbffe2f000000040001c00010001000038000f0ff0e00014000300004000080ff0b0000c00070ff0b0003000180ff0b0004000030003c00e8bf0280ff3b0000c00100010000f3bf00c000200000c00070000000fbbfff2f00bcff0a00004000d0ff06000030000c00f8bf030000300004000030003000080002c0001000000003c0ff0b000000ff6f011000f0bfffafff0b000400004000c0ff0200014001400008000100003c000400fb2f00f0fffebf0100001c0008000500000000f7bf00c0ffcbff0e000400000c00000005c0ff5b0008000100000c00f4bf020000ecfffabf01c0000000fcbffaaf00d0ff02c001000000000700fe2ffe2b00f8bf04c00020000800fdaf0050001400ff6f0000000f0004800000001000000000d0ff02c0fdaf00f0ff0200007000d0fffebf064001e0ff0a0001c0fffbfffebffd2f002c001400fe2f001c0000000600ffebff060004c0ff3b001400fd6f0020000800fe6fff0b000700fb2fff0b00fcbffd2f00f0ff0e0004000110000c0000f000f0ff0200034000100000000140ff3b00fcbf00300100000f00054000d0ff0e000100ff3b00fcbf038000e0fffabf00c0002000fcbffb6f012000fcbf0200000c0003000340ff0b000b00003000ecff0200ff2fff3b0000000080002000f8bffdef00d0fff6bf010000ecff02c00100001c00fcbf01800090000800000000ecfffebffc2f003c00080001c001f0fffebffdaf0000000300feefff0b00f8bf0070001000f4bfff2f00ccfffabfff6f003000000001000110000c00ff2f003c00040001c0ff7b001000034000200000c0ff2f00e0ff1600fc2f00300000c000b0003000180000400150000800fcaf0050000800010000b0ff0e00feef00900000c0fc2f00f0ff02000000002c00080000700000000800007000c0ff06000400000c000b000080fffbff0e000100001c001400fe6f00000007000030000c000f0005c000200000000100005c000800ffaf0030000400ff2f001000f4bf003001500000c000b0ff4b00f4bf0080ff1b00f4bf0300000c00030000b0ffcbfffabf0080ffebff02c003c0fffbff1a00008000000003c0fdef00200000c00040ffbbffeebffdaf010000f4bfff2f002c000000feaf000000fcbffcaf001000040004c0ff0b000b0000800000001300ff2f01100004000100000000ffbf028000d0ff0200feeffedbff0a00fbafffebff020003c0ffebff02c001800060000800fe2f0100000400014001500000c0ff2f01e0ff0200ffef005000f0bf
+InRand = 869e8bb1806d746f8978d27fead08f551a3825d1ce8cb7e0f07476ae742b8f82
+OutPK = 39d9341741d840b49a1ea42aaa9722c7dee138dd713741b43288ba177ce3c8e5aa59bedf4b449964e0a285205af18e087405025e068f8c0a0393eccaf4cc03081fc982bd9d5e6ca138020b9eb351d787104e9c58bf89f4f2bdbd4c65649308cc3d77eec7b1535d2635596a81b0da909fa26ac5254a6d09bd0d4edb5a797589194d94b3d65022a76e04766e934e91704b98e8b758d0b6f962915d302a7405f9ee58540cbac0b97940f1a3979080b876962a5b113779a60164a42bdfa64f36522516425e285d2fd55af31e0cf29e6e73ae5c991d922811d2d60bf89467458e18b7409e2dd354962123de0804b92a7951ca8ef8c1bdabc4ab0cf118f174af0721200d6241399d4ab35de597e56ba39b0b01215ba6a180467800e02dbe09b2e05760c714085c266ca540f6d872750103cf72918ea6baef8320f8199a2f975484576fd615895d66e4b37697a04e71b79fe92f745013872421ad346d8e5aa5e0548d451d7a0d92a3086f4d52849c84cd8a50ca7199411d91310b17a24523dd274541ea37fc48ab3771b110d76bc10a6fe22060459295a4699c11a0b832c29c3d844e8ab5f3226f04e826005bd0bbac50e7339c51a54a989d204ca1671e15d9531022729f541404840d7ebe90f9e1283e6561d3ed2bb4882a94e5112624e205d0d5eab574e003f24665d3e5b12e2debc982896a00bb6501e79dc887f3ae8352a7262f3fd33543c5f1a1126cbea35beeb09503c927a1de5e131ef644ec508872e143f7f51e47ddeb61105ebda015ef5573e0490c0d887cb676b0816c13c3061ad6a48d6fb3526dc03977a25ba445cd47442b9330f2e92f59efe62560cd6eaf408783a6b2b5744c507b575665bc8a0b80c6d42bbc237b8585a133b5eba767fac0783463c341c6267d082a986410e8a85387f5c48aa415dfd65921ed993ca46bd779219a678f423333d98a4d2f083260ef080b1a87281a6d5c984a59c1d85bb08a5b80af4243b00169c1f59265469f57d603b215f29f2f6dff032e551e0ddf73a6424db8ec1d4d84f48c332895a87be5c18a30629d6045900478db6942878680895223a2bca8195da9fc97e6e08e1a029b5654656a279709890294045beca301eb527625ad22968938235dad6df25de662ee6d687af6f559b809c63bcde4a4a20bedc5bce97264d69c14c462751c9fa4fa4c26054fe0f09a430431c4d63cc787100ac142741849219f0a8a0231fe2070ab87fc95b9596f354acc252ce0b4a00a53fb3e1191f50cabe214bd1ae618e2e83a32308d64ee02475e8b8d8530001804525017f8a9d1c197c903ed14eaf5b76c6418c8395972831be313056ca4759c11e4f9902febad2d2806c6300a6e7664512d68d9543c1090091f2edcd9c65f010313c3e18af7d15745ce168663d094dd6d45750c7094cd5cb6b57289af8c69fe1685840da00e5af305a477ddc150f3799cacb1c34297dc9d64ee255265dc254377220cd410089da86707d48c70b06785a7f113be123887b78af2fa7d9c8c6b292f0a4a0bd7c5a7982c6b3bd638b285300ecce36023dc2c329916b3596db2bbdcec5c8ae5ea1be382bc1520950f9260edc81c58be801574a33769520b2b09d329015e8618f95984c519931c2289ee4e949b29c9460d963c15d2dc488cc864a516f40ba5fab00e154e82054e4dccc3cfc73224589d6fade0f41e63021c9a5b85be53b325a7a311edbc559f565781d0909186dcf6be5869635ef36b110d5e7685ddc33372843fe425ebf5f990d2635280fb01a8bc007057af0c6f995e5d30e316a7ccc4d59243d8760cc930733e7a3972a467f15adaa37e476fd3497a50c905e261fbfa863edce31bf7f95eb5009ac796791e0ed0df86af317cbd6891d939647bfc2c815606ba465658e768fd1e1e5101ef1315481a5ce238885a98b7c2ad689785108fe59f2935328212aa83cac332c93fcb11a1621efa0b0c6d59418e1e460e1af0e3f7198fac9f7c4157c05806906e9a5cea7554255d22af04a631dd85643411b7a5a3ded243b1b3b1a334887a2241f001bf6403a5bdc569858649696a12618444aef605f5df1c13d2da470c513442e4f6b877ca0d163e2981d18c731263a40697d381709a8a25e445ea4b266a84efab42653fc09e246d8ce1acc40fcc0a6ababd28af2a141be523bc713dd7c4c333e9b55f0005c533556b5b07fef3fbce365423ffc3e398638ba10dc30a08ba9301e3cbffd929945e45fae3b41d69e1dde5a59c879f91a9946029b9761f2b641c5c393a95f814cfd806c009a6dbb0e6882c103497fef03e5a1aa152f7c816fd8aaec60b845177e421c510706d40d13a256035294cc0ab0be31e11d5af22235b9187d25964f77ff18ab6e91a3b8bb8731be50d610859020266b065392037db58b77947fe8048656ed0f49665a654a9d1dd681cc94f54d3999951add67369aab6afa00b4e76cfcd5a262b420bd19d189a87c0b8d7c154b5212ed36dcd01b70f540b7b08286ccd4e7a1802ae77115ae829fc7fc0469ce0671c2c
+OutRec = 01c000300000000140003000080000000020000c000080000000040001000000000800014000300008000200001000000003c0001000040000c0001000000002c00030000c0003400000000000018000200004000080002000080001c0000000080000c000100000000180000000080001c00020000c000200002000080000c00020000c000340001000080002c0000000040000000000000400014000300000000280000000080000800020000000024000300000000180000000040003c000000004000340001000000002000020000c0001000010000c0000000010000c0000400030000c00014000200000000280002000000002c00020000c0002c000000000000040003000080003c0003000080000c0001000040001c000000004000100002000080003800000000400034000300008000340000000000000000030000800014000000004000380003000000001c0001000000003c000200000000140003000040002c0003000000000000000000c000100003000040003000030000c0002400000000c0000c00000000c0001c0002000080000c000000008000000000000040001800030000000024000200004000140000000040001800030000800024000300000000180000000080003c0002000000000400020000000030000100004000040002000040001c000000008000200001000040000000010000000028000100004000180003000040003c00030000c000080002000000003c00030000c00034000000004000100001000080000c0002000080001000010000c0000c00020000000018000200008000140002000000001c0003000000000c00010000c000000001000040000c0001000040002800010000c00018000100000000140002000080000400020000c0001400020000c0001000000000400020000300000000200001000040000400020000c0001c000200008000140000000000002400000000c000000001000040000c00030000c0002c00010000c000340002000000001800020000000030000200004000080002000080002400030000c000300002000040000c00030000c00028000000000000040003000080001000020000c0002800000000c000240003000080000400030000c000100001000040001400000000800038000300004000180000000000002400020000800014000000004000180001000080002400000000c00038000100004000040001000040003000030000400024000200000000000001000040001800020000c000080001000080003400000000c0000c0000000080002c00010000c0002800020000400014000100004000000000000080000000010000c0002c00030000c00010000100000000000000000040000800020000c0002000000000c0003c00030000c0000400000000c000200001000000001000020000000000000200000000140002000040003c0001000080002c000300000000280000000080001400030000c0001c000100004000380000000000001000010000c0001000000000800038000300008000380003000000003400000000c00020000100008000340000000040000000010000c0002c00030000000024000100004000300000000080000800000000c0003800010000c00014000300004000180002000000000c00020000c000280003000080000c0001000040001c000100000000200003000040001c0003000040003000020000c000000001000080003c0001000040002800000000c00000000000000000200003000080002c00000000c00030000000008000280001000080002c00020000400028000100004000200001000080000c0002000040000c000300004000200001000080003c000200000000000003000000001400030000c000100001000000003000000000c0001800030000000018000100000000000000000080001c0000000080002400010000c00018000200000000000003000040003c0003000000003800000000c000240002000040001000030000c000240002000040003c000200008000000003000040003c0002000000001c0000000000002c0003000040003c0003000040001c00030000000038000200008000340002000080003000000000800030000000008000280000000040000800010000c00008000100000000080003000040001400010000c0000800030000c000300002000040002c0001000000000c0002000040000800020000c000180002000000002c000100008000340003000040002400010000800014000300000000340000000000000400000000c000080002000040002c00000000c000100003000080002c0003000000002800000000000000000100008000300001000040003400000000800004000000000000000001000040002c0003000040000000020000800020000300008000180000000040003c000300004000100001000040002800010000c000140000000080000400020000800
+Key = 76f2c93a81a15cc9e46a714d5ede343d56b2e08761c7c8df10b13e08db01ec06
+
+InNoiseS = 4ca10ae48258b14d83e48384f858eae4c130e49055a044c78590f0a74f1a19e8edd427300659bb38647c032c5722a6ac4b05413b4a0dc870ac1b16f25099428b9beb83af39887340a1c4bd986241429eb8eec081442a418b10ac60be94b3e34374a63c5582274aba579a03202666b16f3066985a26b96dd8993e242ccd6a3d9a2d287fdc82da9091830d0886b80bf3419ae968c615521160e85676c96af65dd92ea10e2c520bca4b882326b7b2c131d22d922827c5b41f2f33b281b50e8b64d92ac48a9bc4516a39272018ef5b707223b59bc2e07dd6831015b610b04638aa9d949cb7444c7d3162cf3a088b628f73c93d66375540330ac2d871d4b0405421caa142a0a2effcaacfe5173862deb228da286993d7605db54efd98f122b1949be7a15481fbd23d97d588b7bce9bf4da31481296149138c1b5753caaa9428b6d5f3be48c69979d351525c61532bf5535aa7d72850c294021d1614fda36c781f2ddbf10a4f723e578a596092ec312827ae456cac024c5a44bb68e5175025ce6876aeb1951d022426faab5a0db9d0cf369324a153533fb50b5a5866c77c00524265020b769517d7970918f5d2bc5a6e874332186d5f9e7a3a03aa28a39de5078002c0845c9eb74791088a81b185958e014f95d1219d702d2ec39a772fa82a3c91fc873a991d54c3c0babb64a7bdd15e78232d3b71a1c08bd60e68fc2f5e46348c8b948c1f93860eab88d24b736c785f2a4564a6754492c9c988c786166708d2b240d3da4c3280bd5616c0aff7a81a9a560809766df9b27e014c6c8aa45b38a6416046ba4b560804a90b54c3f5b6a2abaa7149968c8567c0ed07f2ee99218ed52b88bd98624c1c91b9ae1b082b6d07c8697eccc6a953cec576a25cdf548866a1cb03ded4fb26a1afa6c9287050459de64af1aac1572b1509643c6407f5d4a944904856c42f770717adbc701ff3c9f44aae6bea40c50bdd9f1f9368b667286732023d89581e7f872639f19766b65305b07babe9003e631b6a32511e690a864a18d45263c9436a0f1245db4405b31e1d9a6598922ae6f09a61975b3ac27225baa60e1bf6f01b8a0c07988dc9167648e9b1891c3e01a5031260cd52d08cce39498835b9122ef5d4f24f34c7623448294c4da985b5904583bd6f7e301bb911abaf1c778ee1567e8f4df0e05479cf1b5854e0cc7083a4463a96f2aa4d28944147464d249057b13c3eb8e0e93466a70194309f3339ef597459e9ffb5b15a1cf080c34713b819f6051d2baa142b9602460ac3451161913bd960202f68092c456b6ab71c638877f008862904b3fa62b815414e745b2309629d4691e894282c37e70ab15a82a7b055157b481552c6ee3ef7510a9d9420216b7f2275ef64dab95cc86b3e8ffbcaf508bafa0251c4755d287041290b4f6675f22d3ff9c31808c29d8281f2aaa7c7dede72a42592c10b20a35cee60b31b9d67f5ea6adaa33fb3dd911816aa142975551ebd40569bb1fd904a504b7ac811234029921b6b06b28a1df7c2a2ed59a4ad3541f8852e815b08c09444809decfd2904a62d991cc3c6659d6d4c6906d482bc03cce05cbb9ae142264aeda128148084c5b93b831458b061dd99e94e60a1ef51199f550156047901792628c6d5ff316c20be6d5ea59030d6abb624f8909e12a02dcf69f039ad45ffda6113f2f6aa9be6bb934122ae2de84b7a0add8488c052aaf50d0e098d58c7be3c80fe946c566c00b4739fd59aa2de006885b0cb9c65231f5fa760af69a929443aa3252aa16497dcb2c1180f42cca1310b99278a956a07cf540561dc0bb9e8b6b2ea25e57ae52d636b6297a2afbcb6d014942a33ce4869829de6f1ba761b60b6b93f993938884076c1e4976848f8bb6c4486daddaae7fb8073749a70f0eec61ff2c9687184c09bd15f849108700efe8a01b72c9c97d40957b4e3d64533411887541a5de6518ab756994bb5ed9f7af8da0bbe6959c57e4a2f346884a6716a847015c5545d69770d56d44500097f83ee4b736df867029d979848b64a2c281b2ac3d140ed854f12fd946b770754139e0e320223dac9823a14c28cbae29dc4af992ced62ab7255f265c533cb54b75f48a8321e7aa8f7939c468d26645c57b4abf0569260229a40611920af1add758474b8d026f41785559ad04faa501a649e44abee8607fc4eb21376552ba48931156485c7916d4d63247a757d4f92cf34322202b94acdc3276d99a2a899d3471475bb268ef9ea9976a949437e30e168301d027518aeec04ffc4294a00a2649b271b334f3cbdf88678b8b8c583f62c0815937667aede6ae53aec271e5d885a84b58d348449aa06649d6a635bd7d392885a0ae60218d8a085a509f5e5ad12e24c4d36b04600c0011623d4a4bd2206fce8d5003234405db3ad7e3d4dd6aa790abc6b1e49e8b26436ae6c9f1010b1e052158639c1582d641d8b3ed0a9c289aa5eae66e66a43048876b14cf4a69489224564c4932adaaf83e2b15e0b2248425d71198849f534d801b24cccb2aa434829
+InPK = 39d9341741d840b49a1ea42aaa9722c7dee138dd713741b43288ba177ce3c8e5aa59bedf4b449964e0a285205af18e087405025e068f8c0a0393eccaf4cc03081fc982bd9d5e6ca138020b9eb351d787104e9c58bf89f4f2bdbd4c65649308cc3d77eec7b1535d2635596a81b0da909fa26ac5254a6d09bd0d4edb5a797589194d94b3d65022a76e04766e934e91704b98e8b758d0b6f962915d302a7405f9ee58540cbac0b97940f1a3979080b876962a5b113779a60164a42bdfa64f36522516425e285d2fd55af31e0cf29e6e73ae5c991d922811d2d60bf89467458e18b7409e2dd354962123de0804b92a7951ca8ef8c1bdabc4ab0cf118f174af0721200d6241399d4ab35de597e56ba39b0b01215ba6a180467800e02dbe09b2e05760c714085c266ca540f6d872750103cf72918ea6baef8320f8199a2f975484576fd615895d66e4b37697a04e71b79fe92f745013872421ad346d8e5aa5e0548d451d7a0d92a3086f4d52849c84cd8a50ca7199411d91310b17a24523dd274541ea37fc48ab3771b110d76bc10a6fe22060459295a4699c11a0b832c29c3d844e8ab5f3226f04e826005bd0bbac50e7339c51a54a989d204ca1671e15d9531022729f541404840d7ebe90f9e1283e6561d3ed2bb4882a94e5112624e205d0d5eab574e003f24665d3e5b12e2debc982896a00bb6501e79dc887f3ae8352a7262f3fd33543c5f1a1126cbea35beeb09503c927a1de5e131ef644ec508872e143f7f51e47ddeb61105ebda015ef5573e0490c0d887cb676b0816c13c3061ad6a48d6fb3526dc03977a25ba445cd47442b9330f2e92f59efe62560cd6eaf408783a6b2b5744c507b575665bc8a0b80c6d42bbc237b8585a133b5eba767fac0783463c341c6267d082a986410e8a85387f5c48aa415dfd65921ed993ca46bd779219a678f423333d98a4d2f083260ef080b1a87281a6d5c984a59c1d85bb08a5b80af4243b00169c1f59265469f57d603b215f29f2f6dff032e551e0ddf73a6424db8ec1d4d84f48c332895a87be5c18a30629d6045900478db6942878680895223a2bca8195da9fc97e6e08e1a029b5654656a279709890294045beca301eb527625ad22968938235dad6df25de662ee6d687af6f559b809c63bcde4a4a20bedc5bce97264d69c14c462751c9fa4fa4c26054fe0f09a430431c4d63cc787100ac142741849219f0a8a0231fe2070ab87fc95b9596f354acc252ce0b4a00a53fb3e1191f50cabe214bd1ae618e2e83a32308d64ee02475e8b8d8530001804525017f8a9d1c197c903ed14eaf5b76c6418c8395972831be313056ca4759c11e4f9902febad2d2806c6300a6e7664512d68d9543c1090091f2edcd9c65f010313c3e18af7d15745ce168663d094dd6d45750c7094cd5cb6b57289af8c69fe1685840da00e5af305a477ddc150f3799cacb1c34297dc9d64ee255265dc254377220cd410089da86707d48c70b06785a7f113be123887b78af2fa7d9c8c6b292f0a4a0bd7c5a7982c6b3bd638b285300ecce36023dc2c329916b3596db2bbdcec5c8ae5ea1be382bc1520950f9260edc81c58be801574a33769520b2b09d329015e8618f95984c519931c2289ee4e949b29c9460d963c15d2dc488cc864a516f40ba5fab00e154e82054e4dccc3cfc73224589d6fade0f41e63021c9a5b85be53b325a7a311edbc559f565781d0909186dcf6be5869635ef36b110d5e7685ddc33372843fe425ebf5f990d2635280fb01a8bc007057af0c6f995e5d30e316a7ccc4d59243d8760cc930733e7a3972a467f15adaa37e476fd3497a50c905e261fbfa863edce31bf7f95eb5009ac796791e0ed0df86af317cbd6891d939647bfc2c815606ba465658e768fd1e1e5101ef1315481a5ce238885a98b7c2ad689785108fe59f2935328212aa83cac332c93fcb11a1621efa0b0c6d59418e1e460e1af0e3f7198fac9f7c4157c05806906e9a5cea7554255d22af04a631dd85643411b7a5a3ded243b1b3b1a334887a2241f001bf6403a5bdc569858649696a12618444aef605f5df1c13d2da470c513442e4f6b877ca0d163e2981d18c731263a40697d381709a8a25e445ea4b266a84efab42653fc09e246d8ce1acc40fcc0a6ababd28af2a141be523bc713dd7c4c333e9b55f0005c533556b5b07fef3fbce365423ffc3e398638ba10dc30a08ba9301e3cbffd929945e45fae3b41d69e1dde5a59c879f91a9946029b9761f2b641c5c393a95f814cfd806c009a6dbb0e6882c103497fef03e5a1aa152f7c816fd8aaec60b845177e421c510706d40d13a256035294cc0ab0be31e11d5af22235b9187d25964f77ff18ab6e91a3b8bb8731be50d610859020266b065392037db58b77947fe8048656ed0f49665a654a9d1dd681cc94f54d3999951add67369aab6afa00b4e76cfcd5a262b420bd19d189a87c0b8d7c154b5212ed36dcd01b70f540b7b08286ccd4e7a1802ae77115ae829fc7fc0469ce0671c2c
+InRec = 01c000300000000140003000080000000020000c000080000000040001000000000800014000300008000200001000000003c0001000040000c0001000000002c00030000c0003400000000000018000200004000080002000080001c0000000080000c000100000000180000000080001c00020000c000200002000080000c00020000c000340001000080002c0000000040000000000000400014000300000000280000000080000800020000000024000300000000180000000040003c000000004000340001000000002000020000c0001000010000c0000000010000c0000400030000c00014000200000000280002000000002c00020000c0002c000000000000040003000080003c0003000080000c0001000040001c000000004000100002000080003800000000400034000300008000340000000000000000030000800014000000004000380003000000001c0001000000003c000200000000140003000040002c0003000000000000000000c000100003000040003000030000c0002400000000c0000c00000000c0001c0002000080000c000000008000000000000040001800030000000024000200004000140000000040001800030000800024000300000000180000000080003c0002000000000400020000000030000100004000040002000040001c000000008000200001000040000000010000000028000100004000180003000040003c00030000c000080002000000003c00030000c00034000000004000100001000080000c0002000080001000010000c0000c00020000000018000200008000140002000000001c0003000000000c00010000c000000001000040000c0001000040002800010000c00018000100000000140002000080000400020000c0001400020000c0001000000000400020000300000000200001000040000400020000c0001c000200008000140000000000002400000000c000000001000040000c00030000c0002c00010000c000340002000000001800020000000030000200004000080002000080002400030000c000300002000040000c00030000c00028000000000000040003000080001000020000c0002800000000c000240003000080000400030000c000100001000040001400000000800038000300004000180000000000002400020000800014000000004000180001000080002400000000c00038000100004000040001000040003000030000400024000200000000000001000040001800020000c000080001000080003400000000c0000c0000000080002c00010000c0002800020000400014000100004000000000000080000000010000c0002c00030000c00010000100000000000000000040000800020000c0002000000000c0003c00030000c0000400000000c000200001000000001000020000000000000200000000140002000040003c0001000080002c000300000000280000000080001400030000c0001c000100004000380000000000001000010000c0001000000000800038000300008000380003000000003400000000c00020000100008000340000000040000000010000c0002c00030000000024000100004000300000000080000800000000c0003800010000c00014000300004000180002000000000c00020000c000280003000080000c0001000040001c000100000000200003000040001c0003000040003000020000c000000001000080003c0001000040002800000000c00000000000000000200003000080002c00000000c00030000000008000280001000080002c00020000400028000100004000200001000080000c0002000040000c000300004000200001000080003c000200000000000003000000001400030000c000100001000000003000000000c0001800030000000018000100000000000000000080001c0000000080002400010000c00018000200000000000003000040003c0003000000003800000000c000240002000040001000030000c000240002000040003c000200008000000003000040003c0002000000001c0000000000002c0003000040003c0003000040001c00030000000038000200008000340002000080003000000000800030000000008000280000000040000800010000c00008000100000000080003000040001400010000c0000800030000c000300002000040002c0001000000000c0002000040000800020000c000180002000000002c000100008000340003000040002400010000800014000300000000340000000000000400000000c000080002000040002c00000000c000100003000080002c0003000000002800000000000000000100008000300001000040003400000000800004000000000000000001000040002c0003000040000000020000800020000300008000180000000040003c000300004000100001000040002800010000c000140000000080000400020000800
+Key = 76f2c93a81a15cc9e46a714d5ede343d56b2e08761c7c8df10b13e08db01ec06
+
diff --git a/src/crypto/newhope/newhope_vectors_test.cc b/src/crypto/newhope/newhope_vectors_test.cc
new file mode 100644
index 0000000..fe84cd4
--- /dev/null
+++ b/src/crypto/newhope/newhope_vectors_test.cc
@@ -0,0 +1,122 @@
+/* Copyright (c) 2016, 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 <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+
+#include "../test/file_test.h"
+#include "../test/scoped_types.h"
+#include "internal.h"
+
+
+static bool TestNewhope(FileTest *t, void *arg) {
+ ScopedNEWHOPE_POLY a(NEWHOPE_POLY_new());
+ ScopedNEWHOPE_POLY s(NEWHOPE_POLY_new()), sp(NEWHOPE_POLY_new());
+ ScopedNEWHOPE_POLY e(NEWHOPE_POLY_new()), ep(NEWHOPE_POLY_new()),
+ epp(NEWHOPE_POLY_new());
+ ScopedNEWHOPE_POLY in_pk(NEWHOPE_POLY_new());
+ ScopedNEWHOPE_POLY in_rec(NEWHOPE_POLY_new());
+
+ if (t->GetType() == "InRandA") {
+ std::vector<uint8_t> a_bytes, s_bytes, e_bytes, expected_pk;
+ if (!t->GetBytes(&a_bytes, "InRandA") ||
+ !t->GetBytes(&s_bytes, "InNoiseS") ||
+ !t->GetBytes(&e_bytes, "InNoiseE") ||
+ !t->GetBytes(&expected_pk, "OutPK")) {
+ return false;
+ }
+ NEWHOPE_POLY_frombytes(a.get(), a_bytes.data());
+ NEWHOPE_POLY_frombytes(s.get(), s_bytes.data());
+ NEWHOPE_POLY_frombytes(e.get(), e_bytes.data());
+
+ NEWHOPE_POLY pk;
+ NEWHOPE_offer_computation(&pk, s.get(), e.get(), a.get());
+
+ uint8_t pk_bytes[NEWHOPE_POLY_LENGTH];
+ NEWHOPE_POLY_tobytes(pk_bytes, &pk);
+ return t->ExpectBytesEqual(expected_pk.data(), expected_pk.size(),
+ pk_bytes, NEWHOPE_POLY_LENGTH);
+ } else if (t->GetType() == "InPK") {
+ std::vector<uint8_t> rand, in_pk_bytes, a_bytes, sp_bytes, ep_bytes,
+ epp_bytes, expected_pk, expected_rec, expected_key;
+ if (!t->GetBytes(&in_pk_bytes, "InPK") ||
+ !t->GetBytes(&rand, "InRand") ||
+ !t->GetBytes(&a_bytes, "InA") ||
+ !t->GetBytes(&sp_bytes, "InNoiseSP") ||
+ !t->GetBytes(&ep_bytes, "InNoiseEP") ||
+ !t->GetBytes(&epp_bytes, "InNoiseEPP") ||
+ !t->GetBytes(&expected_pk, "OutPK") ||
+ !t->GetBytes(&expected_rec, "OutRec") ||
+ !t->GetBytes(&expected_key, "Key")) {
+ return false;
+ }
+ NEWHOPE_POLY_frombytes(in_pk.get(), in_pk_bytes.data());
+ NEWHOPE_POLY_frombytes(a.get(), a_bytes.data());
+ NEWHOPE_POLY_frombytes(sp.get(), sp_bytes.data());
+ NEWHOPE_POLY_frombytes(ep.get(), ep_bytes.data());
+ NEWHOPE_POLY_frombytes(epp.get(), epp_bytes.data());
+
+ uint8_t key[NEWHOPE_KEY_LENGTH];
+ NEWHOPE_POLY pk, rec;
+ NEWHOPE_accept_computation(key, &pk, &rec,
+ sp.get(), ep.get(), epp.get(),
+ rand.data(), in_pk.get(), a.get());
+
+ uint8_t pk_bytes[NEWHOPE_POLY_LENGTH], rec_bytes[NEWHOPE_POLY_LENGTH];
+ NEWHOPE_POLY_tobytes(pk_bytes, &pk);
+ NEWHOPE_POLY_tobytes(rec_bytes, &rec);
+ return
+ t->ExpectBytesEqual(expected_key.data(), expected_key.size(),
+ key, NEWHOPE_KEY_LENGTH) &&
+ t->ExpectBytesEqual(expected_pk.data(), expected_pk.size(),
+ pk_bytes, NEWHOPE_POLY_LENGTH) &&
+ t->ExpectBytesEqual(expected_rec.data(), expected_rec.size(),
+ rec_bytes, NEWHOPE_POLY_LENGTH);
+ } else if (t->GetType() == "InNoiseS") {
+ std::vector<uint8_t> s_bytes, in_pk_bytes, in_rec_bytes, expected_key;
+ if (!t->GetBytes(&s_bytes, "InNoiseS") ||
+ !t->GetBytes(&in_pk_bytes, "InPK") ||
+ !t->GetBytes(&in_rec_bytes, "InRec") ||
+ !t->GetBytes(&expected_key, "Key")) {
+ return false;
+ }
+ NEWHOPE_POLY_frombytes(s.get(), s_bytes.data());
+ NEWHOPE_POLY_frombytes(in_pk.get(), in_pk_bytes.data());
+ NEWHOPE_POLY_frombytes(in_rec.get(), in_rec_bytes.data());
+
+ uint8_t key[NEWHOPE_KEY_LENGTH];
+ NEWHOPE_finish_computation(key, s.get(), in_pk.get(), in_rec.get());
+
+ return t->ExpectBytesEqual(expected_key.data(), expected_key.size(), key,
+ NEWHOPE_KEY_LENGTH);
+ } else {
+ t->PrintLine("Unknown test '%s'", t->GetType().c_str());
+ return false;
+ }
+}
+
+int main(int argc, char **argv) {
+ CRYPTO_library_init();
+
+ if (argc != 2) {
+ fprintf(stderr, "%s <test file>\n", argv[0]);
+ return 1;
+ }
+
+ return FileTestMain(TestNewhope, nullptr, argv[1]);
+}
diff --git a/src/crypto/newhope/poly.c b/src/crypto/newhope/poly.c
index a9839fa..a84bdeb 100644
--- a/src/crypto/newhope/poly.c
+++ b/src/crypto/newhope/poly.c
@@ -27,7 +27,7 @@
extern uint16_t newhope_psis_bitrev_montgomery[];
extern uint16_t newhope_psis_inv_montgomery[];
-void newhope_poly_frombytes(NEWHOPE_POLY* r, const uint8_t* a) {
+void NEWHOPE_POLY_frombytes(NEWHOPE_POLY* r, const uint8_t* a) {
int i;
for (i = 0; i < PARAM_N / 4; i++) {
r->coeffs[4 * i + 0] =
@@ -43,7 +43,7 @@
}
}
-void newhope_poly_tobytes(uint8_t* r, const NEWHOPE_POLY* p) {
+void NEWHOPE_POLY_tobytes(uint8_t* r, const NEWHOPE_POLY* p) {
int i;
uint16_t t0, t1, t2, t3, m;
int16_t c;
diff --git a/src/crypto/obj/obj.c b/src/crypto/obj/obj.c
index 94f739c..16d964c 100644
--- a/src/crypto/obj/obj.c
+++ b/src/crypto/obj/obj.c
@@ -87,7 +87,7 @@
CRYPTO_STATIC_MUTEX_lock_write(&global_next_nid_lock);
ret = global_next_nid++;
- CRYPTO_STATIC_MUTEX_unlock(&global_next_nid_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&global_next_nid_lock);
return ret;
}
@@ -200,11 +200,11 @@
match = lh_ASN1_OBJECT_retrieve(global_added_by_data, obj);
if (match != NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
return match->nid;
}
}
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
nid_ptr = bsearch(obj, kNIDsInOIDOrder, NUM_OBJ, sizeof(unsigned), obj_cmp);
if (nid_ptr == NULL) {
@@ -243,11 +243,11 @@
template.sn = short_name;
match = lh_ASN1_OBJECT_retrieve(global_added_by_short_name, &template);
if (match != NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
return match->nid;
}
}
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
nid_ptr = bsearch(short_name, kNIDsInShortNameOrder, NUM_SN, sizeof(unsigned), short_name_cmp);
if (nid_ptr == NULL) {
@@ -277,11 +277,11 @@
template.ln = long_name;
match = lh_ASN1_OBJECT_retrieve(global_added_by_long_name, &template);
if (match != NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
return match->nid;
}
}
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
nid_ptr = bsearch(long_name, kNIDsInLongNameOrder, NUM_LN, sizeof(unsigned), long_name_cmp);
if (nid_ptr == NULL) {
@@ -330,11 +330,11 @@
template.nid = nid;
match = lh_ASN1_OBJECT_retrieve(global_added_by_nid, &template);
if (match != NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
return match;
}
}
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&global_added_lock);
err:
OPENSSL_PUT_ERROR(OBJ, OBJ_R_UNKNOWN_NID);
@@ -618,7 +618,7 @@
if (obj->ln != NULL) {
ok &= lh_ASN1_OBJECT_insert(global_added_by_long_name, &old_object, obj);
}
- CRYPTO_STATIC_MUTEX_unlock(&global_added_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&global_added_lock);
return ok;
}
diff --git a/src/crypto/obj/obj_mac.num b/src/crypto/obj/obj_mac.num
index 074657a..3a7844c 100644
--- a/src/crypto/obj/obj_mac.num
+++ b/src/crypto/obj/obj_mac.num
@@ -946,3 +946,4 @@
dh_std_kdf 946
dh_cofactor_kdf 947
X25519 948
+cecpq1 949
diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt
index f3990d0..75f2cbf 100644
--- a/src/crypto/obj/objects.txt
+++ b/src/crypto/obj/objects.txt
@@ -1333,3 +1333,6 @@
# NID for X25519 (no corresponding OID).
: X25519
+
+# NID for CECPQ1 (no corresponding OID)
+ : cecpq1
diff --git a/src/crypto/rand/urandom.c b/src/crypto/rand/urandom.c
index a519983..434fe43 100644
--- a/src/crypto/rand/urandom.c
+++ b/src/crypto/rand/urandom.c
@@ -69,7 +69,7 @@
CRYPTO_STATIC_MUTEX_lock_read(&requested_lock);
urandom_buffering = urandom_buffering_requested;
int fd = urandom_fd_requested;
- CRYPTO_STATIC_MUTEX_unlock(&requested_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&requested_lock);
if (fd == -2) {
do {
@@ -106,7 +106,7 @@
CRYPTO_STATIC_MUTEX_lock_write(&requested_lock);
urandom_fd_requested = fd;
- CRYPTO_STATIC_MUTEX_unlock(&requested_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&requested_lock);
CRYPTO_once(&once, init_once);
if (urandom_fd != fd) {
@@ -127,7 +127,7 @@
CRYPTO_STATIC_MUTEX_lock_write(&requested_lock);
urandom_buffering_requested = 1;
urandom_fd_requested = fd;
- CRYPTO_STATIC_MUTEX_unlock(&requested_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&requested_lock);
CRYPTO_once(&once, init_once);
if (urandom_buffering != 1 || (fd >= 0 && urandom_fd != fd)) {
diff --git a/src/crypto/refcount_lock.c b/src/crypto/refcount_lock.c
index bb8ef86..ea6a06d 100644
--- a/src/crypto/refcount_lock.c
+++ b/src/crypto/refcount_lock.c
@@ -31,7 +31,7 @@
if (*count < CRYPTO_REFCOUNT_MAX) {
(*count)++;
}
- CRYPTO_STATIC_MUTEX_unlock(&g_refcount_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_refcount_lock);
}
int CRYPTO_refcount_dec_and_test_zero(CRYPTO_refcount_t *count) {
@@ -45,7 +45,7 @@
(*count)--;
}
ret = (*count == 0);
- CRYPTO_STATIC_MUTEX_unlock(&g_refcount_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_refcount_lock);
return ret;
}
diff --git a/src/crypto/rsa/rsa_impl.c b/src/crypto/rsa/rsa_impl.c
index 548c535..af44a3d 100644
--- a/src/crypto/rsa/rsa_impl.c
+++ b/src/crypto/rsa/rsa_impl.c
@@ -234,7 +234,7 @@
}
if (ret != NULL) {
- CRYPTO_MUTEX_unlock(&rsa->lock);
+ CRYPTO_MUTEX_unlock_write(&rsa->lock);
return ret;
}
@@ -243,7 +243,7 @@
/* We didn't find a free BN_BLINDING to use so increase the length of
* the arrays by one and use the newly created element. */
- CRYPTO_MUTEX_unlock(&rsa->lock);
+ CRYPTO_MUTEX_unlock_write(&rsa->lock);
ret = BN_BLINDING_new();
if (ret == NULL) {
return NULL;
@@ -281,14 +281,14 @@
rsa->blindings_inuse = new_blindings_inuse;
rsa->num_blindings++;
- CRYPTO_MUTEX_unlock(&rsa->lock);
+ CRYPTO_MUTEX_unlock_write(&rsa->lock);
return ret;
err2:
OPENSSL_free(new_blindings);
err1:
- CRYPTO_MUTEX_unlock(&rsa->lock);
+ CRYPTO_MUTEX_unlock_write(&rsa->lock);
BN_BLINDING_free(ret);
return NULL;
}
@@ -305,7 +305,7 @@
CRYPTO_MUTEX_lock_write(&rsa->lock);
rsa->blindings_inuse[blinding_index] = 0;
- CRYPTO_MUTEX_unlock(&rsa->lock);
+ CRYPTO_MUTEX_unlock_write(&rsa->lock);
}
/* signing */
diff --git a/src/crypto/stack/make_macros.sh b/src/crypto/stack/make_macros.sh
index 4837e44..3c3691b 100644
--- a/src/crypto/stack/make_macros.sh
+++ b/src/crypto/stack/make_macros.sh
@@ -36,13 +36,13 @@
((STACK_OF(${type})*) sk_new_null())
#define sk_${type}_num(sk)\\
- sk_num(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk))
+ sk_num(CHECKED_CAST(const _STACK*, const STACK_OF(${type})*, sk))
#define sk_${type}_zero(sk)\\
sk_zero(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk));
#define sk_${type}_value(sk, i)\\
- ((${ptrtype}) sk_value(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk), (i)))
+ ((${ptrtype}) sk_value(CHECKED_CAST(const _STACK*, const STACK_OF(${type})*, sk), (i)))
#define sk_${type}_set(sk, i, p)\\
((${ptrtype}) sk_set(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), (i), CHECKED_CAST(void*, ${ptrtype}, p)))
@@ -75,13 +75,13 @@
((${ptrtype}) sk_pop(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk)))
#define sk_${type}_dup(sk)\\
- ((STACK_OF(${type})*) sk_dup(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk)))
+ ((STACK_OF(${type})*) sk_dup(CHECKED_CAST(const _STACK*, const STACK_OF(${type})*, sk)))
#define sk_${type}_sort(sk)\\
sk_sort(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk))
#define sk_${type}_is_sorted(sk)\\
- sk_is_sorted(CHECKED_CAST(_STACK*, const STACK_OF(${type})*, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK*, const STACK_OF(${type})*, sk))
#define sk_${type}_set_cmp_func(sk, comp)\\
((int (*) (const ${type} **a, const ${type} **b)) sk_set_cmp_func(CHECKED_CAST(_STACK*, STACK_OF(${type})*, sk), CHECKED_CAST(stack_cmp_func, int (*) (const ${type} **a, const ${type} **b), comp)))
@@ -89,7 +89,6 @@
#define sk_${type}_deep_copy(sk, copy_func, free_func)\\
((STACK_OF(${type})*) sk_deep_copy(CHECKED_CAST(const _STACK*, const STACK_OF(${type})*, sk), CHECKED_CAST(void* (*) (void*), ${ptrtype} (*) (${ptrtype}), copy_func), CHECKED_CAST(void (*) (void*), void (*) (${ptrtype}), free_func)))
-
EOF
}
diff --git a/src/crypto/test/scoped_types.h b/src/crypto/test/scoped_types.h
index 5c49a7e..2a2c371 100644
--- a/src/crypto/test/scoped_types.h
+++ b/src/crypto/test/scoped_types.h
@@ -34,6 +34,7 @@
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
+#include <openssl/newhope.h>
#include <openssl/pkcs8.h>
#include <openssl/rsa.h>
#include <openssl/stack.h>
@@ -111,6 +112,7 @@
using ScopedEC_POINT = ScopedOpenSSLType<EC_POINT, EC_POINT_free>;
using ScopedEVP_PKEY = ScopedOpenSSLType<EVP_PKEY, EVP_PKEY_free>;
using ScopedEVP_PKEY_CTX = ScopedOpenSSLType<EVP_PKEY_CTX, EVP_PKEY_CTX_free>;
+using ScopedNEWHOPE_POLY = ScopedOpenSSLType<NEWHOPE_POLY, NEWHOPE_POLY_free>;
using ScopedPKCS8_PRIV_KEY_INFO = ScopedOpenSSLType<PKCS8_PRIV_KEY_INFO,
PKCS8_PRIV_KEY_INFO_free>;
using ScopedPKCS12 = ScopedOpenSSLType<PKCS12, PKCS12_free>;
diff --git a/src/crypto/thread_none.c b/src/crypto/thread_none.c
index cf4e85a..85768b4 100644
--- a/src/crypto/thread_none.c
+++ b/src/crypto/thread_none.c
@@ -22,7 +22,9 @@
void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock) {}
-void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock) {}
+void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX *lock) {}
+
+void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX *lock) {}
void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock) {}
@@ -30,7 +32,9 @@
void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX *lock) {}
-void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock) {}
+void CRYPTO_STATIC_MUTEX_unlock_read(struct CRYPTO_STATIC_MUTEX *lock) {}
+
+void CRYPTO_STATIC_MUTEX_unlock_write(struct CRYPTO_STATIC_MUTEX *lock) {}
void CRYPTO_once(CRYPTO_once_t *once, void (*init)(void)) {
if (*once) {
diff --git a/src/crypto/thread_pthread.c b/src/crypto/thread_pthread.c
index 2a1c9f8..2baa2b4 100644
--- a/src/crypto/thread_pthread.c
+++ b/src/crypto/thread_pthread.c
@@ -45,7 +45,13 @@
}
}
-void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock) {
+void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX *lock) {
+ if (pthread_rwlock_unlock((pthread_rwlock_t *) lock) != 0) {
+ abort();
+ }
+}
+
+void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX *lock) {
if (pthread_rwlock_unlock((pthread_rwlock_t *) lock) != 0) {
abort();
}
@@ -67,7 +73,13 @@
}
}
-void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock) {
+void CRYPTO_STATIC_MUTEX_unlock_read(struct CRYPTO_STATIC_MUTEX *lock) {
+ if (pthread_rwlock_unlock(&lock->lock) != 0) {
+ abort();
+ }
+}
+
+void CRYPTO_STATIC_MUTEX_unlock_write(struct CRYPTO_STATIC_MUTEX *lock) {
if (pthread_rwlock_unlock(&lock->lock) != 0) {
abort();
}
diff --git a/src/crypto/thread_win.c b/src/crypto/thread_win.c
index 2632565..9f7d82b 100644
--- a/src/crypto/thread_win.c
+++ b/src/crypto/thread_win.c
@@ -27,7 +27,7 @@
#include <openssl/type_check.h>
-OPENSSL_COMPILE_ASSERT(sizeof(CRYPTO_MUTEX) >= sizeof(CRITICAL_SECTION),
+OPENSSL_COMPILE_ASSERT(sizeof(CRYPTO_MUTEX) >= sizeof(SRWLOCK),
CRYPTO_MUTEX_too_small);
static BOOL CALLBACK call_once_init(INIT_ONCE *once, void *arg, void **out) {
@@ -43,52 +43,43 @@
}
void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {
- if (!InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *) lock, 0x400)) {
- abort();
- }
+ InitializeSRWLock((SRWLOCK *) lock);
}
void CRYPTO_MUTEX_lock_read(CRYPTO_MUTEX *lock) {
- /* Since we have to support Windows XP, read locks are actually exclusive. */
- EnterCriticalSection((CRITICAL_SECTION *) lock);
+ AcquireSRWLockShared((SRWLOCK *) lock);
}
void CRYPTO_MUTEX_lock_write(CRYPTO_MUTEX *lock) {
- EnterCriticalSection((CRITICAL_SECTION *) lock);
+ AcquireSRWLockExclusive((SRWLOCK *) lock);
}
-void CRYPTO_MUTEX_unlock(CRYPTO_MUTEX *lock) {
- LeaveCriticalSection((CRITICAL_SECTION *) lock);
+void CRYPTO_MUTEX_unlock_read(CRYPTO_MUTEX *lock) {
+ ReleaseSRWLockShared((SRWLOCK *) lock);
+}
+
+void CRYPTO_MUTEX_unlock_write(CRYPTO_MUTEX *lock) {
+ ReleaseSRWLockExclusive((SRWLOCK *) lock);
}
void CRYPTO_MUTEX_cleanup(CRYPTO_MUTEX *lock) {
- DeleteCriticalSection((CRITICAL_SECTION *) lock);
-}
-
-static BOOL CALLBACK static_lock_init(INIT_ONCE *once, void *arg, void **out) {
- struct CRYPTO_STATIC_MUTEX *lock = arg;
- if (!InitializeCriticalSectionAndSpinCount(&lock->lock, 0x400)) {
- abort();
- }
- return TRUE;
+ /* SRWLOCKs require no cleanup. */
}
void CRYPTO_STATIC_MUTEX_lock_read(struct CRYPTO_STATIC_MUTEX *lock) {
- /* TODO(davidben): Consider replacing these with SRWLOCK now that we no longer
- * need to support Windows XP. Currently, read locks are actually
- * exclusive. */
- if (!InitOnceExecuteOnce(&lock->once, static_lock_init, lock, NULL)) {
- abort();
- }
- EnterCriticalSection(&lock->lock);
+ AcquireSRWLockShared(&lock->lock);
}
void CRYPTO_STATIC_MUTEX_lock_write(struct CRYPTO_STATIC_MUTEX *lock) {
- CRYPTO_STATIC_MUTEX_lock_read(lock);
+ AcquireSRWLockExclusive(&lock->lock);
}
-void CRYPTO_STATIC_MUTEX_unlock(struct CRYPTO_STATIC_MUTEX *lock) {
- LeaveCriticalSection(&lock->lock);
+void CRYPTO_STATIC_MUTEX_unlock_read(struct CRYPTO_STATIC_MUTEX *lock) {
+ ReleaseSRWLockShared(&lock->lock);
+}
+
+void CRYPTO_STATIC_MUTEX_unlock_write(struct CRYPTO_STATIC_MUTEX *lock) {
+ ReleaseSRWLockExclusive(&lock->lock);
}
static CRITICAL_SECTION g_destructors_lock;
diff --git a/src/crypto/x509/by_dir.c b/src/crypto/x509/by_dir.c
index 4f0a49e..4445b05 100644
--- a/src/crypto/x509/by_dir.c
+++ b/src/crypto/x509/by_dir.c
@@ -327,7 +327,7 @@
hent = NULL;
k = 0;
}
- CRYPTO_STATIC_MUTEX_unlock(&g_ent_hashes_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&g_ent_hashes_lock);
} else {
k = 0;
hent = NULL;
@@ -392,7 +392,7 @@
if (sk_X509_OBJECT_find(xl->store_ctx->objs, &idx, &stmp)) {
tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, idx);
}
- CRYPTO_MUTEX_unlock(&xl->store_ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&xl->store_ctx->objs_lock);
/*
* If a CRL, update the last file suffix added for this
@@ -412,14 +412,14 @@
if (!hent) {
hent = OPENSSL_malloc(sizeof(BY_DIR_HASH));
if (hent == NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&g_ent_hashes_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
ok = 0;
goto finish;
}
hent->hash = h;
hent->suffix = k;
if (!sk_BY_DIR_HASH_push(ent->hashes, hent)) {
- CRYPTO_STATIC_MUTEX_unlock(&g_ent_hashes_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
OPENSSL_free(hent);
ok = 0;
goto finish;
@@ -427,7 +427,7 @@
} else if (hent->suffix < k)
hent->suffix = k;
- CRYPTO_STATIC_MUTEX_unlock(&g_ent_hashes_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_ent_hashes_lock);
}
if (tmp != NULL) {
diff --git a/src/crypto/x509/x509_lu.c b/src/crypto/x509/x509_lu.c
index ec3f300..553756d 100644
--- a/src/crypto/x509/x509_lu.c
+++ b/src/crypto/x509/x509_lu.c
@@ -305,7 +305,7 @@
CRYPTO_MUTEX_lock_write(&ctx->objs_lock);
tmp = X509_OBJECT_retrieve_by_subject(ctx->objs, type, name);
- CRYPTO_MUTEX_unlock(&ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->objs_lock);
if (tmp == NULL || type == X509_LU_CRL) {
for (i = vs->current_method;
@@ -364,7 +364,7 @@
} else
sk_X509_OBJECT_push(ctx->objs, obj);
- CRYPTO_MUTEX_unlock(&ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->objs_lock);
return ret;
}
@@ -396,7 +396,7 @@
} else
sk_X509_OBJECT_push(ctx->objs, obj);
- CRYPTO_MUTEX_unlock(&ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->objs_lock);
return ret;
}
@@ -504,7 +504,7 @@
* cache
*/
X509_OBJECT xobj;
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
if (!X509_STORE_get_by_subject(ctx, X509_LU_X509, nm, &xobj)) {
sk_X509_free(sk);
return NULL;
@@ -513,7 +513,7 @@
CRYPTO_MUTEX_lock_write(&ctx->ctx->objs_lock);
idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt);
if (idx < 0) {
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
sk_X509_free(sk);
return NULL;
}
@@ -522,13 +522,13 @@
obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx);
x = obj->data.x509;
if (!sk_X509_push(sk, X509_up_ref(x))) {
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
X509_free(x);
sk_X509_pop_free(sk, X509_free);
return NULL;
}
}
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
return sk;
}
@@ -552,7 +552,7 @@
CRYPTO_MUTEX_lock_write(&ctx->ctx->objs_lock);
idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_CRL, nm, &cnt);
if (idx < 0) {
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
sk_X509_CRL_free(sk);
return NULL;
}
@@ -562,13 +562,13 @@
x = obj->data.crl;
X509_CRL_up_ref(x);
if (!sk_X509_CRL_push(sk, x)) {
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
X509_CRL_free(x);
sk_X509_CRL_pop_free(sk, X509_CRL_free);
return NULL;
}
}
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
return sk;
}
@@ -656,7 +656,7 @@
}
}
}
- CRYPTO_MUTEX_unlock(&ctx->ctx->objs_lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->ctx->objs_lock);
return ret;
}
diff --git a/src/crypto/x509/x_crl.c b/src/crypto/x509/x_crl.c
index cd64890..934571d 100644
--- a/src/crypto/x509/x_crl.c
+++ b/src/crypto/x509/x_crl.c
@@ -460,14 +460,14 @@
CRYPTO_STATIC_MUTEX_lock_read(&g_crl_sort_lock);
const int is_sorted = sk_X509_REVOKED_is_sorted(crl->crl->revoked);
- CRYPTO_STATIC_MUTEX_unlock(&g_crl_sort_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&g_crl_sort_lock);
if (!is_sorted) {
CRYPTO_STATIC_MUTEX_lock_write(&g_crl_sort_lock);
if (!sk_X509_REVOKED_is_sorted(crl->crl->revoked)) {
sk_X509_REVOKED_sort(crl->crl->revoked);
}
- CRYPTO_STATIC_MUTEX_unlock(&g_crl_sort_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_crl_sort_lock);
}
if (!sk_X509_REVOKED_find(crl->crl->revoked, &idx, &rtmp))
diff --git a/src/crypto/x509/x_pubkey.c b/src/crypto/x509/x_pubkey.c
index 1549195..23534b2 100644
--- a/src/crypto/x509/x_pubkey.c
+++ b/src/crypto/x509/x_pubkey.c
@@ -140,10 +140,10 @@
CRYPTO_STATIC_MUTEX_lock_read(&g_pubkey_lock);
if (key->pkey != NULL) {
- CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);
return EVP_PKEY_up_ref(key->pkey);
}
- CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);
/* Re-encode the |X509_PUBKEY| to DER and parse it. */
int spki_len = i2d_X509_PUBKEY(key, &spki);
@@ -161,12 +161,12 @@
/* Check to see if another thread set key->pkey first */
CRYPTO_STATIC_MUTEX_lock_write(&g_pubkey_lock);
if (key->pkey) {
- CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
EVP_PKEY_free(ret);
ret = key->pkey;
} else {
key->pkey = ret;
- CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
}
OPENSSL_free(spki);
diff --git a/src/crypto/x509/x_x509.c b/src/crypto/x509/x_x509.c
index 15af177..e21258d 100644
--- a/src/crypto/x509/x_x509.c
+++ b/src/crypto/x509/x_x509.c
@@ -55,6 +55,7 @@
* copied and put under another distribution licence
* [including the GNU Public Licence.] */
+#include <assert.h>
#include <stdio.h>
#include <openssl/asn1t.h>
@@ -204,12 +205,26 @@
return NULL;
}
-int i2d_X509_AUX(X509 *a, unsigned char **pp)
+/*
+ * Serialize trusted certificate to *pp or just return the required buffer
+ * length if pp == NULL. We ultimately want to avoid modifying *pp in the
+ * error path, but that depends on similar hygiene in lower-level functions.
+ * Here we avoid compounding the problem.
+ */
+static int i2d_x509_aux_internal(X509 *a, unsigned char **pp)
{
int length, tmplen;
unsigned char *start = pp != NULL ? *pp : NULL;
+
+ assert(pp == NULL || *pp != NULL);
+
+ /*
+ * This might perturb *pp on error, but fixing that belongs in i2d_X509()
+ * not here. It should be that if a == NULL length is zero, but we check
+ * both just in case.
+ */
length = i2d_X509(a, pp);
- if (length < 0 || a == NULL) {
+ if (length <= 0 || a == NULL) {
return length;
}
@@ -224,6 +239,42 @@
return length;
}
+/*
+ * Serialize trusted certificate to *pp, or just return the required buffer
+ * length if pp == NULL.
+ *
+ * When pp is not NULL, but *pp == NULL, we allocate the buffer, but since
+ * we're writing two ASN.1 objects back to back, we can't have i2d_X509() do
+ * the allocation, nor can we allow i2d_X509_CERT_AUX() to increment the
+ * allocated buffer.
+ */
+int i2d_X509_AUX(X509 *a, unsigned char **pp)
+{
+ int length;
+ unsigned char *tmp;
+
+ /* Buffer provided by caller */
+ if (pp == NULL || *pp != NULL)
+ return i2d_x509_aux_internal(a, pp);
+
+ /* Obtain the combined length */
+ if ((length = i2d_x509_aux_internal(a, NULL)) <= 0)
+ return length;
+
+ /* Allocate requisite combined storage */
+ *pp = tmp = OPENSSL_malloc(length);
+ if (tmp == NULL)
+ return -1; /* Push error onto error stack? */
+
+ /* Encode, but keep *pp at the originally malloced pointer */
+ length = i2d_x509_aux_internal(a, &tmp);
+ if (length <= 0) {
+ OPENSSL_free(*pp);
+ *pp = NULL;
+ }
+ return length;
+}
+
void X509_get0_signature(ASN1_BIT_STRING **psig, X509_ALGOR **palg,
const X509 *x)
{
diff --git a/src/crypto/x509v3/pcy_cache.c b/src/crypto/x509v3/pcy_cache.c
index f1e512e..b8a4be2 100644
--- a/src/crypto/x509v3/pcy_cache.c
+++ b/src/crypto/x509v3/pcy_cache.c
@@ -241,7 +241,7 @@
CRYPTO_STATIC_MUTEX_lock_read(&g_x509_policy_cache_lock);
cache = x->policy_cache;
- CRYPTO_STATIC_MUTEX_unlock(&g_x509_policy_cache_lock);
+ CRYPTO_STATIC_MUTEX_unlock_read(&g_x509_policy_cache_lock);
if (cache != NULL)
return cache;
@@ -250,7 +250,7 @@
if (x->policy_cache == NULL)
policy_cache_new(x);
cache = x->policy_cache;
- CRYPTO_STATIC_MUTEX_unlock(&g_x509_policy_cache_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_x509_policy_cache_lock);
return cache;
}
diff --git a/src/crypto/x509v3/v3_purp.c b/src/crypto/x509v3/v3_purp.c
index 85bc15b..f9324d4 100644
--- a/src/crypto/x509v3/v3_purp.c
+++ b/src/crypto/x509v3/v3_purp.c
@@ -431,7 +431,7 @@
CRYPTO_STATIC_MUTEX_lock_write(&g_x509_cache_extensions_lock);
if (x->ex_flags & EXFLAG_SET) {
- CRYPTO_STATIC_MUTEX_unlock(&g_x509_cache_extensions_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_x509_cache_extensions_lock);
return;
}
@@ -564,7 +564,7 @@
}
x->ex_flags |= EXFLAG_SET;
- CRYPTO_STATIC_MUTEX_unlock(&g_x509_cache_extensions_lock);
+ CRYPTO_STATIC_MUTEX_unlock_write(&g_x509_cache_extensions_lock);
}
/*
diff --git a/src/decrepit/bio/base64_bio.c b/src/decrepit/bio/base64_bio.c
index 2056138..8415bfe 100644
--- a/src/decrepit/bio/base64_bio.c
+++ b/src/decrepit/bio/base64_bio.c
@@ -452,7 +452,7 @@
case BIO_CTRL_WPENDING: /* More to write in buffer */
assert(ctx->buf_len >= ctx->buf_off);
ret = ctx->buf_len - ctx->buf_off;
- if ((ret == 0) && (ctx->encode != B64_NONE) && (ctx->base64.num != 0)) {
+ if ((ret == 0) && (ctx->encode != B64_NONE) && (ctx->base64.data_used != 0)) {
ret = 1;
} else if (ret <= 0) {
ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
@@ -484,7 +484,7 @@
ctx->tmp_len = 0;
goto again;
}
- } else if (ctx->encode != B64_NONE && ctx->base64.num != 0) {
+ } else if (ctx->encode != B64_NONE && ctx->base64.data_used != 0) {
ctx->buf_off = 0;
EVP_EncodeFinal(&(ctx->base64), (uint8_t *)ctx->buf, &(ctx->buf_len));
/* push out the bytes */
diff --git a/src/decrepit/evp/CMakeLists.txt b/src/decrepit/evp/CMakeLists.txt
index ee3bf33..e631a9a 100644
--- a/src/decrepit/evp/CMakeLists.txt
+++ b/src/decrepit/evp/CMakeLists.txt
@@ -5,5 +5,6 @@
OBJECT
+ dss1.c
evp_do_all.c
)
diff --git a/src/decrepit/evp/dss1.c b/src/decrepit/evp/dss1.c
new file mode 100644
index 0000000..9c370ac
--- /dev/null
+++ b/src/decrepit/evp/dss1.c
@@ -0,0 +1,20 @@
+/* Copyright (c) 2016, 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/digest.h>
+
+
+const EVP_MD *EVP_dss1(void) {
+ return EVP_sha1();
+}
diff --git a/src/include/openssl/base64.h b/src/include/openssl/base64.h
index f28e7dd..4bf3888 100644
--- a/src/include/openssl/base64.h
+++ b/src/include/openssl/base64.h
@@ -87,15 +87,16 @@
/* Decoding */
-/* EVP_DecodedLength sets |*out_len| to the maximum number of bytes
- * that will be needed to call |EVP_DecodeBase64| on an input of
- * length |len|. */
+/* EVP_DecodedLength sets |*out_len| to the maximum number of bytes that will
+ * be needed to call |EVP_DecodeBase64| on an input of length |len|. It returns
+ * one on success or zero if |len| is not a valid length for a base64-encoded
+ * string. */
OPENSSL_EXPORT int EVP_DecodedLength(size_t *out_len, size_t len);
/* EVP_DecodeBase64 decodes |in_len| bytes from base64 and writes
* |*out_len| bytes to |out|. |max_out| is the size of the output
* buffer. If it is not enough for the maximum output size, the
- * operation fails. */
+ * operation fails. It returns one on success or zero on error. */
OPENSSL_EXPORT int EVP_DecodeBase64(uint8_t *out, size_t *out_len,
size_t max_out, const uint8_t *in,
size_t in_len);
@@ -105,9 +106,7 @@
*
* OpenSSL provides a streaming base64 implementation, however its behavior is
* very specific to PEM. It is also very lenient of invalid input. Use of any of
- * these functions is thus deprecated.
- *
- * TODO(davidben): Import upstream's rewrite that rejects the invalid input. */
+ * these functions is thus deprecated. */
/* EVP_EncodeInit initialises |*ctx|, which is typically stack
* allocated, for an encoding operation.
@@ -159,21 +158,25 @@
*
* WARNING: EVP_DecodeBlock's return value does not take padding into
* account. It also strips leading whitespace and trailing
- * whitespace. */
+ * whitespace and minuses. */
OPENSSL_EXPORT int EVP_DecodeBlock(uint8_t *dst, const uint8_t *src,
size_t src_len);
struct evp_encode_ctx_st {
- unsigned num; /* number saved in a partial encode/decode */
- unsigned length; /* The length is either the output line length
- * (in input bytes) or the shortest input line
- * length that is ok. Once decoding begins,
- * the length is adjusted up each time a longer
- * line is decoded */
- uint8_t enc_data[80]; /* data to encode */
- unsigned line_num; /* number read on current line */
- int expect_nl;
+ /* data_used indicates the number of bytes of |data| that are valid. When
+ * encoding, |data| will be filled and encoded as a lump. When decoding, only
+ * the first four bytes of |data| will be used. */
+ unsigned data_used;
+ uint8_t data[48];
+
+ /* eof_seen indicates that the end of the base64 data has been seen when
+ * decoding. Only whitespace can follow. */
+ char eof_seen;
+
+ /* error_encountered indicates that invalid base64 data was found. This will
+ * cause all future calls to fail. */
+ char error_encountered;
};
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index a7fe261..3e547ac 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -362,8 +362,6 @@
*
* Memory BIOs support |BIO_gets| and |BIO_puts|.
*
- * |BIO_eof| is true if no data is in the BIO.
- *
* |BIO_ctrl_pending| returns the number of bytes currently stored. */
/* BIO_s_mem returns a |BIO_METHOD| that uses a in-memory buffer. */
@@ -420,12 +418,7 @@
* underlying file descriptor when the BIO is freed.
*
* |BIO_reset| attempts to seek the file pointer to the start of file using
- * |lseek|.
- *
- * |BIO_seek| sets the file pointer to position |off| from start of file using
- * |lseek|.
- *
- * |BIO_tell| returns the current file position. */
+ * |lseek|. */
/* BIO_s_fd returns a |BIO_METHOD| for file descriptor fds. */
OPENSSL_EXPORT const BIO_METHOD *BIO_s_fd(void);
@@ -460,11 +453,6 @@
* |BIO_reset| attempts to seek the file pointer to the start of file using
* |fseek|.
*
- * |BIO_seek| sets the file pointer to the given position from the start of
- * file using |fseek|.
- *
- * |BIO_eof| calls |feof|.
- *
* Setting the close flag causes |fclose| to be called on the stream when the
* BIO is freed. */
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index c66f887..e9be0f5 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -802,14 +802,6 @@
BN_CTX *ctx,
const BN_MONT_CTX *mont);
-OPENSSL_EXPORT int BN_mod_exp_mont_word(BIGNUM *r, BN_ULONG a, const BIGNUM *p,
- const BIGNUM *m, BN_CTX *ctx,
- const BN_MONT_CTX *mont);
-OPENSSL_EXPORT int BN_mod_exp2_mont(BIGNUM *r, const BIGNUM *a1,
- const BIGNUM *p1, const BIGNUM *a2,
- const BIGNUM *p2, const BIGNUM *m,
- BN_CTX *ctx, const BN_MONT_CTX *mont);
-
/* Deprecated functions */
@@ -829,6 +821,20 @@
* is updated. */
OPENSSL_EXPORT BIGNUM *BN_mpi2bn(const uint8_t *in, size_t len, BIGNUM *out);
+/* BN_mod_exp_mont_word is like |BN_mod_exp_mont| except that the base |a| is
+ * given as a |BN_ULONG| instead of a |BIGNUM *|. It returns one on success
+ * or zero otherwise. */
+OPENSSL_EXPORT int BN_mod_exp_mont_word(BIGNUM *r, BN_ULONG a, const BIGNUM *p,
+ const BIGNUM *m, BN_CTX *ctx,
+ const BN_MONT_CTX *mont);
+
+/* BN_mod_exp2_mont calculates (a1^p1) * (a2^p2) mod m. It returns 1 on success
+ * or zero otherwise. */
+OPENSSL_EXPORT int BN_mod_exp2_mont(BIGNUM *r, const BIGNUM *a1,
+ const BIGNUM *p1, const BIGNUM *a2,
+ const BIGNUM *p2, const BIGNUM *m,
+ BN_CTX *ctx, const BN_MONT_CTX *mont);
+
/* Private functions */
diff --git a/src/include/openssl/buf.h b/src/include/openssl/buf.h
index f4e315c..8ae856b 100644
--- a/src/include/openssl/buf.h
+++ b/src/include/openssl/buf.h
@@ -80,6 +80,10 @@
/* BUF_MEM_free frees |buf->data| if needed and then frees |buf| itself. */
OPENSSL_EXPORT void BUF_MEM_free(BUF_MEM *buf);
+/* BUF_MEM_reserve ensures |buf| has capacity |cap| and allocates memory if
+ * needed. It returns one on success and zero on error. */
+OPENSSL_EXPORT int BUF_MEM_reserve(BUF_MEM *buf, size_t cap);
+
/* BUF_MEM_grow ensures that |buf| has length |len| and allocates memory if
* needed. If the length of |buf| increased, the new bytes are filled with
* zeros. It returns the length of |buf|, or zero if there's an error. */
diff --git a/src/include/openssl/crypto.h b/src/include/openssl/crypto.h
index 3fd299f..80d7196 100644
--- a/src/include/openssl/crypto.h
+++ b/src/include/openssl/crypto.h
@@ -50,6 +50,10 @@
* internal version of BoringSSL. */
OPENSSL_EXPORT int CRYPTO_is_confidential_build(void);
+/* CRYPTO_has_asm returns one unless BoringSSL was built with OPENSSL_NO_ASM,
+ * in which case it returns zero. */
+OPENSSL_EXPORT int CRYPTO_has_asm(void);
+
/* Deprecated functions. */
diff --git a/src/include/openssl/dh.h b/src/include/openssl/dh.h
index 5cdeb86..a087651 100644
--- a/src/include/openssl/dh.h
+++ b/src/include/openssl/dh.h
@@ -174,22 +174,15 @@
/* ASN.1 functions. */
-/* d2i_DHparams parses an ASN.1, DER encoded Diffie-Hellman parameters
- * structure from |len| bytes at |*inp|. If |ret| is not NULL then, on exit, a
- * pointer to the result is in |*ret|. If |*ret| is already non-NULL on entry
- * then the result is written directly into |*ret|, otherwise a fresh |DH| is
- * allocated. However, one should not depend on writing into |*ret| because
- * this behaviour is likely to change in the future.
- *
- * On successful exit, |*inp| is advanced past the DER structure. It
- * returns the result or NULL on error. */
-OPENSSL_EXPORT DH *d2i_DHparams(DH **ret, const unsigned char **inp, long len);
+/* DH_parse_parameters decodes a DER-encoded DHParameter structure (PKCS #3)
+ * from |cbs| and advances |cbs|. It returns a newly-allocated |DH| or NULL on
+ * error. */
+OPENSSL_EXPORT DH *DH_parse_parameters(CBS *cbs);
-/* i2d_DHparams marshals |in| to an ASN.1, DER structure. If |outp| is not NULL
- * then the result is written to |*outp| and |*outp| is advanced just past the
- * output. It returns the number of bytes in the result, whether written or
- * not, or a negative value on error. */
-OPENSSL_EXPORT int i2d_DHparams(const DH *in, unsigned char **outp);
+/* DH_marshal_parameters marshals |dh| as a DER-encoded DHParameter structure
+ * (PKCS #3) and appends the result to |cbb|. It returns one on success and zero
+ * on error. */
+OPENSSL_EXPORT int DH_marshal_parameters(CBB *cbb, const DH *dh);
/* ex_data functions.
@@ -213,6 +206,26 @@
void (*callback)(int, int, void *),
void *cb_arg);
+/* d2i_DHparams parses an ASN.1, DER encoded Diffie-Hellman parameters structure
+ * from |len| bytes at |*inp|. If |ret| is not NULL then, on exit, a pointer to
+ * the result is in |*ret|. Note that, even if |*ret| is already non-NULL on
+ * entry, it will not be written to. Rather, a fresh |DH| is allocated and the
+ * previous one is freed.
+ *
+ * On successful exit, |*inp| is advanced past the DER structure. It
+ * returns the result or NULL on error.
+ *
+ * Use |DH_parse_parameters| instead. */
+OPENSSL_EXPORT DH *d2i_DHparams(DH **ret, const unsigned char **inp, long len);
+
+/* i2d_DHparams marshals |in| to an ASN.1, DER structure. If |outp| is not NULL
+ * then the result is written to |*outp| and |*outp| is advanced just past the
+ * output. It returns the number of bytes in the result, whether written or
+ * not, or a negative value on error.
+ *
+ * Use |DH_marshal_parameters| instead. */
+OPENSSL_EXPORT int i2d_DHparams(const DH *in, unsigned char **outp);
+
struct dh_st {
BIGNUM *p;
@@ -248,5 +261,7 @@
#define DH_R_INVALID_PUBKEY 101
#define DH_R_MODULUS_TOO_LARGE 102
#define DH_R_NO_PRIVATE_VALUE 103
+#define DH_R_DECODE_ERROR 104
+#define DH_R_ENCODE_ERROR 105
#endif /* OPENSSL_HEADER_DH_H */
diff --git a/src/include/openssl/digest.h b/src/include/openssl/digest.h
index db3ead7..07ea07a 100644
--- a/src/include/openssl/digest.h
+++ b/src/include/openssl/digest.h
@@ -212,6 +212,12 @@
* |name|, or NULL if the name is unknown. */
OPENSSL_EXPORT const EVP_MD *EVP_get_digestbyname(const char *);
+/* EVP_dss1 returns the value of EVP_sha1(). This was provided by OpenSSL to
+ * specifiy the original DSA signatures, which were fixed to use SHA-1. Note,
+ * however, that attempting to sign or verify DSA signatures with the EVP
+ * interface will always fail. */
+OPENSSL_EXPORT const EVP_MD *EVP_dss1(void);
+
/* Digest operation accessors. */
diff --git a/src/include/openssl/hkdf.h b/src/include/openssl/hkdf.h
index 8c96c4c..a484a30 100644
--- a/src/include/openssl/hkdf.h
+++ b/src/include/openssl/hkdf.h
@@ -37,6 +37,23 @@
const uint8_t *salt, size_t salt_len,
const uint8_t *info, size_t info_len);
+/* HKDF_extract computes a HKDF PRK (as specified by RFC 5869) from initial
+ * keying material |secret| and salt |salt| using |digest|, and outputs
+ * |out_len| bytes to |out_key|. The maximum output size is |EVP_MAX_MD_SIZE|.
+ * It returns one on success and zero on error. */
+OPENSSL_EXPORT int HKDF_extract(uint8_t *out_key, size_t *out_len,
+ const EVP_MD *digest, const uint8_t *secret,
+ size_t secret_len, const uint8_t *salt,
+ size_t salt_len);
+
+/* HKDF_expand computes a HKDF OKM (as specified by RFC 5869) of length
+ * |out_len| from the PRK |prk| and info |info| using |digest|, and outputs
+ * the result to |out_key|. It returns one on success and zero on error. */
+OPENSSL_EXPORT int HKDF_expand(uint8_t *out_key, size_t out_len,
+ const EVP_MD *digest, uint8_t *prk,
+ size_t prk_len, const uint8_t *info,
+ size_t info_len);
+
#if defined(__cplusplus)
} /* extern C */
diff --git a/src/include/openssl/newhope.h b/src/include/openssl/newhope.h
index af573ba..31d559f 100644
--- a/src/include/openssl/newhope.h
+++ b/src/include/openssl/newhope.h
@@ -38,37 +38,92 @@
/* NEWHOPE_POLY_free frees |p|. */
OPENSSL_EXPORT void NEWHOPE_POLY_free(NEWHOPE_POLY *p);
-/* NEWHOPE_SERVERMSG_LENGTH is the length of the server's message to the
- * client. */
-#define NEWHOPE_SERVERMSG_LENGTH (((1024 * 14) / 8) + 32)
+/* NEWHOPE_POLY_LENGTH is the size in bytes of the packed representation of a
+ * polynomial, encoded with 14 bits per coefficient. */
+#define NEWHOPE_POLY_LENGTH ((1024 * 14) / 8)
-/* NEWHOPE_CLIENTMSG_LENGTH is the length of the client's message to the
- * server. */
-#define NEWHOPE_CLIENTMSG_LENGTH (((1024 * 14) / 8) + 1024 / 4)
+/* NEWHOPE_RECONCILIATION_LENGTH is the size in bytes of the packed
+ * representation of the reconciliation data, encoded as 2 bits per
+ * coefficient. */
+#define NEWHOPE_RECONCILIATION_LENGTH ((1024 * 2) / 8)
-/* NEWHOPE_keygen initializes |out_msg| and |out_sk| for a new key
- * exchange. |msg| must have room for |NEWHOPE_SERVERMSG_LENGTH| bytes. Neither
+/* NEWHOPE_OFFERMSG_LENGTH is the length of the offering party's message to the
+ * accepting party. */
+#define NEWHOPE_OFFERMSG_LENGTH (NEWHOPE_POLY_LENGTH + 32)
+
+/* NEWHOPE_ACCEPTMSG_LENGTH is the length of the accepting party's message to
+ * the offering party. */
+#define NEWHOPE_ACCEPTMSG_LENGTH \
+ (NEWHOPE_POLY_LENGTH + NEWHOPE_RECONCILIATION_LENGTH)
+
+/* NEWHOPE_KEY_LENGTH is the size of the result of the key agreement. This
+ * result is not exposed to callers: instead, it is whitened with SHA-256, whose
+ * output happens to be the same size. */
+#define NEWHOPE_KEY_LENGTH 32
+
+/* NEWHOPE_offer initializes |out_msg| and |out_sk| for a new key
+ * exchange. |msg| must have room for |NEWHOPE_OFFERMSG_LENGTH| bytes. Neither
* output may be cached. */
-OPENSSL_EXPORT void NEWHOPE_keygen(uint8_t out_msg[NEWHOPE_SERVERMSG_LENGTH],
- NEWHOPE_POLY *out_sk);
+OPENSSL_EXPORT void NEWHOPE_offer(uint8_t out_msg[NEWHOPE_OFFERMSG_LENGTH],
+ NEWHOPE_POLY *out_sk);
-/* NEWHOPE_server_compute_key completes a key exchange given a client message
- * |msg| and the previously generated server secret |sk|. The result of the
- * key exchange is written to |out_key|, which must have space for
+/* NEWHOPE_accept completes a key exchange given an offer message |msg|. The
+ * result of the key exchange is written to |out_key|, which must have space for
+ * |SHA256_DIGEST_LENGTH| bytes. The message to be send to the offering party is
+ * written to |out_msg|, which must have room for |NEWHOPE_ACCEPTMSG_LENGTH|
+ * bytes. Returns 1 on success and 0 on error. */
+OPENSSL_EXPORT int NEWHOPE_accept(uint8_t out_key[SHA256_DIGEST_LENGTH],
+ uint8_t out_msg[NEWHOPE_ACCEPTMSG_LENGTH],
+ const uint8_t msg[NEWHOPE_OFFERMSG_LENGTH],
+ size_t msg_len);
+
+/* NEWHOPE_finish completes a key exchange for the offering party, given an
+ * accept message |msg| and the previously generated secret |sk|. The result of
+ * the key exchange is written to |out_key|, which must have space for
* |SHA256_DIGEST_LENGTH| bytes. Returns 1 on success and 0 on error. */
-OPENSSL_EXPORT int NEWHOPE_server_compute_key(
- uint8_t out_key[SHA256_DIGEST_LENGTH], const NEWHOPE_POLY *sk,
- const uint8_t msg[NEWHOPE_CLIENTMSG_LENGTH], size_t msg_len);
+OPENSSL_EXPORT int NEWHOPE_finish(uint8_t out_key[SHA256_DIGEST_LENGTH],
+ const NEWHOPE_POLY *sk,
+ const uint8_t msg[NEWHOPE_ACCEPTMSG_LENGTH],
+ size_t msg_len);
-/* NEWHOPE_client_compute_key completes a key exchange given a server message
- * |msg|. The result of the key exchange is written to |out_key|, which must
- * have space for |SHA256_DIGEST_LENGTH| bytes. The message to be send to the
- * client is written to |out_msg|, which must have room for
- * |NEWHOPE_CLIENTMSG_LENGTH| bytes. Returns 1 on success and 0 on error. */
-OPENSSL_EXPORT int NEWHOPE_client_compute_key(
- uint8_t out_key[SHA256_DIGEST_LENGTH],
- uint8_t out_msg[NEWHOPE_CLIENTMSG_LENGTH],
- const uint8_t msg[NEWHOPE_SERVERMSG_LENGTH], size_t msg_len);
+
+/* Lower-level functions. */
+
+/* NEWHOPE_offer_computation is the work of |NEWHOPE_offer|, less the encoding
+ * parts. The inputs are the noise polynomials |s| and |e|, and random
+ * polynomial |a|. The output is the polynomial |pk|. */
+OPENSSL_EXPORT void NEWHOPE_offer_computation(
+ NEWHOPE_POLY *out_pk,
+ const NEWHOPE_POLY *s, const NEWHOPE_POLY *e, const NEWHOPE_POLY *a);
+
+/* NEWHOPE_accept_computation is the work of |NEWHOPE_accept|, less the encoding
+ * parts. The inputs from the peer are |pk| and |a|. The locally-generated
+ * inputs are the noise polynomials |sp|, |ep|, and |epp|, and the random bytes
+ * |rand|. The outputs are |out_bp| and |out_reconciliation|, and the result of
+ * key agreement |key|. Returns 1 on success and 0 on failure. */
+OPENSSL_EXPORT void NEWHOPE_accept_computation(
+ uint8_t out_key[NEWHOPE_KEY_LENGTH], NEWHOPE_POLY *out_bp,
+ NEWHOPE_POLY *out_reconciliation,
+ const NEWHOPE_POLY *sp, const NEWHOPE_POLY *ep, const NEWHOPE_POLY *epp,
+ const uint8_t rand[32],
+ const NEWHOPE_POLY *pk, const NEWHOPE_POLY *a);
+
+/* NEWHOPE_finish_computation is the work of |NEWHOPE_finish|, less the encoding
+ * parts. Given the peer's |bp| and |reconciliation|, and locally-generated
+ * noise |noise|, the result of the key agreement is written to out_key.
+ * Returns 1 on success and 0 on failure. */
+OPENSSL_EXPORT void NEWHOPE_finish_computation(
+ uint8_t out_key[NEWHOPE_KEY_LENGTH], const NEWHOPE_POLY *noise,
+ const NEWHOPE_POLY *bp, const NEWHOPE_POLY *reconciliation);
+
+/* NEWHOPE_POLY_frombytes decodes |a| into |r|. */
+OPENSSL_EXPORT void NEWHOPE_POLY_frombytes(
+ NEWHOPE_POLY *r, const uint8_t a[NEWHOPE_POLY_LENGTH]);
+
+/* NEWHOPE_POLY_tobytes packs the polynomial |p| into the compact representation
+ * |r|. */
+OPENSSL_EXPORT void NEWHOPE_POLY_tobytes(uint8_t r[NEWHOPE_POLY_LENGTH],
+ const NEWHOPE_POLY* p);
#if defined(__cplusplus)
diff --git a/src/include/openssl/nid.h b/src/include/openssl/nid.h
index d51a67c..5ed44ee 100644
--- a/src/include/openssl/nid.h
+++ b/src/include/openssl/nid.h
@@ -4159,5 +4159,8 @@
#define SN_X25519 "X25519"
#define NID_X25519 948
+#define SN_cecpq1 "cecpq1"
+#define NID_cecpq1 949
+
#endif /* OPENSSL_HEADER_NID_H */
diff --git a/src/include/openssl/pem.h b/src/include/openssl/pem.h
index b899d9f..58aecaf 100644
--- a/src/include/openssl/pem.h
+++ b/src/include/openssl/pem.h
@@ -65,6 +65,10 @@
#include <openssl/stack.h>
#include <openssl/x509.h>
+/* For compatibility with open-iscsi, which assumes that it can get
+ * |OPENSSL_malloc| from pem.h or err.h */
+#include <openssl/crypto.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 1d0b486..3770c6e 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -487,6 +487,16 @@
* and zero on failure. */
OPENSSL_EXPORT int SSL_set_mtu(SSL *ssl, unsigned mtu);
+/* DTLSv1_set_initial_timeout_duration sets the initial duration for a DTLS
+ * handshake timeout.
+ *
+ * This duration overrides the default of 1 second, which is the strong
+ * recommendation of RFC 6347 (see section 4.2.4.1). However, there may exist
+ * situations where a shorter timeout would be beneficial, such as for
+ * time-sensitive applications. */
+OPENSSL_EXPORT void DTLSv1_set_initial_timeout_duration(SSL *ssl,
+ unsigned duration_ms);
+
/* DTLSv1_get_timeout queries the next DTLS handshake timeout. If there is a
* timeout in progress, it sets |*out| to the time remaining and returns one.
* Otherwise, it returns zero.
@@ -527,6 +537,7 @@
#define TLS1_VERSION 0x0301
#define TLS1_1_VERSION 0x0302
#define TLS1_2_VERSION 0x0303
+#define TLS1_3_VERSION 0x0304
#define DTLS1_VERSION 0xfeff
#define DTLS1_2_VERSION 0xfefd
@@ -1087,6 +1098,9 @@
/* SSL_CIPHER_is_ECDHE returns one if |cipher| uses ECDHE. */
OPENSSL_EXPORT int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher);
+/* SSL_CIPHER_is_CECPQ1 returns one if |cipher| uses CECPQ1. */
+OPENSSL_EXPORT int SSL_CIPHER_is_CECPQ1(const SSL_CIPHER *cipher);
+
/* SSL_CIPHER_get_min_version returns the minimum protocol version required
* for |cipher|. */
OPENSSL_EXPORT uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher);
@@ -1829,9 +1843,9 @@
OPENSSL_EXPORT int SSL_set1_curves(SSL *ssl, const int *curves,
size_t curves_len);
-/* SSL_get_curve_name returns a human-readable name for the elliptic curve
- * specified by the given TLS curve id, or NULL if the curve if unknown. */
-OPENSSL_EXPORT const char *SSL_get_curve_name(uint16_t curve_id);
+/* SSL_get_curve_name returns a human-readable name for the group specified by
+ * the given TLS group id, or NULL if the group is unknown. */
+OPENSSL_EXPORT const char *SSL_get_curve_name(uint16_t group_id);
/* Multiplicative Diffie-Hellman.
@@ -3220,6 +3234,7 @@
#define SSL_OP_NO_TLSv1 0x04000000L
#define SSL_OP_NO_TLSv1_2 0x08000000L
#define SSL_OP_NO_TLSv1_1 0x10000000L
+#define SSL_OP_NO_TLSv1_3 0x20000000L
#define SSL_OP_NO_DTLSv1 SSL_OP_NO_TLSv1
#define SSL_OP_NO_DTLSv1_2 SSL_OP_NO_TLSv1_2
@@ -3346,6 +3361,7 @@
#define SSL_TXT_kDHE "kDHE"
#define SSL_TXT_kEDH "kEDH"
#define SSL_TXT_kECDHE "kECDHE"
+#define SSL_TXT_kCECPQ1 "kCECPQ1"
#define SSL_TXT_kEECDH "kEECDH"
#define SSL_TXT_kPSK "kPSK"
#define SSL_TXT_aRSA "aRSA"
@@ -3376,6 +3392,7 @@
#define SSL_TXT_TLSV1 "TLSv1"
#define SSL_TXT_TLSV1_1 "TLSv1.1"
#define SSL_TXT_TLSV1_2 "TLSv1.2"
+#define SSL_TXT_TLSV1_3 "TLSv1.3"
#define SSL_TXT_ALL "ALL"
#define SSL_TXT_CMPDEF "COMPLEMENTOFDEFAULT"
@@ -3785,9 +3802,9 @@
/* SRTP profiles we are willing to do from RFC 5764 */
STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles;
- /* EC extension values inherited by SSL structure */
- size_t tlsext_ellipticcurvelist_length;
- uint16_t *tlsext_ellipticcurvelist;
+ /* Supported group values inherited by SSL structure */
+ size_t supported_group_list_len;
+ uint16_t *supported_group_list;
/* The client's Channel ID private key. */
EVP_PKEY *tlsext_channel_id_private;
@@ -3853,7 +3870,9 @@
BIO *wbio; /* used by SSL_write */
/* bbio, if non-NULL, is a buffer placed in front of |wbio| to pack handshake
- * messages within one flight into a single |BIO_write|.
+ * messages within one flight into a single |BIO_write|. In this case, |wbio|
+ * and |bbio| are equal and the true caller-configured BIO is
+ * |bbio->next_bio|.
*
* TODO(davidben): This does not work right for DTLS. It assumes the MTU is
* smaller than the buffer size so that the buffer's internal flushing never
@@ -3869,8 +3888,6 @@
* handshake_func is == 0 until then, we use this test instead of an "init"
* member. */
- int shutdown; /* we have shut things down, 0x01 sent, 0x02
- * for received */
int state; /* where we are */
BUF_MEM *init_buf; /* buffer used during init */
@@ -3882,6 +3899,10 @@
struct ssl3_state_st *s3; /* SSLv3 variables */
struct dtls1_state_st *d1; /* DTLSv1 variables */
+ /* initial_timeout_duration_ms is the default DTLS timeout duration in
+ * milliseconds. It's used to initialize the timer any time it's restarted. */
+ unsigned initial_timeout_duration_ms;
+
/* callback that allows applications to peek at protocol messages */
void (*msg_callback)(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg);
@@ -3946,8 +3967,8 @@
char *tlsext_hostname;
/* RFC4507 session ticket expected to be received or sent */
int tlsext_ticket_expected;
- size_t tlsext_ellipticcurvelist_length;
- uint16_t *tlsext_ellipticcurvelist; /* our list */
+ size_t supported_group_list_len;
+ uint16_t *supported_group_list; /* our list */
SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */
@@ -4027,6 +4048,14 @@
uint16_t cap;
} SSL3_BUFFER;
+/* An ssl_shutdown_t describes the shutdown state of one end of the connection,
+ * whether it is alive or has been shutdown via close_notify or fatal alert. */
+enum ssl_shutdown_t {
+ ssl_shutdown_none = 0,
+ ssl_shutdown_close_notify = 1,
+ ssl_shutdown_fatal_alert = 2,
+};
+
typedef struct ssl3_state_st {
uint8_t read_sequence[8];
uint8_t write_sequence[8];
@@ -4069,12 +4098,13 @@
* the handshake hash for TLS 1.1 and below. */
EVP_MD_CTX handshake_md5;
- /* clean_shutdown is one if the connection was cleanly shutdown with a
- * close_notify and zero otherwise. */
- char clean_shutdown;
+ /* recv_shutdown is the shutdown state for the receive half of the
+ * connection. */
+ enum ssl_shutdown_t recv_shutdown;
- /* we allow one fatal and one warning alert to be outstanding, send close
- * alert via the warning alert */
+ /* recv_shutdown is the shutdown state for the send half of the connection. */
+ enum ssl_shutdown_t send_shutdown;
+
int alert_dispatch;
uint8_t send_alert[2];
@@ -4108,9 +4138,12 @@
uint8_t peer_finish_md[EVP_MAX_MD_SIZE];
int peer_finish_md_len;
- unsigned long message_size;
int message_type;
+ /* message_complete is one if the current message is complete and zero
+ * otherwise. */
+ unsigned message_complete:1;
+
/* used to hold the new cipher we are going to use */
const SSL_CIPHER *new_cipher;
@@ -4181,11 +4214,11 @@
/* ocsp_stapling_requested is true if a client requested OCSP stapling. */
unsigned ocsp_stapling_requested:1;
- /* Server-only: peer_ellipticcurvelist contains the EC curve IDs advertised
- * by the peer. This is only set on the server's end. The server does not
- * advertise this extension to the client. */
- uint16_t *peer_ellipticcurvelist;
- size_t peer_ellipticcurvelist_length;
+ /* Server-only: peer_supported_group_list contains the supported group IDs
+ * advertised by the peer. This is only set on the server's end. The server
+ * does not advertise this extension to the client. */
+ uint16_t *peer_supported_group_list;
+ size_t peer_supported_group_list_len;
/* extended_master_secret indicates whether the extended master secret
* computation is used in this handshake. Note that this is different from
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index 957b740..64e1d31 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -312,17 +312,12 @@
#define SSL3_ST_CW_CLNT_HELLO_B (0x111 | SSL_ST_CONNECT)
/* read from server */
#define SSL3_ST_CR_SRVR_HELLO_A (0x120 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_SRVR_HELLO_B (0x121 | SSL_ST_CONNECT)
#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A (0x126 | SSL_ST_CONNECT)
-#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B (0x127 | SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_A (0x130 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_CERT_B (0x131 | SSL_ST_CONNECT)
#define SSL3_ST_CR_KEY_EXCH_A (0x140 | SSL_ST_CONNECT)
#define SSL3_ST_CR_KEY_EXCH_B (0x141 | SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_REQ_A (0x150 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_CERT_REQ_B (0x151 | SSL_ST_CONNECT)
#define SSL3_ST_CR_SRVR_DONE_A (0x160 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_SRVR_DONE_B (0x161 | SSL_ST_CONNECT)
/* write to server */
#define SSL3_ST_CW_CERT_A (0x170 | SSL_ST_CONNECT)
#define SSL3_ST_CW_CERT_B (0x171 | SSL_ST_CONNECT)
@@ -344,11 +339,12 @@
/* read from server */
#define SSL3_ST_CR_CHANGE (0x1C0 | SSL_ST_CONNECT)
#define SSL3_ST_CR_FINISHED_A (0x1D0 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_FINISHED_B (0x1D1 | SSL_ST_CONNECT)
#define SSL3_ST_CR_SESSION_TICKET_A (0x1E0 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_SESSION_TICKET_B (0x1E1 | SSL_ST_CONNECT)
#define SSL3_ST_CR_CERT_STATUS_A (0x1F0 | SSL_ST_CONNECT)
-#define SSL3_ST_CR_CERT_STATUS_B (0x1F1 | SSL_ST_CONNECT)
+
+/* SSL3_ST_CR_SRVR_HELLO_B is a legacy alias for |SSL3_ST_CR_SRVR_HELLO_A| used
+ * by some consumers which check |SSL_state|. */
+#define SSL3_ST_CR_SRVR_HELLO_B SSL3_ST_CR_SRVR_HELLO_A
/* server */
/* extra state */
@@ -359,7 +355,6 @@
#define SSL3_ST_SR_CLNT_HELLO_A (0x110 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_CLNT_HELLO_B (0x111 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_CLNT_HELLO_C (0x112 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CLNT_HELLO_D (0x115 | SSL_ST_ACCEPT)
/* write to client */
#define SSL3_ST_SW_HELLO_REQ_A (0x120 | SSL_ST_ACCEPT)
#define SSL3_ST_SW_HELLO_REQ_B (0x121 | SSL_ST_ACCEPT)
@@ -377,19 +372,13 @@
#define SSL3_ST_SW_SRVR_DONE_B (0x171 | SSL_ST_ACCEPT)
/* read from client */
#define SSL3_ST_SR_CERT_A (0x180 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CERT_B (0x181 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_KEY_EXCH_A (0x190 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_KEY_EXCH_B (0x191 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_KEY_EXCH_C (0x192 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_CERT_VRFY_A (0x1A0 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CERT_VRFY_B (0x1A1 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_CHANGE (0x1B0 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_NEXT_PROTO_A (0x210 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_NEXT_PROTO_B (0x211 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_CHANNEL_ID_A (0x230 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CHANNEL_ID_B (0x231 | SSL_ST_ACCEPT)
#define SSL3_ST_SR_FINISHED_A (0x1C0 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_FINISHED_B (0x1C1 | SSL_ST_ACCEPT)
/* write to client */
#define SSL3_ST_SW_CHANGE_A (0x1D0 | SSL_ST_ACCEPT)
@@ -406,7 +395,7 @@
#define SSL3_MT_HELLO_REQUEST 0
#define SSL3_MT_CLIENT_HELLO 1
#define SSL3_MT_SERVER_HELLO 2
-#define SSL3_MT_NEWSESSION_TICKET 4
+#define SSL3_MT_NEW_SESSION_TICKET 4
#define SSL3_MT_CERTIFICATE 11
#define SSL3_MT_SERVER_KEY_EXCHANGE 12
#define SSL3_MT_CERTIFICATE_REQUEST 13
@@ -417,9 +406,13 @@
#define SSL3_MT_CERTIFICATE_STATUS 22
#define SSL3_MT_SUPPLEMENTAL_DATA 23
#define SSL3_MT_NEXT_PROTO 67
-#define SSL3_MT_ENCRYPTED_EXTENSIONS 203
+#define SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS 203
#define DTLS1_MT_HELLO_VERIFY_REQUEST 3
+/* SSL3_MT_NEWSESSION_TICKET is a legacy alias for |SSL3_MT_NEW_SESSION_TICKET|
+ * for consumers which use |SSL_CTX_set_msg_callback|. */
+#define SSL3_MT_NEWSESSION_TICKET SSL3_MT_NEW_SESSION_TICKET
+
#define SSL3_MT_CCS 1
diff --git a/src/include/openssl/stack_macros.h b/src/include/openssl/stack_macros.h
index 809424c..2a60b8f 100644
--- a/src/include/openssl/stack_macros.h
+++ b/src/include/openssl/stack_macros.h
@@ -27,14 +27,15 @@
((STACK_OF(ACCESS_DESCRIPTION) *)sk_new_null())
#define sk_ACCESS_DESCRIPTION_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk))
#define sk_ACCESS_DESCRIPTION_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk));
-#define sk_ACCESS_DESCRIPTION_value(sk, i) \
- ((ACCESS_DESCRIPTION *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk), (i)))
+#define sk_ACCESS_DESCRIPTION_value(sk, i) \
+ ((ACCESS_DESCRIPTION *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk), \
+ (i)))
#define sk_ACCESS_DESCRIPTION_set(sk, i, p) \
((ACCESS_DESCRIPTION *)sk_set( \
@@ -80,13 +81,14 @@
#define sk_ACCESS_DESCRIPTION_dup(sk) \
((STACK_OF(ACCESS_DESCRIPTION) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk)))
#define sk_ACCESS_DESCRIPTION_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ACCESS_DESCRIPTION) *, sk))
#define sk_ACCESS_DESCRIPTION_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ACCESS_DESCRIPTION) *, sk))
#define sk_ACCESS_DESCRIPTION_set_cmp_func(sk, comp) \
((int (*)(const ACCESS_DESCRIPTION **a, const ACCESS_DESCRIPTION **b)) \
@@ -113,14 +115,15 @@
#define sk_ASN1_ADB_TABLE_new_null() ((STACK_OF(ASN1_ADB_TABLE) *)sk_new_null())
#define sk_ASN1_ADB_TABLE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk))
#define sk_ASN1_ADB_TABLE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk));
-#define sk_ASN1_ADB_TABLE_value(sk, i) \
- ((ASN1_ADB_TABLE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk), (i)))
+#define sk_ASN1_ADB_TABLE_value(sk, i) \
+ ((ASN1_ADB_TABLE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk), \
+ (i)))
#define sk_ASN1_ADB_TABLE_set(sk, i, p) \
((ASN1_ADB_TABLE *)sk_set( \
@@ -166,13 +169,14 @@
#define sk_ASN1_ADB_TABLE_dup(sk) \
((STACK_OF(ASN1_ADB_TABLE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk)))
#define sk_ASN1_ADB_TABLE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_ADB_TABLE) *, sk))
#define sk_ASN1_ADB_TABLE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_ADB_TABLE) *, sk))
#define sk_ASN1_ADB_TABLE_set_cmp_func(sk, comp) \
((int (*)(const ASN1_ADB_TABLE **a, const ASN1_ADB_TABLE **b)) \
@@ -200,14 +204,15 @@
((STACK_OF(ASN1_GENERALSTRING) *)sk_new_null())
#define sk_ASN1_GENERALSTRING_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk))
#define sk_ASN1_GENERALSTRING_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk));
-#define sk_ASN1_GENERALSTRING_value(sk, i) \
- ((ASN1_GENERALSTRING *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk), (i)))
+#define sk_ASN1_GENERALSTRING_value(sk, i) \
+ ((ASN1_GENERALSTRING *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk), \
+ (i)))
#define sk_ASN1_GENERALSTRING_set(sk, i, p) \
((ASN1_GENERALSTRING *)sk_set( \
@@ -253,13 +258,14 @@
#define sk_ASN1_GENERALSTRING_dup(sk) \
((STACK_OF(ASN1_GENERALSTRING) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk)))
#define sk_ASN1_GENERALSTRING_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_GENERALSTRING) *, sk))
#define sk_ASN1_GENERALSTRING_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_GENERALSTRING) *, sk))
#define sk_ASN1_GENERALSTRING_set_cmp_func(sk, comp) \
((int (*)(const ASN1_GENERALSTRING **a, const ASN1_GENERALSTRING **b)) \
@@ -286,14 +292,14 @@
#define sk_ASN1_INTEGER_new_null() ((STACK_OF(ASN1_INTEGER) *)sk_new_null())
#define sk_ASN1_INTEGER_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_INTEGER) *, sk))
#define sk_ASN1_INTEGER_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk));
#define sk_ASN1_INTEGER_value(sk, i) \
((ASN1_INTEGER *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_INTEGER) *, sk), (i)))
#define sk_ASN1_INTEGER_set(sk, i, p) \
((ASN1_INTEGER *)sk_set( \
@@ -338,13 +344,13 @@
#define sk_ASN1_INTEGER_dup(sk) \
((STACK_OF(ASN1_INTEGER) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_INTEGER) *, sk)))
#define sk_ASN1_INTEGER_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_INTEGER) *, sk))
#define sk_ASN1_INTEGER_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_INTEGER) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_INTEGER) *, sk))
#define sk_ASN1_INTEGER_set_cmp_func(sk, comp) \
((int (*)(const ASN1_INTEGER **a, const ASN1_INTEGER **b))sk_set_cmp_func( \
@@ -369,14 +375,14 @@
#define sk_ASN1_OBJECT_new_null() ((STACK_OF(ASN1_OBJECT) *)sk_new_null())
#define sk_ASN1_OBJECT_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_OBJECT) *, sk))
#define sk_ASN1_OBJECT_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk));
#define sk_ASN1_OBJECT_value(sk, i) \
((ASN1_OBJECT *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_OBJECT) *, sk), (i)))
#define sk_ASN1_OBJECT_set(sk, i, p) \
((ASN1_OBJECT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk), \
@@ -419,13 +425,13 @@
#define sk_ASN1_OBJECT_dup(sk) \
((STACK_OF(ASN1_OBJECT) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_OBJECT) *, sk)))
#define sk_ASN1_OBJECT_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_OBJECT) *, sk))
#define sk_ASN1_OBJECT_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_OBJECT) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_OBJECT) *, sk))
#define sk_ASN1_OBJECT_set_cmp_func(sk, comp) \
((int (*)(const ASN1_OBJECT **a, const ASN1_OBJECT **b))sk_set_cmp_func( \
@@ -452,14 +458,15 @@
((STACK_OF(ASN1_STRING_TABLE) *)sk_new_null())
#define sk_ASN1_STRING_TABLE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk))
#define sk_ASN1_STRING_TABLE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk));
-#define sk_ASN1_STRING_TABLE_value(sk, i) \
- ((ASN1_STRING_TABLE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk), (i)))
+#define sk_ASN1_STRING_TABLE_value(sk, i) \
+ ((ASN1_STRING_TABLE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk), \
+ (i)))
#define sk_ASN1_STRING_TABLE_set(sk, i, p) \
((ASN1_STRING_TABLE *)sk_set( \
@@ -505,13 +512,14 @@
#define sk_ASN1_STRING_TABLE_dup(sk) \
((STACK_OF(ASN1_STRING_TABLE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk)))
#define sk_ASN1_STRING_TABLE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_STRING_TABLE) *, sk))
#define sk_ASN1_STRING_TABLE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_STRING_TABLE) *, sk))
#define sk_ASN1_STRING_TABLE_set_cmp_func(sk, comp) \
((int (*)(const ASN1_STRING_TABLE **a, const ASN1_STRING_TABLE **b)) \
@@ -538,14 +546,14 @@
#define sk_ASN1_TYPE_new_null() ((STACK_OF(ASN1_TYPE) *)sk_new_null())
#define sk_ASN1_TYPE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_TYPE) *, sk))
#define sk_ASN1_TYPE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk));
#define sk_ASN1_TYPE_value(sk, i) \
((ASN1_TYPE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_TYPE) *, sk), (i)))
#define sk_ASN1_TYPE_set(sk, i, p) \
((ASN1_TYPE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk), (i), \
@@ -588,13 +596,13 @@
#define sk_ASN1_TYPE_dup(sk) \
((STACK_OF(ASN1_TYPE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_TYPE) *, sk)))
#define sk_ASN1_TYPE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_TYPE) *, sk))
#define sk_ASN1_TYPE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_TYPE) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_TYPE) *, sk))
#define sk_ASN1_TYPE_set_cmp_func(sk, comp) \
((int (*)(const ASN1_TYPE **a, const ASN1_TYPE **b))sk_set_cmp_func( \
@@ -617,14 +625,14 @@
#define sk_ASN1_VALUE_new_null() ((STACK_OF(ASN1_VALUE) *)sk_new_null())
#define sk_ASN1_VALUE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_VALUE) *, sk))
#define sk_ASN1_VALUE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk));
#define sk_ASN1_VALUE_value(sk, i) \
((ASN1_VALUE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_VALUE) *, sk), (i)))
#define sk_ASN1_VALUE_set(sk, i, p) \
((ASN1_VALUE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk), \
@@ -667,13 +675,13 @@
#define sk_ASN1_VALUE_dup(sk) \
((STACK_OF(ASN1_VALUE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_VALUE) *, sk)))
#define sk_ASN1_VALUE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(ASN1_VALUE) *, sk))
#define sk_ASN1_VALUE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_VALUE) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(ASN1_VALUE) *, sk))
#define sk_ASN1_VALUE_set_cmp_func(sk, comp) \
((int (*)(const ASN1_VALUE **a, const ASN1_VALUE **b))sk_set_cmp_func( \
@@ -696,12 +704,14 @@
#define sk_BIO_new_null() ((STACK_OF(BIO) *)sk_new_null())
-#define sk_BIO_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk))
+#define sk_BIO_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(BIO) *, sk))
#define sk_BIO_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk));
-#define sk_BIO_value(sk, i) \
- ((BIO *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk), (i)))
+#define sk_BIO_value(sk, i) \
+ ((BIO *)sk_value(CHECKED_CAST(const _STACK *, const STACK_OF(BIO) *, sk), \
+ (i)))
#define sk_BIO_set(sk, i, p) \
((BIO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk), (i), \
@@ -738,13 +748,14 @@
#define sk_BIO_pop(sk) \
((BIO *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk)))
-#define sk_BIO_dup(sk) \
- ((STACK_OF(BIO) *)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk)))
+#define sk_BIO_dup(sk) \
+ ((STACK_OF(BIO) *)sk_dup( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(BIO) *, sk)))
#define sk_BIO_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BIO) *, sk))
#define sk_BIO_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BIO) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(BIO) *, sk))
#define sk_BIO_set_cmp_func(sk, comp) \
((int (*)(const BIO **a, const BIO **b))sk_set_cmp_func( \
@@ -767,14 +778,14 @@
#define sk_BY_DIR_ENTRY_new_null() ((STACK_OF(BY_DIR_ENTRY) *)sk_new_null())
#define sk_BY_DIR_ENTRY_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk))
#define sk_BY_DIR_ENTRY_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk));
#define sk_BY_DIR_ENTRY_value(sk, i) \
((BY_DIR_ENTRY *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk), (i)))
#define sk_BY_DIR_ENTRY_set(sk, i, p) \
((BY_DIR_ENTRY *)sk_set( \
@@ -819,13 +830,13 @@
#define sk_BY_DIR_ENTRY_dup(sk) \
((STACK_OF(BY_DIR_ENTRY) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk)))
#define sk_BY_DIR_ENTRY_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_ENTRY) *, sk))
#define sk_BY_DIR_ENTRY_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_ENTRY) *, sk))
#define sk_BY_DIR_ENTRY_set_cmp_func(sk, comp) \
((int (*)(const BY_DIR_ENTRY **a, const BY_DIR_ENTRY **b))sk_set_cmp_func( \
@@ -850,14 +861,14 @@
#define sk_BY_DIR_HASH_new_null() ((STACK_OF(BY_DIR_HASH) *)sk_new_null())
#define sk_BY_DIR_HASH_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_HASH) *, sk))
#define sk_BY_DIR_HASH_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk));
#define sk_BY_DIR_HASH_value(sk, i) \
((BY_DIR_HASH *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_HASH) *, sk), (i)))
#define sk_BY_DIR_HASH_set(sk, i, p) \
((BY_DIR_HASH *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk), \
@@ -900,13 +911,13 @@
#define sk_BY_DIR_HASH_dup(sk) \
((STACK_OF(BY_DIR_HASH) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_HASH) *, sk)))
#define sk_BY_DIR_HASH_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(BY_DIR_HASH) *, sk))
#define sk_BY_DIR_HASH_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(BY_DIR_HASH) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(BY_DIR_HASH) *, sk))
#define sk_BY_DIR_HASH_set_cmp_func(sk, comp) \
((int (*)(const BY_DIR_HASH **a, const BY_DIR_HASH **b))sk_set_cmp_func( \
@@ -931,14 +942,14 @@
#define sk_CONF_VALUE_new_null() ((STACK_OF(CONF_VALUE) *)sk_new_null())
#define sk_CONF_VALUE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(CONF_VALUE) *, sk))
#define sk_CONF_VALUE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk));
#define sk_CONF_VALUE_value(sk, i) \
((CONF_VALUE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(CONF_VALUE) *, sk), (i)))
#define sk_CONF_VALUE_set(sk, i, p) \
((CONF_VALUE *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk), \
@@ -981,13 +992,13 @@
#define sk_CONF_VALUE_dup(sk) \
((STACK_OF(CONF_VALUE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(CONF_VALUE) *, sk)))
#define sk_CONF_VALUE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(CONF_VALUE) *, sk))
#define sk_CONF_VALUE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(CONF_VALUE) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(CONF_VALUE) *, sk))
#define sk_CONF_VALUE_set_cmp_func(sk, comp) \
((int (*)(const CONF_VALUE **a, const CONF_VALUE **b))sk_set_cmp_func( \
@@ -1013,15 +1024,17 @@
#define sk_CRYPTO_EX_DATA_FUNCS_new_null() \
((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_new_null())
-#define sk_CRYPTO_EX_DATA_FUNCS_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+#define sk_CRYPTO_EX_DATA_FUNCS_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, \
+ sk))
#define sk_CRYPTO_EX_DATA_FUNCS_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk));
-#define sk_CRYPTO_EX_DATA_FUNCS_value(sk, i) \
- ((CRYPTO_EX_DATA_FUNCS *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk), \
+#define sk_CRYPTO_EX_DATA_FUNCS_value(sk, i) \
+ ((CRYPTO_EX_DATA_FUNCS *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, \
+ sk), \
(i)))
#define sk_CRYPTO_EX_DATA_FUNCS_set(sk, i, p) \
@@ -1066,16 +1079,16 @@
((CRYPTO_EX_DATA_FUNCS *)sk_pop( \
CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
-#define sk_CRYPTO_EX_DATA_FUNCS_dup(sk) \
- ((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
+#define sk_CRYPTO_EX_DATA_FUNCS_dup(sk) \
+ ((STACK_OF(CRYPTO_EX_DATA_FUNCS) *)sk_dup(CHECKED_CAST( \
+ const _STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk)))
#define sk_CRYPTO_EX_DATA_FUNCS_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
#define sk_CRYPTO_EX_DATA_FUNCS_is_sorted(sk) \
- sk_is_sorted( \
- CHECKED_CAST(_STACK *, const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(CRYPTO_EX_DATA_FUNCS) *, sk))
#define sk_CRYPTO_EX_DATA_FUNCS_set_cmp_func(sk, comp) \
((int (*)(const CRYPTO_EX_DATA_FUNCS **a, const CRYPTO_EX_DATA_FUNCS **b)) \
@@ -1105,14 +1118,14 @@
#define sk_DIST_POINT_new_null() ((STACK_OF(DIST_POINT) *)sk_new_null())
#define sk_DIST_POINT_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(DIST_POINT) *, sk))
#define sk_DIST_POINT_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk));
#define sk_DIST_POINT_value(sk, i) \
((DIST_POINT *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(DIST_POINT) *, sk), (i)))
#define sk_DIST_POINT_set(sk, i, p) \
((DIST_POINT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk), \
@@ -1155,13 +1168,13 @@
#define sk_DIST_POINT_dup(sk) \
((STACK_OF(DIST_POINT) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(DIST_POINT) *, sk)))
#define sk_DIST_POINT_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(DIST_POINT) *, sk))
#define sk_DIST_POINT_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(DIST_POINT) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(DIST_POINT) *, sk))
#define sk_DIST_POINT_set_cmp_func(sk, comp) \
((int (*)(const DIST_POINT **a, const DIST_POINT **b))sk_set_cmp_func( \
@@ -1186,14 +1199,14 @@
#define sk_GENERAL_NAME_new_null() ((STACK_OF(GENERAL_NAME) *)sk_new_null())
#define sk_GENERAL_NAME_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAME) *, sk))
#define sk_GENERAL_NAME_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk));
#define sk_GENERAL_NAME_value(sk, i) \
((GENERAL_NAME *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAME) *, sk), (i)))
#define sk_GENERAL_NAME_set(sk, i, p) \
((GENERAL_NAME *)sk_set( \
@@ -1238,13 +1251,13 @@
#define sk_GENERAL_NAME_dup(sk) \
((STACK_OF(GENERAL_NAME) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAME) *, sk)))
#define sk_GENERAL_NAME_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAME) *, sk))
#define sk_GENERAL_NAME_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAME) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAME) *, sk))
#define sk_GENERAL_NAME_set_cmp_func(sk, comp) \
((int (*)(const GENERAL_NAME **a, const GENERAL_NAME **b))sk_set_cmp_func( \
@@ -1269,14 +1282,14 @@
#define sk_GENERAL_NAMES_new_null() ((STACK_OF(GENERAL_NAMES) *)sk_new_null())
#define sk_GENERAL_NAMES_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAMES) *, sk))
#define sk_GENERAL_NAMES_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk));
#define sk_GENERAL_NAMES_value(sk, i) \
((GENERAL_NAMES *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAMES) *, sk), (i)))
#define sk_GENERAL_NAMES_set(sk, i, p) \
((GENERAL_NAMES *)sk_set( \
@@ -1322,13 +1335,14 @@
#define sk_GENERAL_NAMES_dup(sk) \
((STACK_OF(GENERAL_NAMES) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAMES) *, sk)))
#define sk_GENERAL_NAMES_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_NAMES) *, sk))
#define sk_GENERAL_NAMES_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_NAMES) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_NAMES) *, sk))
#define sk_GENERAL_NAMES_set_cmp_func(sk, comp) \
((int (*)(const GENERAL_NAMES **a, const GENERAL_NAMES **b))sk_set_cmp_func( \
@@ -1354,14 +1368,15 @@
((STACK_OF(GENERAL_SUBTREE) *)sk_new_null())
#define sk_GENERAL_SUBTREE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk))
#define sk_GENERAL_SUBTREE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk));
-#define sk_GENERAL_SUBTREE_value(sk, i) \
- ((GENERAL_SUBTREE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk), (i)))
+#define sk_GENERAL_SUBTREE_value(sk, i) \
+ ((GENERAL_SUBTREE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk), \
+ (i)))
#define sk_GENERAL_SUBTREE_set(sk, i, p) \
((GENERAL_SUBTREE *)sk_set( \
@@ -1407,13 +1422,14 @@
#define sk_GENERAL_SUBTREE_dup(sk) \
((STACK_OF(GENERAL_SUBTREE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk)))
#define sk_GENERAL_SUBTREE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(GENERAL_SUBTREE) *, sk))
#define sk_GENERAL_SUBTREE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(GENERAL_SUBTREE) *, sk))
#define sk_GENERAL_SUBTREE_set_cmp_func(sk, comp) \
((int (*)(const GENERAL_SUBTREE **a, const GENERAL_SUBTREE **b)) \
@@ -1439,14 +1455,14 @@
#define sk_POLICYINFO_new_null() ((STACK_OF(POLICYINFO) *)sk_new_null())
#define sk_POLICYINFO_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(POLICYINFO) *, sk))
#define sk_POLICYINFO_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk));
#define sk_POLICYINFO_value(sk, i) \
((POLICYINFO *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICYINFO) *, sk), (i)))
#define sk_POLICYINFO_set(sk, i, p) \
((POLICYINFO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk), \
@@ -1489,13 +1505,13 @@
#define sk_POLICYINFO_dup(sk) \
((STACK_OF(POLICYINFO) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICYINFO) *, sk)))
#define sk_POLICYINFO_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICYINFO) *, sk))
#define sk_POLICYINFO_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICYINFO) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(POLICYINFO) *, sk))
#define sk_POLICYINFO_set_cmp_func(sk, comp) \
((int (*)(const POLICYINFO **a, const POLICYINFO **b))sk_set_cmp_func( \
@@ -1520,14 +1536,15 @@
#define sk_POLICYQUALINFO_new_null() ((STACK_OF(POLICYQUALINFO) *)sk_new_null())
#define sk_POLICYQUALINFO_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(POLICYQUALINFO) *, sk))
#define sk_POLICYQUALINFO_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk));
-#define sk_POLICYQUALINFO_value(sk, i) \
- ((POLICYQUALINFO *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk), (i)))
+#define sk_POLICYQUALINFO_value(sk, i) \
+ ((POLICYQUALINFO *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICYQUALINFO) *, sk), \
+ (i)))
#define sk_POLICYQUALINFO_set(sk, i, p) \
((POLICYQUALINFO *)sk_set( \
@@ -1573,13 +1590,14 @@
#define sk_POLICYQUALINFO_dup(sk) \
((STACK_OF(POLICYQUALINFO) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICYQUALINFO) *, sk)))
#define sk_POLICYQUALINFO_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICYQUALINFO) *, sk))
#define sk_POLICYQUALINFO_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICYQUALINFO) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICYQUALINFO) *, sk))
#define sk_POLICYQUALINFO_set_cmp_func(sk, comp) \
((int (*)(const POLICYQUALINFO **a, const POLICYQUALINFO **b)) \
@@ -1605,14 +1623,15 @@
#define sk_POLICY_MAPPING_new_null() ((STACK_OF(POLICY_MAPPING) *)sk_new_null())
#define sk_POLICY_MAPPING_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(POLICY_MAPPING) *, sk))
#define sk_POLICY_MAPPING_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk));
-#define sk_POLICY_MAPPING_value(sk, i) \
- ((POLICY_MAPPING *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk), (i)))
+#define sk_POLICY_MAPPING_value(sk, i) \
+ ((POLICY_MAPPING *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICY_MAPPING) *, sk), \
+ (i)))
#define sk_POLICY_MAPPING_set(sk, i, p) \
((POLICY_MAPPING *)sk_set( \
@@ -1658,13 +1677,14 @@
#define sk_POLICY_MAPPING_dup(sk) \
((STACK_OF(POLICY_MAPPING) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICY_MAPPING) *, sk)))
#define sk_POLICY_MAPPING_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(POLICY_MAPPING) *, sk))
#define sk_POLICY_MAPPING_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(POLICY_MAPPING) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(POLICY_MAPPING) *, sk))
#define sk_POLICY_MAPPING_set_cmp_func(sk, comp) \
((int (*)(const POLICY_MAPPING **a, const POLICY_MAPPING **b)) \
@@ -1691,15 +1711,17 @@
#define sk_RSA_additional_prime_new_null() \
((STACK_OF(RSA_additional_prime) *)sk_new_null())
-#define sk_RSA_additional_prime_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(RSA_additional_prime) *, sk))
+#define sk_RSA_additional_prime_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(RSA_additional_prime) *, \
+ sk))
#define sk_RSA_additional_prime_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(RSA_additional_prime) *, sk));
-#define sk_RSA_additional_prime_value(sk, i) \
- ((RSA_additional_prime *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(RSA_additional_prime) *, sk), \
+#define sk_RSA_additional_prime_value(sk, i) \
+ ((RSA_additional_prime *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(RSA_additional_prime) *, \
+ sk), \
(i)))
#define sk_RSA_additional_prime_set(sk, i, p) \
@@ -1744,16 +1766,16 @@
((RSA_additional_prime *)sk_pop( \
CHECKED_CAST(_STACK *, STACK_OF(RSA_additional_prime) *, sk)))
-#define sk_RSA_additional_prime_dup(sk) \
- ((STACK_OF(RSA_additional_prime) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(RSA_additional_prime) *, sk)))
+#define sk_RSA_additional_prime_dup(sk) \
+ ((STACK_OF(RSA_additional_prime) *)sk_dup(CHECKED_CAST( \
+ const _STACK *, const STACK_OF(RSA_additional_prime) *, sk)))
#define sk_RSA_additional_prime_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(RSA_additional_prime) *, sk))
#define sk_RSA_additional_prime_is_sorted(sk) \
- sk_is_sorted( \
- CHECKED_CAST(_STACK *, const STACK_OF(RSA_additional_prime) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(RSA_additional_prime) *, sk))
#define sk_RSA_additional_prime_set_cmp_func(sk, comp) \
((int (*)(const RSA_additional_prime **a, const RSA_additional_prime **b)) \
@@ -1782,14 +1804,14 @@
#define sk_SSL_COMP_new_null() ((STACK_OF(SSL_COMP) *)sk_new_null())
#define sk_SSL_COMP_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(SSL_COMP) *, sk))
#define sk_SSL_COMP_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk));
#define sk_SSL_COMP_value(sk, i) \
((SSL_COMP *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(SSL_COMP) *, sk), (i)))
#define sk_SSL_COMP_set(sk, i, p) \
((SSL_COMP *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk), (i), \
@@ -1830,13 +1852,13 @@
#define sk_SSL_COMP_dup(sk) \
((STACK_OF(SSL_COMP) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(SSL_COMP) *, sk)))
#define sk_SSL_COMP_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SSL_COMP) *, sk))
#define sk_SSL_COMP_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SSL_COMP) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(SSL_COMP) *, sk))
#define sk_SSL_COMP_set_cmp_func(sk, comp) \
((int (*)(const SSL_COMP **a, const SSL_COMP **b))sk_set_cmp_func( \
@@ -1860,15 +1882,17 @@
#define sk_SSL_CUSTOM_EXTENSION_new_null() \
((STACK_OF(SSL_CUSTOM_EXTENSION) *)sk_new_null())
-#define sk_SSL_CUSTOM_EXTENSION_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(SSL_CUSTOM_EXTENSION) *, sk))
+#define sk_SSL_CUSTOM_EXTENSION_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, \
+ sk))
#define sk_SSL_CUSTOM_EXTENSION_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SSL_CUSTOM_EXTENSION) *, sk));
-#define sk_SSL_CUSTOM_EXTENSION_value(sk, i) \
- ((SSL_CUSTOM_EXTENSION *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, sk), \
+#define sk_SSL_CUSTOM_EXTENSION_value(sk, i) \
+ ((SSL_CUSTOM_EXTENSION *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, \
+ sk), \
(i)))
#define sk_SSL_CUSTOM_EXTENSION_set(sk, i, p) \
@@ -1913,16 +1937,16 @@
((SSL_CUSTOM_EXTENSION *)sk_pop( \
CHECKED_CAST(_STACK *, STACK_OF(SSL_CUSTOM_EXTENSION) *, sk)))
-#define sk_SSL_CUSTOM_EXTENSION_dup(sk) \
- ((STACK_OF(SSL_CUSTOM_EXTENSION) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, sk)))
+#define sk_SSL_CUSTOM_EXTENSION_dup(sk) \
+ ((STACK_OF(SSL_CUSTOM_EXTENSION) *)sk_dup(CHECKED_CAST( \
+ const _STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, sk)))
#define sk_SSL_CUSTOM_EXTENSION_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SSL_CUSTOM_EXTENSION) *, sk))
#define sk_SSL_CUSTOM_EXTENSION_is_sorted(sk) \
- sk_is_sorted( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_CUSTOM_EXTENSION) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(SSL_CUSTOM_EXTENSION) *, sk))
#define sk_SSL_CUSTOM_EXTENSION_set_cmp_func(sk, comp) \
((int (*)(const SSL_CUSTOM_EXTENSION **a, const SSL_CUSTOM_EXTENSION **b)) \
@@ -1954,14 +1978,16 @@
((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_new_null())
#define sk_STACK_OF_X509_NAME_ENTRY_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
#define sk_STACK_OF_X509_NAME_ENTRY_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk));
-#define sk_STACK_OF_X509_NAME_ENTRY_value(sk, i) \
- ((STACK_OF_X509_NAME_ENTRY *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk), \
+#define sk_STACK_OF_X509_NAME_ENTRY_value(sk, i) \
+ ((STACK_OF_X509_NAME_ENTRY *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, \
+ sk), \
(i)))
#define sk_STACK_OF_X509_NAME_ENTRY_set(sk, i, p) \
@@ -2008,16 +2034,16 @@
((STACK_OF_X509_NAME_ENTRY *)sk_pop( \
CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
-#define sk_STACK_OF_X509_NAME_ENTRY_dup(sk) \
- ((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
+#define sk_STACK_OF_X509_NAME_ENTRY_dup(sk) \
+ ((STACK_OF(STACK_OF_X509_NAME_ENTRY) *)sk_dup(CHECKED_CAST( \
+ const _STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk)))
#define sk_STACK_OF_X509_NAME_ENTRY_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
#define sk_STACK_OF_X509_NAME_ENTRY_is_sorted(sk) \
- sk_is_sorted( \
- CHECKED_CAST(_STACK *, const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(STACK_OF_X509_NAME_ENTRY) *, sk))
#define sk_STACK_OF_X509_NAME_ENTRY_set_cmp_func(sk, comp) \
((int (*)(const STACK_OF_X509_NAME_ENTRY **a, \
@@ -2047,14 +2073,14 @@
#define sk_SXNETID_new_null() ((STACK_OF(SXNETID) *)sk_new_null())
#define sk_SXNETID_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(SXNETID) *, sk))
#define sk_SXNETID_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk));
-#define sk_SXNETID_value(sk, i) \
- ((SXNETID *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk), \
- (i)))
+#define sk_SXNETID_value(sk, i) \
+ ((SXNETID *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(SXNETID) *, sk), (i)))
#define sk_SXNETID_set(sk, i, p) \
((SXNETID *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk), (i), \
@@ -2095,13 +2121,13 @@
#define sk_SXNETID_dup(sk) \
((STACK_OF(SXNETID) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(SXNETID) *, sk)))
#define sk_SXNETID_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SXNETID) *, sk))
#define sk_SXNETID_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SXNETID) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(SXNETID) *, sk))
#define sk_SXNETID_set_cmp_func(sk, comp) \
((int (*)(const SXNETID **a, const SXNETID **b))sk_set_cmp_func( \
@@ -2122,12 +2148,14 @@
#define sk_X509_new_null() ((STACK_OF(X509) *)sk_new_null())
-#define sk_X509_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk))
+#define sk_X509_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509) *, sk))
#define sk_X509_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk));
-#define sk_X509_value(sk, i) \
- ((X509 *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk), (i)))
+#define sk_X509_value(sk, i) \
+ ((X509 *)sk_value(CHECKED_CAST(const _STACK *, const STACK_OF(X509) *, sk), \
+ (i)))
#define sk_X509_set(sk, i, p) \
((X509 *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk), (i), \
@@ -2164,13 +2192,14 @@
#define sk_X509_pop(sk) \
((X509 *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk)))
-#define sk_X509_dup(sk) \
- ((STACK_OF(X509) *)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk)))
+#define sk_X509_dup(sk) \
+ ((STACK_OF(X509) *)sk_dup( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509) *, sk)))
#define sk_X509_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509) *, sk))
#define sk_X509_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509) *, sk))
#define sk_X509_set_cmp_func(sk, comp) \
((int (*)(const X509 **a, const X509 **b))sk_set_cmp_func( \
@@ -2195,14 +2224,15 @@
((STACK_OF(X509V3_EXT_METHOD) *)sk_new_null())
#define sk_X509V3_EXT_METHOD_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk))
#define sk_X509V3_EXT_METHOD_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk));
-#define sk_X509V3_EXT_METHOD_value(sk, i) \
- ((X509V3_EXT_METHOD *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk), (i)))
+#define sk_X509V3_EXT_METHOD_value(sk, i) \
+ ((X509V3_EXT_METHOD *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk), \
+ (i)))
#define sk_X509V3_EXT_METHOD_set(sk, i, p) \
((X509V3_EXT_METHOD *)sk_set( \
@@ -2248,13 +2278,14 @@
#define sk_X509V3_EXT_METHOD_dup(sk) \
((STACK_OF(X509V3_EXT_METHOD) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk)))
#define sk_X509V3_EXT_METHOD_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509V3_EXT_METHOD) *, sk))
#define sk_X509V3_EXT_METHOD_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509V3_EXT_METHOD) *, sk))
#define sk_X509V3_EXT_METHOD_set_cmp_func(sk, comp) \
((int (*)(const X509V3_EXT_METHOD **a, const X509V3_EXT_METHOD **b)) \
@@ -2281,14 +2312,14 @@
#define sk_X509_ALGOR_new_null() ((STACK_OF(X509_ALGOR) *)sk_new_null())
#define sk_X509_ALGOR_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_ALGOR) *, sk))
#define sk_X509_ALGOR_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk));
#define sk_X509_ALGOR_value(sk, i) \
((X509_ALGOR *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_ALGOR) *, sk), (i)))
#define sk_X509_ALGOR_set(sk, i, p) \
((X509_ALGOR *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk), \
@@ -2331,13 +2362,13 @@
#define sk_X509_ALGOR_dup(sk) \
((STACK_OF(X509_ALGOR) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_ALGOR) *, sk)))
#define sk_X509_ALGOR_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_ALGOR) *, sk))
#define sk_X509_ALGOR_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_ALGOR) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_ALGOR) *, sk))
#define sk_X509_ALGOR_set_cmp_func(sk, comp) \
((int (*)(const X509_ALGOR **a, const X509_ALGOR **b))sk_set_cmp_func( \
@@ -2362,14 +2393,15 @@
#define sk_X509_ATTRIBUTE_new_null() ((STACK_OF(X509_ATTRIBUTE) *)sk_new_null())
#define sk_X509_ATTRIBUTE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk))
#define sk_X509_ATTRIBUTE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk));
-#define sk_X509_ATTRIBUTE_value(sk, i) \
- ((X509_ATTRIBUTE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk), (i)))
+#define sk_X509_ATTRIBUTE_value(sk, i) \
+ ((X509_ATTRIBUTE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk), \
+ (i)))
#define sk_X509_ATTRIBUTE_set(sk, i, p) \
((X509_ATTRIBUTE *)sk_set( \
@@ -2415,13 +2447,14 @@
#define sk_X509_ATTRIBUTE_dup(sk) \
((STACK_OF(X509_ATTRIBUTE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk)))
#define sk_X509_ATTRIBUTE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_ATTRIBUTE) *, sk))
#define sk_X509_ATTRIBUTE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_ATTRIBUTE) *, sk))
#define sk_X509_ATTRIBUTE_set_cmp_func(sk, comp) \
((int (*)(const X509_ATTRIBUTE **a, const X509_ATTRIBUTE **b)) \
@@ -2446,14 +2479,14 @@
#define sk_X509_CRL_new_null() ((STACK_OF(X509_CRL) *)sk_new_null())
#define sk_X509_CRL_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_CRL) *, sk))
#define sk_X509_CRL_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk));
#define sk_X509_CRL_value(sk, i) \
((X509_CRL *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_CRL) *, sk), (i)))
#define sk_X509_CRL_set(sk, i, p) \
((X509_CRL *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk), (i), \
@@ -2494,13 +2527,13 @@
#define sk_X509_CRL_dup(sk) \
((STACK_OF(X509_CRL) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_CRL) *, sk)))
#define sk_X509_CRL_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_CRL) *, sk))
#define sk_X509_CRL_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_CRL) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_CRL) *, sk))
#define sk_X509_CRL_set_cmp_func(sk, comp) \
((int (*)(const X509_CRL **a, const X509_CRL **b))sk_set_cmp_func( \
@@ -2523,14 +2556,15 @@
#define sk_X509_EXTENSION_new_null() ((STACK_OF(X509_EXTENSION) *)sk_new_null())
#define sk_X509_EXTENSION_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_EXTENSION) *, sk))
#define sk_X509_EXTENSION_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk));
-#define sk_X509_EXTENSION_value(sk, i) \
- ((X509_EXTENSION *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk), (i)))
+#define sk_X509_EXTENSION_value(sk, i) \
+ ((X509_EXTENSION *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_EXTENSION) *, sk), \
+ (i)))
#define sk_X509_EXTENSION_set(sk, i, p) \
((X509_EXTENSION *)sk_set( \
@@ -2576,13 +2610,14 @@
#define sk_X509_EXTENSION_dup(sk) \
((STACK_OF(X509_EXTENSION) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_EXTENSION) *, sk)))
#define sk_X509_EXTENSION_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_EXTENSION) *, sk))
#define sk_X509_EXTENSION_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_EXTENSION) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_EXTENSION) *, sk))
#define sk_X509_EXTENSION_set_cmp_func(sk, comp) \
((int (*)(const X509_EXTENSION **a, const X509_EXTENSION **b)) \
@@ -2608,14 +2643,14 @@
#define sk_X509_INFO_new_null() ((STACK_OF(X509_INFO) *)sk_new_null())
#define sk_X509_INFO_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_INFO) *, sk))
#define sk_X509_INFO_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk));
#define sk_X509_INFO_value(sk, i) \
((X509_INFO *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_INFO) *, sk), (i)))
#define sk_X509_INFO_set(sk, i, p) \
((X509_INFO *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk), (i), \
@@ -2658,13 +2693,13 @@
#define sk_X509_INFO_dup(sk) \
((STACK_OF(X509_INFO) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_INFO) *, sk)))
#define sk_X509_INFO_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_INFO) *, sk))
#define sk_X509_INFO_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_INFO) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_INFO) *, sk))
#define sk_X509_INFO_set_cmp_func(sk, comp) \
((int (*)(const X509_INFO **a, const X509_INFO **b))sk_set_cmp_func( \
@@ -2687,14 +2722,14 @@
#define sk_X509_LOOKUP_new_null() ((STACK_OF(X509_LOOKUP) *)sk_new_null())
#define sk_X509_LOOKUP_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_LOOKUP) *, sk))
#define sk_X509_LOOKUP_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk));
#define sk_X509_LOOKUP_value(sk, i) \
((X509_LOOKUP *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_LOOKUP) *, sk), (i)))
#define sk_X509_LOOKUP_set(sk, i, p) \
((X509_LOOKUP *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk), \
@@ -2737,13 +2772,13 @@
#define sk_X509_LOOKUP_dup(sk) \
((STACK_OF(X509_LOOKUP) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_LOOKUP) *, sk)))
#define sk_X509_LOOKUP_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_LOOKUP) *, sk))
#define sk_X509_LOOKUP_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_LOOKUP) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_LOOKUP) *, sk))
#define sk_X509_LOOKUP_set_cmp_func(sk, comp) \
((int (*)(const X509_LOOKUP **a, const X509_LOOKUP **b))sk_set_cmp_func( \
@@ -2768,14 +2803,14 @@
#define sk_X509_NAME_new_null() ((STACK_OF(X509_NAME) *)sk_new_null())
#define sk_X509_NAME_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME) *, sk))
#define sk_X509_NAME_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk));
#define sk_X509_NAME_value(sk, i) \
((X509_NAME *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME) *, sk), (i)))
#define sk_X509_NAME_set(sk, i, p) \
((X509_NAME *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk), (i), \
@@ -2818,13 +2853,13 @@
#define sk_X509_NAME_dup(sk) \
((STACK_OF(X509_NAME) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME) *, sk)))
#define sk_X509_NAME_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME) *, sk))
#define sk_X509_NAME_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME) *, sk))
#define sk_X509_NAME_set_cmp_func(sk, comp) \
((int (*)(const X509_NAME **a, const X509_NAME **b))sk_set_cmp_func( \
@@ -2848,14 +2883,15 @@
((STACK_OF(X509_NAME_ENTRY) *)sk_new_null())
#define sk_X509_NAME_ENTRY_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk))
#define sk_X509_NAME_ENTRY_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk));
-#define sk_X509_NAME_ENTRY_value(sk, i) \
- ((X509_NAME_ENTRY *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk), (i)))
+#define sk_X509_NAME_ENTRY_value(sk, i) \
+ ((X509_NAME_ENTRY *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk), \
+ (i)))
#define sk_X509_NAME_ENTRY_set(sk, i, p) \
((X509_NAME_ENTRY *)sk_set( \
@@ -2901,13 +2937,14 @@
#define sk_X509_NAME_ENTRY_dup(sk) \
((STACK_OF(X509_NAME_ENTRY) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk)))
#define sk_X509_NAME_ENTRY_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_NAME_ENTRY) *, sk))
#define sk_X509_NAME_ENTRY_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_NAME_ENTRY) *, sk))
#define sk_X509_NAME_ENTRY_set_cmp_func(sk, comp) \
((int (*)(const X509_NAME_ENTRY **a, const X509_NAME_ENTRY **b)) \
@@ -2933,14 +2970,14 @@
#define sk_X509_OBJECT_new_null() ((STACK_OF(X509_OBJECT) *)sk_new_null())
#define sk_X509_OBJECT_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_OBJECT) *, sk))
#define sk_X509_OBJECT_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk));
#define sk_X509_OBJECT_value(sk, i) \
((X509_OBJECT *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_OBJECT) *, sk), (i)))
#define sk_X509_OBJECT_set(sk, i, p) \
((X509_OBJECT *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk), \
@@ -2983,13 +3020,13 @@
#define sk_X509_OBJECT_dup(sk) \
((STACK_OF(X509_OBJECT) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_OBJECT) *, sk)))
#define sk_X509_OBJECT_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_OBJECT) *, sk))
#define sk_X509_OBJECT_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_OBJECT) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_OBJECT) *, sk))
#define sk_X509_OBJECT_set_cmp_func(sk, comp) \
((int (*)(const X509_OBJECT **a, const X509_OBJECT **b))sk_set_cmp_func( \
@@ -3015,14 +3052,15 @@
((STACK_OF(X509_POLICY_DATA) *)sk_new_null())
#define sk_X509_POLICY_DATA_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_DATA) *, sk))
#define sk_X509_POLICY_DATA_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk));
-#define sk_X509_POLICY_DATA_value(sk, i) \
- ((X509_POLICY_DATA *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk), (i)))
+#define sk_X509_POLICY_DATA_value(sk, i) \
+ ((X509_POLICY_DATA *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_DATA) *, sk), \
+ (i)))
#define sk_X509_POLICY_DATA_set(sk, i, p) \
((X509_POLICY_DATA *)sk_set( \
@@ -3068,13 +3106,14 @@
#define sk_X509_POLICY_DATA_dup(sk) \
((STACK_OF(X509_POLICY_DATA) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_DATA) *, sk)))
#define sk_X509_POLICY_DATA_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_DATA) *, sk))
#define sk_X509_POLICY_DATA_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_DATA) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_DATA) *, sk))
#define sk_X509_POLICY_DATA_set_cmp_func(sk, comp) \
((int (*)(const X509_POLICY_DATA **a, const X509_POLICY_DATA **b)) \
@@ -3102,14 +3141,15 @@
((STACK_OF(X509_POLICY_NODE) *)sk_new_null())
#define sk_X509_POLICY_NODE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_NODE) *, sk))
#define sk_X509_POLICY_NODE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk));
-#define sk_X509_POLICY_NODE_value(sk, i) \
- ((X509_POLICY_NODE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk), (i)))
+#define sk_X509_POLICY_NODE_value(sk, i) \
+ ((X509_POLICY_NODE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_NODE) *, sk), \
+ (i)))
#define sk_X509_POLICY_NODE_set(sk, i, p) \
((X509_POLICY_NODE *)sk_set( \
@@ -3155,13 +3195,14 @@
#define sk_X509_POLICY_NODE_dup(sk) \
((STACK_OF(X509_POLICY_NODE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_NODE) *, sk)))
#define sk_X509_POLICY_NODE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_POLICY_NODE) *, sk))
#define sk_X509_POLICY_NODE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_POLICY_NODE) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_POLICY_NODE) *, sk))
#define sk_X509_POLICY_NODE_set_cmp_func(sk, comp) \
((int (*)(const X509_POLICY_NODE **a, const X509_POLICY_NODE **b)) \
@@ -3188,14 +3229,14 @@
#define sk_X509_PURPOSE_new_null() ((STACK_OF(X509_PURPOSE) *)sk_new_null())
#define sk_X509_PURPOSE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_PURPOSE) *, sk))
#define sk_X509_PURPOSE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk));
#define sk_X509_PURPOSE_value(sk, i) \
((X509_PURPOSE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_PURPOSE) *, sk), (i)))
#define sk_X509_PURPOSE_set(sk, i, p) \
((X509_PURPOSE *)sk_set( \
@@ -3240,13 +3281,13 @@
#define sk_X509_PURPOSE_dup(sk) \
((STACK_OF(X509_PURPOSE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_PURPOSE) *, sk)))
#define sk_X509_PURPOSE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_PURPOSE) *, sk))
#define sk_X509_PURPOSE_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_PURPOSE) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_PURPOSE) *, sk))
#define sk_X509_PURPOSE_set_cmp_func(sk, comp) \
((int (*)(const X509_PURPOSE **a, const X509_PURPOSE **b))sk_set_cmp_func( \
@@ -3271,14 +3312,14 @@
#define sk_X509_REVOKED_new_null() ((STACK_OF(X509_REVOKED) *)sk_new_null())
#define sk_X509_REVOKED_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_REVOKED) *, sk))
#define sk_X509_REVOKED_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk));
#define sk_X509_REVOKED_value(sk, i) \
((X509_REVOKED *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_REVOKED) *, sk), (i)))
#define sk_X509_REVOKED_set(sk, i, p) \
((X509_REVOKED *)sk_set( \
@@ -3323,13 +3364,13 @@
#define sk_X509_REVOKED_dup(sk) \
((STACK_OF(X509_REVOKED) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_REVOKED) *, sk)))
#define sk_X509_REVOKED_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_REVOKED) *, sk))
#define sk_X509_REVOKED_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_REVOKED) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_REVOKED) *, sk))
#define sk_X509_REVOKED_set_cmp_func(sk, comp) \
((int (*)(const X509_REVOKED **a, const X509_REVOKED **b))sk_set_cmp_func( \
@@ -3354,14 +3395,14 @@
#define sk_X509_TRUST_new_null() ((STACK_OF(X509_TRUST) *)sk_new_null())
#define sk_X509_TRUST_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_TRUST) *, sk))
#define sk_X509_TRUST_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk));
#define sk_X509_TRUST_value(sk, i) \
((X509_TRUST *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_TRUST) *, sk), (i)))
#define sk_X509_TRUST_set(sk, i, p) \
((X509_TRUST *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk), \
@@ -3404,13 +3445,13 @@
#define sk_X509_TRUST_dup(sk) \
((STACK_OF(X509_TRUST) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_TRUST) *, sk)))
#define sk_X509_TRUST_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_TRUST) *, sk))
#define sk_X509_TRUST_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_TRUST) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(X509_TRUST) *, sk))
#define sk_X509_TRUST_set_cmp_func(sk, comp) \
((int (*)(const X509_TRUST **a, const X509_TRUST **b))sk_set_cmp_func( \
@@ -3437,14 +3478,15 @@
((STACK_OF(X509_VERIFY_PARAM) *)sk_new_null())
#define sk_X509_VERIFY_PARAM_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk))
#define sk_X509_VERIFY_PARAM_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk));
-#define sk_X509_VERIFY_PARAM_value(sk, i) \
- ((X509_VERIFY_PARAM *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk), (i)))
+#define sk_X509_VERIFY_PARAM_value(sk, i) \
+ ((X509_VERIFY_PARAM *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk), \
+ (i)))
#define sk_X509_VERIFY_PARAM_set(sk, i, p) \
((X509_VERIFY_PARAM *)sk_set( \
@@ -3490,13 +3532,14 @@
#define sk_X509_VERIFY_PARAM_dup(sk) \
((STACK_OF(X509_VERIFY_PARAM) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk)))
#define sk_X509_VERIFY_PARAM_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(X509_VERIFY_PARAM) *, sk))
#define sk_X509_VERIFY_PARAM_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(X509_VERIFY_PARAM) *, sk))
#define sk_X509_VERIFY_PARAM_set_cmp_func(sk, comp) \
((int (*)(const X509_VERIFY_PARAM **a, const X509_VERIFY_PARAM **b)) \
@@ -3515,21 +3558,23 @@
free_func)))
/* void */
-#define sk_void_new(comp) \
- ((STACK_OF(void)*)sk_new(CHECKED_CAST( \
+#define sk_void_new(comp) \
+ ((STACK_OF(void) *)sk_new(CHECKED_CAST( \
stack_cmp_func, int (*)(const void **a, const void **b), comp)))
-#define sk_void_new_null() ((STACK_OF(void)*)sk_new_null())
+#define sk_void_new_null() ((STACK_OF(void) *)sk_new_null())
-#define sk_void_num(sk) sk_num(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk))
+#define sk_void_num(sk) \
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(void) *, sk))
#define sk_void_zero(sk) sk_zero(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk));
-#define sk_void_value(sk, i) \
- ((void *)sk_value(CHECKED_CAST(_STACK *, const STACK_OF(void) *, sk), (i)))
+#define sk_void_value(sk, i) \
+ ((void *)sk_value(CHECKED_CAST(const _STACK *, const STACK_OF(void) *, sk), \
+ (i)))
-#define sk_void_set(sk, i, p) \
- ((void *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), (i), \
+#define sk_void_set(sk, i, p) \
+ ((void *)sk_set(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk), (i), \
CHECKED_CAST(void *, void *, p)))
#define sk_void_free(sk) sk_free(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk))
@@ -3543,10 +3588,10 @@
CHECKED_CAST(void *, void *, p), (where))
#define sk_void_delete(sk, where) \
- ((void *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), (where)))
+ ((void *)sk_delete(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk), (where)))
-#define sk_void_delete_ptr(sk, p) \
- ((void *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+#define sk_void_delete_ptr(sk, p) \
+ ((void *)sk_delete_ptr(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk), \
CHECKED_CAST(void *, void *, p)))
#define sk_void_find(sk, out_index, p) \
@@ -3554,31 +3599,32 @@
CHECKED_CAST(void *, void *, p))
#define sk_void_shift(sk) \
- ((void *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk)))
+ ((void *)sk_shift(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk)))
#define sk_void_push(sk, p) \
sk_push(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk), \
CHECKED_CAST(void *, void *, p))
#define sk_void_pop(sk) \
- ((void *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(void)*, sk)))
+ ((void *)sk_pop(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk)))
-#define sk_void_dup(sk) \
- ((STACK_OF(void)*)sk_dup(CHECKED_CAST(_STACK *, const STACK_OF(void) *, sk)))
+#define sk_void_dup(sk) \
+ ((STACK_OF(void) *)sk_dup( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(void) *, sk)))
#define sk_void_sort(sk) sk_sort(CHECKED_CAST(_STACK *, STACK_OF(void) *, sk))
#define sk_void_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(void) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(void) *, sk))
#define sk_void_set_cmp_func(sk, comp) \
((int (*)(const void **a, const void **b))sk_set_cmp_func( \
- CHECKED_CAST(_STACK *, STACK_OF(void)*, sk), \
+ CHECKED_CAST(_STACK *, STACK_OF(void) *, sk), \
CHECKED_CAST(stack_cmp_func, int (*)(const void **a, const void **b), \
comp)))
#define sk_void_deep_copy(sk, copy_func, free_func) \
- ((STACK_OF(void)*)sk_deep_copy( \
+ ((STACK_OF(void) *)sk_deep_copy( \
CHECKED_CAST(const _STACK *, const STACK_OF(void) *, sk), \
CHECKED_CAST(void *(*)(void *), void *(*)(void *), copy_func), \
CHECKED_CAST(void (*)(void *), void (*)(void *), free_func)))
@@ -3594,14 +3640,16 @@
((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_new_null())
#define sk_SRTP_PROTECTION_PROFILE_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
#define sk_SRTP_PROTECTION_PROFILE_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk));
-#define sk_SRTP_PROTECTION_PROFILE_value(sk, i) \
- ((const SRTP_PROTECTION_PROFILE *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk), \
+#define sk_SRTP_PROTECTION_PROFILE_value(sk, i) \
+ ((const SRTP_PROTECTION_PROFILE *)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, \
+ sk), \
(i)))
#define sk_SRTP_PROTECTION_PROFILE_set(sk, i, p) \
@@ -3649,16 +3697,16 @@
((const SRTP_PROTECTION_PROFILE *)sk_pop( \
CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
-#define sk_SRTP_PROTECTION_PROFILE_dup(sk) \
- ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
+#define sk_SRTP_PROTECTION_PROFILE_dup(sk) \
+ ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_dup(CHECKED_CAST( \
+ const _STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk)))
#define sk_SRTP_PROTECTION_PROFILE_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
#define sk_SRTP_PROTECTION_PROFILE_is_sorted(sk) \
- sk_is_sorted( \
- CHECKED_CAST(_STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, \
+ const STACK_OF(SRTP_PROTECTION_PROFILE) *, sk))
#define sk_SRTP_PROTECTION_PROFILE_set_cmp_func(sk, comp) \
((int (*)(const SRTP_PROTECTION_PROFILE **a, \
@@ -3670,14 +3718,15 @@
const SRTP_PROTECTION_PROFILE **b), \
comp)))
-#define sk_SRTP_PROTECTION_PROFILE_deep_copy(sk, copy_func, free_func) \
- ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_deep_copy( \
- CHECKED_CAST(const _STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, \
- sk), \
- CHECKED_CAST(void *(*)(void *), const SRTP_PROTECTION_PROFILE *(*)( \
- const SRTP_PROTECTION_PROFILE *), \
- copy_func), \
- CHECKED_CAST(void (*)(void *), \
+#define sk_SRTP_PROTECTION_PROFILE_deep_copy(sk, copy_func, free_func) \
+ ((STACK_OF(SRTP_PROTECTION_PROFILE) *)sk_deep_copy( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(SRTP_PROTECTION_PROFILE) *, \
+ sk), \
+ CHECKED_CAST( \
+ void *(*)(void *), \
+ const SRTP_PROTECTION_PROFILE *(*)(const SRTP_PROTECTION_PROFILE *), \
+ copy_func), \
+ CHECKED_CAST(void (*)(void *), \
void (*)(const SRTP_PROTECTION_PROFILE *), free_func)))
/* SSL_CIPHER */
@@ -3689,14 +3738,14 @@
#define sk_SSL_CIPHER_new_null() ((STACK_OF(SSL_CIPHER) *)sk_new_null())
#define sk_SSL_CIPHER_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CIPHER) *, sk))
#define sk_SSL_CIPHER_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk));
#define sk_SSL_CIPHER_value(sk, i) \
((const SSL_CIPHER *)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CIPHER) *, sk), (i)))
#define sk_SSL_CIPHER_set(sk, i, p) \
((const SSL_CIPHER *)sk_set( \
@@ -3742,13 +3791,13 @@
#define sk_SSL_CIPHER_dup(sk) \
((STACK_OF(SSL_CIPHER) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CIPHER) *, sk)))
#define sk_SSL_CIPHER_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(SSL_CIPHER) *, sk))
#define sk_SSL_CIPHER_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(SSL_CIPHER) *, sk))
+ sk_is_sorted(CHECKED_CAST(const _STACK *, const STACK_OF(SSL_CIPHER) *, sk))
#define sk_SSL_CIPHER_set_cmp_func(sk, comp) \
((int (*)(const SSL_CIPHER **a, const SSL_CIPHER **b))sk_set_cmp_func( \
@@ -3774,14 +3823,15 @@
#define sk_OPENSSL_STRING_new_null() ((STACK_OF(OPENSSL_STRING) *)sk_new_null())
#define sk_OPENSSL_STRING_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_STRING) *, sk))
#define sk_OPENSSL_STRING_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk));
-#define sk_OPENSSL_STRING_value(sk, i) \
- ((OPENSSL_STRING)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk), (i)))
+#define sk_OPENSSL_STRING_value(sk, i) \
+ ((OPENSSL_STRING)sk_value( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_STRING) *, sk), \
+ (i)))
#define sk_OPENSSL_STRING_set(sk, i, p) \
((OPENSSL_STRING)sk_set( \
@@ -3827,13 +3877,14 @@
#define sk_OPENSSL_STRING_dup(sk) \
((STACK_OF(OPENSSL_STRING) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_STRING) *, sk)))
#define sk_OPENSSL_STRING_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_STRING) *, sk))
#define sk_OPENSSL_STRING_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_STRING) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_STRING) *, sk))
#define sk_OPENSSL_STRING_set_cmp_func(sk, comp) \
((int (*)(const OPENSSL_STRING **a, const OPENSSL_STRING **b)) \
@@ -3859,14 +3910,14 @@
#define sk_OPENSSL_BLOCK_new_null() ((STACK_OF(OPENSSL_BLOCK) *)sk_new_null())
#define sk_OPENSSL_BLOCK_num(sk) \
- sk_num(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk))
+ sk_num(CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk))
#define sk_OPENSSL_BLOCK_zero(sk) \
sk_zero(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk));
#define sk_OPENSSL_BLOCK_value(sk, i) \
((OPENSSL_BLOCK)sk_value( \
- CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk), (i)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk), (i)))
#define sk_OPENSSL_BLOCK_set(sk, i, p) \
((OPENSSL_BLOCK)sk_set( \
@@ -3911,13 +3962,14 @@
#define sk_OPENSSL_BLOCK_dup(sk) \
((STACK_OF(OPENSSL_BLOCK) *)sk_dup( \
- CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk)))
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk)))
#define sk_OPENSSL_BLOCK_sort(sk) \
sk_sort(CHECKED_CAST(_STACK *, STACK_OF(OPENSSL_BLOCK) *, sk))
#define sk_OPENSSL_BLOCK_is_sorted(sk) \
- sk_is_sorted(CHECKED_CAST(_STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk))
+ sk_is_sorted( \
+ CHECKED_CAST(const _STACK *, const STACK_OF(OPENSSL_BLOCK) *, sk))
#define sk_OPENSSL_BLOCK_set_cmp_func(sk, comp) \
((int (*)(const OPENSSL_BLOCK **a, const OPENSSL_BLOCK **b))sk_set_cmp_func( \
diff --git a/src/include/openssl/thread.h b/src/include/openssl/thread.h
index ac4ced0..02539e8 100644
--- a/src/include/openssl/thread.h
+++ b/src/include/openssl/thread.h
@@ -73,10 +73,9 @@
#elif defined(OPENSSL_WINDOWS)
/* CRYPTO_MUTEX can appear in public header files so we really don't want to
* pull in windows.h. It's statically asserted that this structure is large
- * enough to contain a Windows CRITICAL_SECTION by thread_win.c. */
+ * enough to contain a Windows SRWLOCK by thread_win.c. */
typedef union crypto_mutex_st {
- double alignment;
- uint8_t padding[4*sizeof(void*) + 2*sizeof(int)];
+ void *handle;
} CRYPTO_MUTEX;
#elif defined(__MACH__) && defined(__APPLE__)
typedef pthread_rwlock_t CRYPTO_MUTEX;
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index e0f1399..6ed9fa9 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -196,8 +196,10 @@
/* ExtensionType values from RFC6091 */
#define TLSEXT_TYPE_cert_type 9
+/* ExtensionType values from draft-ietf-tls-tls13-latest */
+#define TLSEXT_TYPE_supported_groups 10
+
/* ExtensionType values from RFC4492 */
-#define TLSEXT_TYPE_elliptic_curves 10
#define TLSEXT_TYPE_ec_point_formats 11
/* ExtensionType value from RFC5054 */
@@ -439,6 +441,12 @@
#define TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305 \
TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+/* CECPQ1 ciphersuites. These are specific to BoringSSL and not standard. */
+#define TLS1_CK_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256 0x030016B7
+#define TLS1_CK_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0x030016B8
+#define TLS1_CK_CECPQ1_RSA_WITH_AES_256_GCM_SHA384 0x030016B9
+#define TLS1_CK_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384 0x030016BA
+
/* XXX
* Inconsistency alert:
* The OpenSSL names of ciphers with ephemeral DH here include the string
@@ -619,6 +627,17 @@
#define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 \
TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+/* CECPQ1 ciphersuites. These are specific to BoringSSL and not standard. */
+#define TLS1_TXT_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256 \
+ "CECPQ1-RSA-CHACHA20-POLY1305-SHA256"
+#define TLS1_TXT_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256 \
+ "CECPQ1-ECDSA-CHACHA20-POLY1305-SHA256"
+#define TLS1_TXT_CECPQ1_RSA_WITH_AES_256_GCM_SHA384 \
+ "CECPQ1-RSA-AES256-GCM-SHA384"
+#define TLS1_TXT_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384 \
+ "CECPQ1-ECDSA-AES256-GCM-SHA384"
+
+
#define TLS_CT_RSA_SIGN 1
#define TLS_CT_DSS_SIGN 2
#define TLS_CT_RSA_FIXED_DH 3
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index cc95a70..8d8784b 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -249,12 +249,12 @@
/* TODO(davidben): What is this code doing and do we need it? */
if (ssl->d1->mtu < dtls1_min_mtu() &&
!(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
- long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+ long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
ssl->d1->mtu = (unsigned)mtu;
} else {
ssl->d1->mtu = kDefaultMTU;
- BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+ BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
}
}
@@ -274,7 +274,7 @@
}
ret -= overhead;
- size_t pending = BIO_wpending(SSL_get_wbio(ssl));
+ size_t pending = BIO_wpending(ssl->wbio);
if (ret <= pending) {
return 0;
}
@@ -290,7 +290,7 @@
/* During the handshake, wbio is buffered to pack messages together. Flush the
* buffer if the ChangeCipherSpec would not fit in a packet. */
if (dtls1_max_record_size(ssl) == 0) {
- int ret = BIO_flush(SSL_get_wbio(ssl));
+ int ret = BIO_flush(ssl->wbio);
if (ret <= 0) {
ssl->rwstate = SSL_WRITING;
return ret;
@@ -339,13 +339,13 @@
/* During the handshake, wbio is buffered to pack messages together. Flush
* the buffer if there isn't enough room to make progress. */
if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
- int flush_ret = BIO_flush(SSL_get_wbio(ssl));
+ int flush_ret = BIO_flush(ssl->wbio);
if (flush_ret <= 0) {
ssl->rwstate = SSL_WRITING;
ret = flush_ret;
goto err;
}
- assert(BIO_wpending(SSL_get_wbio(ssl)) == 0);
+ assert(BIO_wpending(ssl->wbio) == 0);
}
size_t todo = dtls1_max_record_size(ssl);
@@ -483,17 +483,6 @@
return frag;
}
-/* dtls1_max_handshake_message_len returns the maximum number of bytes
- * permitted in a DTLS handshake message for |ssl|. The minimum is 16KB, but may
- * be greater if the maximum certificate list size requires it. */
-static size_t dtls1_max_handshake_message_len(const SSL *ssl) {
- size_t max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
- if (max_len < ssl->max_cert_list) {
- return ssl->max_cert_list;
- }
- return max_len;
-}
-
/* dtls1_process_fragment reads a handshake fragment and processes it. It
* returns one if a fragment was successfully processed and 0 or -1 on error. */
static int dtls1_process_fragment(SSL *ssl) {
@@ -521,7 +510,7 @@
const size_t msg_len = msg_hdr.msg_len;
if (frag_off > msg_len || frag_off + frag_len < frag_off ||
frag_off + frag_len > msg_len ||
- msg_len > dtls1_max_handshake_message_len(ssl) ||
+ msg_len > ssl_max_handshake_message_len(ssl) ||
frag_len > ssl->s3->rrec.length) {
OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -567,9 +556,9 @@
}
/* dtls1_get_message reads a handshake message of message type |msg_type| (any
- * if |msg_type| == -1), maximum acceptable body length |max|. Read an entire
- * handshake message. Handshake messages arrive in fragments. */
-long dtls1_get_message(SSL *ssl, int st1, int stn, int msg_type, long max,
+ * if |msg_type| == -1). Read an entire handshake message. Handshake messages
+ * arrive in fragments. */
+long dtls1_get_message(SSL *ssl, int msg_type,
enum ssl_hash_message_t hash_message, int *ok) {
pitem *item = NULL;
hm_fragment *frag = NULL;
@@ -589,8 +578,9 @@
goto f_err;
}
*ok = 1;
+ assert(ssl->init_buf->length >= DTLS1_HM_HEADER_LENGTH);
ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
- ssl->init_num = (int)ssl->s3->tmp.message_size;
+ ssl->init_num = (int)ssl->init_buf->length - DTLS1_HM_HEADER_LENGTH;
return ssl->init_num;
}
@@ -610,17 +600,11 @@
assert(ssl->d1->handshake_read_seq == frag->msg_header.seq);
assert(frag->reassembly == NULL);
- if (frag->msg_header.msg_len > (size_t)max) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
- goto err;
- }
-
/* Reconstruct the assembled message. */
- size_t len;
CBB cbb;
CBB_zero(&cbb);
- if (!BUF_MEM_grow(ssl->init_buf, (size_t)frag->msg_header.msg_len +
- DTLS1_HM_HEADER_LENGTH) ||
+ if (!BUF_MEM_reserve(ssl->init_buf, (size_t)frag->msg_header.msg_len +
+ DTLS1_HM_HEADER_LENGTH) ||
!CBB_init_fixed(&cbb, (uint8_t *)ssl->init_buf->data,
ssl->init_buf->max) ||
!CBB_add_u8(&cbb, frag->msg_header.type) ||
@@ -629,19 +613,19 @@
!CBB_add_u24(&cbb, 0 /* frag_off */) ||
!CBB_add_u24(&cbb, frag->msg_header.msg_len) ||
!CBB_add_bytes(&cbb, frag->fragment, frag->msg_header.msg_len) ||
- !CBB_finish(&cbb, NULL, &len)) {
+ !CBB_finish(&cbb, NULL, &ssl->init_buf->length)) {
CBB_cleanup(&cbb);
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- assert(len == (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
+ assert(ssl->init_buf->length ==
+ (size_t)frag->msg_header.msg_len + DTLS1_HM_HEADER_LENGTH);
ssl->d1->handshake_read_seq++;
/* TODO(davidben): This function has a lot of implicit outputs. Simplify the
* |ssl_get_message| API. */
ssl->s3->tmp.message_type = frag->msg_header.type;
- ssl->s3->tmp.message_size = frag->msg_header.msg_len;
ssl->init_msg = (uint8_t *)ssl->init_buf->data + DTLS1_HM_HEADER_LENGTH;
ssl->init_num = frag->msg_header.msg_len;
@@ -662,7 +646,6 @@
pitem_free(item);
dtls1_hm_fragment_free(frag);
- ssl->state = stn;
*ok = 1;
return ssl->init_num;
@@ -689,7 +672,7 @@
if (!SSL_in_init(ssl)) {
/* done, no need to send a retransmit */
- BIO_set_flags(SSL_get_rbio(ssl), BIO_FLAGS_READ);
+ BIO_set_flags(ssl->rbio, BIO_FLAGS_READ);
return code;
}
@@ -737,25 +720,39 @@
ret = dtls1_do_handshake_write(ssl, use_epoch);
}
- /* TODO(davidben): Check return value? */
- (void)BIO_flush(SSL_get_wbio(ssl));
return ret;
}
-
int dtls1_retransmit_buffered_messages(SSL *ssl) {
- pqueue sent = ssl->d1->sent_messages;
- piterator iter = pqueue_iterator(sent);
- pitem *item;
+ /* Ensure we are packing handshake messages. */
+ const int was_buffered = ssl_is_wbio_buffered(ssl);
+ assert(was_buffered == SSL_in_init(ssl));
+ if (!was_buffered && !ssl_init_wbio_buffer(ssl)) {
+ return -1;
+ }
+ assert(ssl_is_wbio_buffered(ssl));
+ int ret = -1;
+ piterator iter = pqueue_iterator(ssl->d1->sent_messages);
+ pitem *item;
for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) {
hm_fragment *frag = (hm_fragment *)item->data;
if (dtls1_retransmit_message(ssl, frag) <= 0) {
- return -1;
+ goto err;
}
}
- return 1;
+ ret = BIO_flush(ssl->wbio);
+ if (ret <= 0) {
+ ssl->rwstate = SSL_WRITING;
+ goto err;
+ }
+
+err:
+ if (!was_buffered) {
+ ssl_free_wbio_buffer(ssl);
+ }
+ return ret;
}
/* dtls1_buffer_change_cipher_spec adds a ChangeCipherSpec to the current
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
index 2fc9094..19ad1f8 100644
--- a/src/ssl/d1_clnt.c
+++ b/src/ssl/d1_clnt.c
@@ -161,30 +161,27 @@
if (ssl->init_buf == NULL) {
buf = BUF_MEM_new();
if (buf == NULL ||
- !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+ !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
ret = -1;
goto end;
}
ssl->init_buf = buf;
buf = NULL;
}
+ ssl->init_num = 0;
- if (!ssl_init_wbio_buffer(ssl, 0)) {
+ if (!ssl_init_wbio_buffer(ssl)) {
ret = -1;
goto end;
}
- /* don't push the buffering BIO quite yet */
-
ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
- ssl->init_num = 0;
ssl->d1->send_cookie = 0;
ssl->hit = 0;
break;
case SSL3_ST_CW_CLNT_HELLO_A:
case SSL3_ST_CW_CLNT_HELLO_B:
- ssl->shutdown = 0;
dtls1_start_timer(ssl);
ret = ssl3_send_client_hello(ssl);
if (ret <= 0) {
@@ -192,22 +189,14 @@
}
if (ssl->d1->send_cookie) {
- ssl->state = SSL3_ST_CW_FLUSH;
ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
} else {
- ssl->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
+ ssl->s3->tmp.next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
}
-
- ssl->init_num = 0;
- /* turn on buffering for the next lot of output */
- if (ssl->bbio != ssl->wbio) {
- ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
- }
-
+ ssl->state = SSL3_ST_CW_FLUSH;
break;
case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
- case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
ret = dtls1_get_hello_verify(ssl);
if (ret <= 0) {
goto end;
@@ -219,11 +208,9 @@
} else {
ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_CR_SRVR_HELLO_A:
- case SSL3_ST_CR_SRVR_HELLO_B:
ret = ssl3_get_server_hello(ssl);
if (ret <= 0) {
goto end;
@@ -238,12 +225,10 @@
} else {
ssl->state = SSL3_ST_CR_CERT_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CERT_A:
- case SSL3_ST_CR_CERT_B:
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ret = ssl3_get_server_certificate(ssl);
if (ret <= 0) {
goto end;
@@ -257,7 +242,6 @@
skip = 1;
ssl->state = SSL3_ST_CR_KEY_EXCH_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_VERIFY_SERVER_CERT:
@@ -267,31 +251,29 @@
}
ssl->state = SSL3_ST_CR_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_KEY_EXCH_A:
- case SSL3_ST_CR_KEY_EXCH_B:
ret = ssl3_get_server_key_exchange(ssl);
if (ret <= 0) {
goto end;
}
- ssl->state = SSL3_ST_CR_CERT_REQ_A;
- ssl->init_num = 0;
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+ ssl->state = SSL3_ST_CR_CERT_REQ_A;
+ } else {
+ ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+ }
break;
case SSL3_ST_CR_CERT_REQ_A:
- case SSL3_ST_CR_CERT_REQ_B:
ret = ssl3_get_certificate_request(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_CR_SRVR_DONE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_SRVR_DONE_A:
- case SSL3_ST_CR_SRVR_DONE_B:
ret = ssl3_get_server_done(ssl);
if (ret <= 0) {
goto end;
@@ -302,7 +284,6 @@
} else {
ssl->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A;
}
- ssl->init_num = 0;
ssl->state = ssl->s3->tmp.next_state;
break;
@@ -316,7 +297,6 @@
goto end;
}
ssl->state = SSL3_ST_CW_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CW_KEY_EXCH_A:
@@ -333,8 +313,6 @@
} else {
ssl->state = SSL3_ST_CW_CHANGE_A;
}
-
- ssl->init_num = 0;
break;
case SSL3_ST_CW_CERT_VRFY_A:
@@ -346,7 +324,6 @@
goto end;
}
ssl->state = SSL3_ST_CW_CHANGE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CW_CHANGE_A:
@@ -361,7 +338,6 @@
}
ssl->state = SSL3_ST_CW_FINISHED_A;
- ssl->init_num = 0;
if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
ret = -1;
@@ -392,27 +368,22 @@
ssl->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
}
}
- ssl->init_num = 0;
break;
case SSL3_ST_CR_SESSION_TICKET_A:
- case SSL3_ST_CR_SESSION_TICKET_B:
ret = ssl3_get_new_session_ticket(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_CR_CHANGE;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CERT_STATUS_A:
- case SSL3_ST_CR_CERT_STATUS_B:
ret = ssl3_get_cert_status(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CHANGE:
@@ -429,9 +400,7 @@
break;
case SSL3_ST_CR_FINISHED_A:
- case SSL3_ST_CR_FINISHED_B:
- ret =
- ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
+ ret = ssl3_get_finished(ssl);
if (ret <= 0) {
goto end;
}
@@ -443,7 +412,6 @@
ssl->state = SSL_ST_OK;
}
- ssl->init_num = 0;
break;
case SSL3_ST_CW_FLUSH:
@@ -462,6 +430,10 @@
/* Remove write buffering now. */
ssl_free_wbio_buffer(ssl);
+ /* |init_buf| cannot be released because post-handshake retransmit
+ * relies on that buffer being available as scratch space.
+ *
+ * TODO(davidben): Fix this. */
ssl->init_num = 0;
ssl->s3->initial_handshake_complete = 1;
@@ -510,11 +482,7 @@
CBS hello_verify_request, cookie;
uint16_t server_version;
- n = ssl->method->ssl_get_message(
- ssl, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A,
- DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B, -1,
- /* Use the same maximum size as ssl3_get_server_hello. */
- 20000, ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
if (!ok) {
return n;
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index e48fbf1..c1c9f91 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -157,18 +157,27 @@
return cipher->algorithm_enc != SSL_RC4 && cipher->algorithm_enc != SSL_eNULL;
}
+void DTLSv1_set_initial_timeout_duration(SSL *ssl, unsigned int duration_ms) {
+ ssl->initial_timeout_duration_ms = duration_ms;
+}
+
void dtls1_start_timer(SSL *ssl) {
- /* If timer is not set, initialize duration with 1 second */
+ /* If timer is not set, initialize duration (by default, 1 second) */
if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) {
- ssl->d1->timeout_duration = 1;
+ ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms;
}
/* Set timeout to current time */
get_current_time(ssl, &ssl->d1->next_timeout);
/* Add duration to current time */
- ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration;
- BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
+ ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration_ms / 1000;
+ ssl->d1->next_timeout.tv_usec += (ssl->d1->timeout_duration_ms % 1000) * 1000;
+ if (ssl->d1->next_timeout.tv_usec >= 1000000) {
+ ssl->d1->next_timeout.tv_sec++;
+ ssl->d1->next_timeout.tv_usec -= 1000000;
+ }
+ BIO_ctrl(ssl->rbio, BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
&ssl->d1->next_timeout);
}
@@ -230,9 +239,9 @@
}
void dtls1_double_timeout(SSL *ssl) {
- ssl->d1->timeout_duration *= 2;
- if (ssl->d1->timeout_duration > 60) {
- ssl->d1->timeout_duration = 60;
+ ssl->d1->timeout_duration_ms *= 2;
+ if (ssl->d1->timeout_duration_ms > 60000) {
+ ssl->d1->timeout_duration_ms = 60000;
}
dtls1_start_timer(ssl);
}
@@ -241,8 +250,8 @@
/* Reset everything */
ssl->d1->num_timeouts = 0;
memset(&ssl->d1->next_timeout, 0, sizeof(struct timeval));
- ssl->d1->timeout_duration = 1;
- BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
+ ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms;
+ BIO_ctrl(ssl->rbio, BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
&ssl->d1->next_timeout);
/* Clear retransmission buffer */
dtls1_clear_record_buffer(ssl);
@@ -254,8 +263,7 @@
/* Reduce MTU after 2 unsuccessful retransmissions */
if (ssl->d1->num_timeouts > DTLS1_MTU_TIMEOUTS &&
!(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
- long mtu = BIO_ctrl(SSL_get_wbio(ssl), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0,
- NULL);
+ long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);
if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
ssl->d1->mtu = (unsigned)mtu;
}
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 4690486..34eeddb 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -131,6 +131,16 @@
* more data is needed. */
static int dtls1_get_record(SSL *ssl) {
again:
+ switch (ssl->s3->recv_shutdown) {
+ case ssl_shutdown_none:
+ break;
+ case ssl_shutdown_fatal_alert:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+ return -1;
+ case ssl_shutdown_close_notify:
+ return 0;
+ }
+
/* Read a new packet if there is no unconsumed one. */
if (ssl_read_buffer_len(ssl) == 0) {
int ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
@@ -217,7 +227,9 @@
* alerts also aren't delivered reliably, so we may even time out because the
* peer never received our close_notify. Report to the caller that the channel
* has fully shut down. */
- ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+ if (ssl->s3->recv_shutdown == ssl_shutdown_none) {
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+ }
}
/* Return up to 'len' payload bytes received in 'type' records.
@@ -273,26 +285,7 @@
/* we now have a packet which can be read and processed */
- /* If the other end has shut down, throw anything we read away (even in
- * 'peek' mode) */
- if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
- rr->length = 0;
- return 0;
- }
-
-
if (type == rr->type) {
- /* Make sure that we are not getting application data when we
- * are doing a handshake for the first time. */
- if (SSL_in_init(ssl) && (type == SSL3_RT_APPLICATION_DATA) &&
- (ssl->s3->aead_read_ctx == NULL)) {
- /* TODO(davidben): Is this check redundant with the handshake_func
- * check? */
- al = SSL_AD_UNEXPECTED_MESSAGE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE);
- goto f_err;
- }
-
/* Discard empty records. */
if (rr->length == 0) {
goto start;
@@ -354,8 +347,7 @@
if (alert_level == SSL3_AL_WARNING) {
if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
- ssl->s3->clean_shutdown = 1;
- ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
return 0;
}
} else if (alert_level == SSL3_AL_FATAL) {
@@ -364,7 +356,7 @@
OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr);
ERR_add_error_data(2, "SSL alert number ", tmp);
- ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+ ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
SSL_CTX_remove_session(ssl->ctx, ssl->session);
return 0;
} else {
diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c
index e7b1607..324bff7 100644
--- a/src/ssl/d1_srtp.c
+++ b/src/ssl/d1_srtp.c
@@ -124,7 +124,7 @@
#include "internal.h"
-const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = {
+static const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = {
{
"SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80,
},
@@ -188,6 +188,7 @@
}
} while (col);
+ sk_SRTP_PROTECTION_PROFILE_free(*out);
*out = profiles;
return 1;
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
index d353281..9913e73 100644
--- a/src/ssl/d1_srvr.c
+++ b/src/ssl/d1_srvr.c
@@ -158,7 +158,7 @@
if (ssl->init_buf == NULL) {
buf = BUF_MEM_new();
- if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+ if (buf == NULL || !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
ret = -1;
goto end;
}
@@ -168,7 +168,7 @@
ssl->init_num = 0;
- if (!ssl_init_wbio_buffer(ssl, 1)) {
+ if (!ssl_init_wbio_buffer(ssl)) {
ret = -1;
goto end;
}
@@ -185,15 +185,12 @@
case SSL3_ST_SR_CLNT_HELLO_A:
case SSL3_ST_SR_CLNT_HELLO_B:
case SSL3_ST_SR_CLNT_HELLO_C:
- case SSL3_ST_SR_CLNT_HELLO_D:
- ssl->shutdown = 0;
ret = ssl3_get_client_hello(ssl);
if (ret <= 0) {
goto end;
}
dtls1_stop_timer(ssl);
ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SRVR_HELLO_A:
@@ -213,12 +210,11 @@
} else {
ssl->state = SSL3_ST_SW_CERT_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_A:
case SSL3_ST_SW_CERT_B:
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
dtls1_start_timer(ssl);
ret = ssl3_send_server_certificate(ssl);
if (ret <= 0) {
@@ -233,7 +229,6 @@
skip = 1;
ssl->state = SSL3_ST_SW_KEY_EXCH_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_STATUS_A:
@@ -243,7 +238,6 @@
goto end;
}
ssl->state = SSL3_ST_SW_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_KEY_EXCH_A:
@@ -271,7 +265,6 @@
}
ssl->state = SSL3_ST_SW_CERT_REQ_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_REQ_A:
@@ -286,7 +279,6 @@
skip = 1;
}
ssl->state = SSL3_ST_SW_SRVR_DONE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SRVR_DONE_A:
@@ -298,7 +290,6 @@
}
ssl->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
ssl->state = SSL3_ST_SW_FLUSH;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_FLUSH:
@@ -311,36 +302,30 @@
break;
case SSL3_ST_SR_CERT_A:
- case SSL3_ST_SR_CERT_B:
if (ssl->s3->tmp.cert_request) {
ret = ssl3_get_client_certificate(ssl);
if (ret <= 0) {
goto end;
}
}
- ssl->init_num = 0;
ssl->state = SSL3_ST_SR_KEY_EXCH_A;
break;
case SSL3_ST_SR_KEY_EXCH_A:
case SSL3_ST_SR_KEY_EXCH_B:
- case SSL3_ST_SR_KEY_EXCH_C:
ret = ssl3_get_client_key_exchange(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_SR_CERT_VRFY_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SR_CERT_VRFY_A:
- case SSL3_ST_SR_CERT_VRFY_B:
ret = ssl3_get_cert_verify(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_SR_CHANGE;
- ssl->init_num = 0;
break;
case SSL3_ST_SR_CHANGE:
@@ -358,9 +343,7 @@
break;
case SSL3_ST_SR_FINISHED_A:
- case SSL3_ST_SR_FINISHED_B:
- ret = ssl3_get_finished(ssl, SSL3_ST_SR_FINISHED_A,
- SSL3_ST_SR_FINISHED_B);
+ ret = ssl3_get_finished(ssl);
if (ret <= 0) {
goto end;
}
@@ -372,7 +355,6 @@
} else {
ssl->state = SSL3_ST_SW_CHANGE_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SESSION_TICKET_A:
@@ -382,7 +364,6 @@
goto end;
}
ssl->state = SSL3_ST_SW_CHANGE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CHANGE_A:
@@ -395,7 +376,6 @@
}
ssl->state = SSL3_ST_SW_FINISHED_A;
- ssl->init_num = 0;
if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
ret = -1;
@@ -416,7 +396,6 @@
} else {
ssl->s3->tmp.next_state = SSL_ST_OK;
}
- ssl->init_num = 0;
break;
case SSL_ST_OK:
@@ -425,6 +404,10 @@
/* remove buffering on output */
ssl_free_wbio_buffer(ssl);
+ /* |init_buf| cannot be released because post-handshake retransmit
+ * relies on that buffer being available as scratch space.
+ *
+ * TODO(davidben): Fix this. */
ssl->init_num = 0;
ssl->s3->initial_handshake_complete = 1;
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 982f304..3b56e78 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -167,6 +167,7 @@
#define SSL_kECDHE 0x00000004L
/* SSL_kPSK is only set for plain PSK, not ECDHE_PSK. */
#define SSL_kPSK 0x00000008L
+#define SSL_kCECPQ1 0x00000010L
/* Bits for |algorithm_auth| (server authentication). */
#define SSL_aRSA 0x00000001L
@@ -174,6 +175,8 @@
/* SSL_aPSK is set for both PSK and ECDHE_PSK. */
#define SSL_aPSK 0x00000004L
+#define SSL_aCERT (SSL_aRSA | SSL_aECDSA)
+
/* Bits for |algorithm_enc| (symmetric encryption). */
#define SSL_3DES 0x00000001L
#define SSL_RC4 0x00000002L
@@ -239,17 +242,16 @@
* server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */
int ssl_cipher_get_key_type(const SSL_CIPHER *cipher);
-/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server
- * public key in the key exchange, sent in a server Certificate message.
- * Otherwise it returns 0. */
-int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher);
+/* ssl_cipher_uses_certificate_auth returns one if |cipher| authenticates the
+ * server and, optionally, the client with a certificate. Otherwise it returns
+ * zero. */
+int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher);
/* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a
* ServerKeyExchange message. Otherwise it returns 0.
*
- * Unlike |ssl_cipher_has_server_public_key|, this function may return zero
- * while still allowing |cipher| an optional ServerKeyExchange. This is the
- * case for plain PSK ciphers. */
+ * This function may return zero while still allowing |cipher| an optional
+ * ServerKeyExchange. This is the case for plain PSK ciphers. */
int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher);
/* ssl_cipher_get_record_split_len, for TLS 1.0 CBC mode ciphers, returns the
@@ -282,6 +284,8 @@
/* omit_version_in_ad is non-zero if the version should be omitted
* in the AEAD's ad parameter. */
char omit_version_in_ad;
+ /* omit_ad is non-zero if the AEAD's ad parameter should be omitted. */
+ char omit_ad;
/* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
* variable nonce rather than prepended. */
char xor_fixed_nonce;
@@ -526,45 +530,66 @@
int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len);
-/* ECDH curves. */
+/* ECDH groups. */
-#define SSL_CURVE_SECP256R1 23
-#define SSL_CURVE_SECP384R1 24
-#define SSL_CURVE_SECP521R1 25
-#define SSL_CURVE_X25519 29
+#define SSL_GROUP_SECP256R1 23
+#define SSL_GROUP_SECP384R1 24
+#define SSL_GROUP_SECP521R1 25
+#define SSL_GROUP_X25519 29
+#define SSL_GROUP_CECPQ1 65165
/* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
* TLS. */
struct ssl_ecdh_method_st {
int nid;
- uint16_t curve_id;
+ uint16_t group_id;
const char name[8];
/* cleanup releases state in |ctx|. */
void (*cleanup)(SSL_ECDH_CTX *ctx);
- /* generate_keypair generates a keypair and writes the public value to
+ /* offer generates a keypair and writes the public value to
* |out_public_key|. It returns one on success and zero on error. */
- int (*generate_keypair)(SSL_ECDH_CTX *ctx, CBB *out_public_key);
+ int (*offer)(SSL_ECDH_CTX *ctx, CBB *out_public_key);
- /* compute_secret performs a key exchange against |peer_key| and, on
- * success, returns one and sets |*out_secret| and |*out_secret_len| to
- * a newly-allocated buffer containing the shared secret. The caller must
- * release this buffer with |OPENSSL_free|. Otherwise, it returns zero and
- * sets |*out_alert| to an alert to send to the peer. */
- int (*compute_secret)(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
- size_t *out_secret_len, uint8_t *out_alert,
- const uint8_t *peer_key, size_t peer_key_len);
+ /* accept performs a key exchange against the |peer_key| generated by |offer|.
+ * On success, it returns one, writes the public value to |out_public_key|,
+ * and sets |*out_secret| and |*out_secret_len| to a newly-allocated buffer
+ * containing the shared secret. The caller must release this buffer with
+ * |OPENSSL_free|. On failure, it returns zero and sets |*out_alert| to an
+ * alert to send to the peer. */
+ int (*accept)(SSL_ECDH_CTX *ctx, CBB *out_public_key, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len);
+
+ /* finish performs a key exchange against the |peer_key| generated by
+ * |accept|. On success, it returns one and sets |*out_secret| and
+ * |*out_secret_len| to a newly-allocated buffer containing the shared
+ * secret. The caller must release this buffer with |OPENSSL_free|. On
+ * failure, it returns zero and sets |*out_alert| to an alert to send to the
+ * peer. */
+ int (*finish)(SSL_ECDH_CTX *ctx, uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len);
+
+ /* get_key initializes |out| with a length-prefixed key from |cbs|. It returns
+ * one on success and zero on error. */
+ int (*get_key)(CBS *cbs, CBS *out);
+
+ /* add_key initializes |out_contents| to receive a key. Typically it will then
+ * be passed to |offer| or |accept|. It returns one on success and zero on
+ * error. */
+ int (*add_key)(CBB *cbb, CBB *out_contents);
} /* SSL_ECDH_METHOD */;
-/* ssl_nid_to_curve_id looks up the curve corresponding to |nid|. On success, it
- * sets |*out_curve_id| to the curve ID and returns one. Otherwise, it returns
+/* ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
+ * sets |*out_group_id| to the group ID and returns one. Otherwise, it returns
* zero. */
-int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid);
+int ssl_nid_to_group_id(uint16_t *out_group_id, int nid);
-/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |curve_id|. It returns one
+/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |group_id|. It returns one
* on success and zero on error. */
-int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id);
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id);
/* SSL_ECDH_CTX_init_for_dhe sets up |ctx| for use with legacy DHE-based ciphers
* where the server specifies a group. It takes ownership of |params|. */
@@ -574,12 +599,31 @@
* call it in the zero state. */
void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx);
-/* The following functions call the corresponding method of
- * |SSL_ECDH_METHOD|. */
-int SSL_ECDH_CTX_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out_public_key);
-int SSL_ECDH_CTX_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
- size_t *out_secret_len, uint8_t *out_alert,
- const uint8_t *peer_key, size_t peer_key_len);
+/* SSL_ECDH_CTX_get_key calls the |get_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out);
+
+/* SSL_ECDH_CTX_add_key calls the |add_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents);
+
+/* SSL_ECDH_CTX_offer calls the |offer| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key);
+
+/* SSL_ECDH_CTX_accept calls the |accept| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+ uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len);
+
+/* SSL_ECDH_CTX_finish the |finish| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len);
+
+/* Handshake messages. */
+
+/* ssl_max_handshake_message_len returns the maximum number of bytes permitted
+ * in a handshake message for |ssl|. */
+size_t ssl_max_handshake_message_len(const SSL *ssl);
/* Transport buffers. */
@@ -828,8 +872,7 @@
void (*ssl_free)(SSL *ssl);
int (*ssl_accept)(SSL *ssl);
int (*ssl_connect)(SSL *ssl);
- long (*ssl_get_message)(SSL *ssl, int header_state, int body_state,
- int msg_type, long max,
+ long (*ssl_get_message)(SSL *ssl, int msg_type,
enum ssl_hash_message_t hash_message, int *ok);
int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek);
int (*ssl_read_change_cipher_spec)(SSL *ssl);
@@ -956,13 +999,12 @@
* timeout. */
struct timeval next_timeout;
- /* Timeout duration */
- unsigned short timeout_duration;
+ /* timeout_duration_ms is the timeout duration in milliseconds. */
+ unsigned timeout_duration_ms;
} DTLS1_STATE;
extern const SSL3_ENC_METHOD TLSv1_enc_data;
extern const SSL3_ENC_METHOD SSLv3_enc_data;
-extern const SRTP_PROTECTION_PROFILE kSRTPProfiles[];
int ssl_clear_bad_session(SSL *ssl);
CERT *ssl_cert_new(void);
@@ -1020,14 +1062,14 @@
int ssl3_send_server_certificate(SSL *ssl);
int ssl3_send_new_session_ticket(SSL *ssl);
int ssl3_send_certificate_status(SSL *ssl);
-int ssl3_get_finished(SSL *ssl, int state_a, int state_b);
+int ssl3_get_finished(SSL *ssl);
int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
void ssl3_cleanup_key_block(SSL *ssl);
int ssl3_do_write(SSL *ssl, int type);
int ssl3_send_alert(SSL *ssl, int level, int desc);
int ssl3_get_req_cert_type(SSL *ssl, uint8_t *p);
-long ssl3_get_message(SSL *ssl, int header_state, int body_state, int msg_type,
- long max, enum ssl_hash_message_t hash_message, int *ok);
+long ssl3_get_message(SSL *ssl, int msg_type,
+ enum ssl_hash_message_t hash_message, int *ok);
/* ssl3_hash_current_message incorporates the current handshake message into the
* handshake hash. It returns one on success and zero on allocation failure. */
@@ -1132,11 +1174,15 @@
int dtls1_connect(SSL *ssl);
void dtls1_free(SSL *ssl);
-long dtls1_get_message(SSL *ssl, int st1, int stn, int mt, long max,
- enum ssl_hash_message_t hash_message, int *ok);
+long dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message,
+ int *ok);
int dtls1_dispatch_alert(SSL *ssl);
-int ssl_init_wbio_buffer(SSL *ssl, int push);
+/* ssl_is_wbio_buffered returns one if |ssl|'s write BIO is buffered and zero
+ * otherwise. */
+int ssl_is_wbio_buffered(const SSL *ssl);
+
+int ssl_init_wbio_buffer(SSL *ssl);
void ssl_free_wbio_buffer(SSL *ssl);
int tls1_change_cipher_state(SSL *ssl, int which);
@@ -1147,21 +1193,21 @@
char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx);
-/* tls1_check_curve_id returns one if |curve_id| is consistent with both our
- * and the peer's curve preferences. Note: if called as the client, only our
+/* tls1_check_group_id returns one if |group_id| is consistent with both our
+ * and the peer's group preferences. Note: if called as the client, only our
* preferences are checked; the peer (the server) does not send preferences. */
-int tls1_check_curve_id(SSL *ssl, uint16_t curve_id);
+int tls1_check_group_id(SSL *ssl, uint16_t group_id);
-/* tls1_get_shared_curve sets |*out_curve_id| to the first preferred shared
- * curve between client and server preferences and returns one. If none may be
+/* tls1_get_shared_group sets |*out_group_id| to the first preferred shared
+ * group between client and server preferences and returns one. If none may be
* found, it returns zero. */
-int tls1_get_shared_curve(SSL *ssl, uint16_t *out_curve_id);
+int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_id);
/* tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves|
- * into a newly allocated array of TLS curve IDs. On success, the function
- * returns one and writes the array to |*out_curve_ids| and its size to
- * |*out_curve_ids_len|. Otherwise, it returns zero. */
-int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len,
+ * into a newly allocated array of TLS group IDs. On success, the function
+ * returns one and writes the array to |*out_group_ids| and its size to
+ * |*out_group_ids_len|. Otherwise, it returns zero. */
+int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
const int *curves, size_t ncurves);
/* tls1_check_ec_cert returns one if |x| is an ECC certificate with curve and
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 5d364ab..d5e304d 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -211,13 +211,13 @@
ssl, !ssl->server, ssl->s3->tmp.peer_finish_md);
}
-int ssl3_get_finished(SSL *ssl, int a, int b) {
+int ssl3_get_finished(SSL *ssl) {
int al, finished_len, ok;
long message_len;
uint8_t *p;
- message_len = ssl->method->ssl_get_message(
- ssl, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE, ssl_dont_hash_message, &ok);
+ message_len = ssl->method->ssl_get_message(ssl, SSL3_MT_FINISHED,
+ ssl_dont_hash_message, &ok);
if (!ok) {
return message_len;
@@ -298,125 +298,122 @@
return ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, l);
}
-/* Obtain handshake message of message type |msg_type| (any if |msg_type| == -1),
- * maximum acceptable body length |max|. The first four bytes (msg_type and
- * length) are read in state |header_state|, the body is read in state
- * |body_state|. */
-long ssl3_get_message(SSL *ssl, int header_state, int body_state, int msg_type,
- long max, enum ssl_hash_message_t hash_message, int *ok) {
- uint8_t *p;
- unsigned long l;
- long n;
- int al;
+size_t ssl_max_handshake_message_len(const SSL *ssl) {
+ /* kMaxMessageLen is the default maximum message size for handshakes which do
+ * not accept peer certificate chains. */
+ static const size_t kMaxMessageLen = 16384;
+
+ if ((!ssl->server || (ssl->verify_mode & SSL_VERIFY_PEER)) &&
+ kMaxMessageLen < ssl->max_cert_list) {
+ return ssl->max_cert_list;
+ }
+ return kMaxMessageLen;
+}
+
+static int extend_handshake_buffer(SSL *ssl, size_t length) {
+ if (!BUF_MEM_reserve(ssl->init_buf, length)) {
+ return -1;
+ }
+ while (ssl->init_buf->length < length) {
+ int ret =
+ ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE,
+ (uint8_t *)ssl->init_buf->data + ssl->init_buf->length,
+ length - ssl->init_buf->length, 0);
+ if (ret <= 0) {
+ return ret;
+ }
+ ssl->init_buf->length += (size_t)ret;
+ }
+ return 1;
+}
+
+/* Obtain handshake message of message type |msg_type| (any if |msg_type| ==
+ * -1). */
+long ssl3_get_message(SSL *ssl, int msg_type,
+ enum ssl_hash_message_t hash_message, int *ok) {
+ *ok = 0;
if (ssl->s3->tmp.reuse_message) {
/* A ssl_dont_hash_message call cannot be combined with reuse_message; the
* ssl_dont_hash_message would have to have been applied to the previous
* call. */
assert(hash_message == ssl_hash_message);
+ assert(ssl->s3->tmp.message_complete);
ssl->s3->tmp.reuse_message = 0;
if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
- goto f_err;
+ return -1;
}
*ok = 1;
- ssl->state = body_state;
+ assert(ssl->init_buf->length >= 4);
ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
- ssl->init_num = (int)ssl->s3->tmp.message_size;
+ ssl->init_num = (int)ssl->init_buf->length - 4;
return ssl->init_num;
}
- p = (uint8_t *)ssl->init_buf->data;
-
- if (ssl->state == header_state) {
- assert(ssl->init_num < 4);
-
- for (;;) {
- while (ssl->init_num < 4) {
- int bytes_read = ssl3_read_bytes(
- ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], 4 - ssl->init_num, 0);
- if (bytes_read <= 0) {
- *ok = 0;
- return bytes_read;
- }
- ssl->init_num += bytes_read;
- }
-
- static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
- if (ssl->server || memcmp(p, kHelloRequest, sizeof(kHelloRequest)) != 0) {
- break;
- }
-
- /* The server may always send 'Hello Request' messages -- we are doing
- * a handshake anyway now, so ignore them if their format is correct.
- * Does not count for 'Finished' MAC. */
- ssl->init_num = 0;
-
- if (ssl->msg_callback) {
- ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, p, 4, ssl,
- ssl->msg_callback_arg);
- }
- }
-
- /* ssl->init_num == 4 */
-
- if (msg_type >= 0 && *p != msg_type) {
- al = SSL_AD_UNEXPECTED_MESSAGE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
- goto f_err;
- }
- ssl->s3->tmp.message_type = *(p++);
-
- n2l3(p, l);
- if (l > (unsigned long)max) {
- al = SSL_AD_ILLEGAL_PARAMETER;
- OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
- goto f_err;
- }
-
- if (l && !BUF_MEM_grow_clean(ssl->init_buf, l + 4)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
- goto err;
- }
- ssl->s3->tmp.message_size = l;
- ssl->state = body_state;
-
- ssl->init_msg = (uint8_t *)ssl->init_buf->data + 4;
- ssl->init_num = 0;
+again:
+ if (ssl->s3->tmp.message_complete) {
+ ssl->s3->tmp.message_complete = 0;
+ ssl->init_buf->length = 0;
}
- /* next state (body_state) */
- p = ssl->init_msg;
- n = ssl->s3->tmp.message_size - ssl->init_num;
- while (n > 0) {
- int bytes_read =
- ssl3_read_bytes(ssl, SSL3_RT_HANDSHAKE, &p[ssl->init_num], n, 0);
- if (bytes_read <= 0) {
- *ok = 0;
- return bytes_read;
- }
- ssl->init_num += bytes_read;
- n -= bytes_read;
+ /* Read the message header, if we haven't yet. */
+ int ret = extend_handshake_buffer(ssl, 4);
+ if (ret <= 0) {
+ return ret;
}
+ /* Parse out the length. Cap it so the peer cannot force us to buffer up to
+ * 2^24 bytes. */
+ const uint8_t *p = (uint8_t *)ssl->init_buf->data;
+ size_t msg_len = (((uint32_t)p[1]) << 16) | (((uint32_t)p[2]) << 8) | p[3];
+ if (msg_len > ssl_max_handshake_message_len(ssl)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+ return -1;
+ }
+
+ /* Read the message body, if we haven't yet. */
+ ret = extend_handshake_buffer(ssl, 4 + msg_len);
+ if (ret <= 0) {
+ return ret;
+ }
+
+ /* We have now received a complete message. */
+ ssl->s3->tmp.message_complete = 1;
+ if (ssl->msg_callback) {
+ ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
+ ssl->init_buf->length, ssl, ssl->msg_callback_arg);
+ }
+
+ static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
+ if (!ssl->server && ssl->init_buf->length == sizeof(kHelloRequest) &&
+ memcmp(kHelloRequest, ssl->init_buf->data, sizeof(kHelloRequest)) == 0) {
+ /* The server may always send 'Hello Request' messages -- we are doing a
+ * handshake anyway now, so ignore them if their format is correct. Does
+ * not count for 'Finished' MAC. */
+ goto again;
+ }
+
+ uint8_t actual_type = ((const uint8_t *)ssl->init_buf->data)[0];
+ if (msg_type >= 0 && actual_type != msg_type) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+ return -1;
+ }
+ ssl->s3->tmp.message_type = actual_type;
+
+ ssl->init_msg = (uint8_t*)ssl->init_buf->data + 4;
+ ssl->init_num = ssl->init_buf->length - 4;
+
/* Feed this message into MAC computation. */
if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
- goto err;
+ return -1;
}
- if (ssl->msg_callback) {
- ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
- (size_t)ssl->init_num + 4, ssl, ssl->msg_callback_arg);
- }
+
*ok = 1;
return ssl->init_num;
-
-f_err:
- ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-
-err:
- *ok = 0;
- return -1;
}
int ssl3_hash_current_message(SSL *ssl) {
diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c
index 6f381cf..2665f15 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/s3_clnt.c
@@ -200,7 +200,7 @@
if (ssl->init_buf == NULL) {
buf = BUF_MEM_new();
if (buf == NULL ||
- !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+ !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
ret = -1;
goto end;
}
@@ -208,14 +208,13 @@
ssl->init_buf = buf;
buf = NULL;
}
+ ssl->init_num = 0;
- if (!ssl_init_wbio_buffer(ssl, 0)) {
+ if (!ssl_init_wbio_buffer(ssl)) {
ret = -1;
goto end;
}
- /* don't push the buffering BIO quite yet */
-
if (!ssl3_init_handshake_buffer(ssl)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ret = -1;
@@ -223,28 +222,19 @@
}
ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CW_CLNT_HELLO_A:
case SSL3_ST_CW_CLNT_HELLO_B:
- ssl->shutdown = 0;
ret = ssl3_send_client_hello(ssl);
if (ret <= 0) {
goto end;
}
- ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
- ssl->init_num = 0;
-
- /* turn on buffering for the next lot of output */
- if (ssl->bbio != ssl->wbio) {
- ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
- }
-
+ ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
+ ssl->state = SSL3_ST_CW_FLUSH;
break;
case SSL3_ST_CR_SRVR_HELLO_A:
- case SSL3_ST_CR_SRVR_HELLO_B:
ret = ssl3_get_server_hello(ssl);
if (ret <= 0) {
goto end;
@@ -259,12 +249,10 @@
} else {
ssl->state = SSL3_ST_CR_CERT_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CERT_A:
- case SSL3_ST_CR_CERT_B:
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ret = ssl3_get_server_certificate(ssl);
if (ret <= 0) {
goto end;
@@ -278,7 +266,6 @@
skip = 1;
ssl->state = SSL3_ST_CR_KEY_EXCH_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_VERIFY_SERVER_CERT:
@@ -288,31 +275,29 @@
}
ssl->state = SSL3_ST_CR_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_KEY_EXCH_A:
- case SSL3_ST_CR_KEY_EXCH_B:
ret = ssl3_get_server_key_exchange(ssl);
if (ret <= 0) {
goto end;
}
- ssl->state = SSL3_ST_CR_CERT_REQ_A;
- ssl->init_num = 0;
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+ ssl->state = SSL3_ST_CR_CERT_REQ_A;
+ } else {
+ ssl->state = SSL3_ST_CR_SRVR_DONE_A;
+ }
break;
case SSL3_ST_CR_CERT_REQ_A:
- case SSL3_ST_CR_CERT_REQ_B:
ret = ssl3_get_certificate_request(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_CR_SRVR_DONE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_SRVR_DONE_A:
- case SSL3_ST_CR_SRVR_DONE_B:
ret = ssl3_get_server_done(ssl);
if (ret <= 0) {
goto end;
@@ -322,7 +307,6 @@
} else {
ssl->state = SSL3_ST_CW_KEY_EXCH_A;
}
- ssl->init_num = 0;
break;
@@ -335,7 +319,6 @@
goto end;
}
ssl->state = SSL3_ST_CW_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CW_KEY_EXCH_A:
@@ -351,8 +334,6 @@
} else {
ssl->state = SSL3_ST_CW_CHANGE_A;
}
-
- ssl->init_num = 0;
break;
case SSL3_ST_CW_CERT_VRFY_A:
@@ -363,7 +344,6 @@
goto end;
}
ssl->state = SSL3_ST_CW_CHANGE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_CW_CHANGE_A:
@@ -381,7 +361,6 @@
if (ssl->s3->next_proto_neg_seen) {
ssl->state = SSL3_ST_CW_NEXT_PROTO_A;
}
- ssl->init_num = 0;
if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
ret = -1;
@@ -448,27 +427,22 @@
}
}
}
- ssl->init_num = 0;
break;
case SSL3_ST_CR_SESSION_TICKET_A:
- case SSL3_ST_CR_SESSION_TICKET_B:
ret = ssl3_get_new_session_ticket(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_CR_CHANGE;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CERT_STATUS_A:
- case SSL3_ST_CR_CERT_STATUS_B:
ret = ssl3_get_cert_status(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
- ssl->init_num = 0;
break;
case SSL3_ST_CR_CHANGE:
@@ -485,9 +459,7 @@
break;
case SSL3_ST_CR_FINISHED_A:
- case SSL3_ST_CR_FINISHED_B:
- ret = ssl3_get_finished(ssl, SSL3_ST_CR_FINISHED_A,
- SSL3_ST_CR_FINISHED_B);
+ ret = ssl3_get_finished(ssl);
if (ret <= 0) {
goto end;
}
@@ -497,7 +469,6 @@
} else {
ssl->state = SSL_ST_OK;
}
- ssl->init_num = 0;
break;
case SSL3_ST_CW_FLUSH:
@@ -528,13 +499,13 @@
BUF_MEM_free(ssl->init_buf);
ssl->init_buf = NULL;
+ ssl->init_num = 0;
/* Remove write buffering now. */
ssl_free_wbio_buffer(ssl);
const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
- ssl->init_num = 0;
ssl->s3->tmp.in_false_start = 0;
ssl->s3->initial_handshake_complete = 1;
@@ -733,10 +704,8 @@
uint16_t server_version, cipher_suite;
uint8_t compression_method;
- n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_SRVR_HELLO_A,
- SSL3_ST_CR_SRVR_HELLO_B, SSL3_MT_SERVER_HELLO,
- 20000, /* ?? */
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO, ssl_hash_message,
+ &ok);
if (!ok) {
uint32_t err = ERR_peek_error();
@@ -866,7 +835,8 @@
/* If doing a full handshake with TLS 1.2, the server may request a client
* certificate which requires hashing the handshake transcript under a
* different hash. Otherwise, the handshake buffer may be released. */
- if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION) {
+ if (ssl->hit || ssl3_protocol_version(ssl) < TLS1_2_VERSION ||
+ !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ssl3_free_handshake_buffer(ssl);
}
@@ -963,9 +933,8 @@
CBS cbs, certificate_list;
const uint8_t *data;
- n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B,
- SSL3_MT_CERTIFICATE, (long)ssl->max_cert_list,
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE, ssl_hash_message,
+ &ok);
if (!ok) {
return n;
@@ -1053,11 +1022,7 @@
EC_KEY *ecdh = NULL;
EC_POINT *srvr_ecpoint = NULL;
- /* use same message size as in ssl3_get_certificate_request() as
- * ServerKeyExchange message may be skipped */
- long n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_CR_KEY_EXCH_A, SSL3_ST_CR_KEY_EXCH_B, -1, ssl->max_cert_list,
- ssl_hash_message, &ok);
+ long n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
if (!ok) {
return n;
}
@@ -1167,26 +1132,27 @@
if (!CBS_stow(&dh_Ys, &ssl->s3->tmp.peer_key, &peer_key_len)) {
goto err;
}
- /* |dh_Ys| has a u16 length prefix, so this fits in a |uint16_t|. */
+ /* |dh_Ys| was initialized with CBS_get_u16_length_prefixed, so peer_key_len
+ * fits in a uint16_t. */
assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
} else if (alg_k & SSL_kECDHE) {
/* Parse the server parameters. */
- uint8_t curve_type;
- uint16_t curve_id;
+ uint8_t group_type;
+ uint16_t group_id;
CBS point;
- if (!CBS_get_u8(&server_key_exchange, &curve_type) ||
- curve_type != NAMED_CURVE_TYPE ||
- !CBS_get_u16(&server_key_exchange, &curve_id) ||
+ if (!CBS_get_u8(&server_key_exchange, &group_type) ||
+ group_type != NAMED_CURVE_TYPE ||
+ !CBS_get_u16(&server_key_exchange, &group_id) ||
!CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
goto f_err;
}
- ssl->session->key_exchange_info = curve_id;
+ ssl->session->key_exchange_info = group_id;
- /* Ensure the curve is consistent with preferences. */
- if (!tls1_check_curve_id(ssl, curve_id)) {
+ /* Ensure the group is consistent with preferences. */
+ if (!tls1_check_group_id(ssl, group_id)) {
al = SSL_AD_ILLEGAL_PARAMETER;
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
goto f_err;
@@ -1194,11 +1160,31 @@
/* Initialize ECDH and save the peer public key for later. */
size_t peer_key_len;
- if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
+ if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, group_id) ||
!CBS_stow(&point, &ssl->s3->tmp.peer_key, &peer_key_len)) {
goto err;
}
- /* |point| has a u8 length prefix, so this fits in a |uint16_t|. */
+ /* |point| was initialized with CBS_get_u8_length_prefixed, so peer_key_len
+ * fits in a uint16_t. */
+ assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
+ ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
+ } else if (alg_k & SSL_kCECPQ1) {
+ if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1)) {
+ goto err;
+ }
+ CBS key;
+ if (!CBS_get_u16_length_prefixed(&server_key_exchange, &key)) {
+ al = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ goto f_err;
+ }
+
+ size_t peer_key_len;
+ if (!CBS_stow(&key, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+ goto err;
+ }
+ /* |key| was initialized with CBS_get_u16_length_prefixed, so peer_key_len
+ * fits in a uint16_t. */
assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
} else if (!(alg_k & SSL_kPSK)) {
@@ -1215,7 +1201,7 @@
CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange));
/* ServerKeyExchange should be signed by the server's public key. */
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
pkey = X509_get_pubkey(ssl->session->peer);
if (pkey == NULL) {
goto err;
@@ -1302,9 +1288,7 @@
X509_NAME *xn = NULL;
STACK_OF(X509_NAME) *ca_sk = NULL;
- long n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_CR_CERT_REQ_A, SSL3_ST_CR_CERT_REQ_B, -1, ssl->max_cert_list,
- ssl_hash_message, &ok);
+ long n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
if (!ok) {
return n;
@@ -1409,9 +1393,8 @@
int ssl3_get_new_session_ticket(SSL *ssl) {
int ok, al;
- long n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_CR_SESSION_TICKET_A, SSL3_ST_CR_SESSION_TICKET_B,
- SSL3_MT_NEWSESSION_TICKET, 16384, ssl_hash_message, &ok);
+ long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
+ ssl_hash_message, &ok);
if (!ok) {
return n;
@@ -1487,9 +1470,7 @@
CBS certificate_status, ocsp_response;
uint8_t status_type;
- n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_CR_CERT_STATUS_A, SSL3_ST_CR_CERT_STATUS_B,
- -1, 16384, ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
if (!ok) {
return n;
@@ -1530,10 +1511,8 @@
int ok;
long n;
- n = ssl->method->ssl_get_message(ssl, SSL3_ST_CR_SRVR_DONE_A,
- SSL3_ST_CR_SRVR_DONE_B, SSL3_MT_SERVER_DONE,
- 30, /* should be very small, like 0 :-) */
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_DONE, ssl_hash_message,
+ &ok);
if (!ok) {
return n;
@@ -1657,31 +1636,24 @@
!CBB_flush(&cbb)) {
goto err;
}
- } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
- /* Generate a keypair and serialize the public half. ECDHE uses a u8 length
- * prefix while DHE uses u16. */
+ } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
+ /* Generate a keypair and serialize the public half. */
CBB child;
- int child_ok;
- if (alg_k & SSL_kECDHE) {
- child_ok = CBB_add_u8_length_prefixed(&cbb, &child);
- } else {
- child_ok = CBB_add_u16_length_prefixed(&cbb, &child);
- }
-
- if (!child_ok ||
- !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child) ||
- !CBB_flush(&cbb)) {
+ if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child)) {
goto err;
}
/* Compute the premaster. */
uint8_t alert;
- if (!SSL_ECDH_CTX_compute_secret(&ssl->s3->tmp.ecdh_ctx, &pms, &pms_len,
- &alert, ssl->s3->tmp.peer_key,
- ssl->s3->tmp.peer_key_len)) {
+ if (!SSL_ECDH_CTX_accept(&ssl->s3->tmp.ecdh_ctx, &child, &pms, &pms_len,
+ &alert, ssl->s3->tmp.peer_key,
+ ssl->s3->tmp.peer_key_len)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
goto err;
}
+ if (!CBB_flush(&cbb)) {
+ goto err;
+ }
/* The key exchange state may now be discarded. */
SSL_ECDH_CTX_cleanup(&ssl->s3->tmp.ecdh_ctx);
@@ -2020,7 +1992,8 @@
!BN_bn2cbb_padded(&child, 32, sig->r) ||
!BN_bn2cbb_padded(&child, 32, sig->s) ||
!CBB_finish(&cbb, NULL, &length) ||
- !ssl_set_handshake_header(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS, length)) {
+ !ssl_set_handshake_header(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
+ length)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
CBB_cleanup(&cbb);
goto err;
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 7df046f..4cf5aa3 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -220,7 +220,7 @@
sk_X509_NAME_pop_free(ssl->s3->tmp.ca_names, X509_NAME_free);
OPENSSL_free(ssl->s3->tmp.certificate_types);
- OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
+ OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
ssl3_free_handshake_buffer(ssl);
ssl3_free_handshake_hash(ssl);
@@ -382,14 +382,14 @@
}
int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) {
- return tls1_set_curves(&ctx->tlsext_ellipticcurvelist,
- &ctx->tlsext_ellipticcurvelist_length, curves,
+ return tls1_set_curves(&ctx->supported_group_list,
+ &ctx->supported_group_list_len, curves,
curves_len);
}
int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
- return tls1_set_curves(&ssl->tlsext_ellipticcurvelist,
- &ssl->tlsext_ellipticcurvelist_length, curves,
+ return tls1_set_curves(&ssl->supported_group_list,
+ &ssl->supported_group_list_len, curves,
curves_len);
}
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index d9c21d4..c54c10b 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -133,6 +133,16 @@
static int ssl3_get_record(SSL *ssl) {
int ret;
again:
+ switch (ssl->s3->recv_shutdown) {
+ case ssl_shutdown_none:
+ break;
+ case ssl_shutdown_fatal_alert:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+ return -1;
+ case ssl_shutdown_close_notify:
+ return 0;
+ }
+
/* Ensure the buffer is large enough to decrypt in-place. */
ret = ssl_read_buffer_extend_to(ssl, ssl_record_prefix_len(ssl));
if (ret <= 0) {
@@ -393,27 +403,9 @@
/* we now have a packet which can be read and processed */
- /* If the other end has shut down, throw anything we read away (even in
- * 'peek' mode) */
- if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
- rr->length = 0;
- return 0;
- }
-
if (type != 0 && type == rr->type) {
ssl->s3->warning_alert_count = 0;
- /* Make sure that we are not getting application data when we are doing a
- * handshake for the first time. */
- if (SSL_in_init(ssl) && type == SSL3_RT_APPLICATION_DATA &&
- ssl->s3->aead_read_ctx == NULL) {
- /* TODO(davidben): Is this check redundant with the handshake_func
- * check? */
- al = SSL_AD_UNEXPECTED_MESSAGE;
- OPENSSL_PUT_ERROR(SSL, SSL_R_APP_DATA_IN_HANDSHAKE);
- goto f_err;
- }
-
/* Discard empty records. */
if (rr->length == 0) {
goto start;
@@ -546,8 +538,7 @@
if (alert_level == SSL3_AL_WARNING) {
if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
- ssl->s3->clean_shutdown = 1;
- ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
return 0;
}
@@ -563,7 +554,7 @@
OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
ERR_add_error_data(2, "SSL alert number ", tmp);
- ssl->shutdown |= SSL_RECEIVED_SHUTDOWN;
+ ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
SSL_CTX_remove_session(ssl->ctx, ssl->session);
return 0;
} else {
@@ -575,7 +566,9 @@
goto start;
}
- if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
+ if (type == 0) {
+ /* This may only occur from read_close_notify. */
+ assert(ssl->s3->send_shutdown == ssl_shutdown_close_notify);
/* close_notify has been sent, so discard all records other than alerts. */
rr->length = 0;
goto start;
@@ -591,9 +584,19 @@
}
int ssl3_send_alert(SSL *ssl, int level, int desc) {
- /* If a fatal one, remove from cache */
- if (level == 2 && ssl->session != NULL) {
- SSL_CTX_remove_session(ssl->ctx, ssl->session);
+ /* It is illegal to send an alert when we've already sent a closing one. */
+ if (ssl->s3->send_shutdown != ssl_shutdown_none) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
+ return -1;
+ }
+
+ if (level == SSL3_AL_FATAL) {
+ if (ssl->session != NULL) {
+ SSL_CTX_remove_session(ssl->ctx, ssl->session);
+ }
+ ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
+ } else if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
+ ssl->s3->send_shutdown = ssl_shutdown_close_notify;
}
ssl->s3->alert_dispatch = 1;
@@ -605,8 +608,7 @@
return ssl->method->ssl_dispatch_alert(ssl);
}
- /* else data is still being written out, we will get written some time in the
- * future */
+ /* The alert will be dispatched later. */
return -1;
}
diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c
index f06ee56..50007eb 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/s3_srvr.c
@@ -208,7 +208,7 @@
if (ssl->init_buf == NULL) {
buf = BUF_MEM_new();
- if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
+ if (!buf || !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
ret = -1;
goto end;
}
@@ -219,7 +219,7 @@
/* Enable a write buffer. This groups handshake messages within a flight
* into a single write. */
- if (!ssl_init_wbio_buffer(ssl, 1)) {
+ if (!ssl_init_wbio_buffer(ssl)) {
ret = -1;
goto end;
}
@@ -257,14 +257,11 @@
case SSL3_ST_SR_CLNT_HELLO_A:
case SSL3_ST_SR_CLNT_HELLO_B:
case SSL3_ST_SR_CLNT_HELLO_C:
- case SSL3_ST_SR_CLNT_HELLO_D:
- ssl->shutdown = 0;
ret = ssl3_get_client_hello(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SRVR_HELLO_A:
@@ -282,12 +279,11 @@
} else {
ssl->state = SSL3_ST_SW_CERT_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_A:
case SSL3_ST_SW_CERT_B:
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ret = ssl3_send_server_certificate(ssl);
if (ret <= 0) {
goto end;
@@ -301,7 +297,6 @@
skip = 1;
ssl->state = SSL3_ST_SW_KEY_EXCH_A;
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_STATUS_A:
@@ -311,7 +306,6 @@
goto end;
}
ssl->state = SSL3_ST_SW_KEY_EXCH_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_KEY_EXCH_A:
@@ -337,7 +331,6 @@
}
ssl->state = SSL3_ST_SW_CERT_REQ_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CERT_REQ_A:
@@ -351,7 +344,6 @@
skip = 1;
}
ssl->state = SSL3_ST_SW_SRVR_DONE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SRVR_DONE_A:
@@ -362,7 +354,6 @@
}
ssl->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
ssl->state = SSL3_ST_SW_FLUSH;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_FLUSH:
@@ -381,37 +372,31 @@
break;
case SSL3_ST_SR_CERT_A:
- case SSL3_ST_SR_CERT_B:
if (ssl->s3->tmp.cert_request) {
ret = ssl3_get_client_certificate(ssl);
if (ret <= 0) {
goto end;
}
}
- ssl->init_num = 0;
ssl->state = SSL3_ST_SR_KEY_EXCH_A;
break;
case SSL3_ST_SR_KEY_EXCH_A:
case SSL3_ST_SR_KEY_EXCH_B:
- case SSL3_ST_SR_KEY_EXCH_C:
ret = ssl3_get_client_key_exchange(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_SR_CERT_VRFY_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SR_CERT_VRFY_A:
- case SSL3_ST_SR_CERT_VRFY_B:
ret = ssl3_get_cert_verify(ssl);
if (ret <= 0) {
goto end;
}
ssl->state = SSL3_ST_SR_CHANGE;
- ssl->init_num = 0;
break;
case SSL3_ST_SR_CHANGE:
@@ -435,12 +420,10 @@
break;
case SSL3_ST_SR_NEXT_PROTO_A:
- case SSL3_ST_SR_NEXT_PROTO_B:
ret = ssl3_get_next_proto(ssl);
if (ret <= 0) {
goto end;
}
- ssl->init_num = 0;
if (ssl->s3->tlsext_channel_id_valid) {
ssl->state = SSL3_ST_SR_CHANNEL_ID_A;
} else {
@@ -449,19 +432,15 @@
break;
case SSL3_ST_SR_CHANNEL_ID_A:
- case SSL3_ST_SR_CHANNEL_ID_B:
ret = ssl3_get_channel_id(ssl);
if (ret <= 0) {
goto end;
}
- ssl->init_num = 0;
ssl->state = SSL3_ST_SR_FINISHED_A;
break;
case SSL3_ST_SR_FINISHED_A:
- case SSL3_ST_SR_FINISHED_B:
- ret = ssl3_get_finished(ssl, SSL3_ST_SR_FINISHED_A,
- SSL3_ST_SR_FINISHED_B);
+ ret = ssl3_get_finished(ssl);
if (ret <= 0) {
goto end;
}
@@ -482,7 +461,6 @@
goto end;
}
}
- ssl->init_num = 0;
break;
case SSL3_ST_SW_SESSION_TICKET_A:
@@ -492,7 +470,6 @@
goto end;
}
ssl->state = SSL3_ST_SW_CHANGE_A;
- ssl->init_num = 0;
break;
case SSL3_ST_SW_CHANGE_A:
@@ -503,7 +480,6 @@
goto end;
}
ssl->state = SSL3_ST_SW_FINISHED_A;
- ssl->init_num = 0;
if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
ret = -1;
@@ -524,7 +500,6 @@
} else {
ssl->s3->tmp.next_state = SSL_ST_OK;
}
- ssl->init_num = 0;
break;
case SSL_ST_OK:
@@ -533,11 +508,11 @@
BUF_MEM_free(ssl->init_buf);
ssl->init_buf = NULL;
+ ssl->init_num = 0;
/* remove buffering on output */
ssl_free_wbio_buffer(ssl);
- ssl->init_num = 0;
/* If we aren't retaining peer certificates then we can discard it
* now. */
@@ -626,7 +601,7 @@
const uint8_t *p;
int ret;
CBS v2_client_hello, cipher_specs, session_id, challenge;
- size_t msg_length, rand_len, len;
+ size_t msg_length, rand_len;
uint8_t msg_type;
uint16_t version, cipher_spec_length, session_id_length, challenge_length;
CBB client_hello, hello_body, cipher_suites;
@@ -731,7 +706,7 @@
/* Add the null compression scheme and finish. */
if (!CBB_add_u8(&hello_body, 1) || !CBB_add_u8(&hello_body, 0) ||
- !CBB_finish(&client_hello, NULL, &len)) {
+ !CBB_finish(&client_hello, NULL, &ssl->init_buf->length)) {
CBB_cleanup(&client_hello);
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
@@ -740,8 +715,7 @@
/* Mark the message for "re"-use by the version-specific method. */
ssl->s3->tmp.reuse_message = 1;
ssl->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO;
- /* The handshake message header is 4 bytes. */
- ssl->s3->tmp.message_size = len - 4;
+ ssl->s3->tmp.message_complete = 1;
/* Consume and discard the V2ClientHello. */
ssl_read_buffer_consume(ssl, 2 + msg_length);
@@ -767,20 +741,17 @@
* SSLv3, even if prompted with TLSv1. */
switch (ssl->state) {
case SSL3_ST_SR_CLNT_HELLO_A:
- case SSL3_ST_SR_CLNT_HELLO_B:
- n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_SR_CLNT_HELLO_A, SSL3_ST_SR_CLNT_HELLO_B,
- SSL3_MT_CLIENT_HELLO, SSL3_RT_MAX_PLAIN_LENGTH,
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_HELLO,
+ ssl_hash_message, &ok);
if (!ok) {
return n;
}
- ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
+ ssl->state = SSL3_ST_SR_CLNT_HELLO_B;
/* fallthrough */
+ case SSL3_ST_SR_CLNT_HELLO_B:
case SSL3_ST_SR_CLNT_HELLO_C:
- case SSL3_ST_SR_CLNT_HELLO_D:
/* We have previously parsed the ClientHello message, and can't call
* ssl_get_message again without hashing the message into the Finished
* digest again. */
@@ -796,9 +767,9 @@
goto f_err;
}
- if (ssl->state == SSL3_ST_SR_CLNT_HELLO_C &&
+ if (ssl->state == SSL3_ST_SR_CLNT_HELLO_B &&
ssl->ctx->select_certificate_cb != NULL) {
- ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
+ ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
switch (ssl->ctx->select_certificate_cb(&early_ctx)) {
case 0:
ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING;
@@ -814,7 +785,7 @@
/* fallthrough */;
}
}
- ssl->state = SSL3_ST_SR_CLNT_HELLO_D;
+ ssl->state = SSL3_ST_SR_CLNT_HELLO_C;
break;
default:
@@ -1053,8 +1024,8 @@
ssl->s3->tlsext_channel_id_valid) {
ssl->s3->tmp.cert_request = 0;
}
- /* Plain PSK forbids Certificate and CertificateRequest. */
- if (ssl->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK) {
+ /* CertificateRequest may only be sent in certificate-based ciphers. */
+ if (!ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ssl->s3->tmp.cert_request = 0;
}
} else {
@@ -1239,25 +1210,31 @@
!CBB_add_u16_length_prefixed(&cbb, &child) ||
!BN_bn2cbb_padded(&child, BN_num_bytes(params->g), params->g) ||
!CBB_add_u16_length_prefixed(&cbb, &child) ||
- !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child)) {
+ !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
goto err;
}
} else if (alg_k & SSL_kECDHE) {
- /* Determine the curve to use. */
- uint16_t curve_id;
- if (!tls1_get_shared_curve(ssl, &curve_id)) {
+ /* Determine the group to use. */
+ uint16_t group_id;
+ if (!tls1_get_shared_group(ssl, &group_id)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
goto err;
}
- ssl->session->key_exchange_info = curve_id;
+ ssl->session->key_exchange_info = group_id;
/* Set up ECDH, generate a key, and emit the public half. */
- if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
+ if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, group_id) ||
!CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
- !CBB_add_u16(&cbb, curve_id) ||
+ !CBB_add_u16(&cbb, group_id) ||
!CBB_add_u8_length_prefixed(&cbb, &child) ||
- !SSL_ECDH_CTX_generate_keypair(&ssl->s3->tmp.ecdh_ctx, &child)) {
+ !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
+ goto err;
+ }
+ } else if (alg_k & SSL_kCECPQ1) {
+ if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, SSL_GROUP_CECPQ1) ||
+ !CBB_add_u16_length_prefixed(&cbb, &child) ||
+ !SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
goto err;
}
} else {
@@ -1272,7 +1249,7 @@
}
/* Add a signature. */
- if (ssl_cipher_has_server_public_key(ssl->s3->tmp.new_cipher)) {
+ if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
if (!ssl_has_private_key(ssl)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
@@ -1449,12 +1426,10 @@
unsigned psk_len = 0;
uint8_t psk[PSK_MAX_PSK_LEN];
- if (ssl->state == SSL3_ST_SR_KEY_EXCH_A ||
- ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
+ if (ssl->state == SSL3_ST_SR_KEY_EXCH_A) {
int ok;
const long n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_SR_KEY_EXCH_A, SSL3_ST_SR_KEY_EXCH_B,
- SSL3_MT_CLIENT_KEY_EXCHANGE, 2048 /* ??? */, ssl_hash_message, &ok);
+ ssl, SSL3_MT_CLIENT_KEY_EXCHANGE, ssl_hash_message, &ok);
if (!ok) {
return n;
}
@@ -1524,7 +1499,7 @@
enum ssl_private_key_result_t decrypt_result;
size_t decrypt_len;
- if (ssl->state == SSL3_ST_SR_KEY_EXCH_B) {
+ if (ssl->state == SSL3_ST_SR_KEY_EXCH_A) {
if (!ssl_has_private_key(ssl) ||
ssl_private_key_type(ssl) != EVP_PKEY_RSA) {
al = SSL_AD_HANDSHAKE_FAILURE;
@@ -1552,7 +1527,7 @@
CBS_data(&encrypted_premaster_secret),
CBS_len(&encrypted_premaster_secret));
} else {
- assert(ssl->state == SSL3_ST_SR_KEY_EXCH_C);
+ assert(ssl->state == SSL3_ST_SR_KEY_EXCH_B);
/* Complete async decrypt. */
decrypt_result = ssl_private_key_decrypt_complete(
ssl, decrypt_buf, &decrypt_len, rsa_size);
@@ -1565,7 +1540,7 @@
goto err;
case ssl_private_key_retry:
ssl->rwstate = SSL_PRIVATE_KEY_OPERATION;
- ssl->state = SSL3_ST_SR_KEY_EXCH_C;
+ ssl->state = SSL3_ST_SR_KEY_EXCH_B;
goto err;
}
@@ -1621,19 +1596,12 @@
OPENSSL_free(decrypt_buf);
decrypt_buf = NULL;
- } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
- /* Parse the ClientKeyExchange. ECDHE uses a u8 length prefix while DHE uses
- * u16. */
+ } else if (alg_k & (SSL_kECDHE|SSL_kDHE|SSL_kCECPQ1)) {
+ /* Parse the ClientKeyExchange. */
CBS peer_key;
- int peer_key_ok;
- if (alg_k & SSL_kECDHE) {
- peer_key_ok = CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key);
- } else {
- peer_key_ok =
- CBS_get_u16_length_prefixed(&client_key_exchange, &peer_key);
- }
-
- if (!peer_key_ok || CBS_len(&client_key_exchange) != 0) {
+ if (!SSL_ECDH_CTX_get_key(&ssl->s3->tmp.ecdh_ctx, &client_key_exchange,
+ &peer_key) ||
+ CBS_len(&client_key_exchange) != 0) {
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
goto f_err;
@@ -1641,9 +1609,9 @@
/* Compute the premaster. */
uint8_t alert;
- if (!SSL_ECDH_CTX_compute_secret(&ssl->s3->tmp.ecdh_ctx, &premaster_secret,
- &premaster_secret_len, &alert,
- CBS_data(&peer_key), CBS_len(&peer_key))) {
+ if (!SSL_ECDH_CTX_finish(&ssl->s3->tmp.ecdh_ctx, &premaster_secret,
+ &premaster_secret_len, &alert, CBS_data(&peer_key),
+ CBS_len(&peer_key))) {
al = alert;
goto f_err;
}
@@ -1734,10 +1702,8 @@
return 1;
}
- n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_SR_CERT_VRFY_A, SSL3_ST_SR_CERT_VRFY_B,
- SSL3_MT_CERTIFICATE_VERIFY, SSL3_RT_MAX_PLAIN_LENGTH,
- ssl_dont_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE_VERIFY,
+ ssl_dont_hash_message, &ok);
if (!ok) {
return n;
@@ -1833,9 +1799,7 @@
int is_first_certificate = 1;
assert(ssl->s3->tmp.cert_request);
- n = ssl->method->ssl_get_message(ssl, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B,
- -1, (long)ssl->max_cert_list,
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
if (!ok) {
return n;
@@ -2023,7 +1987,7 @@
p += placeholder_len;
len = p - ssl_handshake_start(ssl);
- if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
+ if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
goto err;
}
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
@@ -2092,7 +2056,7 @@
/* Skip ticket lifetime hint */
p = ssl_handshake_start(ssl) + 4;
s2n(len - 6, p);
- if (!ssl_set_handshake_header(ssl, SSL3_MT_NEWSESSION_TICKET, len)) {
+ if (!ssl_set_handshake_header(ssl, SSL3_MT_NEW_SESSION_TICKET, len)) {
goto err;
}
ssl->state = SSL3_ST_SW_SESSION_TICKET_B;
@@ -2122,10 +2086,8 @@
return -1;
}
- n = ssl->method->ssl_get_message(ssl, SSL3_ST_SR_NEXT_PROTO_A,
- SSL3_ST_SR_NEXT_PROTO_B, SSL3_MT_NEXT_PROTO,
- 514, /* See the payload format below */
- ssl_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message,
+ &ok);
if (!ok) {
return n;
@@ -2164,10 +2126,8 @@
BIGNUM x, y;
CBS encrypted_extensions, extension;
- n = ssl->method->ssl_get_message(
- ssl, SSL3_ST_SR_CHANNEL_ID_A, SSL3_ST_SR_CHANNEL_ID_B,
- SSL3_MT_ENCRYPTED_EXTENSIONS, 2 + 2 + TLSEXT_CHANNEL_ID_SIZE,
- ssl_dont_hash_message, &ok);
+ n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
+ ssl_dont_hash_message, &ok);
if (!ok) {
return n;
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 4de9d45..1e549ea 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -92,6 +92,15 @@
if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) {
aead_ctx->variable_nonce_included_in_record = 1;
}
+
+ /* The TLS 1.3 construction XORs the fixed nonce into the sequence number
+ * and omits the additional data. */
+ if (version >= TLS1_3_VERSION) {
+ aead_ctx->xor_fixed_nonce = 1;
+ aead_ctx->variable_nonce_len = 8;
+ aead_ctx->variable_nonce_included_in_record = 0;
+ aead_ctx->omit_ad = 1;
+ }
} else {
aead_ctx->variable_nonce_included_in_record = 1;
aead_ctx->random_variable_nonce = 1;
@@ -139,6 +148,10 @@
uint8_t type, uint16_t wire_version,
const uint8_t seqnum[8],
size_t plaintext_len) {
+ if (aead->omit_ad) {
+ return 0;
+ }
+
memcpy(out, seqnum, 8);
size_t len = 8;
out[len++] = type;
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 58ce582..dcee293 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -375,6 +375,52 @@
SSL_HANDSHAKE_MAC_SHA384,
},
+ /* CECPQ1 (combined elliptic curve + post-quantum) suites. */
+
+ /* Cipher 16B7 */
+ {
+ TLS1_TXT_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ TLS1_CK_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ SSL_kCECPQ1,
+ SSL_aRSA,
+ SSL_CHACHA20POLY1305,
+ SSL_AEAD,
+ SSL_HANDSHAKE_MAC_SHA256,
+ },
+
+ /* Cipher 16B8 */
+ {
+ TLS1_TXT_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ TLS1_CK_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ SSL_kCECPQ1,
+ SSL_aECDSA,
+ SSL_CHACHA20POLY1305,
+ SSL_AEAD,
+ SSL_HANDSHAKE_MAC_SHA256,
+ },
+
+ /* Cipher 16B9 */
+ {
+ TLS1_TXT_CECPQ1_RSA_WITH_AES_256_GCM_SHA384,
+ TLS1_CK_CECPQ1_RSA_WITH_AES_256_GCM_SHA384,
+ SSL_kCECPQ1,
+ SSL_aRSA,
+ SSL_AES256GCM,
+ SSL_AEAD,
+ SSL_HANDSHAKE_MAC_SHA384,
+ },
+
+ /* Cipher 16BA */
+ {
+ TLS1_TXT_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384,
+ TLS1_CK_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384,
+ SSL_kCECPQ1,
+ SSL_aECDSA,
+ SSL_AES256GCM,
+ SSL_AEAD,
+ SSL_HANDSHAKE_MAC_SHA384,
+ },
+
/* Cipher C007 */
{
TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA,
@@ -615,6 +661,7 @@
SSL_AEAD,
SSL_HANDSHAKE_MAC_SHA256,
},
+
};
static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]);
@@ -651,8 +698,9 @@
} CIPHER_ALIAS;
static const CIPHER_ALIAS kCipherAliases[] = {
- /* "ALL" doesn't include eNULL (must be specifically enabled) */
- {"ALL", ~0u, ~0u, ~SSL_eNULL, ~0u, 0},
+ /* "ALL" doesn't include eNULL nor kCECPQ1. These must be explicitly
+ * enabled. */
+ {"ALL", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, 0},
/* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */
@@ -667,15 +715,16 @@
{"DH", SSL_kDHE, ~0u, ~0u, ~0u, 0},
{"kECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
+ {"kCECPQ1", SSL_kCECPQ1, ~0u, ~0u, ~0u, 0},
{"kEECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
{"ECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0},
{"kPSK", SSL_kPSK, ~0u, ~0u, ~0u, 0},
/* server authentication aliases */
- {"aRSA", ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
- {"aECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
- {"ECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0},
+ {"aRSA", ~SSL_kCECPQ1, SSL_aRSA, ~SSL_eNULL, ~0u, 0},
+ {"aECDSA", ~SSL_kCECPQ1, SSL_aECDSA, ~0u, ~0u, 0},
+ {"ECDSA", ~SSL_kCECPQ1, SSL_aECDSA, ~0u, ~0u, 0},
{"aPSK", ~0u, SSL_aPSK, ~0u, ~0u, 0},
/* aliases combining key exchange and server authentication */
@@ -690,29 +739,29 @@
{"3DES", ~0u, ~0u, SSL_3DES, ~0u, 0},
{"RC4", ~0u, ~0u, SSL_RC4, ~0u, 0},
{"AES128", ~0u, ~0u, SSL_AES128 | SSL_AES128GCM, ~0u, 0},
- {"AES256", ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0},
- {"AES", ~0u, ~0u, SSL_AES, ~0u, 0},
- {"AESGCM", ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0},
- {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
+ {"AES256", ~SSL_kCECPQ1, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0},
+ {"AES", ~SSL_kCECPQ1, ~0u, SSL_AES, ~0u, 0},
+ {"AESGCM", ~SSL_kCECPQ1, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0},
+ {"CHACHA20", ~SSL_kCECPQ1, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
0},
/* MAC aliases */
{"MD5", ~0u, ~0u, ~0u, SSL_MD5, 0},
{"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
{"SHA", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
- {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0},
- {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0},
+ {"SHA256", ~SSL_kCECPQ1, ~0u, ~0u, SSL_SHA256, 0},
+ {"SHA384", ~SSL_kCECPQ1, ~0u, ~0u, SSL_SHA384, 0},
/* Legacy protocol minimum version aliases. "TLSv1" is intentionally the
* same as "SSLv3". */
- {"SSLv3", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
- {"TLSv1", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
- {"TLSv1.2", ~0u, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
+ {"SSLv3", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+ {"TLSv1", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION},
+ {"TLSv1.2", ~SSL_kCECPQ1, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION},
/* Legacy strength classes. */
{"MEDIUM", ~0u, ~0u, SSL_RC4, ~0u, 0},
- {"HIGH", ~0u, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
- {"FIPS", ~0u, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
+ {"HIGH", ~SSL_kCECPQ1, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
+ {"FIPS", ~SSL_kCECPQ1, ~0u, ~(SSL_eNULL|SSL_RC4), ~0u, 0},
};
static const size_t kCipherAliasesLen =
@@ -1404,6 +1453,7 @@
/* Everything else being equal, prefer ECDHE_ECDSA then ECDHE_RSA over other
* key exchange mechanisms */
+
ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, 0, CIPHER_ADD, -1,
0, &head, &tail);
ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1623,6 +1673,10 @@
return (cipher->algorithm_mkey & SSL_kECDHE) != 0;
}
+int SSL_CIPHER_is_CECPQ1(const SSL_CIPHER *cipher) {
+ return (cipher->algorithm_mkey & SSL_kCECPQ1) != 0;
+}
+
uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) {
if (cipher->algorithm_prf != SSL_HANDSHAKE_MAC_DEFAULT) {
/* Cipher suites before TLS 1.2 use the default PRF, while all those added
@@ -1672,6 +1726,17 @@
return "UNKNOWN";
}
+ case SSL_kCECPQ1:
+ switch (cipher->algorithm_auth) {
+ case SSL_aECDSA:
+ return "CECPQ1_ECDSA";
+ case SSL_aRSA:
+ return "CECPQ1_RSA";
+ default:
+ assert(0);
+ return "UNKNOWN";
+ }
+
case SSL_kPSK:
assert(cipher->algorithm_auth == SSL_aPSK);
return "PSK";
@@ -1826,6 +1891,10 @@
kx = "ECDH";
break;
+ case SSL_kCECPQ1:
+ kx = "CECPQ1";
+ break;
+
case SSL_kPSK:
kx = "PSK";
break;
@@ -1957,20 +2026,15 @@
return EVP_PKEY_NONE;
}
-int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher) {
- /* PSK-authenticated ciphers do not use a certificate. (RSA_PSK is not
- * supported.) */
- if (cipher->algorithm_auth & SSL_aPSK) {
- return 0;
- }
-
- /* All other ciphers include it. */
- return 1;
+int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher) {
+ return (cipher->algorithm_auth & SSL_aCERT) != 0;
}
int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) {
/* Ephemeral Diffie-Hellman key exchanges require a ServerKeyExchange. */
- if (cipher->algorithm_mkey & SSL_kDHE || cipher->algorithm_mkey & SSL_kECDHE) {
+ if (cipher->algorithm_mkey & SSL_kDHE ||
+ cipher->algorithm_mkey & SSL_kECDHE ||
+ cipher->algorithm_mkey & SSL_kCECPQ1) {
return 1;
}
diff --git a/src/ssl/ssl_ecdh.c b/src/ssl/ssl_ecdh.c
index d48c93f..1236cd3 100644
--- a/src/ssl/ssl_ecdh.c
+++ b/src/ssl/ssl_ecdh.c
@@ -23,6 +23,7 @@
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/mem.h>
+#include <openssl/newhope.h>
#include <openssl/nid.h>
#include "internal.h"
@@ -35,7 +36,7 @@
BN_clear_free(private_key);
}
-static int ssl_ec_point_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_ec_point_offer(SSL_ECDH_CTX *ctx, CBB *out) {
assert(ctx->data == NULL);
BIGNUM *private_key = BN_new();
if (private_key == NULL) {
@@ -84,12 +85,9 @@
return ret;
}
-static int ssl_ec_point_compute_secret(SSL_ECDH_CTX *ctx,
- uint8_t **out_secret,
- size_t *out_secret_len,
- uint8_t *out_alert,
- const uint8_t *peer_key,
- size_t peer_key_len) {
+static int ssl_ec_point_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
BIGNUM *private_key = (BIGNUM *)ctx->data;
assert(private_key != NULL);
*out_alert = SSL_AD_INTERNAL_ERROR;
@@ -150,6 +148,18 @@
return ret;
}
+static int ssl_ec_point_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+ uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ if (!ssl_ec_point_offer(ctx, out_public_key) ||
+ !ssl_ec_point_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+ peer_key_len)) {
+ return 0;
+ }
+ return 1;
+}
/* X25119 implementation. */
@@ -161,7 +171,7 @@
OPENSSL_free(ctx->data);
}
-static int ssl_x25519_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_x25519_offer(SSL_ECDH_CTX *ctx, CBB *out) {
assert(ctx->data == NULL);
ctx->data = OPENSSL_malloc(32);
@@ -174,10 +184,9 @@
return CBB_add_bytes(out, public_key, sizeof(public_key));
}
-static int ssl_x25519_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
- size_t *out_secret_len, uint8_t *out_alert,
- const uint8_t *peer_key,
- size_t peer_key_len) {
+static int ssl_x25519_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
assert(ctx->data != NULL);
*out_alert = SSL_AD_INTERNAL_ERROR;
@@ -199,6 +208,166 @@
return 1;
}
+static int ssl_x25519_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+ uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ if (!ssl_x25519_offer(ctx, out_public_key) ||
+ !ssl_x25519_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+ peer_key_len)) {
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Combined X25119 + New Hope (post-quantum) implementation. */
+
+typedef struct {
+ uint8_t x25519_key[32];
+ NEWHOPE_POLY *newhope_sk;
+} cecpq1_data;
+
+#define CECPQ1_OFFERMSG_LENGTH (32 + NEWHOPE_OFFERMSG_LENGTH)
+#define CECPQ1_ACCEPTMSG_LENGTH (32 + NEWHOPE_ACCEPTMSG_LENGTH)
+#define CECPQ1_SECRET_LENGTH (32 + SHA256_DIGEST_LENGTH)
+
+static void ssl_cecpq1_cleanup(SSL_ECDH_CTX *ctx) {
+ if (ctx->data == NULL) {
+ return;
+ }
+ cecpq1_data *data = ctx->data;
+ NEWHOPE_POLY_free(data->newhope_sk);
+ OPENSSL_cleanse(data, sizeof(cecpq1_data));
+ OPENSSL_free(data);
+}
+
+static int ssl_cecpq1_offer(SSL_ECDH_CTX *ctx, CBB *out) {
+ assert(ctx->data == NULL);
+ cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+ if (data == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ ctx->data = data;
+ data->newhope_sk = NEWHOPE_POLY_new();
+ if (data->newhope_sk == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ uint8_t x25519_public_key[32];
+ X25519_keypair(x25519_public_key, data->x25519_key);
+
+ uint8_t newhope_offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ NEWHOPE_offer(newhope_offermsg, data->newhope_sk);
+
+ if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
+ !CBB_add_bytes(out, newhope_offermsg, sizeof(newhope_offermsg))) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ssl_cecpq1_accept(SSL_ECDH_CTX *ctx, CBB *cbb, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
+ if (peer_key_len != CECPQ1_OFFERMSG_LENGTH) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ assert(ctx->data == NULL);
+ cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+ if (data == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ data->newhope_sk = NULL;
+ ctx->data = data;
+
+ uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+ if (secret == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ /* Generate message to server, and secret key, at once. */
+
+ uint8_t x25519_public_key[32];
+ X25519_keypair(x25519_public_key, data->x25519_key);
+ if (!X25519(secret, data->x25519_key, peer_key)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ goto err;
+ }
+
+ uint8_t newhope_acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
+ if (!NEWHOPE_accept(secret + 32, newhope_acceptmsg, peer_key + 32,
+ NEWHOPE_OFFERMSG_LENGTH)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ goto err;
+ }
+
+ if (!CBB_add_bytes(cbb, x25519_public_key, sizeof(x25519_public_key)) ||
+ !CBB_add_bytes(cbb, newhope_acceptmsg, sizeof(newhope_acceptmsg))) {
+ goto err;
+ }
+
+ *out_secret = secret;
+ *out_secret_len = CECPQ1_SECRET_LENGTH;
+ return 1;
+
+ err:
+ OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+ OPENSSL_free(secret);
+ return 0;
+}
+
+static int ssl_cecpq1_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
+ if (peer_key_len != CECPQ1_ACCEPTMSG_LENGTH) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ assert(ctx->data != NULL);
+ cecpq1_data *data = ctx->data;
+
+ uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+ if (secret == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (!X25519(secret, data->x25519_key, peer_key)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ goto err;
+ }
+
+ if (!NEWHOPE_finish(secret + 32, data->newhope_sk, peer_key + 32,
+ NEWHOPE_ACCEPTMSG_LENGTH)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ goto err;
+ }
+
+ *out_secret = secret;
+ *out_secret_len = CECPQ1_SECRET_LENGTH;
+ return 1;
+
+ err:
+ OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+ OPENSSL_free(secret);
+ return 0;
+}
+
/* Legacy DHE-based implementation. */
@@ -206,7 +375,7 @@
DH_free((DH *)ctx->data);
}
-static int ssl_dhe_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out) {
+static int ssl_dhe_offer(SSL_ECDH_CTX *ctx, CBB *out) {
DH *dh = (DH *)ctx->data;
/* The group must have been initialized already, but not the key. */
assert(dh != NULL);
@@ -218,10 +387,9 @@
BN_bn2cbb_padded(out, BN_num_bytes(dh->p), dh->pub_key);
}
-static int ssl_dhe_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
- size_t *out_secret_len, uint8_t *out_alert,
- const uint8_t *peer_key,
- size_t peer_key_len) {
+static int ssl_dhe_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
DH *dh = (DH *)ctx->data;
assert(dh != NULL);
assert(dh->priv_key != NULL);
@@ -257,53 +425,91 @@
return 0;
}
+static int ssl_dhe_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+ uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ if (!ssl_dhe_offer(ctx, out_public_key) ||
+ !ssl_dhe_finish(ctx, out_secret, out_secret_len, out_alert, peer_key,
+ peer_key_len)) {
+ return 0;
+ }
+ return 1;
+}
+
static const SSL_ECDH_METHOD kDHEMethod = {
NID_undef, 0, "",
ssl_dhe_cleanup,
- ssl_dhe_generate_keypair,
- ssl_dhe_compute_secret,
+ ssl_dhe_offer,
+ ssl_dhe_accept,
+ ssl_dhe_finish,
+ CBS_get_u16_length_prefixed,
+ CBB_add_u16_length_prefixed,
};
-
static const SSL_ECDH_METHOD kMethods[] = {
{
NID_X9_62_prime256v1,
- SSL_CURVE_SECP256R1,
+ SSL_GROUP_SECP256R1,
"P-256",
ssl_ec_point_cleanup,
- ssl_ec_point_generate_keypair,
- ssl_ec_point_compute_secret,
+ ssl_ec_point_offer,
+ ssl_ec_point_accept,
+ ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_secp384r1,
- SSL_CURVE_SECP384R1,
+ SSL_GROUP_SECP384R1,
"P-384",
ssl_ec_point_cleanup,
- ssl_ec_point_generate_keypair,
- ssl_ec_point_compute_secret,
+ ssl_ec_point_offer,
+ ssl_ec_point_accept,
+ ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_secp521r1,
- SSL_CURVE_SECP521R1,
+ SSL_GROUP_SECP521R1,
"P-521",
ssl_ec_point_cleanup,
- ssl_ec_point_generate_keypair,
- ssl_ec_point_compute_secret,
+ ssl_ec_point_offer,
+ ssl_ec_point_accept,
+ ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_X25519,
- SSL_CURVE_X25519,
+ SSL_GROUP_X25519,
"X25519",
ssl_x25519_cleanup,
- ssl_x25519_generate_keypair,
- ssl_x25519_compute_secret,
+ ssl_x25519_offer,
+ ssl_x25519_accept,
+ ssl_x25519_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
+ },
+ {
+ NID_cecpq1,
+ SSL_GROUP_CECPQ1,
+ "CECPQ1",
+ ssl_cecpq1_cleanup,
+ ssl_cecpq1_offer,
+ ssl_cecpq1_accept,
+ ssl_cecpq1_finish,
+ CBS_get_u16_length_prefixed,
+ CBB_add_u16_length_prefixed,
},
};
-static const SSL_ECDH_METHOD *method_from_curve_id(uint16_t curve_id) {
+static const SSL_ECDH_METHOD *method_from_group_id(uint16_t group_id) {
size_t i;
for (i = 0; i < sizeof(kMethods) / sizeof(kMethods[0]); i++) {
- if (kMethods[i].curve_id == curve_id) {
+ if (kMethods[i].group_id == group_id) {
return &kMethods[i];
}
}
@@ -320,27 +526,27 @@
return NULL;
}
-const char* SSL_get_curve_name(uint16_t curve_id) {
- const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+const char* SSL_get_curve_name(uint16_t group_id) {
+ const SSL_ECDH_METHOD *method = method_from_group_id(group_id);
if (method == NULL) {
return NULL;
}
return method->name;
}
-int ssl_nid_to_curve_id(uint16_t *out_curve_id, int nid) {
+int ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
const SSL_ECDH_METHOD *method = method_from_nid(nid);
if (method == NULL) {
return 0;
}
- *out_curve_id = method->curve_id;
+ *out_group_id = method->group_id;
return 1;
}
-int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t curve_id) {
+int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id) {
SSL_ECDH_CTX_cleanup(ctx);
- const SSL_ECDH_METHOD *method = method_from_curve_id(curve_id);
+ const SSL_ECDH_METHOD *method = method_from_group_id(group_id);
if (method == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE);
return 0;
@@ -356,6 +562,20 @@
ctx->data = params;
}
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) {
+ if (ctx->method == NULL) {
+ return 0;
+ }
+ return ctx->method->get_key(cbs, out);
+}
+
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents) {
+ if (ctx->method == NULL) {
+ return 0;
+ }
+ return ctx->method->add_key(cbb, out_contents);
+}
+
void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
if (ctx->method == NULL) {
return;
@@ -365,13 +585,21 @@
ctx->data = NULL;
}
-int SSL_ECDH_CTX_generate_keypair(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
- return ctx->method->generate_keypair(ctx, out_public_key);
+int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key) {
+ return ctx->method->offer(ctx, out_public_key);
}
-int SSL_ECDH_CTX_compute_secret(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
- size_t *out_secret_len, uint8_t *out_alert,
- const uint8_t *peer_key, size_t peer_key_len) {
- return ctx->method->compute_secret(ctx, out_secret, out_secret_len, out_alert,
- peer_key, peer_key_len);
+int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
+ uint8_t **out_secret, size_t *out_secret_len,
+ uint8_t *out_alert, const uint8_t *peer_key,
+ size_t peer_key_len) {
+ return ctx->method->accept(ctx, out_public_key, out_secret, out_secret_len,
+ out_alert, peer_key, peer_key_len);
+}
+
+int SSL_ECDH_CTX_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
+ return ctx->method->finish(ctx, out_secret, out_secret_len, out_alert,
+ peer_key, peer_key_len);
}
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 84047b2..8e9b196 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -297,6 +297,9 @@
if (method->version != 0) {
SSL_CTX_set_max_version(ret, method->version);
SSL_CTX_set_min_version(ret, method->version);
+ } else if (!method->method->is_dtls) {
+ /* TODO(svaldez): Enable TLS 1.3 once implemented. */
+ SSL_CTX_set_max_version(ret, TLS1_2_VERSION);
}
return ret;
@@ -341,7 +344,7 @@
sk_X509_NAME_pop_free(ctx->client_CA, X509_NAME_free);
sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles);
OPENSSL_free(ctx->psk_identity_hint);
- OPENSSL_free(ctx->tlsext_ellipticcurvelist);
+ OPENSSL_free(ctx->supported_group_list);
OPENSSL_free(ctx->alpn_client_proto_list);
OPENSSL_free(ctx->ocsp_response);
OPENSSL_free(ctx->signed_cert_timestamp_list);
@@ -369,6 +372,10 @@
ssl->min_version = ctx->min_version;
ssl->max_version = ctx->max_version;
+ /* RFC 6347 states that implementations SHOULD use an initial timer value of
+ * 1 second. */
+ ssl->initial_timeout_duration_ms = 1000;
+
ssl->options = ctx->options;
ssl->mode = ctx->mode;
ssl->max_cert_list = ctx->max_cert_list;
@@ -399,14 +406,14 @@
CRYPTO_refcount_inc(&ctx->references);
ssl->initial_ctx = ctx;
- if (ctx->tlsext_ellipticcurvelist) {
- ssl->tlsext_ellipticcurvelist =
- BUF_memdup(ctx->tlsext_ellipticcurvelist,
- ctx->tlsext_ellipticcurvelist_length * 2);
- if (!ssl->tlsext_ellipticcurvelist) {
+ if (ctx->supported_group_list) {
+ ssl->supported_group_list =
+ BUF_memdup(ctx->supported_group_list,
+ ctx->supported_group_list_len * 2);
+ if (!ssl->supported_group_list) {
goto err;
}
- ssl->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length;
+ ssl->supported_group_list_len = ctx->supported_group_list_len;
}
if (ssl->ctx->alpn_client_proto_list) {
@@ -467,14 +474,8 @@
CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
- if (ssl->bbio != NULL) {
- /* If the buffering BIO is in place, pop it off */
- if (ssl->bbio == ssl->wbio) {
- ssl->wbio = BIO_pop(ssl->wbio);
- }
- BIO_free(ssl->bbio);
- ssl->bbio = NULL;
- }
+ ssl_free_wbio_buffer(ssl);
+ assert(ssl->bbio == NULL);
int free_wbio = ssl->wbio != ssl->rbio;
BIO_free_all(ssl->rbio);
@@ -495,7 +496,7 @@
OPENSSL_free(ssl->tlsext_hostname);
SSL_CTX_free(ssl->initial_ctx);
- OPENSSL_free(ssl->tlsext_ellipticcurvelist);
+ OPENSSL_free(ssl->supported_group_list);
OPENSSL_free(ssl->alpn_client_proto_list);
EVP_PKEY_free(ssl->tlsext_channel_id_private);
OPENSSL_free(ssl->psk_identity_hint);
@@ -512,14 +513,12 @@
void SSL_set_connect_state(SSL *ssl) {
ssl->server = 0;
- ssl->shutdown = 0;
ssl->state = SSL_ST_CONNECT;
ssl->handshake_func = ssl->method->ssl_connect;
}
void SSL_set_accept_state(SSL *ssl) {
ssl->server = 1;
- ssl->shutdown = 0;
ssl->state = SSL_ST_ACCEPT;
ssl->handshake_func = ssl->method->ssl_accept;
}
@@ -527,10 +526,7 @@
void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
/* If the output buffering BIO is still in place, remove it. */
if (ssl->bbio != NULL) {
- if (ssl->wbio == ssl->bbio) {
- ssl->wbio = ssl->wbio->next_bio;
- ssl->bbio->next_bio = NULL;
- }
+ ssl->wbio = BIO_pop(ssl->wbio);
}
if (ssl->rbio != rbio) {
@@ -541,11 +537,23 @@
}
ssl->rbio = rbio;
ssl->wbio = wbio;
+
+ /* Re-attach |bbio| to the new |wbio|. */
+ if (ssl->bbio != NULL) {
+ ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
+ }
}
BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; }
-BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
+BIO *SSL_get_wbio(const SSL *ssl) {
+ if (ssl->bbio != NULL) {
+ /* If |bbio| is active, the true caller-configured BIO is its |next_bio|. */
+ assert(ssl->bbio == ssl->wbio);
+ return ssl->bbio->next_bio;
+ }
+ return ssl->wbio;
+}
int SSL_do_handshake(SSL *ssl) {
ssl->rwstate = SSL_NOTHING;
@@ -597,10 +605,6 @@
return -1;
}
- if (ssl->shutdown & SSL_RECEIVED_SHUTDOWN) {
- return 0;
- }
-
/* This may require multiple iterations. False Start will cause
* |ssl->handshake_func| to signal success one step early, but the handshake
* must be completely finished before other modes are accepted. */
@@ -637,7 +641,7 @@
return -1;
}
- if (ssl->shutdown & SSL_SENT_SHUTDOWN) {
+ if (ssl->s3->send_shutdown != ssl_shutdown_none) {
OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
return -1;
}
@@ -662,11 +666,6 @@
/* Functions which use SSL_get_error must clear the error queue on entry. */
ERR_clear_error();
- /* Note that this function behaves differently from what one might expect.
- * Return values are 0 for no success (yet), 1 for success; but calling it
- * once is usually not enough, even if blocking I/O is used (see
- * ssl3_shutdown). */
-
if (ssl->handshake_func == NULL) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
return -1;
@@ -678,44 +677,37 @@
return -1;
}
- /* Do nothing if configured not to send a close_notify. */
if (ssl->quiet_shutdown) {
- ssl->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN;
+ /* Do nothing if configured not to send a close_notify. */
+ ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
return 1;
}
- if (!(ssl->shutdown & SSL_SENT_SHUTDOWN)) {
- ssl->shutdown |= SSL_SENT_SHUTDOWN;
- ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY);
+ /* This function completes in two stages. It sends a close_notify and then it
+ * waits for a close_notify to come in. Perform exactly one action and return
+ * whether or not it succeeds. */
- /* our shutdown alert has been sent now, and if it still needs to be
- * written, ssl->s3->alert_dispatch will be true */
- if (ssl->s3->alert_dispatch) {
- return -1; /* return WANT_WRITE */
+ if (ssl->s3->send_shutdown != ssl_shutdown_close_notify) {
+ /* Send a close_notify. */
+ if (ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) {
+ return -1;
}
} else if (ssl->s3->alert_dispatch) {
- /* resend it if not sent */
- int ret = ssl->method->ssl_dispatch_alert(ssl);
- if (ret == -1) {
- /* we only get to return -1 here the 2nd/Nth invocation, we must have
- * already signalled return 0 upon a previous invoation, return
- * WANT_WRITE */
- return ret;
+ /* Finish sending the close_notify. */
+ if (ssl->method->ssl_dispatch_alert(ssl) <= 0) {
+ return -1;
}
- } else if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) {
- /* If we are waiting for a close from our peer, we are closed */
+ } else if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+ /* Wait for the peer's close_notify. */
ssl->method->ssl_read_close_notify(ssl);
- if (!(ssl->shutdown & SSL_RECEIVED_SHUTDOWN)) {
- return -1; /* return WANT_READ */
+ if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) {
+ return -1;
}
}
- if (ssl->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN) &&
- !ssl->s3->alert_dispatch) {
- return 1;
- } else {
- return 0;
- }
+ /* Return 0 for unidirectional shutdown and 1 for bidirectional shutdown. */
+ return ssl->s3->recv_shutdown == ssl_shutdown_close_notify;
}
int SSL_get_error(const SSL *ssl, int ret_code) {
@@ -738,8 +730,7 @@
}
if (ret_code == 0) {
- if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) && ssl->s3->clean_shutdown) {
- /* The socket was cleanly shut down with a close_notify. */
+ if (ssl->s3->recv_shutdown == ssl_shutdown_close_notify) {
return SSL_ERROR_ZERO_RETURN;
}
/* An EOF was observed which violates the protocol, and the underlying
@@ -1035,35 +1026,36 @@
}
int SSL_set_wfd(SSL *ssl, int fd) {
- if (ssl->rbio == NULL ||
- BIO_method_type(ssl->rbio) != BIO_TYPE_SOCKET ||
- BIO_get_fd(ssl->rbio, NULL) != fd) {
+ BIO *rbio = SSL_get_rbio(ssl);
+ if (rbio == NULL || BIO_method_type(rbio) != BIO_TYPE_SOCKET ||
+ BIO_get_fd(rbio, NULL) != fd) {
BIO *bio = BIO_new(BIO_s_socket());
if (bio == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
return 0;
}
BIO_set_fd(bio, fd, BIO_NOCLOSE);
- SSL_set_bio(ssl, SSL_get_rbio(ssl), bio);
+ SSL_set_bio(ssl, rbio, bio);
} else {
- SSL_set_bio(ssl, SSL_get_rbio(ssl), SSL_get_rbio(ssl));
+ SSL_set_bio(ssl, rbio, rbio);
}
return 1;
}
int SSL_set_rfd(SSL *ssl, int fd) {
- if (ssl->wbio == NULL || BIO_method_type(ssl->wbio) != BIO_TYPE_SOCKET ||
- BIO_get_fd(ssl->wbio, NULL) != fd) {
+ BIO *wbio = SSL_get_wbio(ssl);
+ if (wbio == NULL || BIO_method_type(wbio) != BIO_TYPE_SOCKET ||
+ BIO_get_fd(wbio, NULL) != fd) {
BIO *bio = BIO_new(BIO_s_socket());
if (bio == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
return 0;
}
BIO_set_fd(bio, fd, BIO_NOCLOSE);
- SSL_set_bio(ssl, bio, SSL_get_wbio(ssl));
+ SSL_set_bio(ssl, bio, wbio);
} else {
- SSL_set_bio(ssl, SSL_get_wbio(ssl), SSL_get_wbio(ssl));
+ SSL_set_bio(ssl, wbio, wbio);
}
return 1;
}
@@ -1710,7 +1702,7 @@
mask_a |= SSL_aRSA;
} else if (ssl_private_key_type(ssl) == EVP_PKEY_EC) {
/* An ECC certificate may be usable for ECDSA cipher suites depending on
- * the key usage extension and on the client's curve preferences. */
+ * the key usage extension and on the client's group preferences. */
X509 *x = ssl->cert->x509;
/* This call populates extension flags (ex_flags). */
X509_check_purpose(x, -1, 0);
@@ -1727,12 +1719,15 @@
mask_k |= SSL_kDHE;
}
- /* Check for a shared curve to consider ECDHE ciphers. */
+ /* Check for a shared group to consider ECDHE ciphers. */
uint16_t unused;
- if (tls1_get_shared_curve(ssl, &unused)) {
+ if (tls1_get_shared_group(ssl, &unused)) {
mask_k |= SSL_kECDHE;
}
+ /* CECPQ1 ciphers are always acceptable if supported by both sides. */
+ mask_k |= SSL_kCECPQ1;
+
/* PSK requires a server callback. */
if (ssl->psk_server_callback != NULL) {
mask_k |= SSL_kPSK;
@@ -1779,7 +1774,7 @@
flush_cache = 1;
ctx->handshakes_since_cache_flush = 0;
}
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
if (flush_cache) {
SSL_CTX_flush_sessions(ctx, (unsigned long)time(NULL));
@@ -1789,6 +1784,9 @@
static const char *ssl_get_version(int version) {
switch (version) {
+ case TLS1_3_VERSION:
+ return "TLSv1.3";
+
case TLS1_2_VERSION:
return "TLSv1.2";
@@ -1865,38 +1863,26 @@
int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
-int ssl_init_wbio_buffer(SSL *ssl, int push) {
- BIO *bbio;
+int ssl_is_wbio_buffered(const SSL *ssl) {
+ return ssl->bbio != NULL;
+}
- if (ssl->bbio == NULL) {
- bbio = BIO_new(BIO_f_buffer());
- if (bbio == NULL) {
- return 0;
- }
- ssl->bbio = bbio;
- } else {
- bbio = ssl->bbio;
- if (ssl->bbio == ssl->wbio) {
- ssl->wbio = BIO_pop(ssl->wbio);
- }
+int ssl_init_wbio_buffer(SSL *ssl) {
+ if (ssl->bbio != NULL) {
+ /* Already buffered. */
+ assert(ssl->bbio == ssl->wbio);
+ return 1;
}
- BIO_reset(bbio);
- if (!BIO_set_read_buffer_size(bbio, 1)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
+ BIO *bbio = BIO_new(BIO_f_buffer());
+ if (bbio == NULL ||
+ !BIO_set_read_buffer_size(bbio, 1)) {
+ BIO_free(bbio);
return 0;
}
- if (push) {
- if (ssl->wbio != bbio) {
- ssl->wbio = BIO_push(bbio, ssl->wbio);
- }
- } else {
- if (ssl->wbio == bbio) {
- ssl->wbio = BIO_pop(bbio);
- }
- }
-
+ ssl->bbio = bbio;
+ ssl->wbio = BIO_push(bbio, ssl->wbio);
return 1;
}
@@ -1905,11 +1891,9 @@
return;
}
- if (ssl->bbio == ssl->wbio) {
- /* remove buffering */
- ssl->wbio = BIO_pop(ssl->wbio);
- }
+ assert(ssl->bbio == ssl->wbio);
+ ssl->wbio = BIO_pop(ssl->wbio);
BIO_free(ssl->bbio);
ssl->bbio = NULL;
}
@@ -1931,12 +1915,32 @@
void SSL_set_shutdown(SSL *ssl, int mode) {
/* It is an error to clear any bits that have already been set. (We can't try
* to get a second close_notify or send two.) */
- assert((ssl->shutdown & mode) == ssl->shutdown);
+ assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl));
- ssl->shutdown |= mode;
+ if (mode & SSL_RECEIVED_SHUTDOWN &&
+ ssl->s3->recv_shutdown == ssl_shutdown_none) {
+ ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+ }
+
+ if (mode & SSL_SENT_SHUTDOWN &&
+ ssl->s3->send_shutdown == ssl_shutdown_none) {
+ ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+ }
}
-int SSL_get_shutdown(const SSL *ssl) { return ssl->shutdown; }
+int SSL_get_shutdown(const SSL *ssl) {
+ int ret = 0;
+ if (ssl->s3->recv_shutdown != ssl_shutdown_none) {
+ /* Historically, OpenSSL set |SSL_RECEIVED_SHUTDOWN| on both close_notify
+ * and fatal alert. */
+ ret |= SSL_RECEIVED_SHUTDOWN;
+ }
+ if (ssl->s3->send_shutdown == ssl_shutdown_close_notify) {
+ /* Historically, OpenSSL set |SSL_SENT_SHUTDOWN| on only close_notify. */
+ ret |= SSL_SENT_SHUTDOWN;
+ }
+ return ret;
+}
int SSL_version(const SSL *ssl) { return ssl->version; }
@@ -2293,7 +2297,7 @@
/* False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN. */
return !SSL_IS_DTLS(ssl) &&
- SSL_version(ssl) >= TLS1_2_VERSION &&
+ SSL_version(ssl) == TLS1_2_VERSION &&
(ssl->s3->alpn_selected || ssl->s3->next_proto_neg_seen) &&
cipher != NULL &&
cipher->algorithm_mkey == SSL_kECDHE &&
@@ -2308,6 +2312,7 @@
case TLS1_VERSION:
case TLS1_1_VERSION:
case TLS1_2_VERSION:
+ case TLS1_3_VERSION:
case DTLS1_VERSION:
case DTLS1_2_VERSION:
return &TLSv1_enc_data;
@@ -2332,7 +2337,10 @@
return 0;
}
- max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_2_VERSION;
+ max_version = (ssl->max_version != 0) ? ssl->max_version : TLS1_3_VERSION;
+ if (!(ssl->options & SSL_OP_NO_TLSv1_3) && TLS1_3_VERSION <= max_version) {
+ return TLS1_3_VERSION;
+ }
if (!(ssl->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) {
return TLS1_2_VERSION;
}
@@ -2376,8 +2384,11 @@
client_version = ssl->max_version;
}
- if (client_version >= TLS1_2_VERSION &&
- !(ssl->options & SSL_OP_NO_TLSv1_2)) {
+ if (client_version >= TLS1_3_VERSION &&
+ !(ssl->options & SSL_OP_NO_TLSv1_3)) {
+ version = TLS1_3_VERSION;
+ } else if (client_version >= TLS1_2_VERSION &&
+ !(ssl->options & SSL_OP_NO_TLSv1_2)) {
version = TLS1_2_VERSION;
} else if (client_version >= TLS1_1_VERSION &&
!(ssl->options & SSL_OP_NO_TLSv1_1)) {
@@ -2426,7 +2437,10 @@
version = ssl->max_version;
}
} else {
- if (!(options & SSL_OP_NO_TLSv1_2)) {
+ if (!(options & SSL_OP_NO_TLSv1_3)) {
+ version = TLS1_3_VERSION;
+ }
+ if (!(options & SSL_OP_NO_TLSv1_2) && (options & SSL_OP_NO_TLSv1_3)) {
version = TLS1_2_VERSION;
}
if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2)) {
@@ -2486,6 +2500,9 @@
case TLS1_2_VERSION:
return !(ssl->options & SSL_OP_NO_TLSv1_2);
+ case TLS1_3_VERSION:
+ return !(ssl->options & SSL_OP_NO_TLSv1_3);
+
default:
return 0;
}
@@ -2644,7 +2661,6 @@
}
ssl->hit = 0;
- ssl->shutdown = 0;
/* SSL_clear may be called before or after the |ssl| is initialized in either
* accept or connect state. In the latter case, SSL_clear should preserve the
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 12d065e..8e51a6a 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -396,7 +396,7 @@
SSL_SESSION_up_ref(session);
}
/* TODO(davidben): This should probably move it to the front of the list. */
- CRYPTO_MUTEX_unlock(&ssl->initial_ctx->lock);
+ CRYPTO_MUTEX_unlock_read(&ssl->initial_ctx->lock);
if (session != NULL) {
*out_session = session;
@@ -517,7 +517,7 @@
SSL_SESSION *old_session;
CRYPTO_MUTEX_lock_write(&ctx->lock);
if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, session)) {
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
SSL_SESSION_free(session);
return 0;
}
@@ -525,7 +525,7 @@
if (old_session != NULL) {
if (old_session == session) {
/* |session| was already in the cache. */
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
SSL_SESSION_free(old_session);
return 0;
}
@@ -547,7 +547,7 @@
}
}
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
return 1;
}
@@ -571,7 +571,7 @@
}
if (lock) {
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
}
if (ret) {
@@ -654,11 +654,12 @@
tp.time = time;
CRYPTO_MUTEX_lock_write(&ctx->lock);
lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp);
- CRYPTO_MUTEX_unlock(&ctx->lock);
+ CRYPTO_MUTEX_unlock_write(&ctx->lock);
}
int ssl_clear_bad_session(SSL *ssl) {
- if (ssl->session != NULL && !(ssl->shutdown & SSL_SENT_SHUTDOWN) &&
+ if (ssl->session != NULL &&
+ ssl->s3->send_shutdown != ssl_shutdown_close_notify &&
!SSL_in_init(ssl)) {
SSL_CTX_remove_session(ssl->ctx, ssl->session);
return 1;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index 8fa197d..15d1270 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -110,39 +110,21 @@
case SSL3_ST_CR_SRVR_HELLO_A:
return "SSLv3 read server hello A";
- case SSL3_ST_CR_SRVR_HELLO_B:
- return "SSLv3 read server hello B";
-
case SSL3_ST_CR_CERT_A:
return "SSLv3 read server certificate A";
- case SSL3_ST_CR_CERT_B:
- return "SSLv3 read server certificate B";
-
case SSL3_ST_CR_KEY_EXCH_A:
return "SSLv3 read server key exchange A";
- case SSL3_ST_CR_KEY_EXCH_B:
- return "SSLv3 read server key exchange B";
-
case SSL3_ST_CR_CERT_REQ_A:
return "SSLv3 read server certificate request A";
- case SSL3_ST_CR_CERT_REQ_B:
- return "SSLv3 read server certificate request B";
-
case SSL3_ST_CR_SESSION_TICKET_A:
return "SSLv3 read server session ticket A";
- case SSL3_ST_CR_SESSION_TICKET_B:
- return "SSLv3 read server session ticket B";
-
case SSL3_ST_CR_SRVR_DONE_A:
return "SSLv3 read server done A";
- case SSL3_ST_CR_SRVR_DONE_B:
- return "SSLv3 read server done B";
-
case SSL3_ST_CW_CERT_A:
return "SSLv3 write client certificate A";
@@ -191,10 +173,6 @@
case SSL3_ST_SR_FINISHED_A:
return "SSLv3 read finished A";
- case SSL3_ST_CR_FINISHED_B:
- case SSL3_ST_SR_FINISHED_B:
- return "SSLv3 read finished B";
-
case SSL3_ST_CW_FLUSH:
case SSL3_ST_SW_FLUSH:
return "SSLv3 flush data";
@@ -208,9 +186,6 @@
case SSL3_ST_SR_CLNT_HELLO_C:
return "SSLv3 read client hello C";
- case SSL3_ST_SR_CLNT_HELLO_D:
- return "SSLv3 read client hello D";
-
case SSL3_ST_SW_HELLO_REQ_A:
return "SSLv3 write hello request A";
@@ -259,9 +234,6 @@
case SSL3_ST_SR_CERT_A:
return "SSLv3 read client certificate A";
- case SSL3_ST_SR_CERT_B:
- return "SSLv3 read client certificate B";
-
case SSL3_ST_SR_KEY_EXCH_A:
return "SSLv3 read client key exchange A";
@@ -271,16 +243,10 @@
case SSL3_ST_SR_CERT_VRFY_A:
return "SSLv3 read certificate verify A";
- case SSL3_ST_SR_CERT_VRFY_B:
- return "SSLv3 read certificate verify B";
-
/* DTLS */
case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
return "DTLS1 read hello verify request A";
- case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
- return "DTLS1 read hello verify request B";
-
default:
return "unknown state";
}
@@ -311,33 +277,18 @@
case SSL3_ST_CR_SRVR_HELLO_A:
return "3RSH_A";
- case SSL3_ST_CR_SRVR_HELLO_B:
- return "3RSH_B";
-
case SSL3_ST_CR_CERT_A:
return "3RSC_A";
- case SSL3_ST_CR_CERT_B:
- return "3RSC_B";
-
case SSL3_ST_CR_KEY_EXCH_A:
return "3RSKEA";
- case SSL3_ST_CR_KEY_EXCH_B:
- return "3RSKEB";
-
case SSL3_ST_CR_CERT_REQ_A:
return "3RCR_A";
- case SSL3_ST_CR_CERT_REQ_B:
- return "3RCR_B";
-
case SSL3_ST_CR_SRVR_DONE_A:
return "3RSD_A";
- case SSL3_ST_CR_SRVR_DONE_B:
- return "3RSD_B";
-
case SSL3_ST_CW_CERT_A:
return "3WCC_A";
@@ -386,10 +337,6 @@
case SSL3_ST_CR_FINISHED_A:
return "3RFINA";
- case SSL3_ST_SR_FINISHED_B:
- case SSL3_ST_CR_FINISHED_B:
- return "3RFINB";
-
case SSL3_ST_SW_HELLO_REQ_A:
return "3WHR_A";
@@ -408,9 +355,6 @@
case SSL3_ST_SR_CLNT_HELLO_C:
return "3RCH_C";
- case SSL3_ST_SR_CLNT_HELLO_D:
- return "3RCH_D";
-
case SSL3_ST_SW_SRVR_HELLO_A:
return "3WSH_A";
@@ -444,28 +388,16 @@
case SSL3_ST_SR_CERT_A:
return "3RCC_A";
- case SSL3_ST_SR_CERT_B:
- return "3RCC_B";
-
case SSL3_ST_SR_KEY_EXCH_A:
return "3RCKEA";
- case SSL3_ST_SR_KEY_EXCH_B:
- return "3RCKEB";
-
case SSL3_ST_SR_CERT_VRFY_A:
return "3RCV_A";
- case SSL3_ST_SR_CERT_VRFY_B:
- return "3RCV_B";
-
/* DTLS */
case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
return "DRCHVA";
- case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B:
- return "DRCHVB";
-
default:
return "UNKWN ";
}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 590a2c1..ef38902 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -254,6 +254,31 @@
"TLSv1.2",
};
+static const char *kMustNotIncludeCECPQ1[] = {
+ "ALL",
+ "DEFAULT",
+ "MEDIUM",
+ "HIGH",
+ "FIPS",
+ "SHA",
+ "SHA1",
+ "SHA256",
+ "SHA384",
+ "RSA",
+ "SSLv3",
+ "TLSv1",
+ "TLSv1.2",
+ "aRSA",
+ "RSA",
+ "aECDSA",
+ "ECDSA",
+ "AES",
+ "AES128",
+ "AES256",
+ "AESGCM",
+ "CHACHA20",
+};
+
static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) {
bool in_group = false;
for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) {
@@ -324,6 +349,24 @@
return true;
}
+static bool TestRuleDoesNotIncludeCECPQ1(const char *rule) {
+ ScopedSSL_CTX ctx(SSL_CTX_new(TLS_method()));
+ if (!ctx) {
+ return false;
+ }
+ if (!SSL_CTX_set_cipher_list(ctx.get(), rule)) {
+ fprintf(stderr, "Error: cipher rule '%s' failed\n", rule);
+ return false;
+ }
+ for (size_t i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) {
+ if (SSL_CIPHER_is_CECPQ1(sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i))) {
+ fprintf(stderr, "Error: cipher rule '%s' includes CECPQ1\n",rule);
+ return false;
+ }
+ }
+ return true;
+}
+
static bool TestCipherRules() {
for (const CipherTest &test : kCipherTests) {
if (!TestCipherRule(test)) {
@@ -349,6 +392,12 @@
}
}
+ for (const char *rule : kMustNotIncludeCECPQ1) {
+ if (!TestRuleDoesNotIncludeCECPQ1(rule)) {
+ return false;
+ }
+ }
+
return true;
}
@@ -646,7 +695,10 @@
if (!ctx) {
return false;
}
- return ctx->min_version == version && ctx->max_version == version;
+ // TODO(svaldez): Remove TLS1_2_VERSION fallback upon implementing TLS 1.3.
+ return ctx->min_version == version &&
+ (ctx->max_version == version ||
+ (version == 0 && ctx->max_version == TLS1_2_VERSION));
}
static bool CipherGetRFCName(std::string *out, uint16_t value) {
@@ -1027,23 +1079,9 @@
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
-static bool TestSequenceNumber(bool dtls) {
- ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
- ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
- if (!client_ctx || !server_ctx) {
- return false;
- }
-
- ScopedX509 cert = GetTestCertificate();
- ScopedEVP_PKEY key = GetTestKey();
- if (!cert || !key ||
- !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
- !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
- return false;
- }
-
- // Create a client and server connected to each other.
- ScopedSSL client(SSL_new(client_ctx.get())), server(SSL_new(server_ctx.get()));
+static bool ConnectClientAndServer(ScopedSSL *out_client, ScopedSSL *out_server,
+ SSL_CTX *client_ctx, SSL_CTX *server_ctx) {
+ ScopedSSL client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
if (!client || !server) {
return false;
}
@@ -1083,6 +1121,32 @@
}
}
+ *out_client = std::move(client);
+ *out_server = std::move(server);
+ return true;
+}
+
+static bool TestSequenceNumber(bool dtls) {
+ ScopedSSL_CTX client_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+ ScopedSSL_CTX server_ctx(SSL_CTX_new(dtls ? DTLS_method() : TLS_method()));
+ if (!client_ctx || !server_ctx) {
+ return false;
+ }
+
+ ScopedX509 cert = GetTestCertificate();
+ ScopedEVP_PKEY key = GetTestKey();
+ if (!cert || !key ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+ return false;
+ }
+
+ ScopedSSL client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get())) {
+ return false;
+ }
+
uint64_t client_read_seq = SSL_get_read_sequence(client.get());
uint64_t client_write_seq = SSL_get_write_sequence(client.get());
uint64_t server_read_seq = SSL_get_read_sequence(server.get());
@@ -1131,6 +1195,62 @@
return true;
}
+static bool TestOneSidedShutdown() {
+ ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
+ ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
+ if (!client_ctx || !server_ctx) {
+ return false;
+ }
+
+ ScopedX509 cert = GetTestCertificate();
+ ScopedEVP_PKEY key = GetTestKey();
+ if (!cert || !key ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+ return false;
+ }
+
+ ScopedSSL client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get())) {
+ return false;
+ }
+
+ // Shut down half the connection. SSL_shutdown will return 0 to signal only
+ // one side has shut down.
+ if (SSL_shutdown(client.get()) != 0) {
+ fprintf(stderr, "Could not shutdown.\n");
+ return false;
+ }
+
+ // Reading from the server should consume the EOF.
+ uint8_t byte;
+ if (SSL_read(server.get(), &byte, 1) != 0 ||
+ SSL_get_error(server.get(), 0) != SSL_ERROR_ZERO_RETURN) {
+ fprintf(stderr, "Connection was not shut down cleanly.\n");
+ return false;
+ }
+
+ // However, the server may continue to write data and then shut down the
+ // connection.
+ byte = 42;
+ if (SSL_write(server.get(), &byte, 1) != 1 ||
+ SSL_read(client.get(), &byte, 1) != 1 ||
+ byte != 42) {
+ fprintf(stderr, "Could not send byte.\n");
+ return false;
+ }
+
+ // The server may then shutdown the connection.
+ if (SSL_shutdown(server.get()) != 1 ||
+ SSL_shutdown(client.get()) != 1) {
+ fprintf(stderr, "Could not complete shutdown.\n");
+ return false;
+ }
+
+ return true;
+}
+
int main() {
CRYPTO_library_init();
@@ -1154,7 +1274,8 @@
!TestClientCAList() ||
!TestInternalSessionCache() ||
!TestSequenceNumber(false /* TLS */) ||
- !TestSequenceNumber(true /* DTLS */)) {
+ !TestSequenceNumber(true /* DTLS */) ||
+ !TestOneSidedShutdown()) {
ERR_print_errors_fp(stderr);
return 1;
}
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index eac9579..16cac15 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -291,77 +291,77 @@
return 0;
}
-static const uint16_t eccurves_default[] = {
- SSL_CURVE_X25519,
- SSL_CURVE_SECP256R1,
- SSL_CURVE_SECP384R1,
+static const uint16_t kDefaultGroups[] = {
+ SSL_GROUP_X25519,
+ SSL_GROUP_SECP256R1,
+ SSL_GROUP_SECP384R1,
#if defined(BORINGSSL_ANDROID_SYSTEM)
- SSL_CURVE_SECP521R1,
+ SSL_GROUP_SECP521R1,
#endif
};
-/* tls1_get_curvelist sets |*out_curve_ids| and |*out_curve_ids_len| to the
- * list of allowed curve IDs. If |get_peer_curves| is non-zero, return the
- * peer's curve list. Otherwise, return the preferred list. */
-static void tls1_get_curvelist(SSL *ssl, int get_peer_curves,
- const uint16_t **out_curve_ids,
- size_t *out_curve_ids_len) {
- if (get_peer_curves) {
- /* Only clients send a curve list, so this function is only called
- * on the server. */
+/* tls1_get_grouplist sets |*out_group_ids| and |*out_group_ids_len| to the
+ * list of allowed group IDs. If |get_peer_groups| is non-zero, return the
+ * peer's group list. Otherwise, return the preferred list. */
+static void tls1_get_grouplist(SSL *ssl, int get_peer_groups,
+ const uint16_t **out_group_ids,
+ size_t *out_group_ids_len) {
+ if (get_peer_groups) {
+ /* Only clients send a supported group list, so this function is only
+ * called on the server. */
assert(ssl->server);
- *out_curve_ids = ssl->s3->tmp.peer_ellipticcurvelist;
- *out_curve_ids_len = ssl->s3->tmp.peer_ellipticcurvelist_length;
+ *out_group_ids = ssl->s3->tmp.peer_supported_group_list;
+ *out_group_ids_len = ssl->s3->tmp.peer_supported_group_list_len;
return;
}
- *out_curve_ids = ssl->tlsext_ellipticcurvelist;
- *out_curve_ids_len = ssl->tlsext_ellipticcurvelist_length;
- if (!*out_curve_ids) {
- *out_curve_ids = eccurves_default;
- *out_curve_ids_len = sizeof(eccurves_default) / sizeof(eccurves_default[0]);
+ *out_group_ids = ssl->supported_group_list;
+ *out_group_ids_len = ssl->supported_group_list_len;
+ if (!*out_group_ids) {
+ *out_group_ids = kDefaultGroups;
+ *out_group_ids_len = sizeof(kDefaultGroups) / sizeof(kDefaultGroups[0]);
}
}
-int tls1_get_shared_curve(SSL *ssl, uint16_t *out_curve_id) {
- const uint16_t *curves, *peer_curves, *pref, *supp;
- size_t curves_len, peer_curves_len, pref_len, supp_len, i, j;
+int tls1_get_shared_group(SSL *ssl, uint16_t *out_group_id) {
+ const uint16_t *groups, *peer_groups, *pref, *supp;
+ size_t groups_len, peer_groups_len, pref_len, supp_len, i, j;
/* Can't do anything on client side */
if (ssl->server == 0) {
return 0;
}
- tls1_get_curvelist(ssl, 0 /* local curves */, &curves, &curves_len);
- tls1_get_curvelist(ssl, 1 /* peer curves */, &peer_curves, &peer_curves_len);
+ tls1_get_grouplist(ssl, 0 /* local groups */, &groups, &groups_len);
+ tls1_get_grouplist(ssl, 1 /* peer groups */, &peer_groups, &peer_groups_len);
- if (peer_curves_len == 0) {
- /* Clients are not required to send a supported_curves extension. In this
- * case, the server is free to pick any curve it likes. See RFC 4492,
+ if (peer_groups_len == 0) {
+ /* Clients are not required to send a supported_groups extension. In this
+ * case, the server is free to pick any group it likes. See RFC 4492,
* section 4, paragraph 3.
*
* However, in the interests of compatibility, we will skip ECDH if the
* client didn't send an extension because we can't be sure that they'll
- * support our favoured curve. */
+ * support our favoured group. */
return 0;
}
if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
- pref = curves;
- pref_len = curves_len;
- supp = peer_curves;
- supp_len = peer_curves_len;
+ pref = groups;
+ pref_len = groups_len;
+ supp = peer_groups;
+ supp_len = peer_groups_len;
} else {
- pref = peer_curves;
- pref_len = peer_curves_len;
- supp = curves;
- supp_len = curves_len;
+ pref = peer_groups;
+ pref_len = peer_groups_len;
+ supp = groups;
+ supp_len = groups_len;
}
for (i = 0; i < pref_len; i++) {
for (j = 0; j < supp_len; j++) {
if (pref[i] == supp[j]) {
- *out_curve_id = pref[i];
+ *out_group_id = pref[i];
return 1;
}
}
@@ -370,34 +370,34 @@
return 0;
}
-int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len,
+int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len,
const int *curves, size_t ncurves) {
- uint16_t *curve_ids;
+ uint16_t *group_ids;
size_t i;
- curve_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
- if (curve_ids == NULL) {
+ group_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t));
+ if (group_ids == NULL) {
return 0;
}
for (i = 0; i < ncurves; i++) {
- if (!ssl_nid_to_curve_id(&curve_ids[i], curves[i])) {
- OPENSSL_free(curve_ids);
+ if (!ssl_nid_to_group_id(&group_ids[i], curves[i])) {
+ OPENSSL_free(group_ids);
return 0;
}
}
- OPENSSL_free(*out_curve_ids);
- *out_curve_ids = curve_ids;
- *out_curve_ids_len = ncurves;
+ OPENSSL_free(*out_group_ids);
+ *out_group_ids = group_ids;
+ *out_group_ids_len = ncurves;
return 1;
}
-/* tls1_curve_params_from_ec_key sets |*out_curve_id| and |*out_comp_id| to the
- * TLS curve ID and point format, respectively, for |ec|. It returns one on
+/* tls1_curve_params_from_ec_key sets |*out_group_id| and |*out_comp_id| to the
+ * TLS group ID and point format, respectively, for |ec|. It returns one on
* success and zero on failure. */
-static int tls1_curve_params_from_ec_key(uint16_t *out_curve_id,
+static int tls1_curve_params_from_ec_key(uint16_t *out_group_id,
uint8_t *out_comp_id, EC_KEY *ec) {
int nid;
uint16_t id;
@@ -412,14 +412,14 @@
return 0;
}
- /* Determine curve ID */
+ /* Determine group ID */
nid = EC_GROUP_get_curve_name(grp);
- if (!ssl_nid_to_curve_id(&id, nid)) {
+ if (!ssl_nid_to_group_id(&id, nid)) {
return 0;
}
- /* Set the named curve ID. Arbitrary explicit curves are not supported. */
- *out_curve_id = id;
+ /* Set the named group ID. Arbitrary explicit groups are not supported. */
+ *out_group_id = id;
if (out_comp_id) {
if (EC_KEY_get0_public_key(ec) == NULL) {
@@ -435,35 +435,35 @@
return 1;
}
-/* tls1_check_curve_id returns one if |curve_id| is consistent with both our
- * and the peer's curve preferences. Note: if called as the client, only our
+/* tls1_check_group_id returns one if |group_id| is consistent with both our
+ * and the peer's group preferences. Note: if called as the client, only our
* preferences are checked; the peer (the server) does not send preferences. */
-int tls1_check_curve_id(SSL *ssl, uint16_t curve_id) {
- const uint16_t *curves;
- size_t curves_len, i, get_peer_curves;
+int tls1_check_group_id(SSL *ssl, uint16_t group_id) {
+ const uint16_t *groups;
+ size_t groups_len, i, get_peer_groups;
/* Check against our list, then the peer's list. */
- for (get_peer_curves = 0; get_peer_curves <= 1; get_peer_curves++) {
- if (get_peer_curves && !ssl->server) {
+ for (get_peer_groups = 0; get_peer_groups <= 1; get_peer_groups++) {
+ if (get_peer_groups && !ssl->server) {
/* Servers do not present a preference list so, if we are a client, only
* check our list. */
continue;
}
- tls1_get_curvelist(ssl, get_peer_curves, &curves, &curves_len);
- if (get_peer_curves && curves_len == 0) {
- /* Clients are not required to send a supported_curves extension. In this
- * case, the server is free to pick any curve it likes. See RFC 4492,
+ tls1_get_grouplist(ssl, get_peer_groups, &groups, &groups_len);
+ if (get_peer_groups && groups_len == 0) {
+ /* Clients are not required to send a supported_groups extension. In this
+ * case, the server is free to pick any group it likes. See RFC 4492,
* section 4, paragraph 3. */
continue;
}
- for (i = 0; i < curves_len; i++) {
- if (curves[i] == curve_id) {
+ for (i = 0; i < groups_len; i++) {
+ if (groups[i] == group_id) {
break;
}
}
- if (i == curves_len) {
+ if (i == groups_len) {
return 0;
}
}
@@ -474,7 +474,7 @@
int tls1_check_ec_cert(SSL *ssl, X509 *x) {
int ret = 0;
EVP_PKEY *pkey = X509_get_pubkey(x);
- uint16_t curve_id;
+ uint16_t group_id;
uint8_t comp_id;
if (!pkey) {
@@ -482,8 +482,8 @@
}
EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
if (ec_key == NULL ||
- !tls1_curve_params_from_ec_key(&curve_id, &comp_id, ec_key) ||
- !tls1_check_curve_id(ssl, curve_id) ||
+ !tls1_curve_params_from_ec_key(&group_id, &comp_id, ec_key) ||
+ !tls1_check_group_id(ssl, group_id) ||
comp_id != TLSEXT_ECPOINTFORMAT_uncompressed) {
goto done;
}
@@ -1809,35 +1809,36 @@
}
-/* EC supported curves.
+/* Negotiated Groups
*
- * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
+ * https://tools.ietf.org/html/rfc4492#section-5.1.2
+ * https://tools.ietf.org/html/draft-ietf-tls-tls13-12#section-6.3.2.2 */
-static void ext_ec_curves_init(SSL *ssl) {
- OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
- ssl->s3->tmp.peer_ellipticcurvelist = NULL;
- ssl->s3->tmp.peer_ellipticcurvelist_length = 0;
+static void ext_supported_groups_init(SSL *ssl) {
+ OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
+ ssl->s3->tmp.peer_supported_group_list = NULL;
+ ssl->s3->tmp.peer_supported_group_list_len = 0;
}
-static int ext_ec_curves_add_clienthello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_clienthello(SSL *ssl, CBB *out) {
if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
return 1;
}
- CBB contents, curves_bytes;
- if (!CBB_add_u16(out, TLSEXT_TYPE_elliptic_curves) ||
+ CBB contents, groups_bytes;
+ if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
!CBB_add_u16_length_prefixed(out, &contents) ||
- !CBB_add_u16_length_prefixed(&contents, &curves_bytes)) {
+ !CBB_add_u16_length_prefixed(&contents, &groups_bytes)) {
return 0;
}
- const uint16_t *curves;
- size_t curves_len;
- tls1_get_curvelist(ssl, 0, &curves, &curves_len);
+ const uint16_t *groups;
+ size_t groups_len;
+ tls1_get_grouplist(ssl, 0, &groups, &groups_len);
size_t i;
- for (i = 0; i < curves_len; i++) {
- if (!CBB_add_u16(&curves_bytes, curves[i])) {
+ for (i = 0; i < groups_len; i++) {
+ if (!CBB_add_u16(&groups_bytes, groups[i])) {
return 0;
}
}
@@ -1845,54 +1846,55 @@
return CBB_flush(out);
}
-static int ext_ec_curves_parse_serverhello(SSL *ssl, uint8_t *out_alert,
- CBS *contents) {
+static int ext_supported_groups_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+ CBS *contents) {
/* This extension is not expected to be echoed by servers and is ignored. */
return 1;
}
-static int ext_ec_curves_parse_clienthello(SSL *ssl, uint8_t *out_alert,
- CBS *contents) {
+static int ext_supported_groups_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+ CBS *contents) {
if (contents == NULL) {
return 1;
}
- CBS elliptic_curve_list;
- if (!CBS_get_u16_length_prefixed(contents, &elliptic_curve_list) ||
- CBS_len(&elliptic_curve_list) == 0 ||
- (CBS_len(&elliptic_curve_list) & 1) != 0 ||
+ CBS supported_group_list;
+ if (!CBS_get_u16_length_prefixed(contents, &supported_group_list) ||
+ CBS_len(&supported_group_list) == 0 ||
+ (CBS_len(&supported_group_list) & 1) != 0 ||
CBS_len(contents) != 0) {
return 0;
}
- ssl->s3->tmp.peer_ellipticcurvelist = OPENSSL_malloc(CBS_len(&elliptic_curve_list));
- if (ssl->s3->tmp.peer_ellipticcurvelist == NULL) {
+ ssl->s3->tmp.peer_supported_group_list = OPENSSL_malloc(
+ CBS_len(&supported_group_list));
+ if (ssl->s3->tmp.peer_supported_group_list == NULL) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
- const size_t num_curves = CBS_len(&elliptic_curve_list) / 2;
+ const size_t num_groups = CBS_len(&supported_group_list) / 2;
size_t i;
- for (i = 0; i < num_curves; i++) {
- if (!CBS_get_u16(&elliptic_curve_list,
- &ssl->s3->tmp.peer_ellipticcurvelist[i])) {
+ for (i = 0; i < num_groups; i++) {
+ if (!CBS_get_u16(&supported_group_list,
+ &ssl->s3->tmp.peer_supported_group_list[i])) {
goto err;
}
}
- assert(CBS_len(&elliptic_curve_list) == 0);
- ssl->s3->tmp.peer_ellipticcurvelist_length = num_curves;
+ assert(CBS_len(&supported_group_list) == 0);
+ ssl->s3->tmp.peer_supported_group_list_len = num_groups;
return 1;
err:
- OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
- ssl->s3->tmp.peer_ellipticcurvelist = NULL;
+ OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
+ ssl->s3->tmp.peer_supported_group_list = NULL;
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
-static int ext_ec_curves_add_serverhello(SSL *ssl, CBB *out) {
+static int ext_supported_groups_add_serverhello(SSL *ssl, CBB *out) {
/* Servers don't echo this extension. */
return 1;
}
@@ -2003,12 +2005,12 @@
* intolerant to the last extension being zero-length. See
* https://crbug.com/363583. */
{
- TLSEXT_TYPE_elliptic_curves,
- ext_ec_curves_init,
- ext_ec_curves_add_clienthello,
- ext_ec_curves_parse_serverhello,
- ext_ec_curves_parse_clienthello,
- ext_ec_curves_add_serverhello,
+ TLSEXT_TYPE_supported_groups,
+ ext_supported_groups_init,
+ ext_supported_groups_add_clienthello,
+ ext_supported_groups_parse_serverhello,
+ ext_supported_groups_parse_clienthello,
+ ext_supported_groups_add_serverhello,
},
};
diff --git a/src/ssl/test/README.md b/src/ssl/test/README.md
new file mode 100644
index 0000000..7a46c32
--- /dev/null
+++ b/src/ssl/test/README.md
@@ -0,0 +1,35 @@
+# BoringSSL SSL Tests
+
+This directory contains BoringSSL's protocol-level test suite.
+
+Testing a TLS implementation can be difficult. We need to produce invalid but
+sufficiently correct handshakes to get our implementation close to its edge
+cases. TLS's cryptographic steps mean we cannot use a transcript and effectively
+need a TLS implementation on the other end. But we do not wish to litter
+BoringSSL with options for bugs to test against.
+
+Instead, we use a fork of the Go `crypto/tls` package, heavily patched with
+configurable bugs. This code, along with a test suite and harness written in Go,
+lives in the `runner` directory. The harness runs BoringSSL via a C/C++ shim
+binary which lives in this directory. All communication with the shim binary
+occurs with command-line flags, sockets, and standard I/O.
+
+This strategy also ensures we always test against a second implementation. All
+features should be implemented twice, once in C for BoringSSL and once in Go for
+testing. If possible, the Go code should be suitable for potentially
+upstreaming. However, sometimes test code has different needs. For example, our
+test DTLS code enforces strict ordering on sequence numbers and has controlled
+packet drop simulation.
+
+To run the tests manually, run `go test` from the `runner` directory. It takes
+command-line flags found at the top of `runner/runner.go`. The `-help` option
+also works after using `go test -c` to make a `runner.test` binary first.
+
+If adding a new test, these files may be a good starting point:
+
+ * `runner/runner.go`: the test harness and all the individual tests.
+ * `runner/common.go`: contains the `Config` and `ProtocolBugs` struct which
+ control the Go TLS implementation's behavior.
+ * `test_config.h`, `test_config.cc`: the command-line flags which control the
+ shim's behavior.
+ * `bssl_shim.cc`: the shim binary itself.
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 5effa58..519736d 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -117,11 +117,11 @@
static int g_config_index = 0;
static int g_state_index = 0;
-static bool SetConfigPtr(SSL *ssl, const TestConfig *config) {
+static bool SetTestConfig(SSL *ssl, const TestConfig *config) {
return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1;
}
-static const TestConfig *GetConfigPtr(const SSL *ssl) {
+static const TestConfig *GetTestConfig(const SSL *ssl) {
return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index);
}
@@ -300,7 +300,7 @@
static bool GetCertificate(SSL *ssl, ScopedX509 *out_x509,
ScopedEVP_PKEY *out_pkey) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (!config->digest_prefs.empty()) {
std::unique_ptr<char, Free<char>> digest_prefs(
@@ -353,7 +353,7 @@
if (pkey) {
TestState *test_state = GetTestState(ssl);
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (config->async) {
test_state->private_key = std::move(pkey);
SSL_set_private_key_method(ssl, &g_async_private_key_method);
@@ -370,7 +370,7 @@
}
static int SelectCertificateCallback(const struct ssl_early_callback_ctx *ctx) {
- const TestConfig *config = GetConfigPtr(ctx->ssl);
+ const TestConfig *config = GetTestConfig(ctx->ssl);
GetTestState(ctx->ssl)->early_callback_called = true;
if (!config->expected_server_name.empty()) {
@@ -422,7 +422,7 @@
}
static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
- if (GetConfigPtr(ssl)->async && !GetTestState(ssl)->cert_ready) {
+ if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) {
return -1;
}
@@ -446,7 +446,7 @@
static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) {
SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (!config->expected_ocsp_response.empty()) {
const uint8_t *data;
@@ -468,7 +468,7 @@
static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
unsigned int *out_len, void *arg) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (config->advertise_npn.empty()) {
return SSL_TLSEXT_ERR_NOACK;
}
@@ -480,7 +480,7 @@
static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
const uint8_t* in, unsigned inlen, void* arg) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (config->select_next_proto.empty()) {
return SSL_TLSEXT_ERR_NOACK;
}
@@ -492,7 +492,7 @@
static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen,
const uint8_t* in, unsigned inlen, void* arg) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (config->decline_alpn) {
return SSL_TLSEXT_ERR_NOACK;
}
@@ -514,7 +514,7 @@
char *out_identity,
unsigned max_identity_len,
uint8_t *out_psk, unsigned max_psk_len) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (strcmp(hint ? hint : "", config->psk_identity.c_str()) != 0) {
fprintf(stderr, "Server PSK hint did not match.\n");
@@ -536,7 +536,7 @@
static unsigned PskServerCallback(SSL *ssl, const char *identity,
uint8_t *out_psk, unsigned max_psk_len) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (strcmp(identity, config->psk_identity.c_str()) != 0) {
fprintf(stderr, "Client PSK identity did not match.\n");
@@ -584,7 +584,7 @@
}
static int DDoSCallback(const struct ssl_early_callback_ctx *early_context) {
- const TestConfig *config = GetConfigPtr(early_context->ssl);
+ const TestConfig *config = GetTestConfig(early_context->ssl);
static int callback_num = 0;
callback_num++;
@@ -597,7 +597,7 @@
static void InfoCallback(const SSL *ssl, int type, int val) {
if (type == SSL_CB_HANDSHAKE_DONE) {
- if (GetConfigPtr(ssl)->handshake_never_done) {
+ if (GetTestConfig(ssl)->handshake_never_done) {
fprintf(stderr, "handshake completed\n");
// Abort before any expected error code is printed, to ensure the overall
// test fails.
@@ -633,7 +633,7 @@
}
if (!encrypt) {
- return GetConfigPtr(ssl)->renew_ticket ? 2 : 1;
+ return GetTestConfig(ssl)->renew_ticket ? 2 : 1;
}
return 1;
}
@@ -655,10 +655,10 @@
abort();
}
- if (GetConfigPtr(ssl)->custom_extension_skip) {
+ if (GetTestConfig(ssl)->custom_extension_skip) {
return 0;
}
- if (GetConfigPtr(ssl)->custom_extension_fail_add) {
+ if (GetTestConfig(ssl)->custom_extension_fail_add) {
return -1;
}
@@ -888,7 +888,7 @@
return false;
}
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
TestState *test_state = GetTestState(ssl);
if (test_state->clock_delta.tv_usec != 0 ||
test_state->clock_delta.tv_sec != 0) {
@@ -926,7 +926,7 @@
AsyncBioAllowWrite(test_state->async_bio, 1);
return true;
case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
- ScopedEVP_PKEY pkey = LoadPrivateKey(GetConfigPtr(ssl)->send_channel_id);
+ ScopedEVP_PKEY pkey = LoadPrivateKey(GetTestConfig(ssl)->send_channel_id);
if (!pkey) {
return false;
}
@@ -953,7 +953,7 @@
// DoRead reads from |ssl|, resolving any asynchronous operations. It returns
// the result value of the final |SSL_read| call.
static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
TestState *test_state = GetTestState(ssl);
int ret;
do {
@@ -974,7 +974,7 @@
// WriteAll writes |in_len| bytes from |in| to |ssl|, resolving any asynchronous
// operations. It returns the result of the final |SSL_write| call.
static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
int ret;
do {
ret = SSL_write(ssl, in, in_len);
@@ -989,7 +989,7 @@
// DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It
// returns the result of the final |SSL_shutdown| call.
static int DoShutdown(SSL *ssl) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
int ret;
do {
ret = SSL_shutdown(ssl);
@@ -1001,7 +1001,7 @@
// initial handshake (or False Starts), whether all the properties are
// consistent with the test configuration and invariants.
static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
- const TestConfig *config = GetConfigPtr(ssl);
+ const TestConfig *config = GetTestConfig(ssl);
if (SSL_get_current_cipher(ssl) == nullptr) {
fprintf(stderr, "null cipher after handshake\n");
@@ -1187,7 +1187,7 @@
return false;
}
- if (!SetConfigPtr(ssl.get(), config) ||
+ if (!SetTestConfig(ssl.get(), config) ||
!SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) {
return false;
}
@@ -1219,6 +1219,9 @@
if (config->partial_write) {
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
}
+ if (config->no_tls13) {
+ SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3);
+ }
if (config->no_tls12) {
SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
}
@@ -1310,13 +1313,17 @@
}
if (config->enable_all_curves) {
static const int kAllCurves[] = {
- NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
+ NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
};
if (!SSL_set1_curves(ssl.get(), kAllCurves,
sizeof(kAllCurves) / sizeof(kAllCurves[0]))) {
return false;
}
}
+ if (config->initial_timeout_duration_ms > 0) {
+ DTLSv1_set_initial_timeout_duration(ssl.get(),
+ config->initial_timeout_duration_ms);
+ }
int sock = Connect(config->port);
if (sock == -1) {
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
index 3c6ad82..8b97545 100644
--- a/src/ssl/test/runner/chacha20_poly1305.go
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
package runner
import (
diff --git a/src/ssl/test/runner/chacha20_poly1305_test.go b/src/ssl/test/runner/chacha20_poly1305_test.go
index 4d19b8c..8cecb5c 100644
--- a/src/ssl/test/runner/chacha20_poly1305_test.go
+++ b/src/ssl/test/runner/chacha20_poly1305_test.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
package runner
import (
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index bfd31a5..799f2d5 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -43,6 +43,9 @@
// client indicates that it supports ECC with a curve and point format
// that we're happy with.
suiteECDHE = 1 << iota
+ // suiteCECPQ1 indicates that the cipher suite uses the
+ // experimental, temporary, and non-standard CECPQ1 key agreement.
+ suiteCECPQ1
// suiteECDSA indicates that the cipher suite involves an ECDSA
// signature and therefore may only be selected when the server's
// certificate is ECDSA. If this is not set then the cipher suite is
@@ -104,6 +107,10 @@
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
+ {TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, cecpq1RSAKA, suiteCECPQ1 | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+ {TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, cecpq1ECDSAKA, suiteCECPQ1 | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+ {TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, cecpq1RSAKA, suiteCECPQ1 | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
+ {TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, cecpq1ECDSAKA, suiteCECPQ1 | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil},
@@ -373,6 +380,15 @@
}
}
+func cecpq1ECDSAKA(version uint16) keyAgreement {
+ return &cecpq1KeyAgreement{
+ auth: &signedKeyAgreement{
+ sigType: signatureECDSA,
+ version: version,
+ },
+ }
+}
+
func ecdheRSAKA(version uint16) keyAgreement {
return &ecdheKeyAgreement{
auth: &signedKeyAgreement{
@@ -382,6 +398,15 @@
}
}
+func cecpq1RSAKA(version uint16) keyAgreement {
+ return &cecpq1KeyAgreement{
+ auth: &signedKeyAgreement{
+ sigType: signatureRSA,
+ version: version,
+ },
+ }
+}
+
func dheRSAKA(version uint16) keyAgreement {
return &dheKeyAgreement{
auth: &signedKeyAgreement{
@@ -472,4 +497,8 @@
const (
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc13
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc14
+ TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0x16b7
+ TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0x16b8
+ TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x16b9
+ TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0x16ba
)
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 2e9ce04..57b7b29 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -787,6 +787,10 @@
// on connection shutdown.
NoCloseNotify bool
+ // SendAlertOnShutdown, if non-zero, is the alert to send instead of
+ // close_notify on shutdown.
+ SendAlertOnShutdown alert
+
// ExpectCloseNotify, if true, requires a close_notify from the peer on
// shutdown. Records from the peer received after close_notify is sent
// are not discard.
@@ -834,6 +838,10 @@
// NullAllCiphers, if true, causes every cipher to behave like the null
// cipher.
NullAllCiphers bool
+
+ // SendSCTListOnResume, if not nil, causes the server to send the
+ // supplied SCT list in resumption handshakes.
+ SendSCTListOnResume []byte
}
func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 43548e8..3913995 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -1292,7 +1292,16 @@
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if c.handshakeComplete && !c.config.Bugs.NoCloseNotify {
- alertErr = c.sendAlert(alertCloseNotify)
+ alert := alertCloseNotify
+ if c.config.Bugs.SendAlertOnShutdown != 0 {
+ alert = c.config.Bugs.SendAlertOnShutdown
+ }
+ alertErr = c.sendAlert(alert)
+ // Clear local alerts when sending alerts so we continue to wait
+ // for the peer rather than closing the socket early.
+ if opErr, ok := alertErr.(*net.OpError); ok && opErr.Op == "local error" {
+ alertErr = nil
+ }
}
// Consume a close_notify from the peer if one hasn't been received
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index c3ee521..a31dfc0 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -46,6 +46,7 @@
b := c.rawInput
// Read a new packet only if the current one is empty.
+ var newPacket bool
if len(b.data) == 0 {
// Pick some absurdly large buffer size.
b.resize(maxCiphertext + recordHeaderLen)
@@ -57,6 +58,7 @@
return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length")
}
c.rawInput.resize(n)
+ newPacket = true
}
// Read out one record.
@@ -108,6 +110,13 @@
c.in.setErrorLocked(c.sendAlert(err))
}
b.off = off
+
+ // Require that ChangeCipherSpec always share a packet with either the
+ // previous or next handshake message.
+ if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil {
+ return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: ChangeCipherSpec not packed together with Finished"))
+ }
+
return typ, b, nil
}
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index d2cac98..72d1eb9 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -488,6 +488,10 @@
hs.hello.sessionId = hs.clientHello.sessionId
hs.hello.ticketSupported = c.config.Bugs.RenewTicketOnResume
+ if c.config.Bugs.SendSCTListOnResume != nil {
+ hs.hello.sctList = c.config.Bugs.SendSCTListOnResume
+ }
+
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
hs.finishedHash.discardHandshakeBuffer()
hs.writeClientHash(hs.clientHello.marshal())
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 54aa3d3..9a9962b 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -21,6 +21,7 @@
"math/big"
"./curve25519"
+ "./newhope"
)
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -252,13 +253,16 @@
// A ecdhCurve is an instance of ECDH-style key agreement for TLS.
type ecdhCurve interface {
- // generateKeypair generates a keypair using rand. It returns the
- // encoded public key.
- generateKeypair(rand io.Reader) (publicKey []byte, err error)
+ // offer generates a keypair using rand. It returns the encoded |publicKey|.
+ offer(rand io.Reader) (publicKey []byte, err error)
- // computeSecret performs a key exchange against peerKey and returns
- // the resulting shared secret.
- computeSecret(peerKey []byte) (preMasterSecret []byte, err error)
+ // accept responds to the |peerKey| generated by |offer| with the acceptor's
+ // |publicKey|, and returns agreed-upon |preMasterSecret| to the acceptor.
+ accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error)
+
+ // finish returns the computed |preMasterSecret|, given the |peerKey|
+ // generated by |accept|.
+ finish(peerKey []byte) (preMasterSecret []byte, err error)
}
// ellipticECDHCurve implements ecdhCurve with an elliptic.Curve.
@@ -267,7 +271,7 @@
privateKey []byte
}
-func (e *ellipticECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+func (e *ellipticECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) {
var x, y *big.Int
e.privateKey, x, y, err = elliptic.GenerateKey(e.curve, rand)
if err != nil {
@@ -276,7 +280,19 @@
return elliptic.Marshal(e.curve, x, y), nil
}
-func (e *ellipticECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+func (e *ellipticECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+ publicKey, err = e.offer(rand)
+ if err != nil {
+ return nil, nil, err
+ }
+ preMasterSecret, err = e.finish(peerKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ return
+}
+
+func (e *ellipticECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
x, y := elliptic.Unmarshal(e.curve, peerKey)
if x == nil {
return nil, errors.New("tls: invalid peer key")
@@ -294,7 +310,7 @@
privateKey [32]byte
}
-func (e *x25519ECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) {
+func (e *x25519ECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) {
_, err = io.ReadFull(rand, e.privateKey[:])
if err != nil {
return
@@ -304,7 +320,19 @@
return out[:], nil
}
-func (e *x25519ECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) {
+func (e *x25519ECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+ publicKey, err = e.offer(rand)
+ if err != nil {
+ return nil, nil, err
+ }
+ preMasterSecret, err = e.finish(peerKey)
+ if err != nil {
+ return nil, nil, err
+ }
+ return
+}
+
+func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
if len(peerKey) != 32 {
return nil, errors.New("tls: invalid peer key")
}
@@ -321,6 +349,66 @@
return out[:], nil
}
+// cecpq1Curve is combined elliptic curve (X25519) and post-quantum (new hope) key
+// agreement.
+type cecpq1Curve struct {
+ x25519 *x25519ECDHCurve
+ newhope *newhope.Poly
+}
+
+func (e *cecpq1Curve) offer(rand io.Reader) (publicKey []byte, err error) {
+ var x25519OfferMsg, newhopeOfferMsg []byte
+
+ e.x25519 = new(x25519ECDHCurve)
+ if x25519OfferMsg, err = e.x25519.offer(rand); err != nil {
+ return nil, err
+ }
+
+ newhopeOfferMsg, e.newhope = newhope.Offer(rand)
+
+ return append(x25519OfferMsg, newhopeOfferMsg[:]...), nil
+}
+
+func (e *cecpq1Curve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+ if len(peerKey) != 32+newhope.OfferMsgLen {
+ return nil, nil, errors.New("cecpq1: invalid offer message")
+ }
+
+ var x25519AcceptMsg, newhopeAcceptMsg []byte
+ var x25519Secret []byte
+ var newhopeSecret newhope.Key
+
+ x25519 := new(x25519ECDHCurve)
+ if x25519AcceptMsg, x25519Secret, err = x25519.accept(rand, peerKey[:32]); err != nil {
+ return nil, nil, err
+ }
+
+ if newhopeSecret, newhopeAcceptMsg, err = newhope.Accept(rand, peerKey[32:]); err != nil {
+ return nil, nil, err
+ }
+
+ return append(x25519AcceptMsg, newhopeAcceptMsg[:]...), append(x25519Secret, newhopeSecret[:]...), nil
+}
+
+func (e *cecpq1Curve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
+ if len(peerKey) != 32+newhope.AcceptMsgLen {
+ return nil, errors.New("cecpq1: invalid accept message")
+ }
+
+ var x25519Secret []byte
+ var newhopeSecret newhope.Key
+
+ if x25519Secret, err = e.x25519.finish(peerKey[:32]); err != nil {
+ return nil, err
+ }
+
+ if newhopeSecret, err = e.newhope.Finish(peerKey[32:]); err != nil {
+ return nil, err
+ }
+
+ return append(x25519Secret, newhopeSecret[:]...), nil
+}
+
func curveForCurveID(id CurveID) (ecdhCurve, bool) {
switch id {
case CurveP224:
@@ -551,7 +639,7 @@
return nil, errors.New("tls: preferredCurves includes unsupported curve")
}
- publicKey, err := ka.curve.generateKeypair(config.rand())
+ publicKey, err := ka.curve.offer(config.rand())
if err != nil {
return nil, err
}
@@ -577,7 +665,7 @@
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errClientKeyExchange
}
- return ka.curve.computeSecret(ckx.ciphertext[1:])
+ return ka.curve.finish(ckx.ciphertext[1:])
}
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
@@ -612,11 +700,7 @@
return nil, nil, errors.New("missing ServerKeyExchange message")
}
- publicKey, err := ka.curve.generateKeypair(config.rand())
- if err != nil {
- return nil, nil, err
- }
- preMasterSecret, err := ka.curve.computeSecret(ka.peerKey)
+ publicKey, preMasterSecret, err := ka.curve.accept(config.rand(), ka.peerKey)
if err != nil {
return nil, nil, err
}
@@ -632,6 +716,77 @@
return preMasterSecret, ckx, nil
}
+// cecpq1RSAKeyAgreement is like an ecdheKeyAgreement, but using the cecpq1Curve
+// pseudo-curve, and without any parameters (e.g. curve name) other than the
+// keys being exchanged. The signature may either be ECDSA or RSA.
+type cecpq1KeyAgreement struct {
+ auth keyAgreementAuthentication
+ curve ecdhCurve
+ peerKey []byte
+}
+
+func (ka *cecpq1KeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+ ka.curve = &cecpq1Curve{}
+ publicKey, err := ka.curve.offer(config.rand())
+ if err != nil {
+ return nil, err
+ }
+
+ var params []byte
+ params = append(params, byte(len(publicKey)>>8))
+ params = append(params, byte(len(publicKey)&0xff))
+ params = append(params, publicKey[:]...)
+
+ return ka.auth.signParameters(config, cert, clientHello, hello, params)
+}
+
+func (ka *cecpq1KeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+ if len(ckx.ciphertext) < 2 {
+ return nil, errClientKeyExchange
+ }
+ peerKeyLen := int(ckx.ciphertext[0])<<8 + int(ckx.ciphertext[1])
+ peerKey := ckx.ciphertext[2:]
+ if peerKeyLen != len(peerKey) {
+ return nil, errClientKeyExchange
+ }
+ return ka.curve.finish(peerKey)
+}
+
+func (ka *cecpq1KeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+ if len(skx.key) < 2 {
+ return errServerKeyExchange
+ }
+ peerKeyLen := int(skx.key[0])<<8 + int(skx.key[1])
+ // Save the peer key for later.
+ if len(skx.key) < 2+peerKeyLen {
+ return errServerKeyExchange
+ }
+ ka.peerKey = skx.key[2 : 2+peerKeyLen]
+ if peerKeyLen != len(ka.peerKey) {
+ return errServerKeyExchange
+ }
+
+ // Check the signature.
+ params := skx.key[:2+peerKeyLen]
+ sig := skx.key[2+peerKeyLen:]
+ return ka.auth.verifyParameters(config, clientHello, serverHello, cert, params, sig)
+}
+
+func (ka *cecpq1KeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+ curve := &cecpq1Curve{}
+ publicKey, preMasterSecret, err := curve.accept(config.rand(), ka.peerKey)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ ckx := new(clientKeyExchangeMsg)
+ ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)>>8))
+ ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)&0xff))
+ ckx.ciphertext = append(ckx.ciphertext, publicKey[:]...)
+
+ return preMasterSecret, ckx, nil
+}
+
// dheRSAKeyAgreement implements a TLS key agreement where the server generates
// an ephemeral Diffie-Hellman public/private key pair and signs it. The
// pre-master secret is then calculated using Diffie-Hellman.
diff --git a/src/ssl/test/runner/newhope/newhope.go b/src/ssl/test/runner/newhope/newhope.go
new file mode 100644
index 0000000..36c2c85
--- /dev/null
+++ b/src/ssl/test/runner/newhope/newhope.go
@@ -0,0 +1,319 @@
+// Copyright (c) 2016, 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. */
+
+// package newhope contains a post-quantum key agreement algorithm,
+// reimplemented from the reference implementation at
+// https://github.com/tpoeppelmann/newhope.
+//
+// Note that this package does not interoperate with the reference
+// implementation.
+package newhope
+
+import (
+ "crypto/aes"
+ "crypto/cipher"
+ "errors"
+ "io"
+)
+
+const (
+ // q is the prime that defines the field.
+ q = 12289
+ // n is the number of coefficients in polynomials.
+ n = 1024
+ // k is the width of the noise distribution.
+ k = 16
+
+ // These values are used in the NTT calculation. See the paper for
+ // details about their origins.
+ omega = 49
+ invOmega = 1254
+ sqrtOmega = 7
+ invSqrtOmega = 8778
+ invN = 12277
+
+ // encodedPolyLen is the length, in bytes, of an encoded polynomial. The
+ // encoding uses 14 bits per coefficient.
+ encodedPolyLen = (n * 14) / 8
+
+ // offerMsgLen is the length, in bytes, of the offering (first) message of
+ // the key exchange.
+ OfferMsgLen = encodedPolyLen + 32
+
+ // acceptMsgLen is the length, in bytes, of the accepting (second) message
+ // of the key exchange.
+ AcceptMsgLen = encodedPolyLen + 256
+)
+
+// count16Bits returns the number of '1' bits in v.
+func count16Bits(v uint16) (sum uint16) {
+ for i := 0; i < 16; i++ {
+ sum += v & 1
+ v >>= 1
+ }
+
+ return sum
+}
+
+// Poly is a polynomial of n coefficients.
+type Poly [n]uint16
+
+// Key is the result of a key agreement.
+type Key [32]uint8
+
+// sampleNoise returns a random polynomial where the coefficients are
+// drawn from the noise distribution.
+func sampleNoise(rand io.Reader) *Poly {
+ poly := new(Poly)
+ buf := make([]byte, 4)
+
+ for i := range poly {
+ if _, err := io.ReadFull(rand, buf); err != nil {
+ panic(err)
+ }
+ a := count16Bits(uint16(buf[0])<<8 | uint16(buf[1]))
+ b := count16Bits(uint16(buf[2])<<8 | uint16(buf[3]))
+ poly[i] = (q + a - b) % q
+ }
+
+ return poly
+}
+
+// randomPolynomial returns a random polynomial where the coefficients are
+// drawn uniformly at random from the underlying field.
+func randomPolynomial(rand io.Reader) *Poly {
+ poly := new(Poly)
+
+ buf := make([]byte, 2)
+ for i := range poly {
+ for {
+ if _, err := io.ReadFull(rand, buf); err != nil {
+ panic(err)
+ }
+
+ v := uint16(buf[1])<<8 | uint16(buf[0])
+ v &= 0x3fff
+
+ if v < q {
+ poly[i] = v
+ break
+ }
+ }
+ }
+
+ return poly
+}
+
+type zeroReader struct {
+ io.Reader
+}
+
+func (z *zeroReader) Read(dst []byte) (n int, err error) {
+ for i := range dst {
+ dst[i] = 0
+ }
+ return len(dst), nil
+}
+
+// seedToPolynomial uses AES-CTR to generate a pseudo-random polynomial given a
+// 32-byte seed.
+func seedToPolynomial(seed []byte) *Poly {
+ aes, err := aes.NewCipher(seed[0:16])
+ if err != nil {
+ panic(err)
+ }
+ stream := cipher.NewCTR(aes, seed[16:32])
+ reader := &cipher.StreamReader{S: stream, R: &zeroReader{}}
+ return randomPolynomial(reader)
+}
+
+// forwardNTT converts |in| into the frequency domain.
+func forwardNTT(in *Poly) *Poly {
+ return ntt(in, omega, sqrtOmega, 1, 1)
+}
+
+// inverseNTT converts |in| into the time domain.
+func inverseNTT(in *Poly) *Poly {
+ return ntt(in, invOmega, 1, invSqrtOmega, invN)
+}
+
+// ntt performs the number-theoretic transform (a discrete Fourier transform in
+// a field) on in. Significant magic is in effect here. See the paper for the
+// details of how this works.
+func ntt(in *Poly, omega, preScaleBase, postScaleBase, postScale uint16) *Poly {
+ out := new(Poly)
+ omega_to_the_i := 1
+
+ for i := range out {
+ omegaToTheIJ := 1
+ preScale := int(1)
+ sum := 0
+
+ for j := range in {
+ t := (int(in[j]) * preScale) % q
+ sum += (t * omegaToTheIJ) % q
+ omegaToTheIJ = (omegaToTheIJ * omega_to_the_i) % q
+ preScale = (int(preScaleBase) * preScale) % q
+ }
+
+ out[i] = uint16((sum * int(postScale)) % q)
+
+ omega_to_the_i = (omega_to_the_i * int(omega)) % q
+ postScale = uint16((int(postScale) * int(postScaleBase)) % q)
+ }
+
+ return out
+}
+
+// encodeRec encodes the reconciliation data compactly, for use in the accept
+// message.
+func encodeRec(rec *reconciliationData) []byte {
+ var ret [n / 4]byte
+
+ for i := 0; i < n/4; i++ {
+ ret[i] = rec[4*i] | rec[4*i+1]<<2 | rec[4*i+2]<<4 | rec[4*i+3]<<6
+ }
+
+ return ret[:]
+}
+
+// decodeRec decodes reconciliation data from the accept message.
+func decodeRec(message []byte) (rec *reconciliationData) {
+ rec = new(reconciliationData)
+
+ for i, b := range message {
+ rec[4*i] = b & 0x03
+ rec[4*i+1] = (b >> 2) & 0x3
+ rec[4*i+2] = (b >> 4) & 0x3
+ rec[4*i+3] = b >> 6
+ }
+
+ return rec
+}
+
+// encodePoly returns a byte array that encodes a polynomial compactly, with 14
+// bits per coefficient.
+func encodePoly(poly *Poly) []byte {
+ ret := make([]byte, encodedPolyLen)
+
+ for i := 0; i < n/4; i++ {
+ t0 := poly[4*i]
+ t1 := poly[4*i+1]
+ t2 := poly[4*i+2]
+ t3 := poly[4*i+3]
+
+ ret[7*i] = byte(t0)
+ ret[7*i+1] = byte(t0>>8) | byte(t1<<6)
+ ret[7*i+2] = byte(t1 >> 2)
+ ret[7*i+3] = byte(t1>>10) | byte(t2<<4)
+ ret[7*i+4] = byte(t2 >> 4)
+ ret[7*i+5] = byte(t2>>12) | byte(t3<<2)
+ ret[7*i+6] = byte(t3 >> 6)
+ }
+
+ return ret
+}
+
+// decodePoly inverts encodePoly.
+func decodePoly(encoded []byte) *Poly {
+ ret := new(Poly)
+
+ for i := 0; i < n/4; i++ {
+ ret[4*i] = uint16(encoded[7*i]) | uint16(encoded[7*i+1]&0x3f)<<8
+ ret[4*i+1] = uint16(encoded[7*i+1])>>6 | uint16(encoded[7*i+2])<<2 | uint16(encoded[7*i+3]&0x0f)<<10
+ ret[4*i+2] = uint16(encoded[7*i+3])>>4 | uint16(encoded[7*i+4])<<4 | uint16(encoded[7*i+5]&0x03)<<12
+ ret[4*i+3] = uint16(encoded[7*i+5])>>2 | uint16(encoded[7*i+6])<<6
+ }
+
+ return ret
+}
+
+// Offer starts a new key exchange. It returns a message that should be
+// transmitted to the peer, and a polynomial that must be retained in order to
+// complete the exchange.
+func Offer(rand io.Reader) (offerMsg []byte, sFreq *Poly) {
+ seed := make([]byte, 32)
+
+ if _, err := io.ReadFull(rand, seed); err != nil {
+ panic(err)
+ }
+
+ aFreq := seedToPolynomial(seed)
+ sFreq = forwardNTT(sampleNoise(rand))
+ eFreq := forwardNTT(sampleNoise(rand))
+
+ bFreq := new(Poly)
+ for i := range bFreq {
+ bFreq[i] = uint16((int(sFreq[i])*int(aFreq[i]) + int(eFreq[i])) % q)
+ }
+
+ offerMsg = encodePoly(bFreq)
+ offerMsg = append(offerMsg, seed[:]...)
+ return offerMsg, sFreq
+}
+
+// Accept processes a message generated by |Offer| and returns a reply message
+// and the shared key.
+func Accept(rand io.Reader, offerMsg []byte) (sharedKey Key, acceptMsg []byte, err error) {
+ if len(offerMsg) != OfferMsgLen {
+ return sharedKey, nil, errors.New("newhope: offer message has incorrect length")
+ }
+
+ bFreq := decodePoly(offerMsg)
+ seed := offerMsg[encodedPolyLen:]
+
+ aFreq := seedToPolynomial(seed)
+ sPrimeFreq := forwardNTT(sampleNoise(rand))
+ ePrimeFreq := forwardNTT(sampleNoise(rand))
+
+ uFreq := new(Poly)
+ for i := range uFreq {
+ uFreq[i] = uint16((int(sPrimeFreq[i])*int(aFreq[i]) + int(ePrimeFreq[i])) % q)
+ }
+
+ vFreq := new(Poly)
+ for i := range vFreq {
+ vFreq[i] = uint16((int(sPrimeFreq[i]) * int(bFreq[i])) % q)
+ }
+
+ v := inverseNTT(vFreq)
+ ePrimePrime := sampleNoise(rand)
+ for i := range v {
+ v[i] = uint16((int(v[i]) + int(ePrimePrime[i])) % q)
+ }
+
+ rec := helprec(rand, v)
+
+ sharedKey = reconcile(v, rec)
+ acceptMsg = encodePoly(uFreq)
+ acceptMsg = append(acceptMsg, encodeRec(rec)[:]...)
+ return sharedKey, acceptMsg, nil
+}
+
+// Finish processes the reply from the peer and returns the shared key.
+func (sk *Poly) Finish(acceptMsg []byte) (sharedKey Key, err error) {
+ if len(acceptMsg) != AcceptMsgLen {
+ return sharedKey, errors.New("newhope: accept message has incorrect length")
+ }
+
+ uFreq := decodePoly(acceptMsg[:encodedPolyLen])
+ rec := decodeRec(acceptMsg[encodedPolyLen:])
+
+ for i, u := range uFreq {
+ uFreq[i] = uint16((int(u) * int(sk[i])) % q)
+ }
+ u := inverseNTT(uFreq)
+
+ return reconcile(u, rec), nil
+}
diff --git a/src/ssl/test/runner/newhope/newhope_test.go b/src/ssl/test/runner/newhope/newhope_test.go
new file mode 100644
index 0000000..31e95c7
--- /dev/null
+++ b/src/ssl/test/runner/newhope/newhope_test.go
@@ -0,0 +1,154 @@
+// Copyright (c) 2016, 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. */
+
+package newhope
+
+import (
+ "bytes"
+ "crypto/rand"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "testing"
+)
+
+func TestNTTRoundTrip(t *testing.T) {
+ var a Poly
+ for i := range a {
+ a[i] = uint16(i)
+ }
+
+ frequency := forwardNTT(&a)
+ original := inverseNTT(frequency)
+
+ for i, v := range a {
+ if v != original[i] {
+ t.Errorf("NTT didn't invert correctly: original[%d] = %d", i, original[i])
+ break
+ }
+ }
+}
+
+func TestNTTInv(t *testing.T) {
+ var a Poly
+ for i := range a {
+ a[i] = uint16(i)
+ }
+
+ result := ntt(&a, invOmega, 1, invSqrtOmega, invN)
+ if result[0] != 6656 || result[1] != 1792 || result[2] != 1234 {
+ t.Errorf("NTT^-1 gave bad result: %v", result[:8])
+ }
+}
+
+func disabledTestNoise(t *testing.T) {
+ var buckets [1 + 2*k]int
+ numSamples := 100
+
+ for i := 0; i < numSamples; i++ {
+ noise := sampleNoise(rand.Reader)
+ for _, v := range noise {
+ value := (int(v) + k) % q
+ buckets[value]++
+ }
+ }
+
+ sum := 0
+ squareSum := 0
+
+ for i, count := range buckets {
+ sum += (i - k) * count
+ squareSum += (i - k) * (i - k) * count
+ }
+
+ mean := float64(sum) / float64(n*numSamples)
+ if mean < -0.5 || 0.5 < mean {
+ t.Errorf("mean out of range: %f", mean)
+ }
+
+ expectedVariance := 0.5 * 0.5 * float64(k*2) // I think?
+ variance := float64(squareSum)/float64(n*numSamples) - mean*mean
+
+ if variance < expectedVariance-1.0 || expectedVariance+1.0 < variance {
+ t.Errorf("variance out of range: got %f, want %f", variance, expectedVariance)
+ }
+
+ file, err := ioutil.TempFile("", "noise")
+ fmt.Printf("writing noise to %s\n", file.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i, count := range buckets {
+ dots := ""
+ for i := 0; i < count/(3*numSamples); i++ {
+ dots += "++"
+ }
+ fmt.Fprintf(file, "%+d\t%d\t%s\n", i-k, count, dots)
+ }
+ file.Close()
+}
+
+func TestSeedToPolynomial(t *testing.T) {
+ seed := make([]byte, 32)
+ seed[0] = 1
+ seed[31] = 2
+
+ poly := seedToPolynomial(seed)
+ if poly[0] != 3313 || poly[1] != 9277 || poly[2] != 11020 {
+ t.Errorf("bad result: %v", poly[:3])
+ }
+}
+
+func TestEncodeDecodePoly(t *testing.T) {
+ poly := randomPolynomial(rand.Reader)
+ poly2 := decodePoly(encodePoly(poly))
+ if *poly != *poly2 {
+ t.Errorf("decodePoly(encodePoly) isn't the identity function")
+ }
+}
+
+func TestEncodeDecodeRec(t *testing.T) {
+ var r reconciliationData
+ if _, err := io.ReadFull(rand.Reader, r[:]); err != nil {
+ panic(err)
+ }
+ for i := range r {
+ r[i] &= 3
+ }
+
+ encoded := encodeRec(&r)
+ decoded := decodeRec(encoded)
+
+ if *decoded != r {
+ t.Errorf("bad decode of rec")
+ }
+}
+
+func TestExchange(t *testing.T) {
+ for count := 0; count < 64; count++ {
+ offerMsg, state := Offer(rand.Reader)
+ sharedKey1, acceptMsg, err := Accept(rand.Reader, offerMsg)
+ if err != nil {
+ t.Errorf("Accept: %v", err)
+ }
+ sharedKey2, err := state.Finish(acceptMsg)
+ if err != nil {
+ t.Fatal("Finish: %v", err)
+ }
+
+ if !bytes.Equal(sharedKey1[:], sharedKey2[:]) {
+ t.Fatalf("keys mismatched on iteration %d: %x vs %x", count, sharedKey1, sharedKey2)
+ }
+ }
+}
diff --git a/src/ssl/test/runner/newhope/reconciliation.go b/src/ssl/test/runner/newhope/reconciliation.go
new file mode 100644
index 0000000..07479fe
--- /dev/null
+++ b/src/ssl/test/runner/newhope/reconciliation.go
@@ -0,0 +1,132 @@
+// Copyright (c) 2016, 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. */
+
+package newhope
+
+// This file contains the reconciliation algorithm for NewHope. This is simply a
+// monkey-see-monkey-do version of the reference code, with the exception that
+// the key resulting from reconciliation is whitened with SHA2 rather than SHA3.
+//
+// Thanks to the authors of the reference code for allowing us to release this
+// under the BoringSSL license.
+
+import (
+ "crypto/sha256"
+ "io"
+)
+
+func abs(v int32) int32 {
+ mask := v >> 31
+ return (v ^ mask) - mask
+}
+
+func f(x int32) (v0, v1, k int32) {
+ // Next 6 lines compute t = x/q;
+ b := x * 2730
+ t := b >> 25
+ b = x - t*12289
+ b = 12288 - b
+ b >>= 31
+ t -= b
+
+ r := t & 1
+ xit := (t >> 1)
+ v0 = xit + r // v0 = round(x/(2*q))
+
+ t -= 1
+ r = t & 1
+ v1 = (t >> 1) + r
+
+ k = abs(x - (v0 * 2 * q))
+ return
+}
+
+// reconciliationData is the data needed for reconciliation. There are 2 bits
+// per coefficient; this is the unpacked form.
+type reconciliationData [n]uint8
+
+func helprec(rand io.Reader, v *Poly) *reconciliationData {
+ var randBits [n / (4 * 8)]byte
+ if _, err := io.ReadFull(rand, randBits[:]); err != nil {
+ panic(err)
+ }
+
+ ret := new(reconciliationData)
+
+ for i := uint(0); i < n/4; i++ {
+ rbit := int32((randBits[i>>3] >> (i & 7)) & 1)
+
+ a0, b0, k0 := f(8*int32(v[i]) + 4*rbit)
+ a1, b1, k1 := f(8*int32(v[256+i]) + 4*rbit)
+ a2, b2, k2 := f(8*int32(v[512+i]) + 4*rbit)
+ a3, b3, k3 := f(8*int32(v[768+i]) + 4*rbit)
+
+ k := (2*q - 1 - (k0 + k1 + k2 + k3)) >> 31
+
+ v0 := ((^k) & a0) ^ (k & b0)
+ v1 := ((^k) & a1) ^ (k & b1)
+ v2 := ((^k) & a2) ^ (k & b2)
+ v3 := ((^k) & a3) ^ (k & b3)
+
+ ret[i] = uint8((v0 - v3) & 3)
+ ret[i+256] = uint8((v1 - v3) & 3)
+ ret[i+512] = uint8((v2 - v3) & 3)
+ ret[i+768] = uint8((-k + 2*v3) & 3)
+ }
+
+ return ret
+}
+
+func g(x int32) int32 {
+ // Next 6 lines compute t = x/(4*q);
+ b := x * 2730
+ t := b >> 27
+ b = x - t*49156
+ b = 49155 - b
+ b >>= 31
+ t -= b
+
+ c := t & 1
+ t = (t >> 1) + c // t = round(x/(8*q))
+
+ t *= 8 * q
+
+ return abs(t - x)
+}
+
+func ldDecode(xi0, xi1, xi2, xi3 int32) uint8 {
+ t := g(xi0)
+ t += g(xi1)
+ t += g(xi2)
+ t += g(xi3)
+
+ t -= 8 * q
+ t >>= 31
+ return uint8(t & 1)
+}
+
+func reconcile(v *Poly, reconciliation *reconciliationData) Key {
+ key := new(Key)
+
+ for i := uint(0); i < n/4; i++ {
+ t0 := 16*q + 8*int32(v[i]) - q*(2*int32(reconciliation[i])+int32(reconciliation[i+768]))
+ t1 := 16*q + 8*int32(v[i+256]) - q*(2*int32(reconciliation[256+i])+int32(reconciliation[i+768]))
+ t2 := 16*q + 8*int32(v[i+512]) - q*(2*int32(reconciliation[512+i])+int32(reconciliation[i+768]))
+ t3 := 16*q + 8*int32(v[i+768]) - q*int32(reconciliation[i+768])
+
+ key[i>>3] |= ldDecode(t0, t1, t2, t3) << (i & 7)
+ }
+
+ return sha256.Sum256(key[:])
+}
diff --git a/src/ssl/test/runner/recordingconn.go b/src/ssl/test/runner/recordingconn.go
index 752610e..dfc10c7 100644
--- a/src/ssl/test/runner/recordingconn.go
+++ b/src/ssl/test/runner/recordingconn.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
package runner
import (
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 5b746c6..c10987e 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
package runner
import (
@@ -901,6 +915,10 @@
{"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
{"ECDHE-RSA-CHACHA20-POLY1305-OLD", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD},
{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+ {"CECPQ1-RSA-CHACHA20-POLY1305-SHA256", TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"CECPQ1-ECDSA-CHACHA20-POLY1305-SHA256", TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
+ {"CECPQ1-RSA-AES256-GCM-SHA384", TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384},
+ {"CECPQ1-ECDSA-AES256-GCM-SHA384", TLS_CECPQ1_ECDSA_WITH_AES_256_GCM_SHA384},
{"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},
@@ -1391,7 +1409,7 @@
},
{
name: "DisableEverything",
- flags: []string{"-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
+ flags: []string{"-no-tls13", "-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"},
shouldFail: true,
expectedError: ":WRONG_SSL_VERSION:",
},
@@ -2061,6 +2079,19 @@
shimShutsDown: true,
},
{
+ name: "Unclean-Shutdown-Alert",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendAlertOnShutdown: alertDecompressionFailure,
+ ExpectCloseNotify: true,
+ },
+ },
+ shimShutsDown: true,
+ flags: []string{"-check-close-notify"},
+ shouldFail: true,
+ expectedError: ":SSLV3_ALERT_DECOMPRESSION_FAILURE:",
+ },
+ {
name: "LargePlaintext",
config: Config{
Bugs: ProtocolBugs{
@@ -2268,6 +2299,10 @@
// NULL ciphers must be explicitly enabled.
flags = append(flags, "-cipher", "DEFAULT:NULL-SHA")
}
+ if hasComponent(suite.name, "CECPQ1") {
+ // CECPQ1 ciphers must be explicitly enabled.
+ flags = append(flags, "-cipher", "DEFAULT:kCECPQ1")
+ }
for _, ver := range tlsVersions {
if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
@@ -2714,6 +2749,40 @@
shouldFail: true,
expectedError: ":UNEXPECTED_MESSAGE:",
})
+
+ // Client auth is only legal in certificate-based ciphers.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ClientAuth-PSK",
+ config: Config{
+ CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
+ PreSharedKey: []byte("secret"),
+ ClientAuth: RequireAnyClientCert,
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-psk", "secret",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ })
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "ClientAuth-ECDHE_PSK",
+ config: Config{
+ CipherSuites: []uint16{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
+ PreSharedKey: []byte("secret"),
+ ClientAuth: RequireAnyClientCert,
+ },
+ flags: []string{
+ "-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+ "-key-file", path.Join(*resourceDir, rsaKeyFile),
+ "-psk", "secret",
+ },
+ shouldFail: true,
+ expectedError: ":UNEXPECTED_MESSAGE:",
+ })
}
func addExtendedMasterSecretTests() {
@@ -3879,6 +3948,20 @@
resumeSession: true,
})
testCases = append(testCases, testCase{
+ name: "SendSCTListOnResume",
+ config: Config{
+ Bugs: ProtocolBugs{
+ SendSCTListOnResume: []byte("bogus"),
+ },
+ },
+ flags: []string{
+ "-enable-signed-cert-timestamps",
+ "-expect-signed-cert-timestamps",
+ base64.StdEncoding.EncodeToString(testSCTList),
+ },
+ resumeSession: true,
+ })
+ testCases = append(testCases, testCase{
name: "SignedCertificateTimestampList-Server",
testType: serverTest,
flags: []string{
@@ -4583,6 +4666,24 @@
60 * time.Second,
}
+// shortTimeouts is an alternate set of timeouts which would occur if the
+// initial timeout duration was set to 250ms.
+var shortTimeouts = []time.Duration{
+ 250 * time.Millisecond,
+ 500 * time.Millisecond,
+ 1 * time.Second,
+ 2 * time.Second,
+ 4 * time.Second,
+ 8 * time.Second,
+ 16 * time.Second,
+ 32 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+ 60 * time.Second,
+}
+
func addDTLSRetransmitTests() {
// Test that this is indeed the timeout schedule. Stress all
// four patterns of handshake.
@@ -4659,6 +4760,31 @@
},
flags: []string{"-async"},
})
+
+ // Test the timeout schedule when a shorter initial timeout duration is set.
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ name: "DTLS-Retransmit-Short-Client",
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async", "-initial-timeout-duration-ms", "250"},
+ })
+ testCases = append(testCases, testCase{
+ protocol: dtls,
+ testType: serverTest,
+ name: "DTLS-Retransmit-Short-Server",
+ config: Config{
+ Bugs: ProtocolBugs{
+ TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
+ },
+ },
+ resumeSession: true,
+ flags: []string{"-async", "-initial-timeout-duration-ms", "250"},
+ })
}
func addExportKeyingMaterialTests() {
diff --git a/src/ssl/test/runner/runner_test.go b/src/ssl/test/runner/runner_test.go
index 320ff52..1ba28e0 100644
--- a/src/ssl/test/runner/runner_test.go
+++ b/src/ssl/test/runner/runner_test.go
@@ -1,3 +1,17 @@
+// Copyright (c) 2016, 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. */
+
package runner
import "testing"
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 67a017d..536978b 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -56,6 +56,7 @@
&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 },
@@ -148,6 +149,7 @@
&TestConfig::expect_server_key_exchange_hash },
{ "-expect-key-exchange-info",
&TestConfig::expect_key_exchange_info },
+ { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
};
} // namespace
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index fe117d8..aff194e 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -38,6 +38,7 @@
bool write_different_record_sizes = false;
bool cbc_record_splitting = false;
bool partial_write = false;
+ bool no_tls13 = false;
bool no_tls12 = false;
bool no_tls11 = false;
bool no_tls1 = false;
@@ -104,6 +105,7 @@
bool use_sparse_dh_prime = false;
int expect_key_exchange_info = 0;
bool use_old_client_cert_callback = false;
+ int initial_timeout_duration_ms = 0;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/third_party/android-cmake/AndroidNdkGdb.cmake b/src/third_party/android-cmake/AndroidNdkGdb.cmake
new file mode 100644
index 0000000..0677dcd
--- /dev/null
+++ b/src/third_party/android-cmake/AndroidNdkGdb.cmake
@@ -0,0 +1,96 @@
+# Copyright (c) 2014, Pavel Rojtberg
+# 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. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
+
+# ------------------------------------------------------------------------------
+# Usage:
+# 1. place AndroidNdkGdb.cmake somewhere inside ${CMAKE_MODULE_PATH}
+# 2. inside your project add
+#
+# include(AndroidNdkGdb)
+# android_ndk_gdb_enable()
+# # for each target
+# add_library(MyLibrary ...)
+# android_ndk_gdb_debuggable(MyLibrary)
+
+
+# add gdbserver and general gdb configuration to project
+# also create a mininal NDK skeleton so ndk-gdb finds the paths
+#
+# the optional parameter defines the path to the android project.
+# uses PROJECT_SOURCE_DIR by default.
+macro(android_ndk_gdb_enable)
+ if(ANDROID)
+ # create custom target that depends on the real target so it gets executed afterwards
+ add_custom_target(NDK_GDB ALL)
+
+ if(${ARGC})
+ set(ANDROID_PROJECT_DIR ${ARGV0})
+ else()
+ set(ANDROID_PROJECT_DIR ${PROJECT_SOURCE_DIR})
+ endif()
+
+ set(NDK_GDB_SOLIB_PATH ${ANDROID_PROJECT_DIR}/obj/local/${ANDROID_NDK_ABI_NAME}/)
+ file(MAKE_DIRECTORY ${NDK_GDB_SOLIB_PATH})
+
+ # 1. generate essential Android Makefiles
+ file(MAKE_DIRECTORY ${ANDROID_PROJECT_DIR}/jni)
+ if(NOT EXISTS ${ANDROID_PROJECT_DIR}/jni/Android.mk)
+ file(WRITE ${ANDROID_PROJECT_DIR}/jni/Android.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
+ endif()
+ if(NOT EXISTS ${ANDROID_PROJECT_DIR}/jni/Application.mk)
+ file(WRITE ${ANDROID_PROJECT_DIR}/jni/Application.mk "APP_ABI := ${ANDROID_NDK_ABI_NAME}\n")
+ endif()
+
+ # 2. generate gdb.setup
+ get_directory_property(PROJECT_INCLUDES DIRECTORY ${PROJECT_SOURCE_DIR} INCLUDE_DIRECTORIES)
+ string(REGEX REPLACE ";" " " PROJECT_INCLUDES "${PROJECT_INCLUDES}")
+ file(WRITE ${LIBRARY_OUTPUT_PATH}/gdb.setup "set solib-search-path ${NDK_GDB_SOLIB_PATH}\n")
+ file(APPEND ${LIBRARY_OUTPUT_PATH}/gdb.setup "directory ${PROJECT_INCLUDES}\n")
+
+ # 3. copy gdbserver executable
+ file(COPY ${ANDROID_NDK}/prebuilt/android-${ANDROID_ARCH_NAME}/gdbserver/gdbserver DESTINATION ${LIBRARY_OUTPUT_PATH})
+ endif()
+endmacro()
+
+# register a target for remote debugging
+# copies the debug version to NDK_GDB_SOLIB_PATH then strips symbols of original
+macro(android_ndk_gdb_debuggable TARGET_NAME)
+ if(ANDROID)
+ get_property(TARGET_LOCATION TARGET ${TARGET_NAME} PROPERTY LOCATION)
+
+ # create custom target that depends on the real target so it gets executed afterwards
+ add_dependencies(NDK_GDB ${TARGET_NAME})
+
+ # 4. copy lib to obj
+ add_custom_command(TARGET NDK_GDB POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${TARGET_LOCATION} ${NDK_GDB_SOLIB_PATH})
+
+ # 5. strip symbols
+ add_custom_command(TARGET NDK_GDB POST_BUILD COMMAND ${CMAKE_STRIP} ${TARGET_LOCATION})
+ endif()
+endmacro()
diff --git a/src/third_party/android-cmake/AndroidNdkModules.cmake b/src/third_party/android-cmake/AndroidNdkModules.cmake
new file mode 100644
index 0000000..64f37fd
--- /dev/null
+++ b/src/third_party/android-cmake/AndroidNdkModules.cmake
@@ -0,0 +1,58 @@
+# Copyright (c) 2014, Pavel Rojtberg
+# 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. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
+
+macro(android_ndk_import_module_cpufeatures)
+ if(ANDROID)
+ include_directories(${ANDROID_NDK}/sources/android/cpufeatures)
+ add_library(cpufeatures ${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c)
+ target_link_libraries(cpufeatures dl)
+ endif()
+endmacro()
+
+macro(android_ndk_import_module_native_app_glue)
+ if(ANDROID)
+ include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
+ add_library(native_app_glue ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
+ target_link_libraries(native_app_glue log)
+ endif()
+endmacro()
+
+macro(android_ndk_import_module_ndk_helper)
+ if(ANDROID)
+ android_ndk_import_module_cpufeatures()
+ android_ndk_import_module_native_app_glue()
+
+ include_directories(${ANDROID_NDK}/sources/android/ndk_helper)
+ file(GLOB _NDK_HELPER_SRCS ${ANDROID_NDK}/sources/android/ndk_helper/*.cpp ${ANDROID_NDK}/sources/android/ndk_helper/gl3stub.c)
+ add_library(ndk_helper ${_NDK_HELPER_SRCS})
+ target_link_libraries(ndk_helper log android EGL GLESv2 cpufeatures native_app_glue)
+
+ unset(_NDK_HELPER_SRCS)
+ endif()
+endmacro()
\ No newline at end of file
diff --git a/src/third_party/android-cmake/LICENSE b/src/third_party/android-cmake/LICENSE
new file mode 100644
index 0000000..a96a369
--- /dev/null
+++ b/src/third_party/android-cmake/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2010-2011, Ethan Rublee
+Copyright (c) 2011-2014, Andrey Kamaev
+Copyright (c) 2014, Pavel Rojtberg
+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. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
diff --git a/src/third_party/android-cmake/README.google b/src/third_party/android-cmake/README.google
new file mode 100644
index 0000000..b2a0caf
--- /dev/null
+++ b/src/third_party/android-cmake/README.google
@@ -0,0 +1,12 @@
+URL: https://github.com/taka-no-me/android-cmake/archive/556cc14296c226f753a3778d99d8b60778b7df4f.zip
+Version: 556cc14296c226f753a3778d99d8b60778b7df4f
+License: BSD
+License File: LICENSE
+
+Description:
+android-cmake is a collection of CMake scripts for building against the Android
+NDK.
+
+Local Modifications:
+LICENSE file has been created for compliance purposes. Not included in original
+distribution.
diff --git a/src/third_party/android-cmake/README.md b/src/third_party/android-cmake/README.md
new file mode 100644
index 0000000..ee63021
--- /dev/null
+++ b/src/third_party/android-cmake/README.md
@@ -0,0 +1,240 @@
+# android-cmake
+
+CMake is great, and so is Android. This is a collection of CMake scripts that may be useful to the Android NDK community. It is based on experience from porting OpenCV library to Android: http://opencv.org/platforms/android.html
+
+Main goal is to share these scripts so that devs that use CMake as their build system may easily compile native code for Android.
+
+## TL;DR
+
+ cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake \
+ -DANDROID_NDK=<ndk_path> \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DANDROID_ABI="armeabi-v7a with NEON" \
+ <source_path>
+ cmake --build .
+
+One-liner:
+
+ cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake -DANDROID_NDK=<ndk_path> -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" <source_path> && cmake --build .
+
+_android-cmake_ will search for your NDK install in the following order:
+
+1. Value of `ANDROID_NDK` CMake variable;
+1. Value of `ANDROID_NDK` environment variable;
+1. Search under paths from `ANDROID_NDK_SEARCH_PATHS` CMake variable;
+1. Search platform specific locations (home folder, Windows "Program Files", etc).
+
+So if you have installed the NDK as `~/android-ndk-r10d` then _android-cmake_ will locate it automatically.
+
+## Getting started
+
+To build a cmake-based C/C++ project for Android you need:
+
+* Android NDK (>= r5) http://developer.android.com/tools/sdk/ndk/index.html
+* CMake (>= v2.6.3, >= v2.8.9 recommended) http://www.cmake.org/download
+
+The _android-cmake_ is also capable to build with NDK from AOSP or Linaro Android source tree, but you may be required to manually specify path to `libm` binary to link with.
+
+## Difference from traditional CMake
+
+Folowing the _ndk-build_ the _android-cmake_ supports **only two build targets**:
+
+* `-DCMAKE_BUILD_TYPE=Release`
+* `-DCMAKE_BUILD_TYPE=Debug`
+
+So don't even try other targets that can be found in CMake documentation and don't forget to explicitly specify `Release` or `Debug` because CMake builds without a build configuration by default.
+
+## Difference from _ndk-build_
+
+* Latest GCC available in NDK is used as the default compiler;
+* `Release` builds with `-O3` instead of `-Os`;
+* `Release` builds without debug info (without `-g`) (because _ndk-build_ always creates a stripped version but cmake delays this for `install/strip` target);
+* `-fsigned-char` is added to compiler flags to make `char` signed by default as it is on x86/x86_64;
+* GCC's stack protector is not used neither in `Debug` nor `Release` configurations;
+* No builds for multiple platforms (e.g. building for both arm and x86 require to run cmake twice with different parameters);
+* No file level Neon via `.neon` suffix;
+
+The following features of _ndk-build_ are not supported by the _android-cmake_ yet:
+
+* `armeabi-v7a-hard` ABI
+* `libc++_static`/`libc++_shared` STL runtime
+
+## Basic options
+
+Similarly to the NDK build system _android-cmake_ allows to select between several compiler toolchains and target platforms. Most of the options can be set either as cmake arguments: `-D<NAME>=<VALUE>` or as environment variables:
+
+* **ANDROID_NDK** - path to the Android NDK. If not set then _android-cmake_ will search for the most recent version of supported NDK in commonly used locations;
+* **ANDROID_ABI** - specifies the target Application Binary Interface (ABI). This option nearly matches to the APP_ABI variable used by ndk-build tool from Android NDK. If not specified then set to `armeabi-v7a`. Possible target names are:
+ * `armeabi` - ARMv5TE based CPU with software floating point operations;
+ * **`armeabi-v7a`** - ARMv7 based devices with hardware FPU instructions (VFPv3_D16);
+ * `armeabi-v7a with NEON` - same as armeabi-v7a, but sets NEON as floating-point unit;
+ * `armeabi-v7a with VFPV3` - same as armeabi-v7a, but sets VFPv3_D32 as floating-point unit;
+ * `armeabi-v6 with VFP` - tuned for ARMv6 processors having VFP;
+ * `x86` - IA-32 instruction set
+ * `mips` - MIPS32 instruction set
+ * `arm64-v8a` - ARMv8 AArch64 instruction set - only for NDK r10 and newer
+ * `x86_64` - Intel64 instruction set (r1) - only for NDK r10 and newer
+ * `mips64` - MIPS64 instruction set (r6) - only for NDK r10 and newer
+* **ANDROID_NATIVE_API_LEVEL** - level of android API to build for. Can be set either to full name (example: `android-8`) or a numeric value (example: `17`). The default API level depends on the target ABI:
+ * `android-8` for ARM;
+ * `android-9` for x86 and MIPS;
+ * `android-21` for 64-bit ABIs.
+
+ Building for `android-L` is possible only when it is explicitly selected.
+* **ANDROID_TOOLCHAIN_NAME** - the name of compiler toolchain to be used. This option allows to select between different GCC and Clang versions. The list of possible values depends on the NDK version and will be printed by toolchain file if an invalid value is set. By default _android-cmake_ selects the most recent version of GCC which can build for specified `ANDROID_ABI`.
+
+ Example values are:
+ * `aarch64-linux-android-4.9`
+ * `aarch64-linux-android-clang3.5`
+ * `arm-linux-androideabi-4.8`
+ * `arm-linux-androideabi-4.9`
+ * `arm-linux-androideabi-clang3.5`
+ * `mips64el-linux-android-4.9`
+ * `mipsel-linux-android-4.8`
+ * `x86-4.9`
+ * `x86_64-4.9`
+ * etc.
+* **ANDROID_STL** - the name of C++ runtime to use. The default is `gnustl_static`.
+ * `none` - do not configure the runtime.
+ * `system` - use the default minimal system C++ runtime library.
+ * Implies `-fno-rtti -fno-exceptions`.
+ * `system_re` - use the default minimal system C++ runtime library.
+ * Implies `-frtti -fexceptions`.
+ * `gabi++_static` - use the GAbi++ runtime as a static library.
+ * Implies `-frtti -fno-exceptions`.
+ * Available for NDK r7 and newer.
+ * `gabi++_shared` - use the GAbi++ runtime as a shared library.
+ * Implies `-frtti -fno-exceptions`.
+ * Available for NDK r7 and newer.
+ * `stlport_static` - use the STLport runtime as a static library.
+ * Implies `-fno-rtti -fno-exceptions` for NDK before r7.
+ * Implies `-frtti -fno-exceptions` for NDK r7 and newer.
+ * `stlport_shared` - use the STLport runtime as a shared library.
+ * Implies `-fno-rtti -fno-exceptions` for NDK before r7.
+ * Implies `-frtti -fno-exceptions` for NDK r7 and newer.
+ * **`gnustl_static`** - use the GNU STL as a static library.
+ * Implies `-frtti -fexceptions`.
+ * `gnustl_shared` - use the GNU STL as a shared library.
+ * Implies `-frtti -fno-exceptions`.
+ * Available for NDK r7b and newer.
+ * Silently degrades to `gnustl_static` if not available.
+* **NDK_CCACHE** - path to `ccache` executable. If not set then initialized from `NDK_CCACHE` environment variable.
+
+## Advanced _android-cmake_ options
+
+Normally _android-cmake_ users are not supposed to touch these variables but they might be useful to workaround some build issues:
+
+* **ANDROID_FORCE_ARM_BUILD** = `OFF` - generate 32-bit ARM instructions instead of Thumb. Applicable only for arm ABIs and is forced to be `ON` for `armeabi-v6 with VFP`;
+* **ANDROID_NO_UNDEFINED** = `ON` - show all undefined symbols as linker errors;
+* **ANDROID_SO_UNDEFINED** = `OFF` - allow undefined symbols in shared libraries;
+ * actually it is turned `ON` by default for NDK older than `r7`
+* **ANDROID_STL_FORCE_FEATURES** = `ON` - automatically configure rtti and exceptions support based on C++ runtime;
+* **ANDROID_NDK_LAYOUT** = `RELEASE` - inner layout of Android NDK, should be detected automatically. Possible values are:
+ * `RELEASE` - public releases from Google;
+ * `LINARO` - NDK from Linaro project;
+ * `ANDROID` - NDK from AOSP.
+* **ANDROID_FUNCTION_LEVEL_LINKING** = `ON` - enables saparate putting each function and data items into separate sections and enable garbage collection of unused input sections at link time (`-fdata-sections -ffunction-sections -Wl,--gc-sections`);
+* **ANDROID_GOLD_LINKER** = `ON` - use gold linker with GCC 4.6 for NDK r8b and newer (only for ARM and x86);
+* **ANDROID_NOEXECSTACK** = `ON` - enables or disables stack execution protection code (`-Wl,-z,noexecstack`);
+* **ANDROID_RELRO** = `ON` - Enables RELRO - a memory corruption mitigation technique (`-Wl,-z,relro -Wl,-z,now`);
+* **ANDROID_LIBM_PATH** - path to `libm.so` (set to something like `$(TOP)/out/target/product/<product_name>/obj/lib/libm.so`) to workaround unresolved `sincos`.
+
+## Fine-tuning `CMakeLists.txt` for _android-cmake_
+
+### Recognizing Android build
+
+_android-cmake_ defines `ANDROID` CMake variable which can be used to add Android-specific stuff:
+
+ if (ANDROID)
+ message(STATUS "Hello from Android build!")
+ endif()
+
+The recommended way to identify ARM/MIPS/x86 architecture is examining `CMAKE_SYSTEM_PROCESSOR` which is set to the appropriate value:
+
+* `armv5te` - for `armeabi` ABI
+* `armv6` - for `armeabi-v6 with VFP` ABI
+* `armv7-a` - for `armeabi-v7a`, `armeabi-v7a with VFPV3` and `armeabi-v7a with NEON` ABIs
+* `aarch64` - for `arm64-v8a` ABI
+* `i686` - for `x86` ABI
+* `x86_64` - for `x86_64` ABI
+* `mips` - for `mips` ABI
+* `mips64` - for `mips64` ABI
+
+Other variables that are set by _android-cmake_ and can be used for the fine-grained build configuration are:
+
+* `NEON` - set if target ABI supports Neon;
+* `ANDROID_NATIVE_API_LEVEL` - native Android API level we are building for (note: Java part of Andoid application can be built for another API level)
+* `ANDROID_NDK_RELEASE` - version of the Android NDK
+* `ANDROID_NDK_HOST_SYSTEM_NAME` - "windows", "linux-x86" or "darwin-x86" depending on the host platform
+* `ANDROID_RTTI` - set if rtti is enabled by the runtime
+* `ANDROID_EXCEPTIONS` - set if exceptions are enabled by the runtime
+
+### Finding packages
+
+When crosscompiling CMake `find_*` commands are normally expected to find libraries and packages belonging to the same build target. So _android-cmake_ configures CMake to search in Android-specific paths only and ignore your host system locations. So
+
+ find_package(ZLIB)
+
+will surely find libz.so within the Android NDK.
+
+However sometimes you need to locate a host package even when cross-compiling. For example you can be searching for your documentation generator. The _android-cmake_ recommends you to use `find_host_package` and `find_host_program` macro defined in the `android.toolchain.cmake`:
+
+ find_host_package(Doxygen)
+ find_host_program(PDFLATEX pdflatex)
+
+However this will break regular builds so instead of wrapping package search into platform-specific logic you can copy the following snippet into your project (put it after your top-level `project()` command):
+
+ # Search packages for host system instead of packages for target system
+ # in case of cross compilation these macro should be defined by toolchain file
+ if(NOT COMMAND find_host_package)
+ macro(find_host_package)
+ find_package(${ARGN})
+ endmacro()
+ endif()
+ if(NOT COMMAND find_host_program)
+ macro(find_host_program)
+ find_program(${ARGN})
+ endmacro()
+ endif()
+
+### Compiler flags recycling
+
+Make sure to do the following in your scripts:
+
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}")
+
+The flags will be prepopulated with critical flags, so don't loose them. Also be aware that _android-cmake_ also sets configuration-specific compiler and linker flags.
+
+## Troubleshooting
+
+### Building on Windows
+
+First of all `cygwin` builds are **NOT supported** and will not be supported by _android-cmake_. To build natively on Windows you need a port of make but I recommend http://martine.github.io/ninja/ instead.
+
+To build with Ninja you need:
+
+* Ensure you are using CMake newer than 2.8.9;
+* Download the latest Ninja from https://github.com/martine/ninja/releases;
+* Put the `ninja.exe` into your PATH (or add path to `ninja.exe` to your PATH environment variable);
+* Pass `-GNinja` to `cmake` alongside with other arguments (or choose Ninja generator in `cmake-gui`).
+* Enjoy the fast native multithreaded build :)
+
+But if you still want to stick to old make then:
+
+* Get a Windows port of GNU Make:
+ * Android NDK r7 (and newer) already has `make.exe` on board;
+ * `mingw-make` should work as fine;
+ * Download some other port. For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm.
+* Add path to your `make.exe` to system PATH or always use full path;
+* Pass `-G"MinGW Makefiles"` and `-DCMAKE_MAKE_PROGRAM="<full/path/to/>make.exe"`
+ * It must be `MinGW Makefiles` and not `Unix Makefiles` even if your `make.exe` is not a MinGW's make.
+* Run `make.exe` or `cmake --build .` for single-threaded build.
+
+### Projects with assembler files
+
+The _android-cmake_ should correctly handle projects with assembler sources (`*.s` or `*.S`). But if you still facing problems with assembler then try to upgrade your CMake to version newer than 2.8.5
+
+## Copying
+
+_android-cmake_ is distributed under the terms of [BSD 3-Clause License](http://opensource.org/licenses/BSD-3-Clause)
\ No newline at end of file
diff --git a/src/third_party/android-cmake/android.toolchain.cmake b/src/third_party/android-cmake/android.toolchain.cmake
new file mode 100644
index 0000000..ffa2612
--- /dev/null
+++ b/src/third_party/android-cmake/android.toolchain.cmake
@@ -0,0 +1,1693 @@
+# Copyright (c) 2010-2011, Ethan Rublee
+# Copyright (c) 2011-2014, Andrey Kamaev
+# 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. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.
+
+# ------------------------------------------------------------------------------
+# Android CMake toolchain file, for use with the Android NDK r5-r10d
+# Requires cmake 2.6.3 or newer (2.8.9 or newer is recommended).
+# See home page: https://github.com/taka-no-me/android-cmake
+#
+# Usage Linux:
+# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk
+# $ mkdir build && cd build
+# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake ..
+# $ make -j8
+#
+# Usage Windows:
+# You need native port of make to build your project.
+# Android NDK r7 (and newer) already has make.exe on board.
+# For older NDK you have to install it separately.
+# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm
+#
+# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk
+# $ mkdir build && cd build
+# $ cmake.exe -G"MinGW Makefiles"
+# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake
+# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" ..
+# $ cmake.exe --build .
+#
+#
+# Options (can be set as cmake parameters: -D<option_name>=<value>):
+# ANDROID_NDK=/opt/android-ndk - path to the NDK root.
+# Can be set as environment variable. Can be set only at first cmake run.
+#
+# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary
+# Interface (ABI). This option nearly matches to the APP_ABI variable
+# used by ndk-build tool from Android NDK.
+#
+# Possible targets are:
+# "armeabi" - ARMv5TE based CPU with software floating point operations
+# "armeabi-v7a" - ARMv7 based devices with hardware FPU instructions
+# this ABI target is used by default
+# "armeabi-v7a with NEON" - same as armeabi-v7a, but
+# sets NEON as floating-point unit
+# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but
+# sets VFPV3 as floating-point unit (has 32 registers instead of 16)
+# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP
+# "x86" - IA-32 instruction set
+# "mips" - MIPS32 instruction set
+#
+# 64-bit ABIs for NDK r10 and newer:
+# "arm64-v8a" - ARMv8 AArch64 instruction set
+# "x86_64" - Intel64 instruction set (r1)
+# "mips64" - MIPS64 instruction set (r6)
+#
+# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for.
+# Option is read-only when standalone toolchain is used.
+# Note: building for "android-L" requires explicit configuration.
+#
+# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.9 - the name of compiler
+# toolchain to be used. The list of possible values depends on the NDK
+# version. For NDK r10c the possible values are:
+#
+# * aarch64-linux-android-4.9
+# * aarch64-linux-android-clang3.4
+# * aarch64-linux-android-clang3.5
+# * arm-linux-androideabi-4.6
+# * arm-linux-androideabi-4.8
+# * arm-linux-androideabi-4.9 (default)
+# * arm-linux-androideabi-clang3.4
+# * arm-linux-androideabi-clang3.5
+# * mips64el-linux-android-4.9
+# * mips64el-linux-android-clang3.4
+# * mips64el-linux-android-clang3.5
+# * mipsel-linux-android-4.6
+# * mipsel-linux-android-4.8
+# * mipsel-linux-android-4.9
+# * mipsel-linux-android-clang3.4
+# * mipsel-linux-android-clang3.5
+# * x86-4.6
+# * x86-4.8
+# * x86-4.9
+# * x86-clang3.4
+# * x86-clang3.5
+# * x86_64-4.9
+# * x86_64-clang3.4
+# * x86_64-clang3.5
+#
+# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions
+# instead of Thumb. Is not available for "armeabi-v6 with VFP"
+# (is forced to be ON) ABI.
+#
+# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker
+# errors even if they are not used.
+#
+# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared
+# libraries. Automatically turned for NDK r5x and r6x due to GLESv2
+# problems.
+#
+# ANDROID_STL=gnustl_static - specify the runtime to use.
+#
+# Possible values are:
+# none -> Do not configure the runtime.
+# system -> Use the default minimal system C++ runtime library.
+# Implies -fno-rtti -fno-exceptions.
+# Is not available for standalone toolchain.
+# system_re -> Use the default minimal system C++ runtime library.
+# Implies -frtti -fexceptions.
+# Is not available for standalone toolchain.
+# gabi++_static -> Use the GAbi++ runtime as a static library.
+# Implies -frtti -fno-exceptions.
+# Available for NDK r7 and newer.
+# Is not available for standalone toolchain.
+# gabi++_shared -> Use the GAbi++ runtime as a shared library.
+# Implies -frtti -fno-exceptions.
+# Available for NDK r7 and newer.
+# Is not available for standalone toolchain.
+# stlport_static -> Use the STLport runtime as a static library.
+# Implies -fno-rtti -fno-exceptions for NDK before r7.
+# Implies -frtti -fno-exceptions for NDK r7 and newer.
+# Is not available for standalone toolchain.
+# stlport_shared -> Use the STLport runtime as a shared library.
+# Implies -fno-rtti -fno-exceptions for NDK before r7.
+# Implies -frtti -fno-exceptions for NDK r7 and newer.
+# Is not available for standalone toolchain.
+# gnustl_static -> Use the GNU STL as a static library.
+# Implies -frtti -fexceptions.
+# gnustl_shared -> Use the GNU STL as a shared library.
+# Implies -frtti -fno-exceptions.
+# Available for NDK r7b and newer.
+# Silently degrades to gnustl_static if not available.
+#
+# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on
+# chosen runtime. If disabled, then the user is responsible for settings
+# these options.
+#
+# What?:
+# android-cmake toolchain searches for NDK/toolchain in the following order:
+# ANDROID_NDK - cmake parameter
+# ANDROID_NDK - environment variable
+# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter
+# ANDROID_STANDALONE_TOOLCHAIN - environment variable
+# ANDROID_NDK - default locations
+# ANDROID_STANDALONE_TOOLCHAIN - default locations
+#
+# Make sure to do the following in your scripts:
+# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" )
+# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" )
+# The flags will be prepopulated with critical flags, so don't loose them.
+# Also be aware that toolchain also sets configuration-specific compiler
+# flags and linker flags.
+#
+# ANDROID and BUILD_ANDROID will be set to true, you may test any of these
+# variables to make necessary Android-specific configuration changes.
+#
+# Also ARMEABI or ARMEABI_V7A or X86 or MIPS or ARM64_V8A or X86_64 or MIPS64
+# will be set true, mutually exclusive. NEON option will be set true
+# if VFP is set to NEON.
+#
+# ------------------------------------------------------------------------------
+
+cmake_minimum_required( VERSION 2.6.3 )
+
+if( DEFINED CMAKE_CROSSCOMPILING )
+ # subsequent toolchain loading is not really needed
+ return()
+endif()
+
+if( CMAKE_TOOLCHAIN_FILE )
+ # touch toolchain variable to suppress "unused variable" warning
+endif()
+
+# inherit settings in recursive loads
+get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE )
+if( _CMAKE_IN_TRY_COMPILE )
+ include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL )
+endif()
+
+# this one is important
+if( CMAKE_VERSION VERSION_GREATER "3.0.99" )
+ set( CMAKE_SYSTEM_NAME Android )
+else()
+ set( CMAKE_SYSTEM_NAME Linux )
+endif()
+
+# this one not so much
+set( CMAKE_SYSTEM_VERSION 1 )
+
+# rpath makes low sense for Android
+set( CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG "" )
+set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." )
+
+# NDK search paths
+set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r10d -r10c -r10b -r10 -r9d -r9c -r9b -r9 -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" )
+if( NOT DEFINED ANDROID_NDK_SEARCH_PATHS )
+ if( CMAKE_HOST_WIN32 )
+ file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS )
+ set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}" "$ENV{SystemDrive}/NVPACK" )
+ else()
+ file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS )
+ set( ANDROID_NDK_SEARCH_PATHS /opt "${ANDROID_NDK_SEARCH_PATHS}/NVPACK" )
+ endif()
+endif()
+if( NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH )
+ set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain )
+endif()
+
+# known ABIs
+set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" )
+set( ANDROID_SUPPORTED_ABIS_arm64 "arm64-v8a" )
+set( ANDROID_SUPPORTED_ABIS_x86 "x86" )
+set( ANDROID_SUPPORTED_ABIS_x86_64 "x86_64" )
+set( ANDROID_SUPPORTED_ABIS_mips "mips" )
+set( ANDROID_SUPPORTED_ABIS_mips64 "mips64" )
+
+# API level defaults
+set( ANDROID_DEFAULT_NDK_API_LEVEL 8 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_arm64 21 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_x86_64 21 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 )
+set( ANDROID_DEFAULT_NDK_API_LEVEL_mips64 21 )
+
+
+macro( __LIST_FILTER listvar regex )
+ if( ${listvar} )
+ foreach( __val ${${listvar}} )
+ if( __val MATCHES "${regex}" )
+ list( REMOVE_ITEM ${listvar} "${__val}" )
+ endif()
+ endforeach()
+ endif()
+endmacro()
+
+macro( __INIT_VARIABLE var_name )
+ set( __test_path 0 )
+ foreach( __var ${ARGN} )
+ if( __var STREQUAL "PATH" )
+ set( __test_path 1 )
+ break()
+ endif()
+ endforeach()
+
+ if( __test_path AND NOT EXISTS "${${var_name}}" )
+ unset( ${var_name} CACHE )
+ endif()
+
+ if( " ${${var_name}}" STREQUAL " " )
+ set( __values 0 )
+ foreach( __var ${ARGN} )
+ if( __var STREQUAL "VALUES" )
+ set( __values 1 )
+ elseif( NOT __var STREQUAL "PATH" )
+ if( __var MATCHES "^ENV_.*$" )
+ string( REPLACE "ENV_" "" __var "${__var}" )
+ set( __value "$ENV{${__var}}" )
+ elseif( DEFINED ${__var} )
+ set( __value "${${__var}}" )
+ elseif( __values )
+ set( __value "${__var}" )
+ else()
+ set( __value "" )
+ endif()
+
+ if( NOT " ${__value}" STREQUAL " " AND (NOT __test_path OR EXISTS "${__value}") )
+ set( ${var_name} "${__value}" )
+ break()
+ endif()
+ endif()
+ endforeach()
+ unset( __value )
+ unset( __values )
+ endif()
+
+ if( __test_path )
+ file( TO_CMAKE_PATH "${${var_name}}" ${var_name} )
+ endif()
+ unset( __test_path )
+endmacro()
+
+macro( __DETECT_NATIVE_API_LEVEL _var _path )
+ set( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*.*$" )
+ file( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" )
+ if( NOT __apiFileContent )
+ message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." )
+ endif()
+ string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" )
+ unset( __apiFileContent )
+ unset( __ndkApiLevelRegex )
+endmacro()
+
+macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root )
+ if( EXISTS "${_root}" )
+ file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" )
+ __LIST_FILTER( __gccExePath "^[.].*" )
+ list( LENGTH __gccExePath __gccExePathsCount )
+ if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE )
+ message( WARNING "Could not determine machine name for compiler from ${_root}" )
+ set( ${_var} "" )
+ else()
+ get_filename_component( __gccExeName "${__gccExePath}" NAME_WE )
+ string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" )
+ endif()
+ unset( __gccExePath )
+ unset( __gccExePathsCount )
+ unset( __gccExeName )
+ else()
+ set( ${_var} "" )
+ endif()
+endmacro()
+
+
+# fight against cygwin
+set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools")
+mark_as_advanced( ANDROID_FORBID_SYGWIN )
+if( ANDROID_FORBID_SYGWIN )
+ if( CYGWIN )
+ message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." )
+ endif()
+
+ if( CMAKE_HOST_WIN32 )
+ # remove cygwin from PATH
+ set( __new_path "$ENV{PATH}")
+ __LIST_FILTER( __new_path "cygwin" )
+ set(ENV{PATH} "${__new_path}")
+ unset(__new_path)
+ endif()
+endif()
+
+
+# detect current host platform
+if( NOT DEFINED ANDROID_NDK_HOST_X64 AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64" OR CMAKE_HOST_APPLE) )
+ set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" )
+ mark_as_advanced( ANDROID_NDK_HOST_X64 )
+endif()
+
+set( TOOL_OS_SUFFIX "" )
+if( CMAKE_HOST_APPLE )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" )
+elseif( CMAKE_HOST_WIN32 )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" )
+ set( TOOL_OS_SUFFIX ".exe" )
+elseif( CMAKE_HOST_UNIX )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" )
+else()
+ message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" )
+endif()
+
+if( NOT ANDROID_NDK_HOST_X64 )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} )
+endif()
+
+# see if we have path to Android NDK
+if( NOT ANDROID_NDK AND NOT ANDROID_STANDALONE_TOOLCHAIN )
+ __INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK )
+endif()
+if( NOT ANDROID_NDK )
+ # see if we have path to Android standalone toolchain
+ __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN )
+
+ if( NOT ANDROID_STANDALONE_TOOLCHAIN )
+ #try to find Android NDK in one of the the default locations
+ set( __ndkSearchPaths )
+ foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} )
+ foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} )
+ list( APPEND __ndkSearchPaths "${__ndkSearchPath}/android-ndk${suffix}" )
+ endforeach()
+ endforeach()
+ __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} )
+ unset( __ndkSearchPaths )
+
+ if( ANDROID_NDK )
+ message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" )
+ message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" )
+ else()
+ #try to find Android standalone toolchain in one of the the default locations
+ __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH )
+
+ if( ANDROID_STANDALONE_TOOLCHAIN )
+ message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" )
+ message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" )
+ endif( ANDROID_STANDALONE_TOOLCHAIN )
+ endif( ANDROID_NDK )
+ endif( NOT ANDROID_STANDALONE_TOOLCHAIN )
+endif( NOT ANDROID_NDK )
+
+# remember found paths
+if( ANDROID_NDK )
+ get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE )
+ set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE )
+ set( BUILD_WITH_ANDROID_NDK True )
+ if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" )
+ file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX "r[0-9]+[a-z]?" )
+ string( REGEX MATCH "r([0-9]+)([a-z]?)" ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" )
+ else()
+ set( ANDROID_NDK_RELEASE "r1x" )
+ set( ANDROID_NDK_RELEASE_FULL "unreleased" )
+ endif()
+ string( REGEX REPLACE "r([0-9]+)([a-z]?)" "\\1*1000" ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE}" )
+ string( FIND " abcdefghijklmnopqastuvwxyz" "${CMAKE_MATCH_2}" __ndkReleaseLetterNum )
+ math( EXPR ANDROID_NDK_RELEASE_NUM "${ANDROID_NDK_RELEASE_NUM}+${__ndkReleaseLetterNum}" )
+elseif( ANDROID_STANDALONE_TOOLCHAIN )
+ get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE )
+ # try to detect change
+ if( CMAKE_AR )
+ string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length )
+ string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath )
+ if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN )
+ message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." )
+ endif()
+ unset( __androidStandaloneToolchainPreviousPath )
+ unset( __length )
+ endif()
+ set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE )
+ set( BUILD_WITH_STANDALONE_TOOLCHAIN True )
+else()
+ list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH)
+ message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain.
+ You should either set an environment variable:
+ export ANDROID_NDK=~/my-android-ndk
+ or
+ export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain
+ or put the toolchain or NDK in the default path:
+ sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH}/android-ndk
+ sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" )
+endif()
+
+# android NDK layout
+if( BUILD_WITH_ANDROID_NDK )
+ if( NOT DEFINED ANDROID_NDK_LAYOUT )
+ # try to automatically detect the layout
+ if( EXISTS "${ANDROID_NDK}/RELEASE.TXT")
+ set( ANDROID_NDK_LAYOUT "RELEASE" )
+ elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" )
+ set( ANDROID_NDK_LAYOUT "LINARO" )
+ elseif( EXISTS "${ANDROID_NDK}/../../gcc/" )
+ set( ANDROID_NDK_LAYOUT "ANDROID" )
+ endif()
+ endif()
+ set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" )
+ mark_as_advanced( ANDROID_NDK_LAYOUT )
+ if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment
+ set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" )
+ elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" )
+ set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment
+ set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" )
+ else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE"
+ set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" )
+ endif()
+ get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE )
+
+ # try to detect change of NDK
+ if( CMAKE_AR )
+ string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length )
+ string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath )
+ if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH )
+ message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first.
+ " )
+ endif()
+ unset( __androidNdkPreviousPath )
+ unset( __length )
+ endif()
+endif()
+
+
+# get all the details about standalone toolchain
+if( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" )
+ set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ set( __availableToolchains "standalone" )
+ __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ if( NOT __availableToolchainMachines )
+ message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." )
+ endif()
+ if( __availableToolchainMachines MATCHES x86_64 )
+ set( __availableToolchainArchs "x86_64" )
+ elseif( __availableToolchainMachines MATCHES i686 )
+ set( __availableToolchainArchs "x86" )
+ elseif( __availableToolchainMachines MATCHES aarch64 )
+ set( __availableToolchainArchs "arm64" )
+ elseif( __availableToolchainMachines MATCHES arm )
+ set( __availableToolchainArchs "arm" )
+ elseif( __availableToolchainMachines MATCHES mips64el )
+ set( __availableToolchainArchs "mips64" )
+ elseif( __availableToolchainMachines MATCHES mipsel )
+ set( __availableToolchainArchs "mips" )
+ endif()
+ execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion
+ OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE )
+ string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" )
+ if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" )
+ list( APPEND __availableToolchains "standalone-clang" )
+ list( APPEND __availableToolchainMachines ${__availableToolchainMachines} )
+ list( APPEND __availableToolchainArchs ${__availableToolchainArchs} )
+ list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} )
+ endif()
+endif()
+
+macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath )
+ foreach( __toolchain ${${__availableToolchainsLst}} )
+ if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" )
+ SET( __toolchainVersionRegex "^TOOLCHAIN_VERSION[\t ]+:=[\t ]+(.*)$" )
+ FILE( STRINGS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}/setup.mk" __toolchainVersionStr REGEX "${__toolchainVersionRegex}" )
+ if( __toolchainVersionStr )
+ string( REGEX REPLACE "${__toolchainVersionRegex}" "\\1" __toolchainVersionStr "${__toolchainVersionStr}" )
+ string( REGEX REPLACE "-clang3[.][0-9]$" "-${__toolchainVersionStr}" __gcc_toolchain "${__toolchain}" )
+ else()
+ string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" )
+ endif()
+ unset( __toolchainVersionStr )
+ unset( __toolchainVersionRegex )
+ else()
+ set( __gcc_toolchain "${__toolchain}" )
+ endif()
+ __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" )
+ if( __machine )
+ string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" )
+ if( __machine MATCHES x86_64 )
+ set( __arch "x86_64" )
+ elseif( __machine MATCHES i686 )
+ set( __arch "x86" )
+ elseif( __machine MATCHES aarch64 )
+ set( __arch "arm64" )
+ elseif( __machine MATCHES arm )
+ set( __arch "arm" )
+ elseif( __machine MATCHES mips64el )
+ set( __arch "mips64" )
+ elseif( __machine MATCHES mipsel )
+ set( __arch "mips" )
+ else()
+ set( __arch "" )
+ endif()
+ #message("machine: !${__machine}!\narch: !${__arch}!\nversion: !${__version}!\ntoolchain: !${__toolchain}!\n")
+ if (__arch)
+ list( APPEND __availableToolchainMachines "${__machine}" )
+ list( APPEND __availableToolchainArchs "${__arch}" )
+ list( APPEND __availableToolchainCompilerVersions "${__version}" )
+ list( APPEND ${__availableToolchainsVar} "${__toolchain}" )
+ endif()
+ endif()
+ unset( __gcc_toolchain )
+ endforeach()
+endmacro()
+
+# get all the details about NDK
+if( BUILD_WITH_ANDROID_NDK )
+ file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" )
+ string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" )
+ set( __availableToolchains "" )
+ set( __availableToolchainMachines "" )
+ set( __availableToolchainArchs "" )
+ set( __availableToolchainCompilerVersions "" )
+ if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" )
+ # do not go through all toolchains if we know the name
+ set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" )
+ __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+ if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 )
+ __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" )
+ if( __availableToolchains )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} )
+ endif()
+ endif()
+ endif()
+ if( NOT __availableToolchains )
+ file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" )
+ if( __availableToolchainsLst )
+ list(SORT __availableToolchainsLst) # we need clang to go after gcc
+ endif()
+ __LIST_FILTER( __availableToolchainsLst "^[.]" )
+ __LIST_FILTER( __availableToolchainsLst "llvm" )
+ __LIST_FILTER( __availableToolchainsLst "renderscript" )
+ __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+ if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 )
+ __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" )
+ if( __availableToolchains )
+ set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} )
+ endif()
+ endif()
+ endif()
+ if( NOT __availableToolchains )
+ message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." )
+ endif()
+endif()
+
+# build list of available ABIs
+set( ANDROID_SUPPORTED_ABIS "" )
+set( __uniqToolchainArchNames ${__availableToolchainArchs} )
+list( REMOVE_DUPLICATES __uniqToolchainArchNames )
+list( SORT __uniqToolchainArchNames )
+foreach( __arch ${__uniqToolchainArchNames} )
+ list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} )
+endforeach()
+unset( __uniqToolchainArchNames )
+if( NOT ANDROID_SUPPORTED_ABIS )
+ message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." )
+endif()
+
+# choose target ABI
+__INIT_VARIABLE( ANDROID_ABI VALUES ${ANDROID_SUPPORTED_ABIS} )
+# verify that target ABI is supported
+list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx )
+if( __androidAbiIdx EQUAL -1 )
+ string( REPLACE ";" "\", \"" PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" )
+ message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain.
+ Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\"
+ " )
+endif()
+unset( __androidAbiIdx )
+
+# set target ABI options
+if( ANDROID_ABI STREQUAL "x86" )
+ set( X86 true )
+ set( ANDROID_NDK_ABI_NAME "x86" )
+ set( ANDROID_ARCH_NAME "x86" )
+ set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "i686" )
+elseif( ANDROID_ABI STREQUAL "x86_64" )
+ set( X86 true )
+ set( X86_64 true )
+ set( ANDROID_NDK_ABI_NAME "x86_64" )
+ set( ANDROID_ARCH_NAME "x86_64" )
+ set( CMAKE_SYSTEM_PROCESSOR "x86_64" )
+ set( ANDROID_LLVM_TRIPLE "x86_64-none-linux-android" )
+elseif( ANDROID_ABI STREQUAL "mips64" )
+ set( MIPS64 true )
+ set( ANDROID_NDK_ABI_NAME "mips64" )
+ set( ANDROID_ARCH_NAME "mips64" )
+ set( ANDROID_LLVM_TRIPLE "mips64el-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "mips64" )
+elseif( ANDROID_ABI STREQUAL "mips" )
+ set( MIPS true )
+ set( ANDROID_NDK_ABI_NAME "mips" )
+ set( ANDROID_ARCH_NAME "mips" )
+ set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "mips" )
+elseif( ANDROID_ABI STREQUAL "arm64-v8a" )
+ set( ARM64_V8A true )
+ set( ANDROID_NDK_ABI_NAME "arm64-v8a" )
+ set( ANDROID_ARCH_NAME "arm64" )
+ set( ANDROID_LLVM_TRIPLE "aarch64-none-linux-android" )
+ set( CMAKE_SYSTEM_PROCESSOR "aarch64" )
+ set( VFPV3 true )
+ set( NEON true )
+elseif( ANDROID_ABI STREQUAL "armeabi" )
+ set( ARMEABI true )
+ set( ANDROID_NDK_ABI_NAME "armeabi" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv5te" )
+elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" )
+ set( ARMEABI_V6 true )
+ set( ANDROID_NDK_ABI_NAME "armeabi" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv6" )
+ # need always fallback to older platform
+ set( ARMEABI true )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a")
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" )
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+ set( VFPV3 true )
+elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" )
+ set( ARMEABI_V7A true )
+ set( ANDROID_NDK_ABI_NAME "armeabi-v7a" )
+ set( ANDROID_ARCH_NAME "arm" )
+ set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" )
+ set( CMAKE_SYSTEM_PROCESSOR "armv7-a" )
+ set( VFPV3 true )
+ set( NEON true )
+else()
+ message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." )
+endif()
+
+if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" )
+ # really dirty hack
+ # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run...
+ file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" )
+endif()
+
+if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 )
+ __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD VALUES OFF )
+ set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE )
+ mark_as_advanced( ANDROID_FORCE_ARM_BUILD )
+else()
+ unset( ANDROID_FORCE_ARM_BUILD CACHE )
+endif()
+
+# choose toolchain
+if( ANDROID_TOOLCHAIN_NAME )
+ list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx )
+ if( __toolchainIdx EQUAL -1 )
+ list( SORT __availableToolchains )
+ string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" )
+ set( toolchains_list " * ${toolchains_list}")
+ message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain.
+To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" )
+ endif()
+ list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch )
+ if( NOT __toolchainArch STREQUAL ANDROID_ARCH_NAME )
+ message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." )
+ endif()
+else()
+ set( __toolchainIdx -1 )
+ set( __applicableToolchains "" )
+ set( __toolchainMaxVersion "0.0.0" )
+ list( LENGTH __availableToolchains __availableToolchainsCount )
+ math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" )
+ foreach( __idx RANGE ${__availableToolchainsCount} )
+ list( GET __availableToolchainArchs ${__idx} __toolchainArch )
+ if( __toolchainArch STREQUAL ANDROID_ARCH_NAME )
+ list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion )
+ string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}")
+ if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion )
+ set( __toolchainMaxVersion "${__toolchainVersion}" )
+ set( __toolchainIdx ${__idx} )
+ endif()
+ endif()
+ endforeach()
+ unset( __availableToolchainsCount )
+ unset( __toolchainMaxVersion )
+ unset( __toolchainVersion )
+endif()
+unset( __toolchainArch )
+if( __toolchainIdx EQUAL -1 )
+ message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." )
+endif()
+list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME )
+list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME )
+list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION )
+
+unset( __toolchainIdx )
+unset( __availableToolchains )
+unset( __availableToolchainMachines )
+unset( __availableToolchainArchs )
+unset( __availableToolchainCompilerVersions )
+
+# choose native API level
+__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL )
+string( REPLACE "android-" "" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" )
+string( STRIP "${ANDROID_NATIVE_API_LEVEL}" ANDROID_NATIVE_API_LEVEL )
+# adjust API level
+set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} )
+foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ if( (__level LESS ANDROID_NATIVE_API_LEVEL OR __level STREQUAL ANDROID_NATIVE_API_LEVEL) AND NOT __level LESS __real_api_level )
+ set( __real_api_level ${__level} )
+ endif()
+endforeach()
+if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL STREQUAL __real_api_level )
+ message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'")
+ set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} )
+endif()
+unset(__real_api_level)
+# validate
+list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx )
+if( __levelIdx EQUAL -1 )
+ message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." )
+else()
+ if( BUILD_WITH_ANDROID_NDK )
+ __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" )
+ if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL AND NOT __realApiLevel GREATER 9000 )
+ message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." )
+ endif()
+ unset( __realApiLevel )
+ endif()
+ set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE )
+ set( CMAKE_ANDROID_API ${ANDROID_NATIVE_API_LEVEL} )
+ if( CMAKE_VERSION VERSION_GREATER "2.8" )
+ list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS )
+ set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} )
+ endif()
+endif()
+unset( __levelIdx )
+
+
+# remember target ABI
+set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE )
+if( CMAKE_VERSION VERSION_GREATER "2.8" )
+ list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME} )
+ set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_NAME}} )
+endif()
+
+
+# runtime choice (STL, rtti, exceptions)
+if( NOT ANDROID_STL )
+ set( ANDROID_STL gnustl_static )
+endif()
+set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" )
+set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" )
+mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES )
+
+if( BUILD_WITH_ANDROID_NDK )
+ if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$")
+ message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\".
+The possible values are:
+ none -> Do not configure the runtime.
+ system -> Use the default minimal system C++ runtime library.
+ system_re -> Same as system but with rtti and exceptions.
+ gabi++_static -> Use the GAbi++ runtime as a static library.
+ gabi++_shared -> Use the GAbi++ runtime as a shared library.
+ stlport_static -> Use the STLport runtime as a static library.
+ stlport_shared -> Use the STLport runtime as a shared library.
+ gnustl_static -> (default) Use the GNU STL as a static library.
+ gnustl_shared -> Use the GNU STL as a shared library.
+" )
+ endif()
+elseif( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$")
+ message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\".
+The possible values are:
+ none -> Do not configure the runtime.
+ gnustl_static -> (default) Use the GNU STL as a static library.
+ gnustl_shared -> Use the GNU STL as a shared library.
+" )
+ endif()
+endif()
+
+unset( ANDROID_RTTI )
+unset( ANDROID_EXCEPTIONS )
+unset( ANDROID_STL_INCLUDE_DIRS )
+unset( __libstl )
+unset( __libsupcxx )
+
+if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" )
+ message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf).
+You are strongly recommended to switch to another NDK release.
+" )
+endif()
+
+if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" )
+ message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header:
+See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2
+ diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+ index 5e28c64..65892a1 100644
+ --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+ +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h
+ @@ -51,7 +51,11 @@ typedef long int ssize_t;
+ #endif
+ #ifndef _PTRDIFF_T
+ #define _PTRDIFF_T
+ -typedef long ptrdiff_t;
+ +# ifdef __ANDROID__
+ + typedef int ptrdiff_t;
+ +# else
+ + typedef long ptrdiff_t;
+ +# endif
+ #endif
+" )
+endif()
+
+
+# setup paths and STL for standalone toolchain
+if( BUILD_WITH_STANDALONE_TOOLCHAIN )
+ set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" )
+ set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" )
+
+ if( NOT ANDROID_STL STREQUAL "none" )
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/include/c++/${ANDROID_COMPILER_VERSION}" )
+ if( NOT EXISTS "${ANDROID_STL_INCLUDE_DIRS}" )
+ # old location ( pre r8c )
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" )
+ endif()
+ if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" )
+ list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" )
+ elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" )
+ list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" )
+ else()
+ list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" )
+ endif()
+ # always search static GNU STL to get the location of libsupc++.a
+ if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" )
+ elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" )
+ elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" )
+ elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" )
+ endif()
+ if( __libstl )
+ set( __libsupcxx "${__libstl}/libsupc++.a" )
+ set( __libstl "${__libstl}/libstdc++.a" )
+ endif()
+ if( NOT EXISTS "${__libsupcxx}" )
+ message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain.
+ Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c.
+ You need to either upgrade to newer NDK or manually copy
+ $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a
+ to
+ ${__libsupcxx}
+ " )
+ endif()
+ if( ANDROID_STL STREQUAL "gnustl_shared" )
+ if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" )
+ elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" )
+ elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" )
+ set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" )
+ endif()
+ endif()
+ endif()
+endif()
+
+# clang
+if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" )
+ set( ANDROID_COMPILER_IS_CLANG 1 )
+ execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE )
+ string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}")
+elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" )
+ string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}")
+ string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-${ANDROID_COMPILER_VERSION}" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" )
+ if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" )
+ message( FATAL_ERROR "Could not find the Clang compiler driver" )
+ endif()
+ set( ANDROID_COMPILER_IS_CLANG 1 )
+ set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+else()
+ set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" )
+ unset( ANDROID_COMPILER_IS_CLANG CACHE )
+endif()
+
+string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" )
+if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" )
+ set( _clang_name "clang" )
+endif()
+
+
+# setup paths and STL for NDK
+if( BUILD_WITH_ANDROID_NDK )
+ set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" )
+ set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" )
+
+ if( ANDROID_STL STREQUAL "none" )
+ # do nothing
+ elseif( ANDROID_STL STREQUAL "system" )
+ set( ANDROID_RTTI OFF )
+ set( ANDROID_EXCEPTIONS OFF )
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" )
+ elseif( ANDROID_STL STREQUAL "system_re" )
+ set( ANDROID_RTTI ON )
+ set( ANDROID_EXCEPTIONS ON )
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" )
+ elseif( ANDROID_STL MATCHES "gabi" )
+ if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+ message( FATAL_ERROR "gabi++ is not available in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.")
+ endif()
+ set( ANDROID_RTTI ON )
+ set( ANDROID_EXCEPTIONS OFF )
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" )
+ set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" )
+ elseif( ANDROID_STL MATCHES "stlport" )
+ if( NOT ANDROID_NDK_RELEASE_NUM LESS 8004 ) # before r8d
+ set( ANDROID_EXCEPTIONS ON )
+ else()
+ set( ANDROID_EXCEPTIONS OFF )
+ endif()
+ if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+ set( ANDROID_RTTI OFF )
+ else()
+ set( ANDROID_RTTI ON )
+ endif()
+ set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" )
+ set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" )
+ elseif( ANDROID_STL MATCHES "gnustl" )
+ set( ANDROID_EXCEPTIONS ON )
+ set( ANDROID_RTTI ON )
+ if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" )
+ if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" )
+ # gnustl binary for 4.7 compiler is buggy :(
+ # TODO: look for right fix
+ set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" )
+ else()
+ set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" )
+ endif()
+ else()
+ set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" )
+ endif()
+ set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" "${__libstl}/include/backward" )
+ if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" )
+ set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" )
+ else()
+ set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" )
+ endif()
+ else()
+ message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" )
+ endif()
+ # find libsupc++.a - rtti & exceptions
+ if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" )
+ set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer
+ if( NOT EXISTS "${__libsupcxx}" )
+ set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8
+ endif()
+ if( NOT EXISTS "${__libsupcxx}" ) # before r7
+ if( ARMEABI_V7A )
+ if( ANDROID_FORCE_ARM_BUILD )
+ set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" )
+ else()
+ set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" )
+ endif()
+ elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD )
+ set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" )
+ else()
+ set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" )
+ endif()
+ endif()
+ if( NOT EXISTS "${__libsupcxx}")
+ message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.")
+ endif()
+ endif()
+endif()
+
+
+# case of shared STL linkage
+if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl )
+ string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" )
+ # TODO: check if .so file exists before the renaming
+endif()
+
+
+# ccache support
+__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE )
+if( _ndk_ccache )
+ if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE )
+ unset( NDK_CCACHE CACHE )
+ endif()
+ find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary")
+else()
+ unset( NDK_CCACHE CACHE )
+endif()
+unset( _ndk_ccache )
+
+
+# setup the cross-compiler
+if( NOT CMAKE_C_COMPILER )
+ if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" )
+ set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" )
+ set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" )
+ if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler")
+ set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+ else()
+ set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler")
+ set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+ endif()
+ else()
+ if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler")
+ set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler")
+ else()
+ set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" )
+ set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" )
+ endif()
+ endif()
+ set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" )
+ set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" )
+ if( EXISTS "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" )
+ # Use gcc-ar if we have it for better LTO support.
+ set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" )
+ else()
+ set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" )
+ endif()
+ set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" )
+ set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" )
+ set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" )
+ set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" )
+ set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" )
+endif()
+
+set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" )
+if( CMAKE_VERSION VERSION_LESS 2.8.5 )
+ set( CMAKE_ASM_COMPILER_ARG1 "-c" )
+endif()
+if( APPLE )
+ find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool )
+ if( NOT CMAKE_INSTALL_NAME_TOOL )
+ message( FATAL_ERROR "Could not find install_name_tool, please check your installation." )
+ endif()
+ mark_as_advanced( CMAKE_INSTALL_NAME_TOOL )
+endif()
+
+# Force set compilers because standard identification works badly for us
+include( CMakeForceCompiler )
+CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU )
+if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_C_COMPILER_ID Clang )
+endif()
+set( CMAKE_C_PLATFORM_ID Linux )
+if( X86_64 OR MIPS64 OR ARM64_V8A )
+ set( CMAKE_C_SIZEOF_DATA_PTR 8 )
+else()
+ set( CMAKE_C_SIZEOF_DATA_PTR 4 )
+endif()
+set( CMAKE_C_HAS_ISYSROOT 1 )
+set( CMAKE_C_COMPILER_ABI ELF )
+CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU )
+if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_CXX_COMPILER_ID Clang)
+endif()
+set( CMAKE_CXX_PLATFORM_ID Linux )
+set( CMAKE_CXX_SIZEOF_DATA_PTR ${CMAKE_C_SIZEOF_DATA_PTR} )
+set( CMAKE_CXX_HAS_ISYSROOT 1 )
+set( CMAKE_CXX_COMPILER_ABI ELF )
+set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C )
+# force ASM compiler (required for CMake < 2.8.5)
+set( CMAKE_ASM_COMPILER_ID_RUN TRUE )
+set( CMAKE_ASM_COMPILER_ID GNU )
+set( CMAKE_ASM_COMPILER_WORKS TRUE )
+set( CMAKE_ASM_COMPILER_FORCED TRUE )
+set( CMAKE_COMPILER_IS_GNUASM 1)
+set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm )
+
+foreach( lang C CXX ASM )
+ if( ANDROID_COMPILER_IS_CLANG )
+ set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_CLANG_VERSION} )
+ else()
+ set( CMAKE_${lang}_COMPILER_VERSION ${ANDROID_COMPILER_VERSION} )
+ endif()
+endforeach()
+
+# flags and definitions
+remove_definitions( -DANDROID )
+add_definitions( -DANDROID )
+
+if( ANDROID_SYSROOT MATCHES "[ ;\"]" )
+ if( CMAKE_HOST_WIN32 )
+ # try to convert path to 8.3 form
+ file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" )
+ execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}"
+ OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE
+ RESULT_VARIABLE __result ERROR_QUIET )
+ if( __result EQUAL 0 )
+ file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT )
+ set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" )
+ else()
+ set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" )
+ endif()
+ else()
+ set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" )
+ endif()
+ if( NOT _CMAKE_IN_TRY_COMPILE )
+ # quotes can break try_compile and compiler identification
+ message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n")
+ endif()
+else()
+ set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" )
+endif()
+
+# NDK flags
+if (ARM64_V8A )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" )
+ endif()
+elseif( ARMEABI OR ARMEABI_V7A)
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 )
+ set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" )
+ endif()
+ else()
+ # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI
+ set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" )
+ endif()
+ endif()
+elseif( X86 OR X86_64 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" )
+ endif()
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" )
+ set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" )
+elseif( MIPS OR MIPS64 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-strict-aliasing -finline-functions -funwind-tables -fmessage-length=0" )
+ set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" )
+ set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" )
+ if( NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" )
+ set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" )
+ endif()
+elseif()
+ set( ANDROID_CXX_FLAGS_RELEASE "" )
+ set( ANDROID_CXX_FLAGS_DEBUG "" )
+endif()
+
+set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries
+
+if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" )
+endif()
+
+if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/
+endif()
+
+# ABI-specific flags
+if( ARMEABI_V7A )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" )
+ if( NEON )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" )
+ elseif( VFPV3 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" )
+ else()
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" )
+ endif()
+elseif( ARMEABI_V6 )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2
+elseif( ARMEABI )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" )
+endif()
+
+if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+else()
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "<CMAKE_CXX_COMPILER> <CMAKE_SHARED_LIBRARY_CXX_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_CXX_COMPILER> <FLAGS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+endif()
+
+# STL
+if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" )
+ if( EXISTS "${__libstl}" )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" )
+ set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" )
+ endif()
+ if( EXISTS "${__libsupcxx}" )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" )
+ set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" )
+ # C objects:
+ set( CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_C_CREATE_SHARED_MODULE "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> <CMAKE_SHARED_LIBRARY_SONAME_C_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>" )
+ set( CMAKE_C_LINK_EXECUTABLE "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>" )
+ set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" )
+ set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" )
+ set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" )
+ endif()
+ if( ANDROID_STL MATCHES "gnustl" )
+ if( NOT EXISTS "${ANDROID_LIBM_PATH}" )
+ set( ANDROID_LIBM_PATH -lm )
+ endif()
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" )
+ set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" )
+ endif()
+endif()
+
+# variables controlling optional build flags
+if( ANDROID_NDK_RELEASE_NUM LESS 7000 ) # before r7
+ # libGLESv2.so in NDK's prior to r7 refers to missing external symbols.
+ # So this flag option is required for all projects using OpenGL from native.
+ __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON )
+else()
+ __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF )
+endif()
+__INIT_VARIABLE( ANDROID_NO_UNDEFINED VALUES ON )
+__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON )
+__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON )
+__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON )
+__INIT_VARIABLE( ANDROID_RELRO VALUES ON )
+
+set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" )
+set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" )
+set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Put each function in separate section and enable garbage collection of unused input sections at link time" )
+set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker" )
+set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" )
+set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" )
+mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO )
+
+# linker flags
+set( ANDROID_LINKER_FLAGS "" )
+
+if( ARMEABI_V7A )
+ # this is *required* to use the following linker flags that routes around
+ # a CPU bug in some Cortex-A8 implementations:
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" )
+endif()
+
+if( ANDROID_NO_UNDEFINED )
+ if( MIPS )
+ # there is some sysroot-related problem in mips linker...
+ if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" )
+ endif()
+ else()
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--no-undefined" )
+ endif()
+endif()
+
+if( ANDROID_SO_UNDEFINED )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" )
+endif()
+
+if( ANDROID_FUNCTION_LEVEL_LINKING )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" )
+endif()
+
+if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" )
+ if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE_NUM GREATER 8002) AND (ARMEABI OR ARMEABI_V7A OR X86) )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" )
+ elseif( ANDROID_NDK_RELEASE_NUM GREATER 8002 ) # after r8b
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" )
+ elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE )
+ message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342
+ On Linux and OS X host platform you can workaround this problem using gold linker (default).
+ Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems.
+" )
+ endif()
+endif() # version 4.6
+
+if( ANDROID_NOEXECSTACK )
+ if( ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" )
+ else()
+ set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" )
+ endif()
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" )
+endif()
+
+if( ANDROID_RELRO )
+ set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" )
+endif()
+
+if( ANDROID_COMPILER_IS_CLANG )
+ set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} -Qunused-arguments ${ANDROID_CXX_FLAGS}" )
+ if( BUILD_WITH_ANDROID_NDK )
+ set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" )
+ endif()
+endif()
+
+# cache flags
+set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" )
+set( CMAKE_C_FLAGS "" CACHE STRING "c flags" )
+set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" )
+set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" )
+set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" )
+set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" )
+set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" )
+set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" )
+set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" )
+
+# put flags to cache (for debug purpose only)
+set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" )
+set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" )
+set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" )
+set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" )
+
+# finish flags
+set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" )
+set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" )
+set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" )
+set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" )
+set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" )
+set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" )
+set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" )
+set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" )
+set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" )
+
+if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" )
+ set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" )
+ set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" )
+ set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" )
+endif()
+
+# pie/pic
+if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8) )
+ set( CMAKE_POSITION_INDEPENDENT_CODE TRUE )
+ set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")
+else()
+ set( CMAKE_POSITION_INDEPENDENT_CODE FALSE )
+ set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" )
+ set( CMAKE_C_FLAGS "-fpic ${CMAKE_C_FLAGS}" )
+endif()
+
+# configure rtti
+if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES )
+ if( ANDROID_RTTI )
+ set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" )
+ else()
+ set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" )
+ endif()
+endif()
+
+# configure exceptios
+if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES )
+ if( ANDROID_EXCEPTIONS )
+ set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" )
+ set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" )
+ else()
+ set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" )
+ set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" )
+ endif()
+endif()
+
+# global includes and link directories
+include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} )
+get_filename_component(__android_install_path "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ABSOLUTE) # avoid CMP0015 policy warning
+link_directories( "${__android_install_path}" )
+
+# detect if need link crtbegin_so.o explicitly
+if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK )
+ set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" )
+ string( REPLACE "<CMAKE_CXX_COMPILER>" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_C_COMPILER>" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_CXX_FLAGS>" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" )
+ string( REPLACE "<LANGUAGE_COMPILE_FLAGS>" "" __cmd "${__cmd}" )
+ string( REPLACE "<LINK_FLAGS>" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS>" "-shared" __cmd "${__cmd}" )
+ string( REPLACE "<CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG>" "" __cmd "${__cmd}" )
+ string( REPLACE "<TARGET_SONAME>" "" __cmd "${__cmd}" )
+ string( REPLACE "<TARGET>" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" )
+ string( REPLACE "<OBJECTS>" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" )
+ string( REPLACE "<LINK_LIBRARIES>" "" __cmd "${__cmd}" )
+ separate_arguments( __cmd )
+ foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN )
+ if( ${__var} )
+ set( __tmp "${${__var}}" )
+ separate_arguments( __tmp )
+ string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}")
+ endif()
+ endforeach()
+ string( REPLACE "'" "" __cmd "${__cmd}" )
+ string( REPLACE "\"" "" __cmd "${__cmd}" )
+ execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET )
+ if( __cmd_result EQUAL 0 )
+ set( ANDROID_EXPLICIT_CRT_LINK ON )
+ else()
+ set( ANDROID_EXPLICIT_CRT_LINK OFF )
+ endif()
+endif()
+
+if( ANDROID_EXPLICIT_CRT_LINK )
+ set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" )
+ set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" )
+endif()
+
+# setup output directories
+set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" )
+
+if( DEFINED LIBRARY_OUTPUT_PATH_ROOT
+ OR EXISTS "${CMAKE_SOURCE_DIR}/AndroidManifest.xml"
+ OR (EXISTS "${CMAKE_SOURCE_DIR}/../AndroidManifest.xml" AND EXISTS "${CMAKE_SOURCE_DIR}/../jni/") )
+ set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "Root for binaries output, set this to change where Android libs are installed to" )
+ if( NOT _CMAKE_IN_TRY_COMPILE )
+ if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" )
+ set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" )
+ else()
+ set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" )
+ endif()
+ set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for Android libs" )
+ endif()
+endif()
+
+# copy shaed stl library to build directory
+if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" AND DEFINED LIBRARY_OUTPUT_PATH )
+ get_filename_component( __libstlname "${__libstl}" NAME )
+ execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess )
+ if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}")
+ message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" )
+ endif()
+ unset( __fileCopyProcess )
+ unset( __libstlname )
+endif()
+
+
+# set these global flags for cmake client scripts to change behavior
+set( ANDROID True )
+set( BUILD_ANDROID True )
+
+# where is the target environment
+set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" )
+
+# only search for libraries and includes in the ndk toolchain
+set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+
+
+# macro to find packages on the host OS
+macro( find_host_package )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER )
+ if( CMAKE_HOST_WIN32 )
+ SET( WIN32 1 )
+ SET( UNIX )
+ elseif( CMAKE_HOST_APPLE )
+ SET( APPLE 1 )
+ SET( UNIX )
+ endif()
+ find_package( ${ARGN} )
+ SET( WIN32 )
+ SET( APPLE )
+ SET( UNIX 1 )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+endmacro()
+
+
+# macro to find programs on the host OS
+macro( find_host_program )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER )
+ if( CMAKE_HOST_WIN32 )
+ SET( WIN32 1 )
+ SET( UNIX )
+ elseif( CMAKE_HOST_APPLE )
+ SET( APPLE 1 )
+ SET( UNIX )
+ endif()
+ find_program( ${ARGN} )
+ SET( WIN32 )
+ SET( APPLE )
+ SET( UNIX 1 )
+ set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
+ set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )
+endmacro()
+
+
+# export toolchain settings for the try_compile() command
+if( NOT _CMAKE_IN_TRY_COMPILE )
+ set( __toolchain_config "")
+ foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN
+ ANDROID_NDK_HOST_X64
+ ANDROID_NDK
+ ANDROID_NDK_LAYOUT
+ ANDROID_STANDALONE_TOOLCHAIN
+ ANDROID_TOOLCHAIN_NAME
+ ANDROID_ABI
+ ANDROID_NATIVE_API_LEVEL
+ ANDROID_STL
+ ANDROID_STL_FORCE_FEATURES
+ ANDROID_FORCE_ARM_BUILD
+ ANDROID_NO_UNDEFINED
+ ANDROID_SO_UNDEFINED
+ ANDROID_FUNCTION_LEVEL_LINKING
+ ANDROID_GOLD_LINKER
+ ANDROID_NOEXECSTACK
+ ANDROID_RELRO
+ ANDROID_LIBM_PATH
+ ANDROID_EXPLICIT_CRT_LINK
+ ANDROID_APP_PIE
+ )
+ if( DEFINED ${__var} )
+ if( ${__var} MATCHES " ")
+ set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" )
+ else()
+ set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" )
+ endif()
+ endif()
+ endforeach()
+ file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" )
+ unset( __toolchain_config )
+endif()
+
+
+# force cmake to produce / instead of \ in build commands for Ninja generator
+if( CMAKE_GENERATOR MATCHES "Ninja" AND CMAKE_HOST_WIN32 )
+ # it is a bad hack after all
+ # CMake generates Ninja makefiles with UNIX paths only if it thinks that we are going to build with MinGW
+ set( CMAKE_COMPILER_IS_MINGW TRUE ) # tell CMake that we are MinGW
+ set( CMAKE_CROSSCOMPILING TRUE ) # stop recursion
+ enable_language( C )
+ enable_language( CXX )
+ # unset( CMAKE_COMPILER_IS_MINGW ) # can't unset because CMake does not convert back-slashes in response files without it
+ unset( MINGW )
+endif()
+
+
+# Variables controlling behavior or set by cmake toolchain:
+# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips", "arm64-v8a", "x86_64", "mips64"
+# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14,15,16,17,18,19,21 (depends on NDK version)
+# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none
+# ANDROID_FORBID_SYGWIN : ON/OFF
+# ANDROID_NO_UNDEFINED : ON/OFF
+# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version)
+# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF
+# ANDROID_GOLD_LINKER : ON/OFF
+# ANDROID_NOEXECSTACK : ON/OFF
+# ANDROID_RELRO : ON/OFF
+# ANDROID_FORCE_ARM_BUILD : ON/OFF
+# ANDROID_STL_FORCE_FEATURES : ON/OFF
+# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product/<product_name>/obj/lib/libm.so) to workaround unresolved `sincos`
+# Can be set only at the first run:
+# ANDROID_NDK : path to your NDK install
+# NDK_CCACHE : path to your ccache executable
+# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain
+# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems)
+# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID)
+# LIBRARY_OUTPUT_PATH_ROOT : <any valid path>
+# ANDROID_STANDALONE_TOOLCHAIN
+#
+# Primary read-only variables:
+# ANDROID : always TRUE
+# ARMEABI : TRUE for arm v6 and older devices
+# ARMEABI_V6 : TRUE for arm v6
+# ARMEABI_V7A : TRUE for arm v7a
+# ARM64_V8A : TRUE for arm64-v8a
+# NEON : TRUE if NEON unit is enabled
+# VFPV3 : TRUE if VFP version 3 is enabled
+# X86 : TRUE if configured for x86
+# X86_64 : TRUE if configured for x86_64
+# MIPS : TRUE if configured for mips
+# MIPS64 : TRUE if configured for mips64
+# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used
+# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used
+# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform
+# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86", "mips", "arm64-v8a", "x86_64", "mips64" depending on ANDROID_ABI
+# ANDROID_NDK_RELEASE : from r5 to r10d; set only for NDK
+# ANDROID_NDK_RELEASE_NUM : numeric ANDROID_NDK_RELEASE version (1000*major+minor)
+# ANDROID_ARCH_NAME : "arm", "x86", "mips", "arm64", "x86_64", "mips64" depending on ANDROID_ABI
+# ANDROID_SYSROOT : path to the compiler sysroot
+# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform
+# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used
+#
+# Secondary (less stable) read-only variables:
+# ANDROID_COMPILER_VERSION : GCC version used (not Clang version)
+# ANDROID_CLANG_VERSION : version of clang compiler if clang is used
+# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform
+# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI
+# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux"
+# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK)
+# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools
+# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK
+# ANDROID_STL_INCLUDE_DIRS : stl include paths
+# ANDROID_RTTI : if rtti is enabled by the runtime
+# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime
+# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used
+#
+# Defaults:
+# ANDROID_DEFAULT_NDK_API_LEVEL
+# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH}
+# ANDROID_NDK_SEARCH_PATHS
+# ANDROID_SUPPORTED_ABIS_${ARCH}
+# ANDROID_SUPPORTED_NDK_VERSIONS
diff --git a/src/third_party/android-cmake/ndk_links.md b/src/third_party/android-cmake/ndk_links.md
new file mode 100644
index 0000000..6d93d61
--- /dev/null
+++ b/src/third_party/android-cmake/ndk_links.md
@@ -0,0 +1,211 @@
+
+============== r1 ============== (dead links)
+
+* http://dl.google.com/android/ndk/android-ndk-1.5_r1-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-1.5_r1-darwin-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-1.5_r1-linux-x86.zip
+
+============== r2 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-1.6_r1-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-1.6_r1-darwin-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-1.6_r1-linux-x86.zip
+
+============== r3 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r3-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r3-darwin-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r3-linux-x86.zip
+
+============== r4 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r4-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r4-darwin-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r4-linux-x86.zip
+
+============== r4b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r4b-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r4b-darwin-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r4b-linux-x86.zip
+
+============== r5 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r5-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r5-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r5-linux-x86.tar.bz2
+
+============== r5b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r5b-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r5b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r5b-linux-x86.tar.bz2
+
+============== r5c ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r5c-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r5c-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r5c-linux-x86.tar.bz2
+
+============== r6 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r6-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r6-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r6-linux-x86.tar.bz2
+
+============== r6b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r6b-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r6b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r6b-linux-x86.tar.bz2
+
+============== r7 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r7-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r7-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r7-linux-x86.tar.bz2
+
+============== r7b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r7b-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r7b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r7b-linux-x86.tar.bz2
+
+============== r7c ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r7c-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r7c-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r7c-linux-x86.tar.bz2
+
+============== r8 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r8-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r8-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8-linux-x86.tar.bz2
+
+============== r8b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r8b-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r8b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8b-linux-x86.tar.bz2
+
+============== r8c ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r8c-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r8c-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8c-linux-x86.tar.bz2
+
+============== r8d ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r8d-windows.zip
+* http://dl.google.com/android/ndk/android-ndk-r8d-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8d-linux-x86.tar.bz2
+
+============== r8e ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r8e-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r8e-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8e-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r8e-linux-x86_64.tar.bz2
+
+============== r9 ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86-legacy-toolchains.zip
+* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk-r9-windows-x86_64-legacy-toolchains.zip
+* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-darwin-x86_64-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9-linux-x86_64-legacy-toolchains.tar.bz2
+
+============== r9b ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86-legacy-toolchains.zip
+* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64-legacy-toolchains.zip
+* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-darwin-x86_64-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86-legacy-toolchains.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9b-linux-x86_64-legacy-toolchains.tar.bz2
+
+============== r9c ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r9c-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r9c-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk-r9c-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9c-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9c-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9c-cxx-stl-libs-with-debugging-info.zip
+
+============== r9d ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk-r9d-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9d-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9d-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r9d-cxx-stl-libs-with-debug-info.zip
+
+============== r10 ==============
+
+* http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk32-r10-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk64-r10-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r10-cxx-stl-libs-with-debug-info.zip
+
+============== r10b ==============
+
+* http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk32-r10b-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk32-r10b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10b-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10b-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk32-r10b-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10b-windows-x86.zip
+* http://dl.google.com/android/ndk/android-ndk64-r10b-windows-x86_64.zip
+* http://dl.google.com/android/ndk/android-ndk64-r10b-darwin-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10b-darwin-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10b-linux-x86.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk64-r10b-linux-x86_64.tar.bz2
+* http://dl.google.com/android/ndk/android-ndk-r10b-cxx-stl-libs-with-debug-info.zip
+
+============== r10c ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r10c-windows-x86.exe
+* http://dl.google.com/android/ndk/android-ndk-r10c-windows-x86_64.exe
+* http://dl.google.com/android/ndk/android-ndk-r10c-darwin-x86.bin
+* http://dl.google.com/android/ndk/android-ndk-r10c-darwin-x86_64.bin
+* http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86.bin
+* http://dl.google.com/android/ndk/android-ndk-r10c-linux-x86_64.bin
+
+============== r10d ==============
+
+* http://dl.google.com/android/ndk/android-ndk-r10d-windows-x86.exe
+* http://dl.google.com/android/ndk/android-ndk-r10d-windows-x86_64.exe
+* http://dl.google.com/android/ndk/android-ndk-r10d-darwin-x86.bin
+* http://dl.google.com/android/ndk/android-ndk-r10d-darwin-x86_64.bin
+* http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86.bin
+* http://dl.google.com/android/ndk/android-ndk-r10d-linux-x86_64.bin
diff --git a/src/tool/digest.cc b/src/tool/digest.cc
index 012202c..c2cee7f 100644
--- a/src/tool/digest.cc
+++ b/src/tool/digest.cc
@@ -46,7 +46,7 @@
struct close_delete {
void operator()(int *fd) {
- close(*fd);
+ BORINGSSL_CLOSE(*fd);
}
};
@@ -83,7 +83,7 @@
static bool OpenFile(int *out_fd, const std::string &filename) {
*out_fd = -1;
- int fd = open(filename.c_str(), O_RDONLY | O_BINARY);
+ int fd = BORINGSSL_OPEN(filename.c_str(), O_RDONLY | O_BINARY);
if (fd < 0) {
fprintf(stderr, "Failed to open input file '%s': %s\n", filename.c_str(),
strerror(errno));
@@ -146,7 +146,7 @@
ssize_t n;
do {
- n = read(fd, buf.get(), kBufSize);
+ n = BORINGSSL_READ(fd, buf.get(), kBufSize);
} while (n == -1 && errno == EINTR);
if (n == 0) {
@@ -234,10 +234,10 @@
return false;
}
- file = fdopen(fd, "rb");
+ file = BORINGSSL_FDOPEN(fd, "rb");
if (!file) {
perror("fdopen");
- close(fd);
+ BORINGSSL_CLOSE(fd);
return false;
}
diff --git a/src/tool/internal.h b/src/tool/internal.h
index 6d26209..7737f4c 100644
--- a/src/tool/internal.h
+++ b/src/tool/internal.h
@@ -34,6 +34,20 @@
#pragma warning(pop)
#endif
+#if defined(OPENSSL_WINDOWS)
+ #define BORINGSSL_OPEN _open
+ #define BORINGSSL_FDOPEN _fdopen
+ #define BORINGSSL_CLOSE _close
+ #define BORINGSSL_READ _read
+ #define BORINGSSL_WRITE _write
+#else
+ #define BORINGSSL_OPEN open
+ #define BORINGSSL_FDOPEN fdopen
+ #define BORINGSSL_CLOSE close
+ #define BORINGSSL_READ read
+ #define BORINGSSL_WRITE write
+#endif
+
enum ArgumentType {
kRequiredArgument,
kOptionalArgument,
diff --git a/src/tool/pkcs12.cc b/src/tool/pkcs12.cc
index fe830d6..15e32d3 100644
--- a/src/tool/pkcs12.cc
+++ b/src/tool/pkcs12.cc
@@ -64,7 +64,7 @@
return false;
}
- int fd = open(args_map["-dump"].c_str(), O_RDONLY);
+ int fd = BORINGSSL_OPEN(args_map["-dump"].c_str(), O_RDONLY);
if (fd < 0) {
perror("open");
return false;
@@ -73,7 +73,7 @@
struct stat st;
if (fstat(fd, &st)) {
perror("fstat");
- close(fd);
+ BORINGSSL_CLOSE(fd);
return false;
}
const size_t size = st.st_size;
@@ -82,7 +82,7 @@
read_result_t n;
size_t off = 0;
do {
- n = read(fd, &contents[off], size - off);
+ n = BORINGSSL_READ(fd, &contents[off], size - off);
if (n >= 0) {
off += static_cast<size_t>(n);
}
@@ -90,11 +90,11 @@
if (off != size) {
perror("read");
- close(fd);
+ BORINGSSL_CLOSE(fd);
return false;
}
- close(fd);
+ BORINGSSL_CLOSE(fd);
printf("Enter password: ");
fflush(stdout);
@@ -102,7 +102,7 @@
char password[256];
off = 0;
do {
- n = read(0, &password[off], sizeof(password) - 1 - off);
+ n = BORINGSSL_READ(0, &password[off], sizeof(password) - 1 - off);
if (n >= 0) {
off += static_cast<size_t>(n);
}
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index 68d78e2..58c7382 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -522,15 +522,14 @@
TimeResults results;
NEWHOPE_POLY *sk = NEWHOPE_POLY_new();
- uint8_t clientmsg[NEWHOPE_CLIENTMSG_LENGTH];
- RAND_bytes(clientmsg, sizeof(clientmsg));
+ uint8_t acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
+ RAND_bytes(acceptmsg, sizeof(acceptmsg));
- if (!TimeFunction(&results, [sk, &clientmsg]() -> bool {
- uint8_t server_key[SHA256_DIGEST_LENGTH];
- uint8_t servermsg[NEWHOPE_SERVERMSG_LENGTH];
- NEWHOPE_keygen(servermsg, sk);
- if (!NEWHOPE_server_compute_key(server_key, sk, clientmsg,
- NEWHOPE_CLIENTMSG_LENGTH)) {
+ if (!TimeFunction(&results, [sk, &acceptmsg]() -> bool {
+ uint8_t key[SHA256_DIGEST_LENGTH];
+ uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ NEWHOPE_offer(offermsg, sk);
+ if (!NEWHOPE_finish(key, sk, acceptmsg, NEWHOPE_ACCEPTMSG_LENGTH)) {
return false;
}
return true;
@@ -540,7 +539,7 @@
}
NEWHOPE_POLY_free(sk);
- results.Print("newhope server key exchange");
+ results.Print("newhope key exchange");
return true;
}
diff --git a/src/tool/transport_common.cc b/src/tool/transport_common.cc
index 9115214..be47787 100644
--- a/src/tool/transport_common.cc
+++ b/src/tool/transport_common.cc
@@ -181,6 +181,8 @@
}
fprintf(stderr, " Secure renegotiation: %s\n",
SSL_get_secure_renegotiation_support(ssl) ? "yes" : "no");
+ fprintf(stderr, " Extended master secret: %s\n",
+ SSL_get_extms_support(ssl) ? "yes" : "no");
const uint8_t *next_proto;
unsigned next_proto_len;
@@ -265,7 +267,7 @@
ssize_t n;
do {
- n = read(0, buffer, sizeof(buffer));
+ n = BORINGSSL_READ(0, buffer, sizeof(buffer));
} while (n == -1 && errno == EINTR);
if (n == 0) {
@@ -319,7 +321,7 @@
ssize_t n;
do {
- n = write(1, buffer, ssl_ret);
+ n = BORINGSSL_WRITE(1, buffer, ssl_ret);
} while (n == -1 && errno == EINTR);
if (n != ssl_ret) {
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index 3de0694..9e3445c 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -51,6 +51,7 @@
["crypto/lhash/lhash_test"],
["crypto/modes/gcm_test"],
["crypto/newhope/newhope_test"],
+ ["crypto/newhope/newhope_vectors_test", "crypto/newhope/newhope_test.txt"],
["crypto/obj/obj_test"],
["crypto/pkcs8/pkcs12_test"],
["crypto/pkcs8/pkcs8_test"],
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index 862c93a..2a1e01a 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -21,6 +21,13 @@
Var('chromium_git') + '/external/gyp.git' + '@' + '4cf07e8d616739f6484e46c9359b2a35196b2585',
}
+deps_os = {
+ 'android': {
+ 'boringssl/util/bot/android_tools':
+ Var('chromium_git') + '/android_tools.git' + '@' + '5b5f2f60b78198eaef25d442ac60f823142a8a6e',
+ },
+}
+
hooks = [
{
'name': 'cmake_linux64',
diff --git a/src/util/bot/UPDATING b/src/util/bot/UPDATING
index 543daf0..2007d46 100644
--- a/src/util/bot/UPDATING
+++ b/src/util/bot/UPDATING
@@ -1,9 +1,9 @@
This directory consumes tools from other repositories for use on the
bots. To update to newer revisions, follow these instructions:
-DEPS: Set the external/gyp.git revision to the revision used in Chromium, found at
- https://chromium.googlesource.com/chromium/src/+/master/DEPS
- (Search for 'gyp.git'.)
+DEPS: Set all revisions to those used in Chromium, found at
+ https://chromium.googlesource.com/chromium/src/+/master/DEPS (Search for the
+ corresponding repository name.)
go/bootstrap.py: Set TOOLSET_VERSION to the latest release of Go, found at
https://golang.org/dl/.
diff --git a/src/util/doc.css b/src/util/doc.css
index ab653e4..a868e44 100644
--- a/src/util/doc.css
+++ b/src/util/doc.css
@@ -7,12 +7,15 @@
h2 {
font-family: monospace;
- margin-bottom: 2em;
background-color: #b2c9db;
padding: 7px;
border-radius: 7px;
}
+div.title {
+ margin-bottom: 2em;
+}
+
ol {
list-style: none;
margin-bottom: 4em;
diff --git a/src/util/doc.go b/src/util/doc.go
index ace7a58..681b834 100644
--- a/src/util/doc.go
+++ b/src/util/doc.go
@@ -274,13 +274,12 @@
return nil, err
}
- lineNo := 0
+ lineNo := 1
found := false
for i, line := range lines {
- lineNo++
if line == cppGuard {
lines = lines[i+1:]
- lineNo++
+ lineNo += i + 1
found = true
break
}
@@ -302,9 +301,9 @@
}
for i, line := range lines {
- lineNo++
if len(line) > 0 {
lines = lines[i:]
+ lineNo += i
break
}
}
@@ -390,6 +389,7 @@
if len(lines) == 0 {
return nil, errors.New("expected decl at EOF")
}
+ declLineNo := lineNo
decl, lines, lineNo, err = extractDecl(lines, lineNo)
if err != nil {
return nil, err
@@ -409,10 +409,12 @@
// detected by starting with “The” or “These”.
if len(comment) > 0 &&
!strings.HasPrefix(comment[0], name) &&
+ !strings.HasPrefix(comment[0], "A "+name) &&
+ !strings.HasPrefix(comment[0], "An "+name) &&
!strings.HasPrefix(decl, "#define ") &&
!strings.HasPrefix(comment[0], "The ") &&
!strings.HasPrefix(comment[0], "These ") {
- return nil, fmt.Errorf("Comment for %q doesn't seem to match just above %s:%d\n", name, path, lineNo)
+ return nil, fmt.Errorf("Comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
}
anchor := sanitizeAnchor(name)
// TODO(davidben): Enforce uniqueness. This is
@@ -542,7 +544,10 @@
<body>
<div id="main">
- <h2>{{.Name}}</h2>
+ <div class="title">
+ <h2>{{.Name}}</h2>
+ <a href="headers.html">All headers</a>
+ </div>
{{range .Preamble}}<p>{{. | html | markupPipeWords}}</p>{{end}}
@@ -635,6 +640,9 @@
<body>
<div id="main">
+ <div class="title">
+ <h2>BoringSSL Headers</h2>
+ </div>
<table>
{{range .Sections}}
<tr class="header"><td colspan="2">{{.Name}}</td></tr>
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index a3721fe..663d6c7 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -45,6 +45,9 @@
('linux', 'x86_64'): [
'src/crypto/curve25519/asm/x25519-asm-x86_64.S',
],
+ ('mac', 'x86_64'): [
+ 'src/crypto/curve25519/asm/x25519-asm-x86_64.S',
+ ],
}
diff --git a/src/util/run_android_tests.go b/src/util/run_android_tests.go
index b0a388f..fc94d33 100644
--- a/src/util/run_android_tests.go
+++ b/src/util/run_android_tests.go
@@ -28,18 +28,29 @@
var (
buildDir = flag.String("build-dir", "build", "Specifies the build directory to push.")
+ adbPath = flag.String("adb", "adb", "Specifies the adb binary to use. Defaults to looking in PATH.")
device = flag.String("device", "", "Specifies the device or emulator. See adb's -s argument.")
aarch64 = flag.Bool("aarch64", false, "Build the test runners for aarch64 instead of arm.")
arm = flag.Int("arm", 7, "Which arm revision to build for.")
+ suite = flag.String("suite", "all", "Specifies the test suites to run (all, unit, or ssl).")
allTestsArgs = flag.String("all-tests-args", "", "Specifies space-separated arguments to pass to all_tests.go")
runnerArgs = flag.String("runner-args", "", "Specifies space-separated arguments to pass to ssl/test/runner")
+ jsonOutput = flag.String("json-output", "", "The file to output JSON results to.")
)
+func enableUnitTests() bool {
+ return *suite == "all" || *suite == "unit"
+}
+
+func enableSSLTests() bool {
+ return *suite == "all" || *suite == "ssl"
+}
+
func adb(args ...string) error {
if len(*device) > 0 {
args = append([]string{"-s", *device}, args...)
}
- cmd := exec.Command("adb", args...)
+ cmd := exec.Command(*adbPath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
@@ -50,6 +61,7 @@
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
+ cmd.Env = os.Environ()
if *aarch64 {
cmd.Env = append(cmd.Env, "GOARCH=arm64")
} else {
@@ -118,14 +130,14 @@
func main() {
flag.Parse()
- setWorkingDirectory()
- tests, err := parseTestConfig("util/all_tests.json")
- if err != nil {
- fmt.Printf("Failed to parse input: %s\n", err)
+ if *suite == "all" && *jsonOutput != "" {
+ fmt.Printf("To use -json-output flag, select only one test suite with -suite.\n")
os.Exit(1)
}
+ setWorkingDirectory()
+
// Clear the target directory.
if err := adb("shell", "rm -Rf /data/local/tmp/boringssl-tmp"); err != nil {
fmt.Printf("Failed to clear target directory: %s\n", err)
@@ -140,26 +152,56 @@
}
defer os.RemoveAll(tmpDir)
- seenBinary := make(map[string]struct{})
- binaries := []string{"ssl/test/bssl_shim"}
- files := []string{
- "BUILDING.md",
- "util/all_tests.json",
- "ssl/test/runner/cert.pem",
- "ssl/test/runner/channel_id_key.pem",
- "ssl/test/runner/ecdsa_cert.pem",
- "ssl/test/runner/ecdsa_key.pem",
- "ssl/test/runner/key.pem",
- }
- for _, test := range tests {
- if _, ok := seenBinary[test[0]]; !ok {
- binaries = append(binaries, test[0])
- seenBinary[test[0]] = struct{}{}
+ var binaries, files []string
+
+ if enableUnitTests() {
+ files = append(files,
+ "util/all_tests.json",
+ "BUILDING.md",
+ )
+
+ tests, err := parseTestConfig("util/all_tests.json")
+ if err != nil {
+ fmt.Printf("Failed to parse input: %s\n", err)
+ os.Exit(1)
}
- for _, arg := range test[1:] {
- if strings.Contains(arg, "/") {
- files = append(files, arg)
+
+ seenBinary := make(map[string]struct{})
+ for _, test := range tests {
+ if _, ok := seenBinary[test[0]]; !ok {
+ binaries = append(binaries, test[0])
+ seenBinary[test[0]] = struct{}{}
}
+ for _, arg := range test[1:] {
+ if strings.Contains(arg, "/") {
+ files = append(files, arg)
+ }
+ }
+ }
+
+ fmt.Printf("Building all_tests...\n")
+ if err := goTool("build", "-o", filepath.Join(tmpDir, "util/all_tests"), "util/all_tests.go"); err != nil {
+ fmt.Printf("Error building all_tests.go: %s\n", err)
+ os.Exit(1)
+ }
+ }
+
+ if enableSSLTests() {
+ binaries = append(binaries, "ssl/test/bssl_shim")
+ files = append(files,
+ "BUILDING.md",
+ "util/all_tests.json",
+ "ssl/test/runner/cert.pem",
+ "ssl/test/runner/channel_id_key.pem",
+ "ssl/test/runner/ecdsa_cert.pem",
+ "ssl/test/runner/ecdsa_key.pem",
+ "ssl/test/runner/key.pem",
+ )
+
+ fmt.Printf("Building runner...\n")
+ if err := goTool("test", "-c", "-o", filepath.Join(tmpDir, "ssl/test/runner/runner"), "./ssl/test/runner/"); err != nil {
+ fmt.Printf("Error building runner: %s\n", err)
+ os.Exit(1)
}
}
@@ -179,33 +221,32 @@
}
}
- fmt.Printf("Building all_tests...\n")
- if err := goTool("build", "-o", filepath.Join(tmpDir, "util/all_tests"), "util/all_tests.go"); err != nil {
- fmt.Printf("Error building all_tests.go: %s\n", err)
- os.Exit(1)
- }
-
- fmt.Printf("Building runner...\n")
- if err := goTool("test", "-c", "-o", filepath.Join(tmpDir, "ssl/test/runner/runner"), "./ssl/test/runner/"); err != nil {
- fmt.Printf("Error building runner: %s\n", err)
- os.Exit(1)
- }
-
fmt.Printf("Uploading files...\n")
if err := adb("push", "-p", tmpDir, "/data/local/tmp/boringssl-tmp"); err != nil {
fmt.Printf("Failed to push runner: %s\n", err)
os.Exit(1)
}
- fmt.Printf("Running unit tests...\n")
- if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp && ./util/all_tests %s", *allTestsArgs)); err != nil {
- fmt.Printf("Failed to run unit tests: %s\n", err)
- os.Exit(1)
+ if enableUnitTests() {
+ fmt.Printf("Running unit tests...\n")
+ if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp && ./util/all_tests -json-output results.json %s", *allTestsArgs)); err != nil {
+ fmt.Printf("Failed to run unit tests: %s\n", err)
+ os.Exit(1)
+ }
}
- fmt.Printf("Running SSL tests...\n")
- if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp/ssl/test/runner && ./runner %s", *runnerArgs)); err != nil {
- fmt.Printf("Failed to run SSL tests: %s\n", err)
- os.Exit(1)
+ if enableSSLTests() {
+ fmt.Printf("Running SSL tests...\n")
+ if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp/ssl/test/runner && ./runner -json-output ../../../results.json %s", *runnerArgs)); err != nil {
+ fmt.Printf("Failed to run SSL tests: %s\n", err)
+ os.Exit(1)
+ }
+ }
+
+ if *jsonOutput != "" {
+ if err := adb("pull", "-p", "/data/local/tmp/boringssl-tmp/results.json", *jsonOutput); err != nil {
+ fmt.Printf("Failed to extract results.json: %s\n", err)
+ os.Exit(1)
+ }
}
}