aboutsummaryrefslogtreecommitdiff
path: root/contrib/bearssl/tools
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2019-02-26 05:59:22 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2019-02-26 05:59:22 +0000
commit0957b409a90fd597c1e9124cbaf3edd2b488f4ac (patch)
tree9a8b4e8bd4deb70db7a418f9d6a11df3ea8cddb2 /contrib/bearssl/tools
parent4775b07ebd7c6e098ddba9a377444a51c437d27d (diff)
parentf55287d3221d3d9128a6529f97e55fb74250aec2 (diff)
downloadsrc-0957b409a90fd597c1e9124cbaf3edd2b488f4ac.tar.gz
src-0957b409a90fd597c1e9124cbaf3edd2b488f4ac.zip
Notes
Diffstat (limited to 'contrib/bearssl/tools')
-rw-r--r--contrib/bearssl/tools/brssl.c122
-rw-r--r--contrib/bearssl/tools/brssl.h572
-rw-r--r--contrib/bearssl/tools/certs.c237
-rw-r--r--contrib/bearssl/tools/chain.c154
-rw-r--r--contrib/bearssl/tools/client.c1112
-rw-r--r--contrib/bearssl/tools/errors.c344
-rw-r--r--contrib/bearssl/tools/files.c329
-rw-r--r--contrib/bearssl/tools/impl.c48
-rw-r--r--contrib/bearssl/tools/keys.c234
-rw-r--r--contrib/bearssl/tools/names.c1056
-rw-r--r--contrib/bearssl/tools/server.c1235
-rw-r--r--contrib/bearssl/tools/skey.c784
-rw-r--r--contrib/bearssl/tools/sslio.c760
-rw-r--r--contrib/bearssl/tools/ta.c254
-rw-r--r--contrib/bearssl/tools/twrch.c1069
-rw-r--r--contrib/bearssl/tools/vector.c66
-rw-r--r--contrib/bearssl/tools/verify.c353
-rw-r--r--contrib/bearssl/tools/xmem.c132
18 files changed, 8861 insertions, 0 deletions
diff --git a/contrib/bearssl/tools/brssl.c b/contrib/bearssl/tools/brssl.c
new file mode 100644
index 000000000000..91372b09f421
--- /dev/null
+++ b/contrib/bearssl/tools/brssl.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+/*
+ * Network stuff on Windows requires some specific code.
+ */
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma comment(lib, "Ws2_32.lib")
+#endif
+
+#include "brssl.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: brssl command [ options ]\n");
+ fprintf(stderr, "available commands:\n");
+ fprintf(stderr, " client run SSL client\n");
+ fprintf(stderr, " server run SSL server\n");
+ fprintf(stderr, " verify verify certificate chain\n");
+ fprintf(stderr, " skey decode private key\n");
+ fprintf(stderr, " ta decode trust anchors\n");
+ fprintf(stderr, " chain make C code for certificate chains\n");
+ fprintf(stderr, " twrch run the Twrch protocol\n");
+ fprintf(stderr, " impl report on implementations\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *cmd;
+
+ if (argc < 2) {
+ usage();
+ return EXIT_FAILURE;
+ }
+#ifdef _WIN32
+ {
+ WSADATA wd;
+ int r;
+
+ r = WSAStartup(MAKEWORD(2, 2), &wd);
+ if (r != 0) {
+ fprintf(stderr, "WARNING: network initialisation"
+ " failed (WSAStartup() returned %d)\n", r);
+ }
+ }
+#endif
+ cmd = argv[1];
+ if (eqstr(cmd, "client")) {
+ if (do_client(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "server")) {
+ if (do_server(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "verify")) {
+ if (do_verify(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "skey")) {
+ if (do_skey(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "ta")) {
+ if (do_ta(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "chain")) {
+ if (do_chain(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else if (eqstr(cmd, "twrch")) {
+ int ret;
+
+ ret = do_twrch(argc - 2, argv + 2);
+ if (ret < 0) {
+ return EXIT_FAILURE;
+ } else {
+ return ret;
+ }
+ } else if (eqstr(cmd, "impl")) {
+ if (do_impl(argc - 2, argv + 2) < 0) {
+ return EXIT_FAILURE;
+ }
+ } else {
+ fprintf(stderr, "unknown command: '%s'\n", cmd);
+ usage();
+ return EXIT_FAILURE;
+ }
+ return 0;
+}
diff --git a/contrib/bearssl/tools/brssl.h b/contrib/bearssl/tools/brssl.h
new file mode 100644
index 000000000000..62ea7e9ff1a9
--- /dev/null
+++ b/contrib/bearssl/tools/brssl.h
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef BRSSL_H__
+#define BRSSL_H__
+
+#ifndef _STANDALONE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#elif !defined(STAND_H)
+#include <stand.h>
+#endif
+
+#include "bearssl.h"
+
+/*
+ * malloc() wrapper:
+ * -- If len is 0, then NULL is returned.
+ * -- If len is non-zero, and allocation fails, then an error message is
+ * printed and the process exits with an error code.
+ */
+void *xmalloc(size_t len);
+
+/*
+ * free() wrapper, meant to release blocks allocated with xmalloc().
+ */
+void xfree(void *buf);
+
+/*
+ * Duplicate a character string into a newly allocated block.
+ */
+char *xstrdup(const void *src);
+
+/*
+ * Allocate a new block with the provided length, filled with a copy
+ * of exactly that many bytes starting at address 'src'.
+ */
+void *xblobdup(const void *src, size_t len);
+
+/*
+ * Duplicate a public key, into newly allocated blocks. The returned
+ * key must be later on released with xfreepkey().
+ */
+br_x509_pkey *xpkeydup(const br_x509_pkey *pk);
+
+/*
+ * Release a public key that was allocated with xpkeydup(). If pk is NULL,
+ * this function does nothing.
+ */
+void xfreepkey(br_x509_pkey *pk);
+
+/*
+ * Macros for growable arrays.
+ */
+
+/*
+ * Make a structure type for a vector of 'type'.
+ */
+#define VECTOR(type) struct { \
+ type *buf; \
+ size_t ptr, len; \
+ }
+
+/*
+ * Constant initialiser for a vector.
+ */
+#define VEC_INIT { 0, 0, 0 }
+
+/*
+ * Clear a vector.
+ */
+#define VEC_CLEAR(vec) do { \
+ xfree((vec).buf); \
+ (vec).buf = NULL; \
+ (vec).ptr = 0; \
+ (vec).len = 0; \
+ } while (0)
+
+/*
+ * Clear a vector, first calling the provided function on each vector
+ * element.
+ */
+#define VEC_CLEAREXT(vec, fun) do { \
+ size_t vec_tmp; \
+ for (vec_tmp = 0; vec_tmp < (vec).ptr; vec_tmp ++) { \
+ (fun)(&(vec).buf[vec_tmp]); \
+ } \
+ VEC_CLEAR(vec); \
+ } while (0)
+
+/*
+ * Add a value at the end of a vector.
+ */
+#define VEC_ADD(vec, x) do { \
+ (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), \
+ &(vec).ptr, &(vec).len, 1); \
+ (vec).buf[(vec).ptr ++] = (x); \
+ } while (0)
+
+/*
+ * Add several values at the end of a vector.
+ */
+#define VEC_ADDMANY(vec, xp, num) do { \
+ size_t vec_num = (num); \
+ (vec).buf = vector_expand((vec).buf, sizeof *((vec).buf), \
+ &(vec).ptr, &(vec).len, vec_num); \
+ memcpy((vec).buf + (vec).ptr, \
+ (xp), vec_num * sizeof *((vec).buf)); \
+ (vec).ptr += vec_num; \
+ } while (0)
+
+/*
+ * Access a vector element by index. This is a lvalue, and can be modified.
+ */
+#define VEC_ELT(vec, idx) ((vec).buf[idx])
+
+/*
+ * Get current vector length.
+ */
+#define VEC_LEN(vec) ((vec).ptr)
+
+/*
+ * Copy all vector elements into a newly allocated block.
+ */
+#define VEC_TOARRAY(vec) xblobdup((vec).buf, sizeof *((vec).buf) * (vec).ptr)
+
+/*
+ * Internal function used to handle memory allocations for vectors.
+ */
+void *vector_expand(void *buf,
+ size_t esize, size_t *ptr, size_t *len, size_t extra);
+
+/*
+ * Type for a vector of bytes.
+ */
+typedef VECTOR(unsigned char) bvector;
+
+/*
+ * Compare two strings for equality; returned value is 1 if the strings
+ * are to be considered equal, 0 otherwise. Comparison is case-insensitive
+ * (ASCII letters only) and skips some characters (all whitespace, defined
+ * as ASCII codes 0 to 32 inclusive, and also '-', '_', '.', '/', '+' and
+ * ':').
+ */
+int eqstr(const char *s1, const char *s2);
+
+/*
+ * Convert a string to a positive integer (size_t). Returned value is
+ * (size_t)-1 on error. On error, an explicit error message is printed.
+ */
+size_t parse_size(const char *s);
+
+/*
+ * Structure for a known protocol version.
+ */
+typedef struct {
+ const char *name;
+ unsigned version;
+ const char *comment;
+} protocol_version;
+
+/*
+ * Known protocol versions. Last element has a NULL name.
+ */
+extern const protocol_version protocol_versions[];
+
+/*
+ * Parse a version name. If the name is not recognized, then an error
+ * message is printed, and 0 is returned.
+ */
+unsigned parse_version(const char *name, size_t len);
+
+/*
+ * Type for a known hash function.
+ */
+typedef struct {
+ const char *name;
+ const br_hash_class *hclass;
+ const char *comment;
+} hash_function;
+
+/*
+ * Known hash functions. Last element has a NULL name.
+ */
+extern const hash_function hash_functions[];
+
+/*
+ * Parse hash function names. This function expects a comma-separated
+ * list of names, and returns a bit mask corresponding to the matched
+ * names. If one of the name does not match, or the list is empty, then
+ * an error message is printed, and 0 is returned.
+ */
+unsigned parse_hash_functions(const char *arg);
+
+/*
+ * Get a curve name (by ID). If the curve ID is not known, this returns
+ * NULL.
+ */
+const char *get_curve_name(int id);
+
+/*
+ * Get a curve name (by ID). The name is written in the provided buffer
+ * (zero-terminated). If the curve ID is not known, the name is
+ * "unknown (***)" where "***" is the decimal value of the identifier.
+ * If the name does not fit in the provided buffer, then dst[0] is set
+ * to 0 (unless len is 0, in which case nothing is written), and -1 is
+ * returned. Otherwise, the name is written in dst[] (with a terminating
+ * 0), and this function returns 0.
+ */
+int get_curve_name_ext(int id, char *dst, size_t len);
+
+/*
+ * Type for a known cipher suite.
+ */
+typedef struct {
+ const char *name;
+ uint16_t suite;
+ unsigned req;
+ const char *comment;
+} cipher_suite;
+
+/*
+ * Known cipher suites. Last element has a NULL name.
+ */
+extern const cipher_suite cipher_suites[];
+
+/*
+ * Flags for cipher suite requirements.
+ */
+#define REQ_TLS12 0x0001 /* suite needs TLS 1.2 */
+#define REQ_SHA1 0x0002 /* suite needs SHA-1 */
+#define REQ_SHA256 0x0004 /* suite needs SHA-256 */
+#define REQ_SHA384 0x0008 /* suite needs SHA-384 */
+#define REQ_AESCBC 0x0010 /* suite needs AES/CBC encryption */
+#define REQ_AESGCM 0x0020 /* suite needs AES/GCM encryption */
+#define REQ_AESCCM 0x0040 /* suite needs AES/CCM encryption */
+#define REQ_CHAPOL 0x0080 /* suite needs ChaCha20+Poly1305 */
+#define REQ_3DESCBC 0x0100 /* suite needs 3DES/CBC encryption */
+#define REQ_RSAKEYX 0x0200 /* suite uses RSA key exchange */
+#define REQ_ECDHE_RSA 0x0400 /* suite uses ECDHE_RSA key exchange */
+#define REQ_ECDHE_ECDSA 0x0800 /* suite uses ECDHE_ECDSA key exchange */
+#define REQ_ECDH 0x1000 /* suite uses static ECDH key exchange */
+
+/*
+ * Parse a list of cipher suite names. The names are comma-separated. If
+ * one of the name is not recognised, or the list is empty, then an
+ * appropriate error message is printed, and NULL is returned.
+ * The returned array is allocated with xmalloc() and must be released
+ * by the caller. That array is terminated with a dummy entry whose 'name'
+ * field is NULL. The number of entries (not counting the dummy entry)
+ * is also written into '*num'.
+ */
+cipher_suite *parse_suites(const char *arg, size_t *num);
+
+/*
+ * Get the name of a cipher suite. Returned value is NULL if the suite is
+ * not recognized.
+ */
+const char *get_suite_name(unsigned suite);
+
+/*
+ * Get the name of a cipher suite. The name is written in the provided
+ * buffer; if the suite is not recognised, then the name is
+ * "unknown (0x****)" where "****" is the hexadecimal value of the suite.
+ * If the name does not fit in the provided buffer, then dst[0] is set
+ * to 0 (unless len is 0, in which case nothing is written), and -1 is
+ * returned. Otherwise, the name is written in dst[] (with a terminating
+ * 0), and this function returns 0.
+ */
+int get_suite_name_ext(unsigned suite, char *dst, size_t len);
+
+/*
+ * Tell whether a cipher suite uses ECDHE key exchange.
+ */
+int uses_ecdhe(unsigned suite);
+
+/*
+ * Print out all known names (for protocol versions, cipher suites...).
+ */
+void list_names(void);
+
+/*
+ * Print out all known elliptic curve names.
+ */
+void list_curves(void);
+
+/*
+ * Get the symbolic name for an elliptic curve (by ID).
+ */
+const char *ec_curve_name(int curve);
+
+/*
+ * Get a curve by symbolic name. If the name is not recognized, -1 is
+ * returned.
+ */
+int get_curve_by_name(const char *str);
+
+/*
+ * Get the symbolic name for a hash function name (by ID).
+ */
+const char *hash_function_name(int id);
+
+/*
+ * Read a file completely. The returned block is allocated with xmalloc()
+ * and must be released by the caller.
+ * If the file cannot be found or read completely, or is empty, then an
+ * appropriate error message is written, and NULL is returned.
+ */
+unsigned char *read_file(const char *fname, size_t *len);
+
+/*
+ * Write a file completely. This returns 0 on success, -1 on error. On
+ * error, an appropriate error message is printed.
+ */
+int write_file(const char *fname, const void *data, size_t len);
+
+/*
+ * This function returns non-zero if the provided buffer "looks like"
+ * a DER-encoded ASN.1 object (criteria: it has the tag for a SEQUENCE
+ * with a definite length that matches the total object length).
+ */
+int looks_like_DER(const unsigned char *buf, size_t len);
+
+/*
+ * Type for a named blob (the 'name' is a normalised PEM header name).
+ */
+typedef struct {
+ char *name;
+ unsigned char *data;
+ size_t data_len;
+} pem_object;
+
+/*
+ * Release the contents of a named blob (buffer and name).
+ */
+void free_pem_object_contents(pem_object *po);
+
+/*
+ * Decode a buffer as a PEM file, and return all objects. On error, NULL
+ * is returned and an error message is printed. Absence of any object
+ * is an error.
+ *
+ * The returned array is terminated by a dummy object whose 'name' is
+ * NULL. The number of objects (not counting the dummy terminator) is
+ * written in '*num'.
+ */
+pem_object *decode_pem(const void *src, size_t len, size_t *num);
+
+/*
+ * Get the certificate(s) from a file. This accepts both a single
+ * DER-encoded certificate, and a text file that contains
+ * PEM-encoded certificates (and possibly other objects, which are
+ * then ignored).
+ *
+ * On decoding error, or if the file turns out to contain no certificate
+ * at all, then an error message is printed and NULL is returned.
+ *
+ * The returned array, and all referenced buffers, are allocated with
+ * xmalloc() and must be released by the caller. The returned array
+ * ends with a dummy entry whose 'data' field is NULL.
+ * The number of decoded certificates (not counting the dummy entry)
+ * is written into '*num'.
+ */
+br_x509_certificate *read_certificates(const char *fname, size_t *num);
+
+/*
+ * Release certificates. This releases all certificate data arrays,
+ * and the whole array as well.
+ */
+void free_certificates(br_x509_certificate *certs, size_t num);
+
+/*
+ * Interpret a certificate as a trust anchor. The trust anchor is
+ * newly allocated with xmalloc() and the caller must release it.
+ * On decoding error, an error message is printed, and this function
+ * returns NULL.
+ */
+br_x509_trust_anchor *certificate_to_trust_anchor(br_x509_certificate *xc);
+
+/*
+ * Type for a vector of trust anchors.
+ */
+typedef VECTOR(br_x509_trust_anchor) anchor_list;
+
+/*
+ * Release contents for a trust anchor (assuming they were dynamically
+ * allocated with xmalloc()). The structure itself is NOT released.
+ */
+void free_ta_contents(br_x509_trust_anchor *ta);
+
+/*
+ * Decode certificates from a file and interpret them as trust anchors.
+ * The trust anchors are added to the provided list. The number of found
+ * anchors is returned; on error, 0 is returned (finding no anchor at
+ * all is considered an error). An appropriate error message is displayed.
+ */
+size_t read_trust_anchors(anchor_list *dst, const char *fname);
+
+/*
+ * Get the "signer key type" for the certificate (key type of the
+ * issuing CA). On error, this prints a message on stderr, and returns 0.
+ */
+int get_cert_signer_algo(br_x509_certificate *xc);
+
+/*
+ * Special "no anchor" X.509 validator that wraps around another X.509
+ * validator and turns "not trusted" error codes into success. This is
+ * by definition insecure, but convenient for debug purposes.
+ */
+typedef struct {
+ const br_x509_class *vtable;
+ const br_x509_class **inner;
+} x509_noanchor_context;
+extern const br_x509_class x509_noanchor_vtable;
+
+/*
+ * Initialise a "no anchor" X.509 validator.
+ */
+void x509_noanchor_init(x509_noanchor_context *xwc,
+ const br_x509_class **inner);
+
+/*
+ * Aggregate type for a private key.
+ */
+typedef struct {
+ int key_type; /* BR_KEYTYPE_RSA or BR_KEYTYPE_EC */
+ union {
+ br_rsa_private_key rsa;
+ br_ec_private_key ec;
+ } key;
+} private_key;
+
+/*
+ * Decode a private key from a file. On error, this prints an error
+ * message and returns NULL.
+ */
+private_key *read_private_key(const char *fname);
+
+/*
+ * Free a private key.
+ */
+void free_private_key(private_key *sk);
+
+/*
+ * Get the encoded OID for a given hash function (to use with PKCS#1
+ * signatures). If the hash function ID is 0 (for MD5+SHA-1), or if
+ * the ID is not one of the SHA-* functions (SHA-1, SHA-224, SHA-256,
+ * SHA-384, SHA-512), then this function returns NULL.
+ */
+const unsigned char *get_hash_oid(int id);
+
+/*
+ * Get a hash implementation by ID. This returns NULL if the hash
+ * implementation is not available.
+ */
+const br_hash_class *get_hash_impl(int id);
+
+/*
+ * Find the symbolic name and the description for an error. If 'err' is
+ * recognised then the error symbolic name is returned; if 'comment' is
+ * not NULL then '*comment' is then set to a descriptive human-readable
+ * message. If the error code 'err' is not recognised, then '*comment' is
+ * untouched and this function returns NULL.
+ */
+const char *find_error_name(int err, const char **comment);
+
+/*
+ * Find the symbolic name for an algorithm implementation. Provided
+ * pointer should be a pointer to a vtable or to a function, where
+ * appropriate. If not recognised, then the string "UNKNOWN" is returned.
+ *
+ * If 'long_name' is non-zero, then the returned name recalls the
+ * algorithm type as well; otherwise, only the core implementation name
+ * is returned (e.g. the long name could be 'aes_big_cbcenc' while the
+ * short name is 'big').
+ */
+const char *get_algo_name(const void *algo, int long_name);
+
+/*
+ * Run a SSL engine, with a socket connected to the peer, and using
+ * stdin/stdout to exchange application data. The socket must be a
+ * non-blocking descriptor.
+ *
+ * To help with Win32 compatibility, the socket descriptor is provided
+ * as an "unsigned long" value.
+ *
+ * Returned value:
+ * 0 SSL connection closed successfully
+ * x > 0 SSL error "x"
+ * -1 early socket close
+ * -2 stdout was closed, or something failed badly
+ */
+int run_ssl_engine(br_ssl_engine_context *eng,
+ unsigned long fd, unsigned flags);
+
+#define RUN_ENGINE_VERBOSE 0x0001 /* enable verbose messages */
+#define RUN_ENGINE_TRACE 0x0002 /* hex dump of records */
+
+/*
+ * Do the "client" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_client(int argc, char *argv[]);
+
+/*
+ * Do the "server" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_server(int argc, char *argv[]);
+
+/*
+ * Do the "verify" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_verify(int argc, char *argv[]);
+
+/*
+ * Do the "skey" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_skey(int argc, char *argv[]);
+
+/*
+ * Do the "ta" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_ta(int argc, char *argv[]);
+
+/*
+ * Do the "chain" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_chain(int argc, char *argv[]);
+
+/*
+ * Do the "twrch" command. Returned value is 0 on success, -1 on failure
+ * (processing or arguments), or a non-zero exit code. Command-line
+ * arguments start _after_ the command name.
+ */
+int do_twrch(int argc, char *argv[]);
+
+/*
+ * Do the "impl" command. Returned value is 0 on success, -1 on failure.
+ * Command-line arguments start _after_ the command name.
+ */
+int do_impl(int argc, char *argv[]);
+
+#endif
diff --git a/contrib/bearssl/tools/certs.c b/contrib/bearssl/tools/certs.c
new file mode 100644
index 000000000000..8986446ee130
--- /dev/null
+++ b/contrib/bearssl/tools/certs.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+
+static void
+dn_append(void *ctx, const void *buf, size_t len)
+{
+ VEC_ADDMANY(*(bvector *)ctx, buf, len);
+}
+
+static int
+certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
+ br_x509_certificate *xc)
+{
+ br_x509_decoder_context dc;
+ bvector vdn = VEC_INIT;
+ br_x509_pkey *pk;
+
+ br_x509_decoder_init(&dc, dn_append, &vdn);
+ br_x509_decoder_push(&dc, xc->data, xc->data_len);
+ pk = br_x509_decoder_get_pkey(&dc);
+ if (pk == NULL) {
+ fprintf(stderr, "ERROR: CA decoding failed with error %d\n",
+ br_x509_decoder_last_error(&dc));
+ VEC_CLEAR(vdn);
+ return -1;
+ }
+ ta->dn.data = VEC_TOARRAY(vdn);
+ ta->dn.len = VEC_LEN(vdn);
+ VEC_CLEAR(vdn);
+ ta->flags = 0;
+ if (br_x509_decoder_isCA(&dc)) {
+ ta->flags |= BR_X509_TA_CA;
+ }
+ switch (pk->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta->pkey.key_type = BR_KEYTYPE_RSA;
+ ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
+ ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
+ ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
+ ta->pkey.key.rsa.elen = pk->key.rsa.elen;
+ break;
+ case BR_KEYTYPE_EC:
+ ta->pkey.key_type = BR_KEYTYPE_EC;
+ ta->pkey.key.ec.curve = pk->key.ec.curve;
+ ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
+ ta->pkey.key.ec.qlen = pk->key.ec.qlen;
+ break;
+ default:
+ fprintf(stderr, "ERROR: unsupported public key type in CA\n");
+ xfree(ta->dn.data);
+ return -1;
+ }
+ return 0;
+}
+
+/* see brssl.h */
+br_x509_trust_anchor *
+certificate_to_trust_anchor(br_x509_certificate *xc)
+{
+ br_x509_trust_anchor ta;
+
+ if (certificate_to_trust_anchor_inner(&ta, xc) < 0) {
+ return NULL;
+ } else {
+ return xblobdup(&ta, sizeof ta);
+ }
+}
+
+/* see brssl.h */
+void
+free_ta_contents(br_x509_trust_anchor *ta)
+{
+ xfree(ta->dn.data);
+ switch (ta->pkey.key_type) {
+ case BR_KEYTYPE_RSA:
+ xfree(ta->pkey.key.rsa.n);
+ xfree(ta->pkey.key.rsa.e);
+ break;
+ case BR_KEYTYPE_EC:
+ xfree(ta->pkey.key.ec.q);
+ break;
+ }
+}
+
+/* see brssl.h */
+size_t
+read_trust_anchors(anchor_list *dst, const char *fname)
+{
+ br_x509_certificate *xcs;
+ anchor_list tas = VEC_INIT;
+ size_t u, num;
+
+ xcs = read_certificates(fname, &num);
+ if (xcs == NULL) {
+ return 0;
+ }
+ for (u = 0; u < num; u ++) {
+ br_x509_trust_anchor ta;
+
+ if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
+ VEC_CLEAREXT(tas, free_ta_contents);
+ free_certificates(xcs, num);
+ return 0;
+ }
+ VEC_ADD(tas, ta);
+ }
+ VEC_ADDMANY(*dst, &VEC_ELT(tas, 0), num);
+ VEC_CLEAR(tas);
+ free_certificates(xcs, num);
+ return num;
+}
+
+/* see brssl.h */
+int
+get_cert_signer_algo(br_x509_certificate *xc)
+{
+ br_x509_decoder_context dc;
+ int err;
+
+ br_x509_decoder_init(&dc, 0, 0);
+ br_x509_decoder_push(&dc, xc->data, xc->data_len);
+ err = br_x509_decoder_last_error(&dc);
+ if (err != 0) {
+ fprintf(stderr,
+ "ERROR: certificate decoding failed with error %d\n",
+ -err);
+ return 0;
+ }
+ return br_x509_decoder_get_signer_key_type(&dc);
+}
+
+static void
+xwc_start_chain(const br_x509_class **ctx, const char *server_name)
+{
+ x509_noanchor_context *xwc;
+
+ xwc = (x509_noanchor_context *)ctx;
+ (*xwc->inner)->start_chain(xwc->inner, server_name);
+}
+
+static void
+xwc_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+ x509_noanchor_context *xwc;
+
+ xwc = (x509_noanchor_context *)ctx;
+ (*xwc->inner)->start_cert(xwc->inner, length);
+}
+
+static void
+xwc_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)
+{
+ x509_noanchor_context *xwc;
+
+ xwc = (x509_noanchor_context *)ctx;
+ (*xwc->inner)->append(xwc->inner, buf, len);
+}
+
+static void
+xwc_end_cert(const br_x509_class **ctx)
+{
+ x509_noanchor_context *xwc;
+
+ xwc = (x509_noanchor_context *)ctx;
+ (*xwc->inner)->end_cert(xwc->inner);
+}
+
+static unsigned
+xwc_end_chain(const br_x509_class **ctx)
+{
+ x509_noanchor_context *xwc;
+ unsigned r;
+
+ xwc = (x509_noanchor_context *)ctx;
+ r = (*xwc->inner)->end_chain(xwc->inner);
+ if (r == BR_ERR_X509_NOT_TRUSTED) {
+ r = 0;
+ }
+ return r;
+}
+
+static const br_x509_pkey *
+xwc_get_pkey(const br_x509_class *const *ctx, unsigned *usages)
+{
+ x509_noanchor_context *xwc;
+
+ xwc = (x509_noanchor_context *)ctx;
+ return (*xwc->inner)->get_pkey(xwc->inner, usages);
+}
+
+/* see brssl.h */
+const br_x509_class x509_noanchor_vtable = {
+ sizeof(x509_noanchor_context),
+ xwc_start_chain,
+ xwc_start_cert,
+ xwc_append,
+ xwc_end_cert,
+ xwc_end_chain,
+ xwc_get_pkey
+};
+
+/* see brssl.h */
+void
+x509_noanchor_init(x509_noanchor_context *xwc, const br_x509_class **inner)
+{
+ xwc->vtable = &x509_noanchor_vtable;
+ xwc->inner = inner;
+}
diff --git a/contrib/bearssl/tools/chain.c b/contrib/bearssl/tools/chain.c
new file mode 100644
index 000000000000..671f5e83b9c9
--- /dev/null
+++ b/contrib/bearssl/tools/chain.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static void
+print_blob(const char *name, const unsigned char *buf, size_t len)
+{
+ size_t u;
+
+ printf("\nstatic const unsigned char %s[] = {", name);
+ for (u = 0; u < len; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", buf[u]);
+ }
+ printf("\n};\n");
+}
+
+static void
+usage_chain(void)
+{
+ fprintf(stderr,
+"usage: brssl chain [ options ] file...\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+}
+
+/* see brssl.h */
+int
+do_chain(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int i, num_files;
+ long k, ctr;
+
+ retcode = 0;
+ verbose = 1;
+ num_files = 0;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ num_files ++;
+ continue;
+ }
+ argv[i] = NULL;
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_chain();
+ goto chain_exit_error;
+ }
+ }
+ if (num_files == 0) {
+ fprintf(stderr, "ERROR: no certificate file provided\n");
+ usage_chain();
+ goto chain_exit_error;
+ }
+
+ ctr = 0;
+ for (i = 0; i < argc; i ++) {
+ const char *fname;
+ br_x509_certificate *xcs;
+ size_t u, num;
+
+ fname = argv[i];
+ if (fname == NULL) {
+ continue;
+ }
+ if (verbose) {
+ fprintf(stderr, "Reading file '%s': ", fname);
+ fflush(stderr);
+ }
+ xcs = read_certificates(fname, &num);
+ if (xcs == NULL) {
+ goto chain_exit_error;
+ }
+ if (verbose) {
+ fprintf(stderr, "%lu certificate%s\n",
+ (unsigned long)num, num > 1 ? "s" : "");
+ }
+ for (u = 0; u < num; u ++) {
+ char tmp[50];
+
+ sprintf(tmp, "CERT%ld", ctr ++);
+ print_blob(tmp, xcs[u].data, xcs[u].data_len);
+ xfree(xcs[u].data);
+ }
+ xfree(xcs);
+ }
+
+ printf("\nstatic const br_x509_certificate CHAIN[] = {");
+ for (k = 0; k < ctr; k ++) {
+ if (k != 0) {
+ printf(",");
+ }
+ printf("\n\t{ (unsigned char *)CERT%ld, sizeof CERT%ld }",
+ k, k);
+ }
+ printf("\n};\n");
+ printf("\n#define CHAIN_LEN %ld\n", ctr);
+
+ /*
+ * Release allocated structures.
+ */
+chain_exit:
+ return retcode;
+
+chain_exit_error:
+ retcode = -1;
+ goto chain_exit;
+}
diff --git a/contrib/bearssl/tools/client.c b/contrib/bearssl/tools/client.c
new file mode 100644
index 000000000000..9838857275ec
--- /dev/null
+++ b/contrib/bearssl/tools/client.c
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define SOCKET int
+#define INVALID_SOCKET (-1)
+#endif
+
+#include "brssl.h"
+
+static int
+host_connect(const char *host, const char *port, int verbose)
+{
+ struct addrinfo hints, *si, *p;
+ SOCKET fd;
+ int err;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(host, port, &hints, &si);
+ if (err != 0) {
+ fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+ gai_strerror(err));
+ return INVALID_SOCKET;
+ }
+ fd = INVALID_SOCKET;
+ for (p = si; p != NULL; p = p->ai_next) {
+ if (verbose) {
+ struct sockaddr *sa;
+ void *addr;
+ char tmp[INET6_ADDRSTRLEN + 50];
+
+ sa = (struct sockaddr *)p->ai_addr;
+ if (sa->sa_family == AF_INET) {
+ addr = &((struct sockaddr_in *)
+ (void *)sa)->sin_addr;
+ } else if (sa->sa_family == AF_INET6) {
+ addr = &((struct sockaddr_in6 *)
+ (void *)sa)->sin6_addr;
+ } else {
+ addr = NULL;
+ }
+ if (addr != NULL) {
+ if (!inet_ntop(p->ai_family, addr,
+ tmp, sizeof tmp))
+ {
+ strcpy(tmp, "<invalid>");
+ }
+ } else {
+ sprintf(tmp, "<unknown family: %d>",
+ (int)sa->sa_family);
+ }
+ fprintf(stderr, "connecting to: %s\n", tmp);
+ }
+ fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (fd == INVALID_SOCKET) {
+ if (verbose) {
+ perror("socket()");
+ }
+ continue;
+ }
+ if (connect(fd, p->ai_addr, p->ai_addrlen) == INVALID_SOCKET) {
+ if (verbose) {
+ perror("connect()");
+ }
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ continue;
+ }
+ break;
+ }
+ if (p == NULL) {
+ freeaddrinfo(si);
+ fprintf(stderr, "ERROR: failed to connect\n");
+ return INVALID_SOCKET;
+ }
+ freeaddrinfo(si);
+ if (verbose) {
+ fprintf(stderr, "connected.\n");
+ }
+
+ /*
+ * We make the socket non-blocking, since we are going to use
+ * poll() or select() to organise I/O.
+ */
+#ifdef _WIN32
+ {
+ u_long arg;
+
+ arg = 1;
+ ioctlsocket(fd, FIONBIO, &arg);
+ }
+#else
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+ return fd;
+}
+
+typedef struct {
+ const br_ssl_client_certificate_class *vtable;
+ int verbose;
+ br_x509_certificate *chain;
+ size_t chain_len;
+ private_key *sk;
+ int issuer_key_type;
+} ccert_context;
+
+static void
+cc_start_name_list(const br_ssl_client_certificate_class **pctx)
+{
+ ccert_context *zc;
+
+ zc = (ccert_context *)pctx;
+ if (zc->verbose) {
+ fprintf(stderr, "Server requests a client certificate.\n");
+ fprintf(stderr, "--- anchor DN list start ---\n");
+ }
+}
+
+static void
+cc_start_name(const br_ssl_client_certificate_class **pctx, size_t len)
+{
+ ccert_context *zc;
+
+ zc = (ccert_context *)pctx;
+ if (zc->verbose) {
+ fprintf(stderr, "new anchor name, length = %u\n",
+ (unsigned)len);
+ }
+}
+
+static void
+cc_append_name(const br_ssl_client_certificate_class **pctx,
+ const unsigned char *data, size_t len)
+{
+ ccert_context *zc;
+
+ zc = (ccert_context *)pctx;
+ if (zc->verbose) {
+ size_t u;
+
+ for (u = 0; u < len; u ++) {
+ if (u == 0) {
+ fprintf(stderr, " ");
+ } else if (u > 0 && u % 16 == 0) {
+ fprintf(stderr, "\n ");
+ }
+ fprintf(stderr, " %02x", data[u]);
+ }
+ if (len > 0) {
+ fprintf(stderr, "\n");
+ }
+ }
+}
+
+static void
+cc_end_name(const br_ssl_client_certificate_class **pctx)
+{
+ (void)pctx;
+}
+
+static void
+cc_end_name_list(const br_ssl_client_certificate_class **pctx)
+{
+ ccert_context *zc;
+
+ zc = (ccert_context *)pctx;
+ if (zc->verbose) {
+ fprintf(stderr, "--- anchor DN list end ---\n");
+ }
+}
+
+static void
+print_hashes(unsigned hh, unsigned hh2)
+{
+ int i;
+
+ for (i = 0; i < 8; i ++) {
+ const char *name;
+
+ name = hash_function_name(i);
+ if (((hh >> i) & 1) != 0) {
+ fprintf(stderr, " %s", name);
+ } else if (((hh2 >> i) & 1) != 0) {
+ fprintf(stderr, " (%s)", name);
+ }
+ }
+}
+
+static int
+choose_hash(unsigned hh)
+{
+ static const int f[] = {
+ br_sha256_ID, br_sha224_ID, br_sha384_ID, br_sha512_ID,
+ br_sha1_ID, br_md5sha1_ID, -1
+ };
+
+ size_t u;
+
+ for (u = 0; f[u] >= 0; u ++) {
+ if (((hh >> f[u]) & 1) != 0) {
+ return f[u];
+ }
+ }
+ return -1;
+}
+
+static void
+cc_choose(const br_ssl_client_certificate_class **pctx,
+ const br_ssl_client_context *cc, uint32_t auth_types,
+ br_ssl_client_certificate *choices)
+{
+ ccert_context *zc;
+ int scurve;
+
+ zc = (ccert_context *)pctx;
+ scurve = br_ssl_client_get_server_curve(cc);
+ if (zc->verbose) {
+ unsigned hashes;
+
+ hashes = br_ssl_client_get_server_hashes(cc);
+ if ((auth_types & 0x00FF) != 0) {
+ fprintf(stderr, "supported: RSA signatures:");
+ print_hashes(auth_types, hashes);
+ fprintf(stderr, "\n");
+ }
+ if ((auth_types & 0xFF00) != 0) {
+ fprintf(stderr, "supported: ECDSA signatures:");
+ print_hashes(auth_types >> 8, hashes >> 8);
+ fprintf(stderr, "\n");
+ }
+ if ((auth_types & 0x010000) != 0) {
+ fprintf(stderr, "supported:"
+ " fixed ECDH (cert signed with RSA)\n");
+ }
+ if ((auth_types & 0x020000) != 0) {
+ fprintf(stderr, "supported:"
+ " fixed ECDH (cert signed with ECDSA)\n");
+ }
+ if (scurve) {
+ fprintf(stderr, "server key curve: %s (%d)\n",
+ ec_curve_name(scurve), scurve);
+ } else {
+ fprintf(stderr, "server key is not EC\n");
+ }
+ }
+ switch (zc->sk->key_type) {
+ case BR_KEYTYPE_RSA:
+ if ((choices->hash_id = choose_hash(auth_types)) >= 0) {
+ if (zc->verbose) {
+ fprintf(stderr, "using RSA, hash = %d (%s)\n",
+ choices->hash_id,
+ hash_function_name(choices->hash_id));
+ }
+ choices->auth_type = BR_AUTH_RSA;
+ choices->chain = zc->chain;
+ choices->chain_len = zc->chain_len;
+ return;
+ }
+ break;
+ case BR_KEYTYPE_EC:
+ if (zc->issuer_key_type != 0
+ && scurve == zc->sk->key.ec.curve)
+ {
+ int x;
+
+ x = (zc->issuer_key_type == BR_KEYTYPE_RSA) ? 16 : 17;
+ if (((auth_types >> x) & 1) != 0) {
+ if (zc->verbose) {
+ fprintf(stderr, "using static ECDH\n");
+ }
+ choices->auth_type = BR_AUTH_ECDH;
+ choices->hash_id = -1;
+ choices->chain = zc->chain;
+ choices->chain_len = zc->chain_len;
+ return;
+ }
+ }
+ if ((choices->hash_id = choose_hash(auth_types >> 8)) >= 0) {
+ if (zc->verbose) {
+ fprintf(stderr, "using ECDSA, hash = %d (%s)\n",
+ choices->hash_id,
+ hash_function_name(choices->hash_id));
+ }
+ choices->auth_type = BR_AUTH_ECDSA;
+ choices->chain = zc->chain;
+ choices->chain_len = zc->chain_len;
+ return;
+ }
+ break;
+ }
+ if (zc->verbose) {
+ fprintf(stderr, "no matching client certificate\n");
+ }
+ choices->chain = NULL;
+ choices->chain_len = 0;
+}
+
+static uint32_t
+cc_do_keyx(const br_ssl_client_certificate_class **pctx,
+ unsigned char *data, size_t *len)
+{
+ const br_ec_impl *iec;
+ ccert_context *zc;
+ size_t xoff, xlen;
+ uint32_t r;
+
+ zc = (ccert_context *)pctx;
+ iec = br_ec_get_default();
+ r = iec->mul(data, *len, zc->sk->key.ec.x,
+ zc->sk->key.ec.xlen, zc->sk->key.ec.curve);
+ xoff = iec->xoff(zc->sk->key.ec.curve, &xlen);
+ memmove(data, data + xoff, xlen);
+ *len = xlen;
+ return r;
+}
+
+static size_t
+cc_do_sign(const br_ssl_client_certificate_class **pctx,
+ int hash_id, size_t hv_len, unsigned char *data, size_t len)
+{
+ ccert_context *zc;
+ unsigned char hv[64];
+
+ zc = (ccert_context *)pctx;
+ memcpy(hv, data, hv_len);
+ switch (zc->sk->key_type) {
+ const br_hash_class *hc;
+ const unsigned char *hash_oid;
+ uint32_t x;
+ size_t sig_len;
+
+ case BR_KEYTYPE_RSA:
+ hash_oid = get_hash_oid(hash_id);
+ if (hash_oid == NULL && hash_id != 0) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: cannot RSA-sign with"
+ " unknown hash function: %d\n",
+ hash_id);
+ }
+ return 0;
+ }
+ sig_len = (zc->sk->key.rsa.n_bitlen + 7) >> 3;
+ if (len < sig_len) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: cannot RSA-sign,"
+ " buffer is too small"
+ " (sig=%lu, buf=%lu)\n",
+ (unsigned long)sig_len,
+ (unsigned long)len);
+ }
+ return 0;
+ }
+ x = br_rsa_pkcs1_sign_get_default()(
+ hash_oid, hv, hv_len, &zc->sk->key.rsa, data);
+ if (!x) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: RSA-sign failure\n");
+ }
+ return 0;
+ }
+ return sig_len;
+
+ case BR_KEYTYPE_EC:
+ hc = get_hash_impl(hash_id);
+ if (hc == NULL) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: cannot ECDSA-sign with"
+ " unknown hash function: %d\n",
+ hash_id);
+ }
+ return 0;
+ }
+ if (len < 139) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: cannot ECDSA-sign"
+ " (output buffer = %lu)\n",
+ (unsigned long)len);
+ }
+ return 0;
+ }
+ sig_len = br_ecdsa_sign_asn1_get_default()(
+ br_ec_get_default(), hc, hv, &zc->sk->key.ec, data);
+ if (sig_len == 0) {
+ if (zc->verbose) {
+ fprintf(stderr, "ERROR: ECDSA-sign failure\n");
+ }
+ return 0;
+ }
+ return sig_len;
+
+ default:
+ return 0;
+ }
+}
+
+static const br_ssl_client_certificate_class ccert_vtable = {
+ sizeof(ccert_context),
+ cc_start_name_list,
+ cc_start_name,
+ cc_append_name,
+ cc_end_name,
+ cc_end_name_list,
+ cc_choose,
+ cc_do_keyx,
+ cc_do_sign
+};
+
+static void
+free_alpn(void *alpn)
+{
+ xfree(*(char **)alpn);
+}
+
+static void
+usage_client(void)
+{
+ fprintf(stderr,
+"usage: brssl client server[:port] [ options ]\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+ fprintf(stderr,
+" -trace activate extra debug messages (dump of all packets)\n");
+ fprintf(stderr,
+" -sni name use this specific name for SNI\n");
+ fprintf(stderr,
+" -nosni do not send any SNI\n");
+ fprintf(stderr,
+" -mono use monodirectional buffering\n");
+ fprintf(stderr,
+" -buf length set the I/O buffer length (in bytes)\n");
+ fprintf(stderr,
+" -CA file add certificates in 'file' to trust anchors\n");
+ fprintf(stderr,
+" -cert file set client certificate chain\n");
+ fprintf(stderr,
+" -key file set client private key (for certificate authentication)\n");
+ fprintf(stderr,
+" -nostaticecdh prohibit full-static ECDH (client certificate)\n");
+ fprintf(stderr,
+" -list list supported names (protocols, algorithms...)\n");
+ fprintf(stderr,
+" -vmin name set minimum supported version (default: TLS-1.0)\n");
+ fprintf(stderr,
+" -vmax name set maximum supported version (default: TLS-1.2)\n");
+ fprintf(stderr,
+" -cs names set list of supported cipher suites (comma-separated)\n");
+ fprintf(stderr,
+" -hf names add support for some hash functions (comma-separated)\n");
+ fprintf(stderr,
+" -minhello len set minimum ClientHello length (in bytes)\n");
+ fprintf(stderr,
+" -fallback send the TLS_FALLBACK_SCSV (i.e. claim a downgrade)\n");
+ fprintf(stderr,
+" -noreneg prohibit renegotiations\n");
+ fprintf(stderr,
+" -alpn name add protocol name to list of protocols (ALPN extension)\n");
+ fprintf(stderr,
+" -strictalpn fail on ALPN mismatch\n");
+}
+
+/* see brssl.h */
+int
+do_client(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int trace;
+ int i, bidi;
+ const char *server_name;
+ char *host;
+ char *port;
+ const char *sni;
+ anchor_list anchors = VEC_INIT;
+ unsigned vmin, vmax;
+ VECTOR(char *) alpn_names = VEC_INIT;
+ cipher_suite *suites;
+ size_t num_suites;
+ uint16_t *suite_ids;
+ unsigned hfuns;
+ size_t u;
+ br_ssl_client_context cc;
+ br_x509_minimal_context xc;
+ x509_noanchor_context xwc;
+ const br_hash_class *dnhash;
+ ccert_context zc;
+ br_x509_certificate *chain;
+ size_t chain_len;
+ private_key *sk;
+ int nostaticecdh;
+ unsigned char *iobuf;
+ size_t iobuf_len;
+ size_t minhello_len;
+ int fallback;
+ uint32_t flags;
+ SOCKET fd;
+
+ retcode = 0;
+ verbose = 1;
+ trace = 0;
+ server_name = NULL;
+ host = NULL;
+ port = NULL;
+ sni = NULL;
+ bidi = 1;
+ vmin = 0;
+ vmax = 0;
+ suites = NULL;
+ num_suites = 0;
+ hfuns = 0;
+ suite_ids = NULL;
+ chain = NULL;
+ chain_len = 0;
+ sk = NULL;
+ nostaticecdh = 0;
+ iobuf = NULL;
+ iobuf_len = 0;
+ minhello_len = (size_t)-1;
+ fallback = 0;
+ flags = 0;
+ fd = INVALID_SOCKET;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ if (server_name != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate server name\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ server_name = arg;
+ continue;
+ }
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else if (eqstr(arg, "-trace")) {
+ trace = 1;
+ } else if (eqstr(arg, "-sni")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-sni'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ if (sni != NULL) {
+ fprintf(stderr, "ERROR: duplicate SNI\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ sni = argv[i];
+ } else if (eqstr(arg, "-nosni")) {
+ if (sni != NULL) {
+ fprintf(stderr, "ERROR: duplicate SNI\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ sni = "";
+ } else if (eqstr(arg, "-mono")) {
+ bidi = 0;
+ } else if (eqstr(arg, "-buf")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-buf'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (iobuf_len != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate I/O buffer length\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ iobuf_len = parse_size(arg);
+ if (iobuf_len == (size_t)-1) {
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-CA")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-CA'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (read_trust_anchors(&anchors, arg) == 0) {
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-cert")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cert'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ if (chain != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate certificate chain\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ chain = read_certificates(arg, &chain_len);
+ if (chain == NULL || chain_len == 0) {
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-key")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-key'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ if (sk != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate private key\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ sk = read_private_key(arg);
+ if (sk == NULL) {
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-nostaticecdh")) {
+ nostaticecdh = 1;
+ } else if (eqstr(arg, "-list")) {
+ list_names();
+ goto client_exit;
+ } else if (eqstr(arg, "-vmin")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmin'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (vmin != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate minimum version\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ vmin = parse_version(arg, strlen(arg));
+ if (vmin == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-vmax")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmax'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (vmax != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate maximum version\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ vmax = parse_version(arg, strlen(arg));
+ if (vmax == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-cs")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cs'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (suites != NULL) {
+ fprintf(stderr, "ERROR: duplicate list"
+ " of cipher suites\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ suites = parse_suites(arg, &num_suites);
+ if (suites == NULL) {
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-hf")) {
+ unsigned x;
+
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-hf'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ x = parse_hash_functions(arg);
+ if (x == 0) {
+ usage_client();
+ goto client_exit_error;
+ }
+ hfuns |= x;
+ } else if (eqstr(arg, "-minhello")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-minhello'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ arg = argv[i];
+ if (minhello_len != (size_t)-1) {
+ fprintf(stderr, "ERROR: duplicate minimum"
+ " ClientHello length\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ minhello_len = parse_size(arg);
+ /*
+ * Minimum ClientHello length must fit on 16 bits.
+ */
+ if (minhello_len == (size_t)-1
+ || (((minhello_len >> 12) >> 4) != 0))
+ {
+ usage_client();
+ goto client_exit_error;
+ }
+ } else if (eqstr(arg, "-fallback")) {
+ fallback = 1;
+ } else if (eqstr(arg, "-noreneg")) {
+ flags |= BR_OPT_NO_RENEGOTIATION;
+ } else if (eqstr(arg, "-alpn")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-alpn'\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ VEC_ADD(alpn_names, xstrdup(argv[i]));
+ } else if (eqstr(arg, "-strictalpn")) {
+ flags |= BR_OPT_FAIL_ON_ALPN_MISMATCH;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_client();
+ goto client_exit_error;
+ }
+ }
+ if (server_name == NULL) {
+ fprintf(stderr, "ERROR: no server name/address provided\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ for (u = strlen(server_name); u > 0; u --) {
+ int c = server_name[u - 1];
+ if (c == ':') {
+ break;
+ }
+ if (c < '0' || c > '9') {
+ u = 0;
+ break;
+ }
+ }
+ if (u == 0) {
+ host = xstrdup(server_name);
+ port = xstrdup("443");
+ } else {
+ port = xstrdup(server_name + u);
+ host = xmalloc(u);
+ memcpy(host, server_name, u - 1);
+ host[u - 1] = 0;
+ }
+ if (sni == NULL) {
+ sni = host;
+ }
+
+ if (chain == NULL && sk != NULL) {
+ fprintf(stderr, "ERROR: private key specified, but"
+ " no certificate chain\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ if (chain != NULL && sk == NULL) {
+ fprintf(stderr, "ERROR: certificate chain specified, but"
+ " no private key\n");
+ usage_client();
+ goto client_exit_error;
+ }
+
+ if (vmin == 0) {
+ vmin = BR_TLS10;
+ }
+ if (vmax == 0) {
+ vmax = BR_TLS12;
+ }
+ if (vmax < vmin) {
+ fprintf(stderr, "ERROR: impossible minimum/maximum protocol"
+ " version combination\n");
+ usage_client();
+ goto client_exit_error;
+ }
+ if (suites == NULL) {
+ num_suites = 0;
+
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ num_suites ++;
+ }
+ }
+ suites = xmalloc(num_suites * sizeof *suites);
+ num_suites = 0;
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ suites[num_suites ++] = cipher_suites[u];
+ }
+ }
+ }
+ if (hfuns == 0) {
+ hfuns = (unsigned)-1;
+ }
+ if (iobuf_len == 0) {
+ if (bidi) {
+ iobuf_len = BR_SSL_BUFSIZE_BIDI;
+ } else {
+ iobuf_len = BR_SSL_BUFSIZE_MONO;
+ }
+ }
+ iobuf = xmalloc(iobuf_len);
+
+ /*
+ * Compute implementation requirements and inject implementations.
+ */
+ suite_ids = xmalloc((num_suites + 1) * sizeof *suite_ids);
+ br_ssl_client_zero(&cc);
+ br_ssl_engine_set_versions(&cc.eng, vmin, vmax);
+ dnhash = NULL;
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ dnhash = hc;
+ }
+ }
+ if (dnhash == NULL) {
+ fprintf(stderr, "ERROR: no supported hash function\n");
+ goto client_exit_error;
+ }
+ br_x509_minimal_init(&xc, dnhash,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ if (vmin <= BR_TLS11) {
+ if (!(hfuns & (1 << br_md5_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need MD5\n");
+ goto client_exit_error;
+ }
+ if (!(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need SHA-1\n");
+ goto client_exit_error;
+ }
+ }
+ for (u = 0; u < num_suites; u ++) {
+ unsigned req;
+
+ req = suites[u].req;
+ suite_ids[u] = suites[u].suite;
+ if ((req & REQ_TLS12) != 0 && vmax < BR_TLS12) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires TLS 1.2\n",
+ suites[u].name);
+ goto client_exit_error;
+ }
+ if ((req & REQ_SHA1) != 0 && !(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-1\n",
+ suites[u].name);
+ goto client_exit_error;
+ }
+ if ((req & REQ_SHA256) != 0 && !(hfuns & (1 << br_sha256_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-256\n",
+ suites[u].name);
+ goto client_exit_error;
+ }
+ if ((req & REQ_SHA384) != 0 && !(hfuns & (1 << br_sha384_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-384\n",
+ suites[u].name);
+ goto client_exit_error;
+ }
+ /* TODO: algorithm implementation selection */
+ if ((req & REQ_AESCBC) != 0) {
+ br_ssl_engine_set_default_aes_cbc(&cc.eng);
+ }
+ if ((req & REQ_AESCCM) != 0) {
+ br_ssl_engine_set_default_aes_ccm(&cc.eng);
+ }
+ if ((req & REQ_AESGCM) != 0) {
+ br_ssl_engine_set_default_aes_gcm(&cc.eng);
+ }
+ if ((req & REQ_CHAPOL) != 0) {
+ br_ssl_engine_set_default_chapol(&cc.eng);
+ }
+ if ((req & REQ_3DESCBC) != 0) {
+ br_ssl_engine_set_default_des_cbc(&cc.eng);
+ }
+ if ((req & REQ_RSAKEYX) != 0) {
+ br_ssl_client_set_default_rsapub(&cc);
+ }
+ if ((req & REQ_ECDHE_RSA) != 0) {
+ br_ssl_engine_set_default_ec(&cc.eng);
+ br_ssl_engine_set_default_rsavrfy(&cc.eng);
+ }
+ if ((req & REQ_ECDHE_ECDSA) != 0) {
+ br_ssl_engine_set_default_ecdsa(&cc.eng);
+ }
+ if ((req & REQ_ECDH) != 0) {
+ br_ssl_engine_set_default_ec(&cc.eng);
+ }
+ }
+ if (fallback) {
+ suite_ids[num_suites ++] = 0x5600;
+ }
+ br_ssl_engine_set_suites(&cc.eng, suite_ids, num_suites);
+
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ br_ssl_engine_set_hash(&cc.eng, id, hc);
+ br_x509_minimal_set_hash(&xc, id, hc);
+ }
+ }
+ if (vmin <= BR_TLS11) {
+ br_ssl_engine_set_prf10(&cc.eng, &br_tls10_prf);
+ }
+ if (vmax >= BR_TLS12) {
+ if ((hfuns & ((unsigned)1 << br_sha256_ID)) != 0) {
+ br_ssl_engine_set_prf_sha256(&cc.eng,
+ &br_tls12_sha256_prf);
+ }
+ if ((hfuns & ((unsigned)1 << br_sha384_ID)) != 0) {
+ br_ssl_engine_set_prf_sha384(&cc.eng,
+ &br_tls12_sha384_prf);
+ }
+ }
+ br_x509_minimal_set_rsa(&xc, br_rsa_pkcs1_vrfy_get_default());
+ br_x509_minimal_set_ecdsa(&xc,
+ br_ec_get_default(), br_ecdsa_vrfy_asn1_get_default());
+
+ /*
+ * If there is no provided trust anchor, then certificate validation
+ * will always fail. In that situation, we use our custom wrapper
+ * that tolerates unknown anchors.
+ */
+ if (VEC_LEN(anchors) == 0) {
+ if (verbose) {
+ fprintf(stderr,
+ "WARNING: no configured trust anchor\n");
+ }
+ x509_noanchor_init(&xwc, &xc.vtable);
+ br_ssl_engine_set_x509(&cc.eng, &xwc.vtable);
+ } else {
+ br_ssl_engine_set_x509(&cc.eng, &xc.vtable);
+ }
+
+ if (minhello_len != (size_t)-1) {
+ br_ssl_client_set_min_clienthello_len(&cc, minhello_len);
+ }
+ br_ssl_engine_set_all_flags(&cc.eng, flags);
+ if (VEC_LEN(alpn_names) != 0) {
+ br_ssl_engine_set_protocol_names(&cc.eng,
+ (const char **)&VEC_ELT(alpn_names, 0),
+ VEC_LEN(alpn_names));
+ }
+
+ if (chain != NULL) {
+ zc.vtable = &ccert_vtable;
+ zc.verbose = verbose;
+ zc.chain = chain;
+ zc.chain_len = chain_len;
+ zc.sk = sk;
+ if (nostaticecdh || sk->key_type != BR_KEYTYPE_EC) {
+ zc.issuer_key_type = 0;
+ } else {
+ zc.issuer_key_type = get_cert_signer_algo(&chain[0]);
+ if (zc.issuer_key_type == 0) {
+ goto client_exit_error;
+ }
+ }
+ br_ssl_client_set_client_certificate(&cc, &zc.vtable);
+ }
+
+ br_ssl_engine_set_buffer(&cc.eng, iobuf, iobuf_len, bidi);
+ br_ssl_client_reset(&cc, sni, 0);
+
+ /*
+ * On Unix systems, we need to avoid SIGPIPE.
+ */
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Connect to the peer.
+ */
+ fd = host_connect(host, port, verbose);
+ if (fd == INVALID_SOCKET) {
+ goto client_exit_error;
+ }
+
+ /*
+ * Run the engine until completion.
+ */
+ if (run_ssl_engine(&cc.eng, fd,
+ (verbose ? RUN_ENGINE_VERBOSE : 0)
+ | (trace ? RUN_ENGINE_TRACE : 0)) != 0)
+ {
+ goto client_exit_error;
+ } else {
+ goto client_exit;
+ }
+
+ /*
+ * Release allocated structures.
+ */
+client_exit:
+ xfree(host);
+ xfree(port);
+ xfree(suites);
+ xfree(suite_ids);
+ VEC_CLEAREXT(anchors, &free_ta_contents);
+ VEC_CLEAREXT(alpn_names, &free_alpn);
+ free_certificates(chain, chain_len);
+ free_private_key(sk);
+ xfree(iobuf);
+ if (fd != INVALID_SOCKET) {
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ }
+ return retcode;
+
+client_exit_error:
+ retcode = -1;
+ goto client_exit;
+}
diff --git a/contrib/bearssl/tools/errors.c b/contrib/bearssl/tools/errors.c
new file mode 100644
index 000000000000..22f0c3058d4d
--- /dev/null
+++ b/contrib/bearssl/tools/errors.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static struct {
+ int err;
+ const char *name;
+ const char *comment;
+} errors[] = {
+ {
+ BR_ERR_BAD_PARAM,
+ "BR_ERR_BAD_PARAM",
+ "Caller-provided parameter is incorrect."
+ }, {
+ BR_ERR_BAD_STATE,
+ "BR_ERR_BAD_STATE",
+ "Operation requested by the caller cannot be applied with"
+ " the current context state (e.g. reading data while"
+ " outgoing data is waiting to be sent)."
+ }, {
+ BR_ERR_UNSUPPORTED_VERSION,
+ "BR_ERR_UNSUPPORTED_VERSION",
+ "Incoming protocol or record version is unsupported."
+ }, {
+ BR_ERR_BAD_VERSION,
+ "BR_ERR_BAD_VERSION",
+ "Incoming record version does not match the expected version."
+ }, {
+ BR_ERR_BAD_LENGTH,
+ "BR_ERR_BAD_LENGTH",
+ "Incoming record length is invalid."
+ }, {
+ BR_ERR_TOO_LARGE,
+ "BR_ERR_TOO_LARGE",
+ "Incoming record is too large to be processed, or buffer"
+ " is too small for the handshake message to send."
+ }, {
+ BR_ERR_BAD_MAC,
+ "BR_ERR_BAD_MAC",
+ "Decryption found an invalid padding, or the record MAC is"
+ " not correct."
+ }, {
+ BR_ERR_NO_RANDOM,
+ "BR_ERR_NO_RANDOM",
+ "No initial entropy was provided, and none can be obtained"
+ " from the OS."
+ }, {
+ BR_ERR_UNKNOWN_TYPE,
+ "BR_ERR_UNKNOWN_TYPE",
+ "Incoming record type is unknown."
+ }, {
+ BR_ERR_UNEXPECTED,
+ "BR_ERR_UNEXPECTED",
+ "Incoming record or message has wrong type with regards to"
+ " the current engine state."
+ }, {
+ BR_ERR_BAD_CCS,
+ "BR_ERR_BAD_CCS",
+ "ChangeCipherSpec message from the peer has invalid contents."
+ }, {
+ BR_ERR_BAD_ALERT,
+ "BR_ERR_BAD_ALERT",
+ "Alert message from the peer has invalid contents"
+ " (odd length)."
+ }, {
+ BR_ERR_BAD_HANDSHAKE,
+ "BR_ERR_BAD_HANDSHAKE",
+ "Incoming handshake message decoding failed."
+ }, {
+ BR_ERR_OVERSIZED_ID,
+ "BR_ERR_OVERSIZED_ID",
+ "ServerHello contains a session ID which is larger than"
+ " 32 bytes."
+ }, {
+ BR_ERR_BAD_CIPHER_SUITE,
+ "BR_ERR_BAD_CIPHER_SUITE",
+ "Server wants to use a cipher suite that we did not claim"
+ " to support. This is also reported if we tried to advertise"
+ " a cipher suite that we do not support."
+ }, {
+ BR_ERR_BAD_COMPRESSION,
+ "BR_ERR_BAD_COMPRESSION",
+ "Server wants to use a compression that we did not claim"
+ " to support."
+ }, {
+ BR_ERR_BAD_FRAGLEN,
+ "BR_ERR_BAD_FRAGLEN",
+ "Server's max fragment length does not match client's."
+ }, {
+ BR_ERR_BAD_SECRENEG,
+ "BR_ERR_BAD_SECRENEG",
+ "Secure renegotiation failed."
+ }, {
+ BR_ERR_EXTRA_EXTENSION,
+ "BR_ERR_EXTRA_EXTENSION",
+ "Server sent an extension type that we did not announce,"
+ " or used the same extension type several times in a"
+ " single ServerHello."
+ }, {
+ BR_ERR_BAD_SNI,
+ "BR_ERR_BAD_SNI",
+ "Invalid Server Name Indication contents (when used by"
+ " the server, this extension shall be empty)."
+ }, {
+ BR_ERR_BAD_HELLO_DONE,
+ "BR_ERR_BAD_HELLO_DONE",
+ "Invalid ServerHelloDone from the server (length is not 0)."
+ }, {
+ BR_ERR_LIMIT_EXCEEDED,
+ "BR_ERR_LIMIT_EXCEEDED",
+ "Internal limit exceeded (e.g. server's public key is too"
+ " large)."
+ }, {
+ BR_ERR_BAD_FINISHED,
+ "BR_ERR_BAD_FINISHED",
+ "Finished message from peer does not match the expected"
+ " value."
+ }, {
+ BR_ERR_RESUME_MISMATCH,
+ "BR_ERR_RESUME_MISMATCH",
+ "Session resumption attempt with distinct version or cipher"
+ " suite."
+ }, {
+ BR_ERR_INVALID_ALGORITHM,
+ "BR_ERR_INVALID_ALGORITHM",
+ "Unsupported or invalid algorithm (ECDHE curve, signature"
+ " algorithm, hash function)."
+ }, {
+ BR_ERR_BAD_SIGNATURE,
+ "BR_ERR_BAD_SIGNATURE",
+ "Invalid signature in ServerKeyExchange or"
+ " CertificateVerify message."
+ }, {
+ BR_ERR_WRONG_KEY_USAGE,
+ "BR_ERR_WRONG_KEY_USAGE",
+ "Peer's public key does not have the proper type or is"
+ " not allowed for the requested operation."
+ }, {
+ BR_ERR_NO_CLIENT_AUTH,
+ "BR_ERR_NO_CLIENT_AUTH",
+ "Client did not send a certificate upon request, or the"
+ " client certificate could not be validated."
+ }, {
+ BR_ERR_IO,
+ "BR_ERR_IO",
+ "I/O error or premature close on transport stream."
+ }, {
+ BR_ERR_X509_INVALID_VALUE,
+ "BR_ERR_X509_INVALID_VALUE",
+ "Invalid value in an ASN.1 structure."
+ },
+ {
+ BR_ERR_X509_TRUNCATED,
+ "BR_ERR_X509_TRUNCATED",
+ "Truncated certificate or other ASN.1 object."
+ },
+ {
+ BR_ERR_X509_EMPTY_CHAIN,
+ "BR_ERR_X509_EMPTY_CHAIN",
+ "Empty certificate chain (no certificate at all)."
+ },
+ {
+ BR_ERR_X509_INNER_TRUNC,
+ "BR_ERR_X509_INNER_TRUNC",
+ "Decoding error: inner element extends beyond outer element"
+ " size."
+ },
+ {
+ BR_ERR_X509_BAD_TAG_CLASS,
+ "BR_ERR_X509_BAD_TAG_CLASS",
+ "Decoding error: unsupported tag class (application or"
+ " private)."
+ },
+ {
+ BR_ERR_X509_BAD_TAG_VALUE,
+ "BR_ERR_X509_BAD_TAG_VALUE",
+ "Decoding error: unsupported tag value."
+ },
+ {
+ BR_ERR_X509_INDEFINITE_LENGTH,
+ "BR_ERR_X509_INDEFINITE_LENGTH",
+ "Decoding error: indefinite length."
+ },
+ {
+ BR_ERR_X509_EXTRA_ELEMENT,
+ "BR_ERR_X509_EXTRA_ELEMENT",
+ "Decoding error: extraneous element."
+ },
+ {
+ BR_ERR_X509_UNEXPECTED,
+ "BR_ERR_X509_UNEXPECTED",
+ "Decoding error: unexpected element."
+ },
+ {
+ BR_ERR_X509_NOT_CONSTRUCTED,
+ "BR_ERR_X509_NOT_CONSTRUCTED",
+ "Decoding error: expected constructed element, but is"
+ " primitive."
+ },
+ {
+ BR_ERR_X509_NOT_PRIMITIVE,
+ "BR_ERR_X509_NOT_PRIMITIVE",
+ "Decoding error: expected primitive element, but is"
+ " constructed."
+ },
+ {
+ BR_ERR_X509_PARTIAL_BYTE,
+ "BR_ERR_X509_PARTIAL_BYTE",
+ "Decoding error: BIT STRING length is not multiple of 8."
+ },
+ {
+ BR_ERR_X509_BAD_BOOLEAN,
+ "BR_ERR_X509_BAD_BOOLEAN",
+ "Decoding error: BOOLEAN value has invalid length."
+ },
+ {
+ BR_ERR_X509_OVERFLOW,
+ "BR_ERR_X509_OVERFLOW",
+ "Decoding error: value is off-limits."
+ },
+ {
+ BR_ERR_X509_BAD_DN,
+ "BR_ERR_X509_BAD_DN",
+ "Invalid distinguished name."
+ },
+ {
+ BR_ERR_X509_BAD_TIME,
+ "BR_ERR_X509_BAD_TIME",
+ "Invalid date/time representation."
+ },
+ {
+ BR_ERR_X509_UNSUPPORTED,
+ "BR_ERR_X509_UNSUPPORTED",
+ "Certificate contains unsupported features that cannot be"
+ " ignored."
+ },
+ {
+ BR_ERR_X509_LIMIT_EXCEEDED,
+ "BR_ERR_X509_LIMIT_EXCEEDED",
+ "Key or signature size exceeds internal limits."
+ },
+ {
+ BR_ERR_X509_WRONG_KEY_TYPE,
+ "BR_ERR_X509_WRONG_KEY_TYPE",
+ "Key type does not match that which was expected."
+ },
+ {
+ BR_ERR_X509_BAD_SIGNATURE,
+ "BR_ERR_X509_BAD_SIGNATURE",
+ "Signature is invalid."
+ },
+ {
+ BR_ERR_X509_TIME_UNKNOWN,
+ "BR_ERR_X509_TIME_UNKNOWN",
+ "Validation time is unknown."
+ },
+ {
+ BR_ERR_X509_EXPIRED,
+ "BR_ERR_X509_EXPIRED",
+ "Certificate is expired or not yet valid."
+ },
+ {
+ BR_ERR_X509_DN_MISMATCH,
+ "BR_ERR_X509_DN_MISMATCH",
+ "Issuer/Subject DN mismatch in the chain."
+ },
+ {
+ BR_ERR_X509_BAD_SERVER_NAME,
+ "BR_ERR_X509_BAD_SERVER_NAME",
+ "Expected server name was not found in the chain."
+ },
+ {
+ BR_ERR_X509_CRITICAL_EXTENSION,
+ "BR_ERR_X509_CRITICAL_EXTENSION",
+ "Unknown critical extension in certificate."
+ },
+ {
+ BR_ERR_X509_NOT_CA,
+ "BR_ERR_X509_NOT_CA",
+ "Not a CA, or path length constraint violation."
+ },
+ {
+ BR_ERR_X509_FORBIDDEN_KEY_USAGE,
+ "BR_ERR_X509_FORBIDDEN_KEY_USAGE",
+ "Key Usage extension prohibits intended usage."
+ },
+ {
+ BR_ERR_X509_WEAK_PUBLIC_KEY,
+ "BR_ERR_X509_WEAK_PUBLIC_KEY",
+ "Public key found in certificate is too small."
+ },
+ {
+ BR_ERR_X509_NOT_TRUSTED,
+ "BR_ERR_X509_NOT_TRUSTED",
+ "Chain could not be linked to a trust anchor."
+ },
+ { 0, 0, 0 }
+};
+
+/* see brssl.h */
+const char *
+find_error_name(int err, const char **comment)
+{
+ size_t u;
+
+ for (u = 0; errors[u].name; u ++) {
+ if (errors[u].err == err) {
+ if (comment != NULL) {
+ *comment = errors[u].comment;
+ }
+ return errors[u].name;
+ }
+ }
+ return NULL;
+}
diff --git a/contrib/bearssl/tools/files.c b/contrib/bearssl/tools/files.c
new file mode 100644
index 000000000000..8bf67cc3b4f3
--- /dev/null
+++ b/contrib/bearssl/tools/files.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+
+/* see brssl.h */
+unsigned char *
+read_file(const char *fname, size_t *len)
+{
+ bvector vbuf = VEC_INIT;
+ FILE *f;
+
+ *len = 0;
+ f = fopen(fname, "rb");
+ if (f == NULL) {
+ fprintf(stderr,
+ "ERROR: could not open file '%s' for reading\n", fname);
+ return NULL;
+ }
+ for (;;) {
+ unsigned char tmp[1024];
+ size_t rlen;
+
+ rlen = fread(tmp, 1, sizeof tmp, f);
+ if (rlen == 0) {
+ unsigned char *buf;
+
+ if (ferror(f)) {
+ fprintf(stderr,
+ "ERROR: read error on file '%s'\n",
+ fname);
+ fclose(f);
+ return NULL;
+ }
+ buf = VEC_TOARRAY(vbuf);
+ *len = VEC_LEN(vbuf);
+ VEC_CLEAR(vbuf);
+ fclose(f);
+ return buf;
+ }
+ VEC_ADDMANY(vbuf, tmp, rlen);
+ }
+}
+
+/* see brssl.h */
+int
+write_file(const char *fname, const void *data, size_t len)
+{
+ FILE *f;
+ const unsigned char *buf;
+
+ f = fopen(fname, "wb");
+ if (f == NULL) {
+ fprintf(stderr,
+ "ERROR: could not open file '%s' for reading\n", fname);
+ return -1;
+ }
+ buf = data;
+ while (len > 0) {
+ size_t wlen;
+
+ wlen = fwrite(buf, 1, len, f);
+ if (wlen == 0) {
+ fprintf(stderr,
+ "ERROR: could not write all bytes to '%s'\n",
+ fname);
+ fclose(f);
+ return -1;
+ }
+ buf += wlen;
+ len -= wlen;
+ }
+ if (ferror(f)) {
+ fprintf(stderr, "ERROR: write error on file '%s'\n", fname);
+ fclose(f);
+ return -1;
+ }
+ fclose(f);
+ return 0;
+}
+
+/* see brssl.h */
+int
+looks_like_DER(const unsigned char *buf, size_t len)
+{
+ int fb;
+ size_t dlen;
+
+ if (len < 2) {
+ return 0;
+ }
+ if (*buf ++ != 0x30) {
+ return 0;
+ }
+ fb = *buf ++;
+ len -= 2;
+ if (fb < 0x80) {
+ return (size_t)fb == len;
+ } else if (fb == 0x80) {
+ return 0;
+ } else {
+ fb -= 0x80;
+ if (len < (size_t)fb + 2) {
+ return 0;
+ }
+ len -= (size_t)fb;
+ dlen = 0;
+ while (fb -- > 0) {
+ if (dlen > (len >> 8)) {
+ return 0;
+ }
+ dlen = (dlen << 8) + (size_t)*buf ++;
+ }
+ return dlen == len;
+ }
+}
+
+static void
+vblob_append(void *cc, const void *data, size_t len)
+{
+ bvector *bv;
+
+ bv = cc;
+ VEC_ADDMANY(*bv, data, len);
+}
+
+/* see brssl.h */
+void
+free_pem_object_contents(pem_object *po)
+{
+ if (po != NULL) {
+ xfree(po->name);
+ xfree(po->data);
+ }
+}
+
+/* see brssl.h */
+pem_object *
+decode_pem(const void *src, size_t len, size_t *num)
+{
+ VECTOR(pem_object) pem_list = VEC_INIT;
+ br_pem_decoder_context pc;
+ pem_object po, *pos;
+ const unsigned char *buf;
+ bvector bv = VEC_INIT;
+ int inobj;
+ int extra_nl;
+
+ *num = 0;
+ br_pem_decoder_init(&pc);
+ buf = src;
+ inobj = 0;
+ po.name = NULL;
+ po.data = NULL;
+ po.data_len = 0;
+ extra_nl = 1;
+ while (len > 0) {
+ size_t tlen;
+
+ tlen = br_pem_decoder_push(&pc, buf, len);
+ buf += tlen;
+ len -= tlen;
+ switch (br_pem_decoder_event(&pc)) {
+
+ case BR_PEM_BEGIN_OBJ:
+ po.name = xstrdup(br_pem_decoder_name(&pc));
+ br_pem_decoder_setdest(&pc, vblob_append, &bv);
+ inobj = 1;
+ break;
+
+ case BR_PEM_END_OBJ:
+ if (inobj) {
+ po.data = VEC_TOARRAY(bv);
+ po.data_len = VEC_LEN(bv);
+ VEC_ADD(pem_list, po);
+ VEC_CLEAR(bv);
+ po.name = NULL;
+ po.data = NULL;
+ po.data_len = 0;
+ inobj = 0;
+ }
+ break;
+
+ case BR_PEM_ERROR:
+ xfree(po.name);
+ VEC_CLEAR(bv);
+ fprintf(stderr,
+ "ERROR: invalid PEM encoding\n");
+ VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+ return NULL;
+ }
+
+ /*
+ * We add an extra newline at the end, in order to
+ * support PEM files that lack the newline on their last
+ * line (this is somwehat invalid, but PEM format is not
+ * standardised and such files do exist in the wild, so
+ * we'd better accept them).
+ */
+ if (len == 0 && extra_nl) {
+ extra_nl = 0;
+ buf = (const unsigned char *)"\n";
+ len = 1;
+ }
+ }
+ if (inobj) {
+ fprintf(stderr, "ERROR: unfinished PEM object\n");
+ xfree(po.name);
+ VEC_CLEAR(bv);
+ VEC_CLEAREXT(pem_list, &free_pem_object_contents);
+ return NULL;
+ }
+
+ *num = VEC_LEN(pem_list);
+ VEC_ADD(pem_list, po);
+ pos = VEC_TOARRAY(pem_list);
+ VEC_CLEAR(pem_list);
+ return pos;
+}
+
+/* see brssl.h */
+br_x509_certificate *
+read_certificates(const char *fname, size_t *num)
+{
+ VECTOR(br_x509_certificate) cert_list = VEC_INIT;
+ unsigned char *buf;
+ size_t len;
+ pem_object *pos;
+ size_t u, num_pos;
+ br_x509_certificate *xcs;
+ br_x509_certificate dummy;
+
+ *num = 0;
+
+ /*
+ * TODO: reading the whole file is crude; we could parse them
+ * in a streamed fashion. But it does not matter much in practice.
+ */
+ buf = read_file(fname, &len);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * Check for a DER-encoded certificate.
+ */
+ if (looks_like_DER(buf, len)) {
+ xcs = xmalloc(2 * sizeof *xcs);
+ xcs[0].data = buf;
+ xcs[0].data_len = len;
+ xcs[1].data = NULL;
+ xcs[1].data_len = 0;
+ *num = 1;
+ return xcs;
+ }
+
+ pos = decode_pem(buf, len, &num_pos);
+ xfree(buf);
+ if (pos == NULL) {
+ return NULL;
+ }
+ for (u = 0; u < num_pos; u ++) {
+ if (eqstr(pos[u].name, "CERTIFICATE")
+ || eqstr(pos[u].name, "X509 CERTIFICATE"))
+ {
+ br_x509_certificate xc;
+
+ xc.data = pos[u].data;
+ xc.data_len = pos[u].data_len;
+ pos[u].data = NULL;
+ VEC_ADD(cert_list, xc);
+ }
+ }
+ for (u = 0; u < num_pos; u ++) {
+ free_pem_object_contents(&pos[u]);
+ }
+ xfree(pos);
+
+ if (VEC_LEN(cert_list) == 0) {
+ fprintf(stderr, "ERROR: no certificate in file '%s'\n", fname);
+ return NULL;
+ }
+ *num = VEC_LEN(cert_list);
+ dummy.data = NULL;
+ dummy.data_len = 0;
+ VEC_ADD(cert_list, dummy);
+ xcs = VEC_TOARRAY(cert_list);
+ VEC_CLEAR(cert_list);
+ return xcs;
+}
+
+/* see brssl.h */
+void
+free_certificates(br_x509_certificate *certs, size_t num)
+{
+ size_t u;
+
+ for (u = 0; u < num; u ++) {
+ xfree(certs[u].data);
+ }
+ xfree(certs);
+}
diff --git a/contrib/bearssl/tools/impl.c b/contrib/bearssl/tools/impl.c
new file mode 100644
index 000000000000..e00cc327dce4
--- /dev/null
+++ b/contrib/bearssl/tools/impl.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+/* see brssl.h */
+int
+do_impl(int argc, char *argv[])
+{
+ const br_config_option *opt;
+
+ (void)argc;
+ (void)argv;
+
+ for (opt = br_get_config(); opt->name != NULL; opt ++) {
+ printf("%-25s %8ld\n", opt->name, opt->value);
+ }
+
+ return 0;
+}
diff --git a/contrib/bearssl/tools/keys.c b/contrib/bearssl/tools/keys.c
new file mode 100644
index 000000000000..6e54676380a6
--- /dev/null
+++ b/contrib/bearssl/tools/keys.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static private_key *
+decode_key(const unsigned char *buf, size_t len)
+{
+ br_skey_decoder_context dc;
+ int err;
+ private_key *sk;
+
+ br_skey_decoder_init(&dc);
+ br_skey_decoder_push(&dc, buf, len);
+ err = br_skey_decoder_last_error(&dc);
+ if (err != 0) {
+ const char *errname, *errmsg;
+
+ fprintf(stderr, "ERROR (decoding): err=%d\n", err);
+ errname = find_error_name(err, &errmsg);
+ if (errname != NULL) {
+ fprintf(stderr, " %s: %s\n", errname, errmsg);
+ } else {
+ fprintf(stderr, " (unknown)\n");
+ }
+ return NULL;
+ }
+ switch (br_skey_decoder_key_type(&dc)) {
+ const br_rsa_private_key *rk;
+ const br_ec_private_key *ek;
+
+ case BR_KEYTYPE_RSA:
+ rk = br_skey_decoder_get_rsa(&dc);
+ sk = xmalloc(sizeof *sk);
+ sk->key_type = BR_KEYTYPE_RSA;
+ sk->key.rsa.n_bitlen = rk->n_bitlen;
+ sk->key.rsa.p = xblobdup(rk->p, rk->plen);
+ sk->key.rsa.plen = rk->plen;
+ sk->key.rsa.q = xblobdup(rk->q, rk->qlen);
+ sk->key.rsa.qlen = rk->qlen;
+ sk->key.rsa.dp = xblobdup(rk->dp, rk->dplen);
+ sk->key.rsa.dplen = rk->dplen;
+ sk->key.rsa.dq = xblobdup(rk->dq, rk->dqlen);
+ sk->key.rsa.dqlen = rk->dqlen;
+ sk->key.rsa.iq = xblobdup(rk->iq, rk->iqlen);
+ sk->key.rsa.iqlen = rk->iqlen;
+ break;
+
+ case BR_KEYTYPE_EC:
+ ek = br_skey_decoder_get_ec(&dc);
+ sk = xmalloc(sizeof *sk);
+ sk->key_type = BR_KEYTYPE_EC;
+ sk->key.ec.curve = ek->curve;
+ sk->key.ec.x = xblobdup(ek->x, ek->xlen);
+ sk->key.ec.xlen = ek->xlen;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown key type: %d\n",
+ br_skey_decoder_key_type(&dc));
+ sk = NULL;
+ break;
+ }
+
+ return sk;
+}
+
+/* see brssl.h */
+private_key *
+read_private_key(const char *fname)
+{
+ unsigned char *buf;
+ size_t len;
+ private_key *sk;
+ pem_object *pos;
+ size_t num, u;
+
+ buf = NULL;
+ pos = NULL;
+ sk = NULL;
+ buf = read_file(fname, &len);
+ if (buf == NULL) {
+ goto deckey_exit;
+ }
+ if (looks_like_DER(buf, len)) {
+ sk = decode_key(buf, len);
+ goto deckey_exit;
+ } else {
+ pos = decode_pem(buf, len, &num);
+ if (pos == NULL) {
+ goto deckey_exit;
+ }
+ for (u = 0; pos[u].name; u ++) {
+ const char *name;
+
+ name = pos[u].name;
+ if (eqstr(name, "RSA PRIVATE KEY")
+ || eqstr(name, "EC PRIVATE KEY")
+ || eqstr(name, "PRIVATE KEY"))
+ {
+ sk = decode_key(pos[u].data, pos[u].data_len);
+ goto deckey_exit;
+ }
+ }
+ fprintf(stderr, "ERROR: no private key in file '%s'\n", fname);
+ goto deckey_exit;
+ }
+
+deckey_exit:
+ if (buf != NULL) {
+ xfree(buf);
+ }
+ if (pos != NULL) {
+ for (u = 0; pos[u].name; u ++) {
+ free_pem_object_contents(&pos[u]);
+ }
+ xfree(pos);
+ }
+ return sk;
+}
+
+/* see brssl.h */
+void
+free_private_key(private_key *sk)
+{
+ if (sk == NULL) {
+ return;
+ }
+ switch (sk->key_type) {
+ case BR_KEYTYPE_RSA:
+ xfree(sk->key.rsa.p);
+ xfree(sk->key.rsa.q);
+ xfree(sk->key.rsa.dp);
+ xfree(sk->key.rsa.dq);
+ xfree(sk->key.rsa.iq);
+ break;
+ case BR_KEYTYPE_EC:
+ xfree(sk->key.ec.x);
+ break;
+ }
+ xfree(sk);
+}
+
+/*
+ * OID for hash functions in RSA signatures.
+ */
+static const unsigned char HASH_OID_SHA1[] = {
+ 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A
+};
+
+static const unsigned char HASH_OID_SHA224[] = {
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04
+};
+
+static const unsigned char HASH_OID_SHA256[] = {
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
+};
+
+static const unsigned char HASH_OID_SHA384[] = {
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
+};
+
+static const unsigned char HASH_OID_SHA512[] = {
+ 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
+};
+
+static const unsigned char *HASH_OID[] = {
+ HASH_OID_SHA1,
+ HASH_OID_SHA224,
+ HASH_OID_SHA256,
+ HASH_OID_SHA384,
+ HASH_OID_SHA512
+};
+
+/* see brssl.h */
+const unsigned char *
+get_hash_oid(int id)
+{
+ if (id >= 2 && id <= 6) {
+ return HASH_OID[id - 2];
+ } else {
+ return NULL;
+ }
+}
+
+/* see brssl.h */
+const br_hash_class *
+get_hash_impl(int hash_id)
+{
+ size_t u;
+
+ if (hash_id == 0) {
+ return &br_md5sha1_vtable;
+ }
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+ if (id == hash_id) {
+ return hc;
+ }
+ }
+ return NULL;
+}
diff --git a/contrib/bearssl/tools/names.c b/contrib/bearssl/tools/names.c
new file mode 100644
index 000000000000..3751d93fa416
--- /dev/null
+++ b/contrib/bearssl/tools/names.c
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+#include "bearssl.h"
+
+/* see brssl.h */
+const protocol_version protocol_versions[] = {
+ { "tls10", BR_TLS10, "TLS 1.0" },
+ { "tls11", BR_TLS11, "TLS 1.1" },
+ { "tls12", BR_TLS12, "TLS 1.2" },
+ { NULL, 0, NULL }
+};
+
+/* see brssl.h */
+const hash_function hash_functions[] = {
+ { "md5", &br_md5_vtable, "MD5" },
+ { "sha1", &br_sha1_vtable, "SHA-1" },
+ { "sha224", &br_sha224_vtable, "SHA-224" },
+ { "sha256", &br_sha256_vtable, "SHA-256" },
+ { "sha384", &br_sha384_vtable, "SHA-384" },
+ { "sha512", &br_sha512_vtable, "SHA-512" },
+ { NULL, 0, NULL }
+};
+
+/* see brssl.h */
+const cipher_suite cipher_suites[] = {
+ {
+ "ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
+ BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ REQ_ECDHE_ECDSA | REQ_CHAPOL | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, ChaCha20+Poly1305 encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
+ BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ REQ_ECDHE_RSA | REQ_CHAPOL | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with RSA, ChaCha20+Poly1305 encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ REQ_ECDHE_ECDSA | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-128/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+ BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ REQ_ECDHE_RSA | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with RSA, AES-128/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ REQ_ECDHE_ECDSA | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-256/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ REQ_ECDHE_RSA | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+ "ECDHE with RSA, AES-256/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_128_CCM",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
+ REQ_ECDHE_ECDSA | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-128/CCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_256_CCM",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
+ REQ_ECDHE_ECDSA | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-256/CCM encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_128_CCM_8",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+ REQ_ECDHE_ECDSA | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-128/CCM_8 encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_256_CCM_8",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
+ REQ_ECDHE_ECDSA | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-256/CCM_8 encryption (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-128/CBC + SHA-256 (TLS 1.2+)"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+ BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "ECDHE with RSA, AES-128/CBC + SHA-256 (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+ REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+ "ECDHE with ECDSA, AES-256/CBC + SHA-384 (TLS 1.2+)"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_256_CBC_SHA384",
+ BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+ "ECDHE with RSA, AES-256/CBC + SHA-384 (TLS 1.2+)"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA1,
+ "ECDHE with ECDSA, AES-128/CBC + SHA-1"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_128_CBC_SHA",
+ BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA1,
+ "ECDHE with RSA, AES-128/CBC + SHA-1"
+ },
+ {
+ "ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
+ BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ REQ_ECDHE_ECDSA | REQ_AESCBC | REQ_SHA1,
+ "ECDHE with ECDSA, AES-256/CBC + SHA-1"
+ },
+ {
+ "ECDHE_RSA_WITH_AES_256_CBC_SHA",
+ BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ REQ_ECDHE_RSA | REQ_AESCBC | REQ_SHA1,
+ "ECDHE with RSA, AES-256/CBC + SHA-1"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_128_GCM_SHA256",
+ BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+ REQ_ECDH | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+ "ECDH key exchange (EC cert), AES-128/GCM (TLS 1.2+)"
+ },
+ {
+ "ECDH_RSA_WITH_AES_128_GCM_SHA256",
+ BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+ REQ_ECDH | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+ "ECDH key exchange (RSA cert), AES-128/GCM (TLS 1.2+)"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_256_GCM_SHA384",
+ BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+ REQ_ECDH | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+ "ECDH key exchange (EC cert), AES-256/GCM (TLS 1.2+)"
+ },
+ {
+ "ECDH_RSA_WITH_AES_256_GCM_SHA384",
+ BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+ REQ_ECDH | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+ "ECDH key exchange (RSA cert), AES-256/GCM (TLS 1.2+)"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_128_CBC_SHA256",
+ BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "ECDH key exchange (EC cert), AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+ },
+ {
+ "ECDH_RSA_WITH_AES_128_CBC_SHA256",
+ BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "ECDH key exchange (RSA cert), AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_256_CBC_SHA384",
+ BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+ "ECDH key exchange (EC cert), AES-256/CBC + HMAC/SHA-384 (TLS 1.2+)"
+ },
+ {
+ "ECDH_RSA_WITH_AES_256_CBC_SHA384",
+ BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA384 | REQ_TLS12,
+ "ECDH key exchange (RSA cert), AES-256/CBC + HMAC/SHA-384 (TLS 1.2+)"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_128_CBC_SHA",
+ BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+ "ECDH key exchange (EC cert), AES-128/CBC + HMAC/SHA-1"
+ },
+ {
+ "ECDH_RSA_WITH_AES_128_CBC_SHA",
+ BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+ "ECDH key exchange (RSA cert), AES-128/CBC + HMAC/SHA-1"
+ },
+ {
+ "ECDH_ECDSA_WITH_AES_256_CBC_SHA",
+ BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+ "ECDH key exchange (EC cert), AES-256/CBC + HMAC/SHA-1"
+ },
+ {
+ "ECDH_RSA_WITH_AES_256_CBC_SHA",
+ BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
+ REQ_ECDH | REQ_AESCBC | REQ_SHA1,
+ "ECDH key exchange (RSA cert), AES-256/CBC + HMAC/SHA-1"
+ },
+ {
+ "RSA_WITH_AES_128_GCM_SHA256",
+ BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
+ REQ_RSAKEYX | REQ_AESGCM | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-128/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_256_GCM_SHA384",
+ BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
+ REQ_RSAKEYX | REQ_AESGCM | REQ_SHA384 | REQ_TLS12,
+ "RSA key exchange, AES-256/GCM encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_128_CCM",
+ BR_TLS_RSA_WITH_AES_128_CCM,
+ REQ_RSAKEYX | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-128/CCM encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_256_CCM",
+ BR_TLS_RSA_WITH_AES_256_CCM,
+ REQ_RSAKEYX | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-256/CCM encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_128_CCM_8",
+ BR_TLS_RSA_WITH_AES_128_CCM_8,
+ REQ_RSAKEYX | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-128/CCM_8 encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_256_CCM_8",
+ BR_TLS_RSA_WITH_AES_256_CCM_8,
+ REQ_RSAKEYX | REQ_AESCCM | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-256/CCM_8 encryption (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_128_CBC_SHA256",
+ BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
+ REQ_RSAKEYX | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-128/CBC + HMAC/SHA-256 (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_256_CBC_SHA256",
+ BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
+ REQ_RSAKEYX | REQ_AESCBC | REQ_SHA256 | REQ_TLS12,
+ "RSA key exchange, AES-256/CBC + HMAC/SHA-256 (TLS 1.2+)"
+ },
+ {
+ "RSA_WITH_AES_128_CBC_SHA",
+ BR_TLS_RSA_WITH_AES_128_CBC_SHA,
+ REQ_RSAKEYX | REQ_AESCBC | REQ_SHA1,
+ "RSA key exchange, AES-128/CBC + HMAC/SHA-1"
+ },
+ {
+ "RSA_WITH_AES_256_CBC_SHA",
+ BR_TLS_RSA_WITH_AES_256_CBC_SHA,
+ REQ_RSAKEYX | REQ_AESCBC | REQ_SHA1,
+ "RSA key exchange, AES-256/CBC + HMAC/SHA-1"
+ },
+ {
+ "ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ REQ_ECDHE_ECDSA | REQ_3DESCBC | REQ_SHA1,
+ "ECDHE with ECDSA, 3DES/CBC + SHA-1"
+ },
+ {
+ "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
+ BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ REQ_ECDHE_RSA | REQ_3DESCBC | REQ_SHA1,
+ "ECDHE with RSA, 3DES/CBC + SHA-1"
+ },
+ {
+ "ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+ BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ REQ_ECDH | REQ_3DESCBC | REQ_SHA1,
+ "ECDH key exchange (EC cert), 3DES/CBC + HMAC/SHA-1"
+ },
+ {
+ "ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
+ BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+ REQ_ECDH | REQ_3DESCBC | REQ_SHA1,
+ "ECDH key exchange (RSA cert), 3DES/CBC + HMAC/SHA-1"
+ },
+ {
+ "RSA_WITH_3DES_EDE_CBC_SHA",
+ BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ REQ_RSAKEYX | REQ_3DESCBC | REQ_SHA1,
+ "RSA key exchange, 3DES/CBC + HMAC/SHA-1"
+ },
+ { NULL, 0, 0, NULL }
+};
+
+static const struct {
+ int id;
+ const char *name;
+ const char *sid[4];
+} curves[] = {
+ { BR_EC_sect163k1,
+ "sect163k1",
+ { "sect163k1", "K-163", NULL, NULL } },
+ { BR_EC_sect163r1,
+ "sect163r1",
+ { "sect163r1", NULL, NULL, NULL } },
+ { BR_EC_sect163r2,
+ "sect163r2",
+ { "sect163r2", "B-163", NULL, NULL } },
+ { BR_EC_sect193r1,
+ "sect193r1",
+ { "sect193r1", NULL, NULL, NULL } },
+ { BR_EC_sect193r2,
+ "sect193r2",
+ { "sect193r2", NULL, NULL, NULL } },
+ { BR_EC_sect233k1,
+ "sect233k1",
+ { "sect233k1", "K-233", NULL, NULL } },
+ { BR_EC_sect233r1,
+ "sect233r1",
+ { "sect233r1", "B-233", NULL, NULL } },
+ { BR_EC_sect239k1,
+ "sect239k1",
+ { "sect239k1", NULL, NULL, NULL } },
+ { BR_EC_sect283k1,
+ "sect283k1",
+ { "sect283k1", "K-283", NULL, NULL } },
+ { BR_EC_sect283r1,
+ "sect283r1",
+ { "sect283r1", "B-283", NULL, NULL } },
+ { BR_EC_sect409k1,
+ "sect409k1",
+ { "sect409k1", "K-409", NULL, NULL } },
+ { BR_EC_sect409r1,
+ "sect409r1",
+ { "sect409r1", "B-409", NULL, NULL } },
+ { BR_EC_sect571k1,
+ "sect571k1",
+ { "sect571k1", "K-571", NULL, NULL } },
+ { BR_EC_sect571r1,
+ "sect571r1",
+ { "sect571r1", "B-571", NULL, NULL } },
+ { BR_EC_secp160k1,
+ "secp160k1",
+ { "secp160k1", NULL, NULL, NULL } },
+ { BR_EC_secp160r1,
+ "secp160r1",
+ { "secp160r1", NULL, NULL, NULL } },
+ { BR_EC_secp160r2,
+ "secp160r2",
+ { "secp160r2", NULL, NULL, NULL } },
+ { BR_EC_secp192k1,
+ "secp192k1",
+ { "secp192k1", NULL, NULL, NULL } },
+ { BR_EC_secp192r1,
+ "secp192r1",
+ { "secp192r1", "P-192", NULL, NULL } },
+ { BR_EC_secp224k1,
+ "secp224k1",
+ { "secp224k1", NULL, NULL, NULL } },
+ { BR_EC_secp224r1,
+ "secp224r1",
+ { "secp224r1", "P-224", NULL, NULL } },
+ { BR_EC_secp256k1,
+ "secp256k1",
+ { "secp256k1", NULL, NULL, NULL } },
+ { BR_EC_secp256r1,
+ "secp256r1 (P-256)",
+ { "secp256r1", "P-256", "prime256v1", NULL } },
+ { BR_EC_secp384r1,
+ "secp384r1 (P-384)",
+ { "secp384r1", "P-384", NULL, NULL } },
+ { BR_EC_secp521r1,
+ "secp521r1 (P-521)",
+ { "secp521r1", "P-521", NULL, NULL } },
+ { BR_EC_brainpoolP256r1,
+ "brainpoolP256r1",
+ { "brainpoolP256r1", NULL, NULL, NULL } },
+ { BR_EC_brainpoolP384r1,
+ "brainpoolP384r1",
+ { "brainpoolP384r1", NULL, NULL, NULL } },
+ { BR_EC_brainpoolP512r1,
+ "brainpoolP512r1",
+ { "brainpoolP512r1", NULL, NULL, NULL } },
+ { BR_EC_curve25519,
+ "Curve25519",
+ { "curve25519", "c25519", NULL, NULL } },
+ { BR_EC_curve448,
+ "Curve448",
+ { "curve448", "c448", NULL, NULL } },
+ { 0, 0, { 0, 0, 0, 0 } }
+};
+
+static const struct {
+ const char *long_name;
+ const char *short_name;
+ const void *impl;
+} algo_names[] = {
+ /* Block ciphers */
+ { "aes_big_cbcenc", "big", &br_aes_big_cbcenc_vtable },
+ { "aes_big_cbcdec", "big", &br_aes_big_cbcdec_vtable },
+ { "aes_big_ctr", "big", &br_aes_big_ctr_vtable },
+ { "aes_big_ctrcbc", "big", &br_aes_big_ctrcbc_vtable },
+ { "aes_small_cbcenc", "small", &br_aes_small_cbcenc_vtable },
+ { "aes_small_cbcdec", "small", &br_aes_small_cbcdec_vtable },
+ { "aes_small_ctr", "small", &br_aes_small_ctr_vtable },
+ { "aes_small_ctrcbc", "small", &br_aes_small_ctrcbc_vtable },
+ { "aes_ct_cbcenc", "ct", &br_aes_ct_cbcenc_vtable },
+ { "aes_ct_cbcdec", "ct", &br_aes_ct_cbcdec_vtable },
+ { "aes_ct_ctr", "ct", &br_aes_ct_ctr_vtable },
+ { "aes_ct_ctrcbc", "ct", &br_aes_ct_ctrcbc_vtable },
+ { "aes_ct64_cbcenc", "ct64", &br_aes_ct64_cbcenc_vtable },
+ { "aes_ct64_cbcdec", "ct64", &br_aes_ct64_cbcdec_vtable },
+ { "aes_ct64_ctr", "ct64", &br_aes_ct64_ctr_vtable },
+ { "aes_ct64_ctrcbc", "ct64", &br_aes_ct64_ctrcbc_vtable },
+
+ { "des_tab_cbcenc", "tab", &br_des_tab_cbcenc_vtable },
+ { "des_tab_cbcdec", "tab", &br_des_tab_cbcdec_vtable },
+ { "des_ct_cbcenc", "ct", &br_des_ct_cbcenc_vtable },
+ { "des_ct_cbcdec", "ct", &br_des_ct_cbcdec_vtable },
+
+ { "chacha20_ct", "ct", &br_chacha20_ct_run },
+
+ { "ghash_ctmul", "ctmul", &br_ghash_ctmul },
+ { "ghash_ctmul32", "ctmul32", &br_ghash_ctmul32 },
+ { "ghash_ctmul64", "ctmul64", &br_ghash_ctmul64 },
+
+ { "poly1305_ctmul", "ctmul", &br_poly1305_ctmul_run },
+ { "poly1305_ctmul32", "ctmul32", &br_poly1305_ctmul32_run },
+
+ { "ec_all_m15", "all_m15", &br_ec_all_m15 },
+ { "ec_all_m31", "all_m31", &br_ec_all_m31 },
+ { "ec_c25519_i15", "c25519_i15", &br_ec_c25519_i15 },
+ { "ec_c25519_i31", "c25519_i31", &br_ec_c25519_i31 },
+ { "ec_c25519_m15", "c25519_m15", &br_ec_c25519_m15 },
+ { "ec_c25519_m31", "c25519_m31", &br_ec_c25519_m31 },
+ { "ec_p256_m15", "p256_m15", &br_ec_p256_m15 },
+ { "ec_p256_m31", "p256_m31", &br_ec_p256_m31 },
+ { "ec_prime_i15", "prime_i15", &br_ec_prime_i15 },
+ { "ec_prime_i31", "prime_i31", &br_ec_prime_i31 },
+
+ { "ecdsa_i15_sign_asn1", "i15_asn1", &br_ecdsa_i15_sign_asn1 },
+ { "ecdsa_i15_sign_raw", "i15_raw", &br_ecdsa_i15_sign_raw },
+ { "ecdsa_i31_sign_asn1", "i31_asn1", &br_ecdsa_i31_sign_asn1 },
+ { "ecdsa_i31_sign_raw", "i31_raw", &br_ecdsa_i31_sign_raw },
+ { "ecdsa_i15_vrfy_asn1", "i15_asn1", &br_ecdsa_i15_vrfy_asn1 },
+ { "ecdsa_i15_vrfy_raw", "i15_raw", &br_ecdsa_i15_vrfy_raw },
+ { "ecdsa_i31_vrfy_asn1", "i31_asn1", &br_ecdsa_i31_vrfy_asn1 },
+ { "ecdsa_i31_vrfy_raw", "i31_raw", &br_ecdsa_i31_vrfy_raw },
+
+ { "rsa_i15_pkcs1_sign", "i15", &br_rsa_i15_pkcs1_sign },
+ { "rsa_i31_pkcs1_sign", "i31", &br_rsa_i31_pkcs1_sign },
+ { "rsa_i32_pkcs1_sign", "i32", &br_rsa_i32_pkcs1_sign },
+ { "rsa_i15_pkcs1_vrfy", "i15", &br_rsa_i15_pkcs1_vrfy },
+ { "rsa_i31_pkcs1_vrfy", "i31", &br_rsa_i31_pkcs1_vrfy },
+ { "rsa_i32_pkcs1_vrfy", "i32", &br_rsa_i32_pkcs1_vrfy },
+
+ { 0, 0, 0 }
+};
+
+static const struct {
+ const char *long_name;
+ const char *short_name;
+ const void *(*get)(void);
+} algo_names_dyn[] = {
+ { "aes_pwr8_cbcenc", "pwr8",
+ (const void *(*)(void))&br_aes_pwr8_cbcenc_get_vtable },
+ { "aes_pwr8_cbcdec", "pwr8",
+ (const void *(*)(void))&br_aes_pwr8_cbcdec_get_vtable },
+ { "aes_pwr8_ctr", "pwr8",
+ (const void *(*)(void))&br_aes_pwr8_ctr_get_vtable },
+ { "aes_pwr8_ctrcbc", "pwr8",
+ (const void *(*)(void))&br_aes_pwr8_ctrcbc_get_vtable },
+ { "aes_x86ni_cbcenc", "x86ni",
+ (const void *(*)(void))&br_aes_x86ni_cbcenc_get_vtable },
+ { "aes_x86ni_cbcdec", "x86ni",
+ (const void *(*)(void))&br_aes_x86ni_cbcdec_get_vtable },
+ { "aes_x86ni_ctr", "x86ni",
+ (const void *(*)(void))&br_aes_x86ni_ctr_get_vtable },
+ { "aes_x86ni_ctrcbc", "x86ni",
+ (const void *(*)(void))&br_aes_x86ni_ctrcbc_get_vtable },
+ { "chacha20_sse2", "sse2",
+ (const void *(*)(void))&br_chacha20_sse2_get },
+ { "ghash_pclmul", "pclmul",
+ (const void *(*)(void))&br_ghash_pclmul_get },
+ { "ghash_pwr8", "pwr8",
+ (const void *(*)(void))&br_ghash_pwr8_get },
+ { "poly1305_ctmulq", "ctmulq",
+ (const void *(*)(void))&br_poly1305_ctmulq_get },
+ { "rsa_i62_pkcs1_sign", "i62",
+ (const void *(*)(void))&br_rsa_i62_pkcs1_sign_get },
+ { "rsa_i62_pkcs1_vrfy", "i62",
+ (const void *(*)(void))&br_rsa_i62_pkcs1_vrfy_get },
+ { "ec_c25519_m62", "m62",
+ (const void *(*)(void))&br_ec_c25519_m62_get },
+ { "ec_c25519_m64", "m64",
+ (const void *(*)(void))&br_ec_c25519_m64_get },
+ { "ec_p256_m62", "m62",
+ (const void *(*)(void))&br_ec_p256_m62_get },
+ { "ec_p256_m64", "m64",
+ (const void *(*)(void))&br_ec_p256_m64_get },
+ { 0, 0, 0, }
+};
+
+/* see brssl.h */
+const char *
+get_algo_name(const void *impl, int long_name)
+{
+ size_t u;
+
+ for (u = 0; algo_names[u].long_name; u ++) {
+ if (impl == algo_names[u].impl) {
+ return long_name
+ ? algo_names[u].long_name
+ : algo_names[u].short_name;
+ }
+ }
+ for (u = 0; algo_names_dyn[u].long_name; u ++) {
+ if (impl == algo_names_dyn[u].get()) {
+ return long_name
+ ? algo_names_dyn[u].long_name
+ : algo_names_dyn[u].short_name;
+ }
+ }
+ return "UNKNOWN";
+}
+
+/* see brssl.h */
+const char *
+get_curve_name(int id)
+{
+ size_t u;
+
+ for (u = 0; curves[u].name; u ++) {
+ if (curves[u].id == id) {
+ return curves[u].name;
+ }
+ }
+ return NULL;
+}
+
+/* see brssl.h */
+int
+get_curve_name_ext(int id, char *dst, size_t len)
+{
+ const char *name;
+ char tmp[30];
+ size_t n;
+
+ name = get_curve_name(id);
+ if (name == NULL) {
+ sprintf(tmp, "unknown (%d)", id);
+ name = tmp;
+ }
+ n = 1 + strlen(name);
+ if (n > len) {
+ if (len > 0) {
+ dst[0] = 0;
+ }
+ return -1;
+ }
+ memcpy(dst, name, n);
+ return 0;
+}
+
+/* see brssl.h */
+const char *
+get_suite_name(unsigned suite)
+{
+ size_t u;
+
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if (cipher_suites[u].suite == suite) {
+ return cipher_suites[u].name;
+ }
+ }
+ return NULL;
+}
+
+/* see brssl.h */
+int
+get_suite_name_ext(unsigned suite, char *dst, size_t len)
+{
+ const char *name;
+ char tmp[30];
+ size_t n;
+
+ name = get_suite_name(suite);
+ if (name == NULL) {
+ sprintf(tmp, "unknown (0x%04X)", suite);
+ name = tmp;
+ }
+ n = 1 + strlen(name);
+ if (n > len) {
+ if (len > 0) {
+ dst[0] = 0;
+ }
+ return -1;
+ }
+ memcpy(dst, name, n);
+ return 0;
+}
+
+/* see brssl.h */
+int
+uses_ecdhe(unsigned suite)
+{
+ size_t u;
+
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if (cipher_suites[u].suite == suite) {
+ return (cipher_suites[u].req
+ & (REQ_ECDHE_RSA | REQ_ECDHE_ECDSA)) != 0;
+ }
+ }
+ return 0;
+}
+
+/* see brssl.h */
+void
+list_names(void)
+{
+ size_t u;
+
+ printf("Protocol versions:\n");
+ for (u = 0; protocol_versions[u].name; u ++) {
+ printf(" %-8s %s\n",
+ protocol_versions[u].name,
+ protocol_versions[u].comment);
+ }
+ printf("Hash functions:\n");
+ for (u = 0; hash_functions[u].name; u ++) {
+ printf(" %-8s %s\n",
+ hash_functions[u].name,
+ hash_functions[u].comment);
+ }
+ printf("Cipher suites:\n");
+ for (u = 0; cipher_suites[u].name; u ++) {
+ printf(" %s\n %s\n",
+ cipher_suites[u].name,
+ cipher_suites[u].comment);
+ }
+}
+
+/* see brssl.h */
+void
+list_curves(void)
+{
+ size_t u;
+ for (u = 0; curves[u].name; u ++) {
+ size_t v;
+
+ for (v = 0; curves[u].sid[v]; v ++) {
+ if (v == 0) {
+ printf(" ");
+ } else if (v == 1) {
+ printf(" (");
+ } else {
+ printf(", ");
+ }
+ printf("%s", curves[u].sid[v]);
+ }
+ if (v > 1) {
+ printf(")");
+ }
+ printf("\n");
+ }
+}
+
+static int
+is_ign(int c)
+{
+ if (c == 0) {
+ return 0;
+ }
+ if (c <= 32 || c == '-' || c == '_' || c == '.'
+ || c == '/' || c == '+' || c == ':')
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Get next non-ignored character, normalised:
+ * ASCII letters are converted to lowercase
+ * control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
+ * A terminating zero is returned as 0.
+ */
+static int
+next_char(const char **ps, const char *limit)
+{
+ for (;;) {
+ int c;
+
+ if (*ps == limit) {
+ return 0;
+ }
+ c = *(*ps) ++;
+ if (c == 0) {
+ return 0;
+ }
+ if (c >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ }
+ if (!is_ign(c)) {
+ return c;
+ }
+ }
+}
+
+/*
+ * Partial string equality comparison, with normalisation.
+ */
+static int
+eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
+{
+ const char *lim1, *lim2;
+
+ lim1 = s1 + s1_len;
+ lim2 = s2 + s2_len;
+ for (;;) {
+ int c1, c2;
+
+ c1 = next_char(&s1, lim1);
+ c2 = next_char(&s2, lim2);
+ if (c1 != c2) {
+ return 0;
+ }
+ if (c1 == 0) {
+ return 1;
+ }
+ }
+}
+
+/* see brssl.h */
+int
+eqstr(const char *s1, const char *s2)
+{
+ return eqstr_chunk(s1, strlen(s1), s2, strlen(s2));
+}
+
+static int
+hexval(int c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else {
+ return -1;
+ }
+}
+
+/* see brssl.h */
+size_t
+parse_size(const char *s)
+{
+ int radix;
+ size_t acc;
+ const char *t;
+
+ t = s;
+ if (t[0] == '0' && (t[1] == 'x' || t[1] == 'X')) {
+ radix = 16;
+ t += 2;
+ } else {
+ radix = 10;
+ }
+ acc = 0;
+ for (;;) {
+ int c, d;
+ size_t z;
+
+ c = *t ++;
+ if (c == 0) {
+ return acc;
+ }
+ d = hexval(c);
+ if (d < 0 || d >= radix) {
+ fprintf(stderr, "ERROR: not a valid digit: '%c'\n", c);
+ return (size_t)-1;
+ }
+ z = acc * (size_t)radix + (size_t)d;
+ if (z < (size_t)d || (z / (size_t)radix) != acc
+ || z == (size_t)-1)
+ {
+ fprintf(stderr, "ERROR: value too large: %s\n", s);
+ return (size_t)-1;
+ }
+ acc = z;
+ }
+}
+
+/*
+ * Comma-separated list enumeration. This returns a pointer to the first
+ * word in the string, skipping leading ignored characters. '*len' is
+ * set to the word length (not counting trailing ignored characters).
+ * '*str' is updated to point to immediately after the next comma, or to
+ * the terminating zero, whichever comes first.
+ *
+ * Empty words are skipped. If there is no next non-empty word, then this
+ * function returns NULL and sets *len to 0.
+ */
+static const char *
+next_word(const char **str, size_t *len)
+{
+ int c;
+ const char *begin;
+ size_t u;
+
+ /*
+ * Find next non-ignored character which is not a comma.
+ */
+ for (;;) {
+ c = **str;
+ if (c == 0) {
+ *len = 0;
+ return NULL;
+ }
+ if (!is_ign(c) && c != ',') {
+ break;
+ }
+ (*str) ++;
+ }
+
+ /*
+ * Find next comma or terminator.
+ */
+ begin = *str;
+ for (;;) {
+ c = *(*str);
+ if (c == 0 || c == ',') {
+ break;
+ }
+ (*str) ++;
+ }
+
+ /*
+ * Remove trailing ignored characters.
+ */
+ u = (size_t)(*str - begin);
+ while (u > 0 && is_ign(begin[u - 1])) {
+ u --;
+ }
+ if (c == ',') {
+ (*str) ++;
+ }
+ *len = u;
+ return begin;
+}
+
+/* see brssl.h */
+unsigned
+parse_version(const char *name, size_t len)
+{
+ size_t u;
+
+ for (u = 0;; u ++) {
+ const char *ref;
+
+ ref = protocol_versions[u].name;
+ if (ref == NULL) {
+ fprintf(stderr, "ERROR: unrecognised protocol"
+ " version name: '%s'\n", name);
+ return 0;
+ }
+ if (eqstr_chunk(ref, strlen(ref), name, len)) {
+ return protocol_versions[u].version;
+ }
+ }
+}
+
+/* see brssl.h */
+unsigned
+parse_hash_functions(const char *arg)
+{
+ unsigned r;
+
+ r = 0;
+ for (;;) {
+ const char *name;
+ size_t len;
+ size_t u;
+
+ name = next_word(&arg, &len);
+ if (name == NULL) {
+ break;
+ }
+ for (u = 0;; u ++) {
+ const char *ref;
+
+ ref = hash_functions[u].name;
+ if (ref == 0) {
+ fprintf(stderr, "ERROR: unrecognised"
+ " hash function name: '");
+ fwrite(name, 1, len, stderr);
+ fprintf(stderr, "'\n");
+ return 0;
+ }
+ if (eqstr_chunk(ref, strlen(ref), name, len)) {
+ int id;
+
+ id = (hash_functions[u].hclass->desc
+ >> BR_HASHDESC_ID_OFF)
+ & BR_HASHDESC_ID_MASK;
+ r |= (unsigned)1 << id;
+ break;
+ }
+ }
+ }
+ if (r == 0) {
+ fprintf(stderr, "ERROR: no hash function name provided\n");
+ }
+ return r;
+}
+
+/* see brssl.h */
+cipher_suite *
+parse_suites(const char *arg, size_t *num)
+{
+ VECTOR(cipher_suite) suites = VEC_INIT;
+ cipher_suite *r;
+
+ for (;;) {
+ const char *name;
+ size_t u, len;
+
+ name = next_word(&arg, &len);
+ if (name == NULL) {
+ break;
+ }
+ for (u = 0;; u ++) {
+ const char *ref;
+
+ ref = cipher_suites[u].name;
+ if (ref == NULL) {
+ fprintf(stderr, "ERROR: unrecognised"
+ " cipher suite '");
+ fwrite(name, 1, len, stderr);
+ fprintf(stderr, "'\n");
+ return 0;
+ }
+ if (eqstr_chunk(ref, strlen(ref), name, len)) {
+ VEC_ADD(suites, cipher_suites[u]);
+ break;
+ }
+ }
+ }
+ if (VEC_LEN(suites) == 0) {
+ fprintf(stderr, "ERROR: no cipher suite provided\n");
+ }
+ r = VEC_TOARRAY(suites);
+ *num = VEC_LEN(suites);
+ VEC_CLEAR(suites);
+ return r;
+}
+
+/* see brssl.h */
+const char *
+ec_curve_name(int curve)
+{
+ switch (curve) {
+ case BR_EC_sect163k1: return "sect163k1";
+ case BR_EC_sect163r1: return "sect163r1";
+ case BR_EC_sect163r2: return "sect163r2";
+ case BR_EC_sect193r1: return "sect193r1";
+ case BR_EC_sect193r2: return "sect193r2";
+ case BR_EC_sect233k1: return "sect233k1";
+ case BR_EC_sect233r1: return "sect233r1";
+ case BR_EC_sect239k1: return "sect239k1";
+ case BR_EC_sect283k1: return "sect283k1";
+ case BR_EC_sect283r1: return "sect283r1";
+ case BR_EC_sect409k1: return "sect409k1";
+ case BR_EC_sect409r1: return "sect409r1";
+ case BR_EC_sect571k1: return "sect571k1";
+ case BR_EC_sect571r1: return "sect571r1";
+ case BR_EC_secp160k1: return "secp160k1";
+ case BR_EC_secp160r1: return "secp160r1";
+ case BR_EC_secp160r2: return "secp160r2";
+ case BR_EC_secp192k1: return "secp192k1";
+ case BR_EC_secp192r1: return "secp192r1";
+ case BR_EC_secp224k1: return "secp224k1";
+ case BR_EC_secp224r1: return "secp224r1";
+ case BR_EC_secp256k1: return "secp256k1";
+ case BR_EC_secp256r1: return "secp256r1";
+ case BR_EC_secp384r1: return "secp384r1";
+ case BR_EC_secp521r1: return "secp521r1";
+ case BR_EC_brainpoolP256r1: return "brainpoolP256r1";
+ case BR_EC_brainpoolP384r1: return "brainpoolP384r1";
+ case BR_EC_brainpoolP512r1: return "brainpoolP512r1";
+ default:
+ return "unknown";
+ }
+}
+
+/* see brssl.h */
+int
+get_curve_by_name(const char *str)
+{
+ size_t u, v;
+
+ for (u = 0; curves[u].name; u ++) {
+ for (v = 0; curves[u].sid[v]; v ++) {
+ if (eqstr(curves[u].sid[v], str)) {
+ return curves[u].id;
+ }
+ }
+ }
+ return -1;
+}
+
+/* see brssl.h */
+const char *
+hash_function_name(int id)
+{
+ switch (id) {
+ case br_md5sha1_ID: return "MD5+SHA-1";
+ case br_md5_ID: return "MD5";
+ case br_sha1_ID: return "SHA-1";
+ case br_sha224_ID: return "SHA-224";
+ case br_sha256_ID: return "SHA-256";
+ case br_sha384_ID: return "SHA-384";
+ case br_sha512_ID: return "SHA-512";
+ default:
+ return "unknown";
+ }
+}
diff --git a/contrib/bearssl/tools/server.c b/contrib/bearssl/tools/server.c
new file mode 100644
index 000000000000..a97de35dee8e
--- /dev/null
+++ b/contrib/bearssl/tools/server.c
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define SOCKET int
+#define INVALID_SOCKET (-1)
+#define SOCKADDR_STORAGE struct sockaddr_storage
+#endif
+
+#include "brssl.h"
+
+static SOCKET
+host_bind(const char *host, const char *port, int verbose)
+{
+ struct addrinfo hints, *si, *p;
+ SOCKET fd;
+ int err;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ err = getaddrinfo(host, port, &hints, &si);
+ if (err != 0) {
+ fprintf(stderr, "ERROR: getaddrinfo(): %s\n",
+ gai_strerror(err));
+ return INVALID_SOCKET;
+ }
+ fd = INVALID_SOCKET;
+ for (p = si; p != NULL; p = p->ai_next) {
+ struct sockaddr *sa;
+ struct sockaddr_in sa4;
+ struct sockaddr_in6 sa6;
+ size_t sa_len;
+ void *addr;
+ int opt;
+
+ sa = (struct sockaddr *)p->ai_addr;
+ if (sa->sa_family == AF_INET) {
+ memcpy(&sa4, sa, sizeof sa4);
+ sa = (struct sockaddr *)&sa4;
+ sa_len = sizeof sa4;
+ addr = &sa4.sin_addr;
+ if (host == NULL) {
+ sa4.sin_addr.s_addr = INADDR_ANY;
+ }
+ } else if (sa->sa_family == AF_INET6) {
+ memcpy(&sa6, sa, sizeof sa6);
+ sa = (struct sockaddr *)&sa6;
+ sa_len = sizeof sa6;
+ addr = &sa6.sin6_addr;
+ if (host == NULL) {
+ sa6.sin6_addr = in6addr_any;
+ }
+ } else {
+ addr = NULL;
+ sa_len = p->ai_addrlen;
+ }
+ if (verbose) {
+ char tmp[INET6_ADDRSTRLEN + 50];
+
+ if (addr != NULL) {
+ if (!inet_ntop(p->ai_family, addr,
+ tmp, sizeof tmp))
+ {
+ strcpy(tmp, "<invalid>");
+ }
+ } else {
+ sprintf(tmp, "<unknown family: %d>",
+ (int)sa->sa_family);
+ }
+ fprintf(stderr, "binding to: %s\n", tmp);
+ }
+ fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (fd == INVALID_SOCKET) {
+ if (verbose) {
+ perror("socket()");
+ }
+ continue;
+ }
+ opt = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&opt, sizeof opt);
+#ifdef IPV6_V6ONLY
+ /*
+ * We want to make sure that the server socket works for
+ * both IPv4 and IPv6. But IPV6_V6ONLY is not defined on
+ * some very old systems.
+ */
+ opt = 0;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (void *)&opt, sizeof opt);
+#endif
+ if (bind(fd, sa, sa_len) < 0) {
+ if (verbose) {
+ perror("bind()");
+ }
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ continue;
+ }
+ break;
+ }
+ if (p == NULL) {
+ freeaddrinfo(si);
+ fprintf(stderr, "ERROR: failed to bind\n");
+ return INVALID_SOCKET;
+ }
+ freeaddrinfo(si);
+ if (listen(fd, 5) < 0) {
+ if (verbose) {
+ perror("listen()");
+ }
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ return INVALID_SOCKET;
+ }
+ if (verbose) {
+ fprintf(stderr, "bound.\n");
+ }
+ return fd;
+}
+
+static SOCKET
+accept_client(SOCKET server_fd, int verbose, int nonblock)
+{
+ int fd;
+ SOCKADDR_STORAGE sa;
+ socklen_t sa_len;
+
+ sa_len = sizeof sa;
+ fd = accept(server_fd, (struct sockaddr *)&sa, &sa_len);
+ if (fd == INVALID_SOCKET) {
+ if (verbose) {
+ perror("accept()");
+ }
+ return INVALID_SOCKET;
+ }
+ if (verbose) {
+ char tmp[INET6_ADDRSTRLEN + 50];
+ const char *name;
+
+ name = NULL;
+ switch (((struct sockaddr *)&sa)->sa_family) {
+ case AF_INET:
+ name = inet_ntop(AF_INET,
+ &((struct sockaddr_in *)&sa)->sin_addr,
+ tmp, sizeof tmp);
+ break;
+ case AF_INET6:
+ name = inet_ntop(AF_INET6,
+ &((struct sockaddr_in6 *)&sa)->sin6_addr,
+ tmp, sizeof tmp);
+ break;
+ }
+ if (name == NULL) {
+ sprintf(tmp, "<unknown: %lu>", (unsigned long)
+ ((struct sockaddr *)&sa)->sa_family);
+ name = tmp;
+ }
+ fprintf(stderr, "accepting connection from: %s\n", name);
+ }
+
+ /*
+ * We make the socket non-blocking, since we are going to use
+ * poll() or select() to organise I/O.
+ */
+ if (nonblock) {
+#ifdef _WIN32
+ u_long arg;
+
+ arg = 1;
+ ioctlsocket(fd, FIONBIO, &arg);
+#else
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+ }
+ return fd;
+}
+
+static void
+usage_server(void)
+{
+ fprintf(stderr,
+"usage: brssl server [ options ]\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+ fprintf(stderr,
+" -trace activate extra debug messages (dump of all packets)\n");
+ fprintf(stderr,
+" -b name bind to a specific address or host name\n");
+ fprintf(stderr,
+" -p port bind to a specific port (default: 4433)\n");
+ fprintf(stderr,
+" -mono use monodirectional buffering\n");
+ fprintf(stderr,
+" -buf length set the I/O buffer length (in bytes)\n");
+ fprintf(stderr,
+" -cache length set the session cache storage length (in bytes)\n");
+ fprintf(stderr,
+" -cert fname read certificate chain from file 'fname'\n");
+ fprintf(stderr,
+" -key fname read private key from file 'fname'\n");
+ fprintf(stderr,
+" -CA file add trust anchors from 'file' (for client auth)\n");
+ fprintf(stderr,
+" -anon_ok request but do not require a client certificate\n");
+ fprintf(stderr,
+" -list list supported names (protocols, algorithms...)\n");
+ fprintf(stderr,
+" -vmin name set minimum supported version (default: TLS-1.0)\n");
+ fprintf(stderr,
+" -vmax name set maximum supported version (default: TLS-1.2)\n");
+ fprintf(stderr,
+" -cs names set list of supported cipher suites (comma-separated)\n");
+ fprintf(stderr,
+" -hf names add support for some hash functions (comma-separated)\n");
+ fprintf(stderr,
+" -cbhash test hashing in policy callback\n");
+ fprintf(stderr,
+" -serverpref enforce server's preferences for cipher suites\n");
+ fprintf(stderr,
+" -noreneg prohibit renegotiations\n");
+ fprintf(stderr,
+" -alpn name add protocol name to list of protocols (ALPN extension)\n");
+ fprintf(stderr,
+" -strictalpn fail on ALPN mismatch\n");
+ exit(EXIT_FAILURE);
+}
+
+typedef struct {
+ const br_ssl_server_policy_class *vtable;
+ int verbose;
+ br_x509_certificate *chain;
+ size_t chain_len;
+ int cert_signer_algo;
+ private_key *sk;
+ int cbhash;
+} policy_context;
+
+static void
+print_hashes(unsigned chashes)
+{
+ int i;
+
+ for (i = 2; i <= 6; i ++) {
+ if ((chashes >> i) & 1) {
+ int z;
+
+ switch (i) {
+ case 3: z = 224; break;
+ case 4: z = 256; break;
+ case 5: z = 384; break;
+ case 6: z = 512; break;
+ default:
+ z = 1;
+ break;
+ }
+ fprintf(stderr, " sha%d", z);
+ }
+ }
+}
+
+static unsigned
+choose_hash(unsigned chashes)
+{
+ unsigned hash_id;
+
+ for (hash_id = 6; hash_id >= 2; hash_id --) {
+ if (((chashes >> hash_id) & 1) != 0) {
+ return hash_id;
+ }
+ }
+ /*
+ * Normally unreachable.
+ */
+ return 0;
+}
+
+static int
+sp_choose(const br_ssl_server_policy_class **pctx,
+ const br_ssl_server_context *cc,
+ br_ssl_server_choices *choices)
+{
+ policy_context *pc;
+ const br_suite_translated *st;
+ size_t u, st_num;
+ unsigned chashes;
+
+ pc = (policy_context *)pctx;
+ st = br_ssl_server_get_client_suites(cc, &st_num);
+ chashes = br_ssl_server_get_client_hashes(cc);
+ if (pc->verbose) {
+ fprintf(stderr, "Client parameters:\n");
+ fprintf(stderr, " Maximum version: ");
+ switch (cc->client_max_version) {
+ case BR_SSL30:
+ fprintf(stderr, "SSL 3.0");
+ break;
+ case BR_TLS10:
+ fprintf(stderr, "TLS 1.0");
+ break;
+ case BR_TLS11:
+ fprintf(stderr, "TLS 1.1");
+ break;
+ case BR_TLS12:
+ fprintf(stderr, "TLS 1.2");
+ break;
+ default:
+ fprintf(stderr, "unknown (0x%04X)",
+ (unsigned)cc->client_max_version);
+ break;
+ }
+ fprintf(stderr, "\n");
+ fprintf(stderr, " Compatible cipher suites:\n");
+ for (u = 0; u < st_num; u ++) {
+ char csn[80];
+
+ get_suite_name_ext(st[u][0], csn, sizeof csn);
+ fprintf(stderr, " %s\n", csn);
+ }
+ fprintf(stderr, " Common sign+hash functions:\n");
+ if ((chashes & 0xFF) != 0) {
+ fprintf(stderr, " with RSA:");
+ print_hashes(chashes);
+ fprintf(stderr, "\n");
+ }
+ if ((chashes >> 8) != 0) {
+ fprintf(stderr, " with ECDSA:");
+ print_hashes(chashes >> 8);
+ fprintf(stderr, "\n");
+ }
+ }
+ for (u = 0; u < st_num; u ++) {
+ unsigned tt;
+
+ tt = st[u][1];
+ switch (tt >> 12) {
+ case BR_SSLKEYX_RSA:
+ if (pc->sk->key_type == BR_KEYTYPE_RSA) {
+ choices->cipher_suite = st[u][0];
+ goto choose_ok;
+ }
+ break;
+ case BR_SSLKEYX_ECDHE_RSA:
+ if (pc->sk->key_type == BR_KEYTYPE_RSA) {
+ choices->cipher_suite = st[u][0];
+ if (br_ssl_engine_get_version(&cc->eng)
+ < BR_TLS12)
+ {
+ if (pc->cbhash) {
+ choices->algo_id = 0x0001;
+ } else {
+ choices->algo_id = 0xFF00;
+ }
+ } else {
+ unsigned id;
+
+ id = choose_hash(chashes);
+ if (pc->cbhash) {
+ choices->algo_id =
+ (id << 8) + 0x01;
+ } else {
+ choices->algo_id = 0xFF00 + id;
+ }
+ }
+ goto choose_ok;
+ }
+ break;
+ case BR_SSLKEYX_ECDHE_ECDSA:
+ if (pc->sk->key_type == BR_KEYTYPE_EC) {
+ choices->cipher_suite = st[u][0];
+ if (br_ssl_engine_get_version(&cc->eng)
+ < BR_TLS12)
+ {
+ if (pc->cbhash) {
+ choices->algo_id = 0x0203;
+ } else {
+ choices->algo_id =
+ 0xFF00 + br_sha1_ID;
+ }
+ } else {
+ unsigned id;
+
+ id = choose_hash(chashes >> 8);
+ if (pc->cbhash) {
+ choices->algo_id =
+ (id << 8) + 0x03;
+ } else {
+ choices->algo_id =
+ 0xFF00 + id;
+ }
+ }
+ goto choose_ok;
+ }
+ break;
+ case BR_SSLKEYX_ECDH_RSA:
+ if (pc->sk->key_type == BR_KEYTYPE_EC
+ && pc->cert_signer_algo == BR_KEYTYPE_RSA)
+ {
+ choices->cipher_suite = st[u][0];
+ goto choose_ok;
+ }
+ break;
+ case BR_SSLKEYX_ECDH_ECDSA:
+ if (pc->sk->key_type == BR_KEYTYPE_EC
+ && pc->cert_signer_algo == BR_KEYTYPE_EC)
+ {
+ choices->cipher_suite = st[u][0];
+ goto choose_ok;
+ }
+ break;
+ }
+ }
+ return 0;
+
+choose_ok:
+ choices->chain = pc->chain;
+ choices->chain_len = pc->chain_len;
+ if (pc->verbose) {
+ char csn[80];
+
+ get_suite_name_ext(choices->cipher_suite, csn, sizeof csn);
+ fprintf(stderr, "Using: %s\n", csn);
+ }
+ return 1;
+}
+
+static uint32_t
+sp_do_keyx(const br_ssl_server_policy_class **pctx,
+ unsigned char *data, size_t *len)
+{
+ policy_context *pc;
+ uint32_t r;
+ size_t xoff, xlen;
+
+ pc = (policy_context *)pctx;
+ switch (pc->sk->key_type) {
+ const br_ec_impl *iec;
+
+ case BR_KEYTYPE_RSA:
+ return br_rsa_ssl_decrypt(
+ br_rsa_private_get_default(),
+ &pc->sk->key.rsa, data, *len);
+ case BR_KEYTYPE_EC:
+ iec = br_ec_get_default();
+ r = iec->mul(data, *len, pc->sk->key.ec.x,
+ pc->sk->key.ec.xlen, pc->sk->key.ec.curve);
+ xoff = iec->xoff(pc->sk->key.ec.curve, &xlen);
+ memmove(data, data + xoff, xlen);
+ *len = xlen;
+ return r;
+ default:
+ fprintf(stderr, "ERROR: unknown private key type (%d)\n",
+ (int)pc->sk->key_type);
+ return 0;
+ }
+}
+
+static size_t
+sp_do_sign(const br_ssl_server_policy_class **pctx,
+ unsigned algo_id, unsigned char *data, size_t hv_len, size_t len)
+{
+ policy_context *pc;
+ unsigned char hv[64];
+
+ pc = (policy_context *)pctx;
+ if (algo_id >= 0xFF00) {
+ algo_id &= 0xFF;
+ memcpy(hv, data, hv_len);
+ } else {
+ const br_hash_class *hc;
+ br_hash_compat_context zc;
+
+ if (pc->verbose) {
+ fprintf(stderr, "Callback hashing, algo = 0x%04X,"
+ " data_len = %lu\n",
+ algo_id, (unsigned long)hv_len);
+ }
+ algo_id >>= 8;
+ hc = get_hash_impl(algo_id);
+ if (hc == NULL) {
+ if (pc->verbose) {
+ fprintf(stderr,
+ "ERROR: unsupported hash function %u\n",
+ algo_id);
+ }
+ return 0;
+ }
+ hc->init(&zc.vtable);
+ hc->update(&zc.vtable, data, hv_len);
+ hc->out(&zc.vtable, hv);
+ hv_len = (hc->desc >> BR_HASHDESC_OUT_OFF)
+ & BR_HASHDESC_OUT_MASK;
+ }
+ switch (pc->sk->key_type) {
+ size_t sig_len;
+ uint32_t x;
+ const unsigned char *hash_oid;
+ const br_hash_class *hc;
+
+ case BR_KEYTYPE_RSA:
+ hash_oid = get_hash_oid(algo_id);
+ if (hash_oid == NULL && algo_id != 0) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: cannot RSA-sign with"
+ " unknown hash function: %u\n",
+ algo_id);
+ }
+ return 0;
+ }
+ sig_len = (pc->sk->key.rsa.n_bitlen + 7) >> 3;
+ if (len < sig_len) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: cannot RSA-sign,"
+ " buffer is too small"
+ " (sig=%lu, buf=%lu)\n",
+ (unsigned long)sig_len,
+ (unsigned long)len);
+ }
+ return 0;
+ }
+ x = br_rsa_pkcs1_sign_get_default()(
+ hash_oid, hv, hv_len, &pc->sk->key.rsa, data);
+ if (!x) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: RSA-sign failure\n");
+ }
+ return 0;
+ }
+ return sig_len;
+
+ case BR_KEYTYPE_EC:
+ hc = get_hash_impl(algo_id);
+ if (hc == NULL) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: cannot ECDSA-sign with"
+ " unknown hash function: %u\n",
+ algo_id);
+ }
+ return 0;
+ }
+ if (len < 139) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: cannot ECDSA-sign"
+ " (output buffer = %lu)\n",
+ (unsigned long)len);
+ }
+ return 0;
+ }
+ sig_len = br_ecdsa_sign_asn1_get_default()(
+ br_ec_get_default(), hc, hv, &pc->sk->key.ec, data);
+ if (sig_len == 0) {
+ if (pc->verbose) {
+ fprintf(stderr, "ERROR: ECDSA-sign failure\n");
+ }
+ return 0;
+ }
+ return sig_len;
+
+ default:
+ return 0;
+ }
+}
+
+static const br_ssl_server_policy_class policy_vtable = {
+ sizeof(policy_context),
+ sp_choose,
+ sp_do_keyx,
+ sp_do_sign
+};
+
+void
+free_alpn(void *alpn)
+{
+ xfree(*(char **)alpn);
+}
+
+/* see brssl.h */
+int
+do_server(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int trace;
+ int i, bidi;
+ const char *bind_name;
+ const char *port;
+ unsigned vmin, vmax;
+ cipher_suite *suites;
+ size_t num_suites;
+ uint16_t *suite_ids;
+ unsigned hfuns;
+ int cbhash;
+ br_x509_certificate *chain;
+ size_t chain_len;
+ int cert_signer_algo;
+ private_key *sk;
+ anchor_list anchors = VEC_INIT;
+ VECTOR(char *) alpn_names = VEC_INIT;
+ br_x509_minimal_context xc;
+ const br_hash_class *dnhash;
+ size_t u;
+ br_ssl_server_context cc;
+ policy_context pc;
+ br_ssl_session_cache_lru lru;
+ unsigned char *iobuf, *cache;
+ size_t iobuf_len, cache_len;
+ uint32_t flags;
+ SOCKET server_fd, fd;
+
+ retcode = 0;
+ verbose = 1;
+ trace = 0;
+ bind_name = NULL;
+ port = NULL;
+ bidi = 1;
+ vmin = 0;
+ vmax = 0;
+ suites = NULL;
+ num_suites = 0;
+ hfuns = 0;
+ cbhash = 0;
+ suite_ids = NULL;
+ chain = NULL;
+ chain_len = 0;
+ sk = NULL;
+ iobuf = NULL;
+ iobuf_len = 0;
+ cache = NULL;
+ cache_len = (size_t)-1;
+ flags = 0;
+ server_fd = INVALID_SOCKET;
+ fd = INVALID_SOCKET;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ usage_server();
+ goto server_exit_error;
+ }
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else if (eqstr(arg, "-trace")) {
+ trace = 1;
+ } else if (eqstr(arg, "-b")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-b'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ if (bind_name != NULL) {
+ fprintf(stderr, "ERROR: duplicate bind host\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ bind_name = argv[i];
+ } else if (eqstr(arg, "-p")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-p'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ if (port != NULL) {
+ fprintf(stderr, "ERROR: duplicate bind port\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ port = argv[i];
+ } else if (eqstr(arg, "-mono")) {
+ bidi = 0;
+ } else if (eqstr(arg, "-buf")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-buf'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (iobuf_len != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate I/O buffer length\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ iobuf_len = parse_size(arg);
+ if (iobuf_len == (size_t)-1) {
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-cache")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cache'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (cache_len != (size_t)-1) {
+ fprintf(stderr, "ERROR: duplicate session"
+ " cache length\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ cache_len = parse_size(arg);
+ if (cache_len == (size_t)-1) {
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-cert")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cert'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ if (chain != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate certificate chain\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ chain = read_certificates(arg, &chain_len);
+ if (chain == NULL || chain_len == 0) {
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-key")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-key'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ if (sk != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate private key\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ sk = read_private_key(arg);
+ if (sk == NULL) {
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-CA")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-CA'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (read_trust_anchors(&anchors, arg) == 0) {
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-anon_ok")) {
+ flags |= BR_OPT_TOLERATE_NO_CLIENT_AUTH;
+ } else if (eqstr(arg, "-list")) {
+ list_names();
+ goto server_exit;
+ } else if (eqstr(arg, "-vmin")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmin'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (vmin != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate minimum version\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ vmin = parse_version(arg, strlen(arg));
+ if (vmin == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-vmax")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmax'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (vmax != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate maximum version\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ vmax = parse_version(arg, strlen(arg));
+ if (vmax == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-cs")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cs'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ if (suites != NULL) {
+ fprintf(stderr, "ERROR: duplicate list"
+ " of cipher suites\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ suites = parse_suites(arg, &num_suites);
+ if (suites == NULL) {
+ usage_server();
+ goto server_exit_error;
+ }
+ } else if (eqstr(arg, "-hf")) {
+ unsigned x;
+
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-hf'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ arg = argv[i];
+ x = parse_hash_functions(arg);
+ if (x == 0) {
+ usage_server();
+ goto server_exit_error;
+ }
+ hfuns |= x;
+ } else if (eqstr(arg, "-cbhash")) {
+ cbhash = 1;
+ } else if (eqstr(arg, "-serverpref")) {
+ flags |= BR_OPT_ENFORCE_SERVER_PREFERENCES;
+ } else if (eqstr(arg, "-noreneg")) {
+ flags |= BR_OPT_NO_RENEGOTIATION;
+ } else if (eqstr(arg, "-alpn")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-alpn'\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ VEC_ADD(alpn_names, xstrdup(argv[i]));
+ } else if (eqstr(arg, "-strictalpn")) {
+ flags |= BR_OPT_FAIL_ON_ALPN_MISMATCH;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_server();
+ goto server_exit_error;
+ }
+ }
+ if (port == NULL) {
+ port = "4433";
+ }
+ if (vmin == 0) {
+ vmin = BR_TLS10;
+ }
+ if (vmax == 0) {
+ vmax = BR_TLS12;
+ }
+ if (vmax < vmin) {
+ fprintf(stderr, "ERROR: impossible minimum/maximum protocol"
+ " version combination\n");
+ usage_server();
+ goto server_exit_error;
+ }
+ if (suites == NULL) {
+ num_suites = 0;
+
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ num_suites ++;
+ }
+ }
+ suites = xmalloc(num_suites * sizeof *suites);
+ num_suites = 0;
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ suites[num_suites ++] = cipher_suites[u];
+ }
+ }
+ }
+ if (hfuns == 0) {
+ hfuns = (unsigned)-1;
+ }
+ if (chain == NULL || chain_len == 0) {
+ fprintf(stderr, "ERROR: no certificate chain provided\n");
+ goto server_exit_error;
+ }
+ if (sk == NULL) {
+ fprintf(stderr, "ERROR: no private key provided\n");
+ goto server_exit_error;
+ }
+ switch (sk->key_type) {
+ int curve;
+ uint32_t supp;
+
+ case BR_KEYTYPE_RSA:
+ break;
+ case BR_KEYTYPE_EC:
+ curve = sk->key.ec.curve;
+ supp = br_ec_get_default()->supported_curves;
+ if (curve > 31 || !((supp >> curve) & 1)) {
+ fprintf(stderr, "ERROR: private key curve (%d)"
+ " is not supported\n", curve);
+ goto server_exit_error;
+ }
+ break;
+ default:
+ fprintf(stderr, "ERROR: unsupported private key type (%d)\n",
+ sk->key_type);
+ break;
+ }
+ cert_signer_algo = get_cert_signer_algo(chain);
+ if (cert_signer_algo == 0) {
+ goto server_exit_error;
+ }
+ if (verbose) {
+ const char *csas;
+
+ switch (cert_signer_algo) {
+ case BR_KEYTYPE_RSA: csas = "RSA"; break;
+ case BR_KEYTYPE_EC: csas = "EC"; break;
+ default:
+ csas = "unknown";
+ break;
+ }
+ fprintf(stderr, "Issuing CA key type: %d (%s)\n",
+ cert_signer_algo, csas);
+ }
+ if (iobuf_len == 0) {
+ if (bidi) {
+ iobuf_len = BR_SSL_BUFSIZE_BIDI;
+ } else {
+ iobuf_len = BR_SSL_BUFSIZE_MONO;
+ }
+ }
+ iobuf = xmalloc(iobuf_len);
+ if (cache_len == (size_t)-1) {
+ cache_len = 5000;
+ }
+ cache = xmalloc(cache_len);
+
+ /*
+ * Compute implementation requirements and inject implementations.
+ */
+ suite_ids = xmalloc(num_suites * sizeof *suite_ids);
+ br_ssl_server_zero(&cc);
+ br_ssl_engine_set_versions(&cc.eng, vmin, vmax);
+ br_ssl_engine_set_all_flags(&cc.eng, flags);
+ if (vmin <= BR_TLS11) {
+ if (!(hfuns & (1 << br_md5_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need MD5\n");
+ goto server_exit_error;
+ }
+ if (!(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need SHA-1\n");
+ goto server_exit_error;
+ }
+ }
+ for (u = 0; u < num_suites; u ++) {
+ unsigned req;
+
+ req = suites[u].req;
+ suite_ids[u] = suites[u].suite;
+ if ((req & REQ_TLS12) != 0 && vmax < BR_TLS12) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires TLS 1.2\n",
+ suites[u].name);
+ goto server_exit_error;
+ }
+ if ((req & REQ_SHA1) != 0 && !(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-1\n",
+ suites[u].name);
+ goto server_exit_error;
+ }
+ if ((req & REQ_SHA256) != 0 && !(hfuns & (1 << br_sha256_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-256\n",
+ suites[u].name);
+ goto server_exit_error;
+ }
+ if ((req & REQ_SHA384) != 0 && !(hfuns & (1 << br_sha384_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-384\n",
+ suites[u].name);
+ goto server_exit_error;
+ }
+ /* TODO: algorithm implementation selection */
+ if ((req & REQ_AESCBC) != 0) {
+ br_ssl_engine_set_default_aes_cbc(&cc.eng);
+ }
+ if ((req & REQ_AESCCM) != 0) {
+ br_ssl_engine_set_default_aes_ccm(&cc.eng);
+ }
+ if ((req & REQ_AESGCM) != 0) {
+ br_ssl_engine_set_default_aes_gcm(&cc.eng);
+ }
+ if ((req & REQ_CHAPOL) != 0) {
+ br_ssl_engine_set_default_chapol(&cc.eng);
+ }
+ if ((req & REQ_3DESCBC) != 0) {
+ br_ssl_engine_set_default_des_cbc(&cc.eng);
+ }
+ if ((req & (REQ_ECDHE_RSA | REQ_ECDHE_ECDSA)) != 0) {
+ br_ssl_engine_set_default_ec(&cc.eng);
+ }
+ }
+ br_ssl_engine_set_suites(&cc.eng, suite_ids, num_suites);
+
+ dnhash = NULL;
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ dnhash = hc;
+ br_ssl_engine_set_hash(&cc.eng, id, hc);
+ }
+ }
+ if (vmin <= BR_TLS11) {
+ br_ssl_engine_set_prf10(&cc.eng, &br_tls10_prf);
+ }
+ if (vmax >= BR_TLS12) {
+ if ((hfuns & ((unsigned)1 << br_sha256_ID)) != 0) {
+ br_ssl_engine_set_prf_sha256(&cc.eng,
+ &br_tls12_sha256_prf);
+ }
+ if ((hfuns & ((unsigned)1 << br_sha384_ID)) != 0) {
+ br_ssl_engine_set_prf_sha384(&cc.eng,
+ &br_tls12_sha384_prf);
+ }
+ }
+
+ br_ssl_session_cache_lru_init(&lru, cache, cache_len);
+ br_ssl_server_set_cache(&cc, &lru.vtable);
+
+ if (VEC_LEN(alpn_names) != 0) {
+ br_ssl_engine_set_protocol_names(&cc.eng,
+ (const char **)&VEC_ELT(alpn_names, 0),
+ VEC_LEN(alpn_names));
+ }
+
+ /*
+ * Set the policy handler (that chooses the actual cipher suite,
+ * selects the certificate chain, and runs the private key
+ * operations).
+ */
+ pc.vtable = &policy_vtable;
+ pc.verbose = verbose;
+ pc.chain = chain;
+ pc.chain_len = chain_len;
+ pc.cert_signer_algo = cert_signer_algo;
+ pc.sk = sk;
+ pc.cbhash = cbhash;
+ br_ssl_server_set_policy(&cc, &pc.vtable);
+
+ /*
+ * If trust anchors have been configured, then set an X.509
+ * validation engine and activate client certificate
+ * authentication.
+ */
+ if (VEC_LEN(anchors) != 0) {
+ br_x509_minimal_init(&xc, dnhash,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF)
+ & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ br_x509_minimal_set_hash(&xc, id, hc);
+ }
+ }
+ br_ssl_engine_set_default_rsavrfy(&cc.eng);
+ br_ssl_engine_set_default_ecdsa(&cc.eng);
+ br_x509_minimal_set_rsa(&xc, br_rsa_pkcs1_vrfy_get_default());
+ br_x509_minimal_set_ecdsa(&xc,
+ br_ec_get_default(), br_ecdsa_vrfy_asn1_get_default());
+ br_ssl_engine_set_x509(&cc.eng, &xc.vtable);
+ br_ssl_server_set_trust_anchor_names_alt(&cc,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ }
+
+ br_ssl_engine_set_buffer(&cc.eng, iobuf, iobuf_len, bidi);
+
+ /*
+ * On Unix systems, we need to ignore SIGPIPE.
+ */
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Open the server socket.
+ */
+ server_fd = host_bind(bind_name, port, verbose);
+ if (server_fd == INVALID_SOCKET) {
+ goto server_exit_error;
+ }
+
+ /*
+ * Process incoming clients, one at a time. Note that we do not
+ * accept any client until the previous connection has finished:
+ * this is voluntary, since the tool uses stdin/stdout for
+ * application data, and thus cannot really run two connections
+ * simultaneously.
+ */
+ for (;;) {
+ int x;
+ unsigned run_flags;
+
+ fd = accept_client(server_fd, verbose, 1);
+ if (fd == INVALID_SOCKET) {
+ goto server_exit_error;
+ }
+ br_ssl_server_reset(&cc);
+ run_flags = (verbose ? RUN_ENGINE_VERBOSE : 0)
+ | (trace ? RUN_ENGINE_TRACE : 0);
+ x = run_ssl_engine(&cc.eng, fd, run_flags);
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ fd = INVALID_SOCKET;
+ if (x < -1) {
+ goto server_exit_error;
+ }
+ }
+
+ /*
+ * Release allocated structures.
+ */
+server_exit:
+ xfree(suites);
+ xfree(suite_ids);
+ free_certificates(chain, chain_len);
+ free_private_key(sk);
+ VEC_CLEAREXT(anchors, &free_ta_contents);
+ VEC_CLEAREXT(alpn_names, &free_alpn);
+ xfree(iobuf);
+ xfree(cache);
+ if (fd != INVALID_SOCKET) {
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ }
+ if (server_fd != INVALID_SOCKET) {
+#ifdef _WIN32
+ closesocket(server_fd);
+#else
+ close(server_fd);
+#endif
+ }
+ return retcode;
+
+server_exit_error:
+ retcode = -1;
+ goto server_exit;
+}
diff --git a/contrib/bearssl/tools/skey.c b/contrib/bearssl/tools/skey.c
new file mode 100644
index 000000000000..90ecf636d61c
--- /dev/null
+++ b/contrib/bearssl/tools/skey.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+typedef struct {
+ int print_text;
+ int print_C;
+ const char *rawder;
+ const char *rawpem;
+ const char *pk8der;
+ const char *pk8pem;
+} outspec;
+
+static void
+print_int_text(const char *name, const unsigned char *buf, size_t len)
+{
+ size_t u;
+
+ printf("%s = ", name);
+ for (u = 0; u < len; u ++) {
+ printf("%02X", buf[u]);
+ }
+ printf("\n");
+}
+
+static void
+print_int_C(const char *name, const unsigned char *buf, size_t len)
+{
+ size_t u;
+
+ printf("\nstatic const unsigned char %s[] = {", name);
+ for (u = 0; u < len; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", buf[u]);
+ }
+ printf("\n};\n");
+}
+
+static int
+write_to_file(const char *name, const void *data, size_t len)
+{
+ FILE *f;
+
+ f = fopen(name, "wb");
+ if (f == NULL) {
+ fprintf(stderr,
+ "ERROR: cannot open file '%s' for writing\n",
+ name);
+ return 0;
+ }
+ if (fwrite(data, 1, len, f) != len) {
+ fclose(f);
+ fprintf(stderr,
+ "ERROR: cannot write to file '%s'\n",
+ name);
+ return 0;
+ }
+ fclose(f);
+ return 1;
+}
+
+static int
+write_to_pem_file(const char *name,
+ const void *data, size_t len, const char *banner)
+{
+ void *pem;
+ size_t pemlen;
+ int r;
+
+ pemlen = br_pem_encode(NULL, NULL, len, banner, 0);
+ pem = xmalloc(pemlen + 1);
+ br_pem_encode(pem, data, len, banner, 0);
+ r = write_to_file(name, pem, pemlen);
+ xfree(pem);
+ return r;
+}
+
+static int
+print_rsa(const br_rsa_private_key *sk, outspec *os)
+{
+ int ret;
+ unsigned char *n, *d, *buf;
+ uint32_t e;
+ size_t nlen, dlen, len;
+ br_rsa_compute_modulus cm;
+ br_rsa_compute_pubexp ce;
+ br_rsa_compute_privexp cd;
+ br_rsa_public_key pk;
+ unsigned char ebuf[4];
+
+ n = NULL;
+ d = NULL;
+ buf = NULL;
+ ret = 1;
+ if (os->print_text) {
+ print_int_text("p ", sk->p, sk->plen);
+ print_int_text("q ", sk->q, sk->qlen);
+ print_int_text("dp", sk->dp, sk->dplen);
+ print_int_text("dq", sk->dq, sk->dqlen);
+ print_int_text("iq", sk->iq, sk->iqlen);
+ }
+ if (os->print_C) {
+ print_int_C("RSA_P", sk->p, sk->plen);
+ print_int_C("RSA_Q", sk->q, sk->qlen);
+ print_int_C("RSA_DP", sk->dp, sk->dplen);
+ print_int_C("RSA_DQ", sk->dq, sk->dqlen);
+ print_int_C("RSA_IQ", sk->iq, sk->iqlen);
+ printf("\nstatic const br_rsa_private_key RSA = {\n");
+ printf("\t%lu,\n", (unsigned long)sk->n_bitlen);
+ printf("\t(unsigned char *)RSA_P, sizeof RSA_P,\n");
+ printf("\t(unsigned char *)RSA_Q, sizeof RSA_Q,\n");
+ printf("\t(unsigned char *)RSA_DP, sizeof RSA_DP,\n");
+ printf("\t(unsigned char *)RSA_DQ, sizeof RSA_DQ,\n");
+ printf("\t(unsigned char *)RSA_IQ, sizeof RSA_IQ\n");
+ printf("};\n");
+ }
+
+ if (os->rawder == NULL && os->rawpem == NULL
+ && os->pk8der == NULL && os->pk8pem == NULL)
+ {
+ return ret;
+ }
+
+ cm = br_rsa_compute_modulus_get_default();
+ ce = br_rsa_compute_pubexp_get_default();
+ cd = br_rsa_compute_privexp_get_default();
+ nlen = cm(NULL, sk);
+ if (nlen == 0) {
+ goto print_RSA_error;
+ }
+ n = xmalloc(nlen);
+ if (cm(n, sk) != nlen) {
+ goto print_RSA_error;
+ }
+ e = ce(sk);
+ if (e == 0) {
+ goto print_RSA_error;
+ }
+ dlen = cd(NULL, sk, e);
+ if (dlen == 0) {
+ goto print_RSA_error;
+ }
+ d = xmalloc(dlen);
+ if (cd(d, sk, e) != dlen) {
+ goto print_RSA_error;
+ }
+ ebuf[0] = e >> 24;
+ ebuf[1] = e >> 16;
+ ebuf[2] = e >> 8;
+ ebuf[3] = e;
+ pk.n = n;
+ pk.nlen = nlen;
+ pk.e = ebuf;
+ pk.elen = sizeof ebuf;
+
+ if (os->rawder != NULL || os->rawpem != NULL) {
+ len = br_encode_rsa_raw_der(NULL, sk, &pk, d, dlen);
+ if (len == 0) {
+ goto print_RSA_error;
+ }
+ buf = xmalloc(len);
+ if (br_encode_rsa_raw_der(buf, sk, &pk, d, dlen) != len) {
+ goto print_RSA_error;
+ }
+ if (os->rawder != NULL) {
+ ret &= write_to_file(os->rawder, buf, len);
+ }
+ if (os->rawpem != NULL) {
+ ret &= write_to_pem_file(os->rawpem,
+ buf, len, "RSA PRIVATE KEY");
+ }
+ xfree(buf);
+ buf = NULL;
+ }
+
+ if (os->pk8der != NULL || os->pk8pem != NULL) {
+ len = br_encode_rsa_pkcs8_der(NULL, sk, &pk, d, dlen);
+ if (len == 0) {
+ goto print_RSA_error;
+ }
+ buf = xmalloc(len);
+ if (br_encode_rsa_pkcs8_der(buf, sk, &pk, d, dlen) != len) {
+ goto print_RSA_error;
+ }
+ if (os->pk8der != NULL) {
+ ret &= write_to_file(os->pk8der, buf, len);
+ }
+ if (os->pk8pem != NULL) {
+ ret &= write_to_pem_file(os->pk8pem,
+ buf, len, "PRIVATE KEY");
+ }
+ xfree(buf);
+ buf = NULL;
+ }
+
+print_RSA_exit:
+ xfree(n);
+ xfree(d);
+ xfree(buf);
+ return ret;
+
+print_RSA_error:
+ fprintf(stderr, "ERROR: cannot encode RSA key\n");
+ ret = 0;
+ goto print_RSA_exit;
+}
+
+static int
+print_ec(const br_ec_private_key *sk, outspec *os)
+{
+ br_ec_public_key pk;
+ unsigned kbuf[BR_EC_KBUF_PUB_MAX_SIZE];
+ unsigned char *buf;
+ size_t len;
+ int r;
+
+ if (os->print_text) {
+ print_int_text("x", sk->x, sk->xlen);
+ }
+ if (os->print_C) {
+ print_int_C("EC_X", sk->x, sk->xlen);
+ printf("\nstatic const br_ec_private_key EC = {\n");
+ printf("\t%d,\n", sk->curve);
+ printf("\t(unsigned char *)EC_X, sizeof EC_X\n");
+ printf("};\n");
+ }
+
+ if (os->rawder == NULL && os->rawpem == NULL
+ && os->pk8der == NULL && os->pk8pem == NULL)
+ {
+ return 1;
+ }
+ if (br_ec_compute_pub(br_ec_get_default(), &pk, kbuf, sk) == 0) {
+ fprintf(stderr,
+ "ERROR: cannot re-encode (unsupported curve)\n");
+ return 0;
+ }
+
+ r = 1;
+ if (os->rawder != NULL || os->rawpem != NULL) {
+ len = br_encode_ec_raw_der(NULL, sk, &pk);
+ if (len == 0) {
+ fprintf(stderr, "ERROR: cannot re-encode"
+ " (unsupported curve)\n");
+ return 0;
+ }
+ buf = xmalloc(len);
+ if (br_encode_ec_raw_der(buf, sk, &pk) != len) {
+ fprintf(stderr, "ERROR: re-encode failure\n");
+ xfree(buf);
+ return 0;
+ }
+ if (os->rawder != NULL) {
+ r &= write_to_file(os->rawder, buf, len);
+ }
+ if (os->rawpem != NULL) {
+ r &= write_to_pem_file(os->rawpem,
+ buf, len, "EC PRIVATE KEY");
+ }
+ xfree(buf);
+ }
+ if (os->pk8der != NULL || os->pk8pem != NULL) {
+ len = br_encode_ec_pkcs8_der(NULL, sk, &pk);
+ if (len == 0) {
+ fprintf(stderr, "ERROR: cannot re-encode"
+ " (unsupported curve)\n");
+ return 0;
+ }
+ buf = xmalloc(len);
+ if (br_encode_ec_pkcs8_der(buf, sk, &pk) != len) {
+ fprintf(stderr, "ERROR: re-encode failure\n");
+ xfree(buf);
+ return 0;
+ }
+ if (os->pk8der != NULL) {
+ r &= write_to_file(os->pk8der, buf, len);
+ }
+ if (os->pk8pem != NULL) {
+ r &= write_to_pem_file(os->pk8pem,
+ buf, len, "PRIVATE KEY");
+ }
+ xfree(buf);
+ }
+ return r;
+}
+
+static int
+parse_rsa_spec(const char *kgen_spec, unsigned *size, uint32_t *pubexp)
+{
+ const char *p;
+ char *end;
+ unsigned long ul;
+
+ p = kgen_spec;
+ if (*p != 'r' && *p != 'R') {
+ return 0;
+ }
+ p ++;
+ if (*p != 's' && *p != 'S') {
+ return 0;
+ }
+ p ++;
+ if (*p != 'a' && *p != 'A') {
+ return 0;
+ }
+ p ++;
+ if (*p == 0) {
+ *size = 2048;
+ *pubexp = 3;
+ return 1;
+ } else if (*p != ':') {
+ return 0;
+ }
+ p ++;
+ ul = strtoul(p, &end, 10);
+ if (ul < 512 || ul > 32768) {
+ return 0;
+ }
+ *size = ul;
+ p = end;
+ if (*p == 0) {
+ *pubexp = 3;
+ return 1;
+ } else if (*p != ':') {
+ return 0;
+ }
+ p ++;
+ ul = strtoul(p, &end, 10);
+ if ((ul & 1) == 0 || ul == 1 || ((ul >> 30) >> 2) != 0) {
+ return 0;
+ }
+ *pubexp = ul;
+ if (*end != 0) {
+ return 0;
+ }
+ return 1;
+}
+
+static int
+keygen_rsa(unsigned size, uint32_t pubexp, outspec *os)
+{
+ br_hmac_drbg_context rng;
+ br_prng_seeder seeder;
+ br_rsa_keygen kg;
+ br_rsa_private_key sk;
+ unsigned char *kbuf_priv;
+ uint32_t r;
+
+ seeder = br_prng_seeder_system(NULL);
+ if (seeder == 0) {
+ fprintf(stderr, "ERROR: no system source of randomness\n");
+ return 0;
+ }
+ br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0);
+ if (!seeder(&rng.vtable)) {
+ fprintf(stderr, "ERROR: system source of randomness failed\n");
+ return 0;
+ }
+ kbuf_priv = xmalloc(BR_RSA_KBUF_PRIV_SIZE(size));
+ kg = br_rsa_keygen_get_default();
+ r = kg(&rng.vtable, &sk, kbuf_priv, NULL, NULL, size, pubexp);
+ if (!r) {
+ fprintf(stderr, "ERROR: RSA key pair generation failed\n");
+ } else {
+ r = print_rsa(&sk, os);
+ }
+ xfree(kbuf_priv);
+ return r;
+}
+
+static int
+parse_ec_spec(const char *kgen_spec, int *curve)
+{
+ const char *p;
+
+ *curve = 0;
+ p = kgen_spec;
+ if (*p != 'e' && *p != 'E') {
+ return 0;
+ }
+ p ++;
+ if (*p != 'c' && *p != 'C') {
+ return 0;
+ }
+ p ++;
+ if (*p == 0) {
+ *curve = BR_EC_secp256r1;
+ return 1;
+ }
+ if (*p != ':') {
+ return 0;
+ }
+ *curve = get_curve_by_name(p);
+ return *curve > 0;
+}
+
+static int
+keygen_ec(int curve, outspec *os)
+{
+ br_hmac_drbg_context rng;
+ br_prng_seeder seeder;
+ const br_ec_impl *impl;
+ br_ec_private_key sk;
+ unsigned char kbuf_priv[BR_EC_KBUF_PRIV_MAX_SIZE];
+ size_t len;
+
+ seeder = br_prng_seeder_system(NULL);
+ if (seeder == 0) {
+ fprintf(stderr, "ERROR: no system source of randomness\n");
+ return 0;
+ }
+ br_hmac_drbg_init(&rng, &br_sha256_vtable, NULL, 0);
+ if (!seeder(&rng.vtable)) {
+ fprintf(stderr, "ERROR: system source of randomness failed\n");
+ return 0;
+ }
+ impl = br_ec_get_default();
+ len = br_ec_keygen(&rng.vtable, impl, &sk, kbuf_priv, curve);
+ if (len == 0) {
+ fprintf(stderr, "ERROR: curve is not supported\n");
+ return 0;
+ }
+ return print_ec(&sk, os);
+}
+
+static int
+decode_key(const unsigned char *buf, size_t len, outspec *os)
+{
+ br_skey_decoder_context dc;
+ int err, ret;
+
+ br_skey_decoder_init(&dc);
+ br_skey_decoder_push(&dc, buf, len);
+ err = br_skey_decoder_last_error(&dc);
+ if (err != 0) {
+ const char *errname, *errmsg;
+
+ fprintf(stderr, "ERROR (decoding): err=%d\n", err);
+ errname = find_error_name(err, &errmsg);
+ if (errname != NULL) {
+ fprintf(stderr, " %s: %s\n", errname, errmsg);
+ } else {
+ fprintf(stderr, " (unknown)\n");
+ }
+ return 0;
+ }
+ ret = 1;
+ switch (br_skey_decoder_key_type(&dc)) {
+ const br_rsa_private_key *rk;
+ const br_ec_private_key *ek;
+
+ case BR_KEYTYPE_RSA:
+ rk = br_skey_decoder_get_rsa(&dc);
+ printf("RSA key (%lu bits)\n", (unsigned long)rk->n_bitlen);
+ ret = print_rsa(rk, os);
+ break;
+
+ case BR_KEYTYPE_EC:
+ ek = br_skey_decoder_get_ec(&dc);
+ printf("EC key (curve = %d: %s)\n",
+ ek->curve, ec_curve_name(ek->curve));
+ ret = print_ec(ek, os);
+ break;
+
+ default:
+ fprintf(stderr, "Unknown key type: %d\n",
+ br_skey_decoder_key_type(&dc));
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+usage_skey(void)
+{
+ fprintf(stderr,
+"usage: brssl skey [ options ] file...\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+ fprintf(stderr,
+" -text print private key details (human-readable)\n");
+ fprintf(stderr,
+" -C print private key details (C code)\n");
+ fprintf(stderr,
+" -rawder file save private key in 'file' (raw format, DER)\n");
+ fprintf(stderr,
+" -rawpem file save private key in 'file' (raw format, PEM)\n");
+ fprintf(stderr,
+" -pk8der file save private key in 'file' (PKCS#8 format, DER)\n");
+ fprintf(stderr,
+" -pk8pem file save private key in 'file' (PKCS#8 format, PEM)\n");
+ fprintf(stderr,
+" -gen spec generate a new key using the provided key specification\n");
+ fprintf(stderr,
+" -list list known elliptic curve names\n");
+ fprintf(stderr,
+"Key specification begins with a key type, followed by optional parameters\n");
+ fprintf(stderr,
+"that depend on the key type, separated by colon characters:\n");
+ fprintf(stderr,
+" rsa[:size[:pubexep]] RSA key (defaults: size = 2048, pubexp = 3)\n");
+ fprintf(stderr,
+" ec[:curvename] EC key (default curve: secp256r1)\n");
+}
+
+/* see brssl.h */
+int
+do_skey(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int i, num_files;
+ outspec os;
+ unsigned char *buf;
+ size_t len;
+ pem_object *pos;
+ const char *kgen_spec;
+
+ retcode = 0;
+ verbose = 1;
+ os.print_text = 0;
+ os.print_C = 0;
+ os.rawder = NULL;
+ os.rawpem = NULL;
+ os.pk8der = NULL;
+ os.pk8pem = NULL;
+ num_files = 0;
+ buf = NULL;
+ pos = NULL;
+ kgen_spec = NULL;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ num_files ++;
+ continue;
+ }
+ argv[i] = NULL;
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else if (eqstr(arg, "-text")) {
+ os.print_text = 1;
+ } else if (eqstr(arg, "-C")) {
+ os.print_C = 1;
+ } else if (eqstr(arg, "-rawder")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-rawder'\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ if (os.rawder != NULL) {
+ fprintf(stderr,
+ "ERROR: multiple '-rawder' options\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ os.rawder = argv[i];
+ argv[i] = NULL;
+ } else if (eqstr(arg, "-rawpem")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-rawpem'\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ if (os.rawpem != NULL) {
+ fprintf(stderr,
+ "ERROR: multiple '-rawpem' options\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ os.rawpem = argv[i];
+ argv[i] = NULL;
+ } else if (eqstr(arg, "-pk8der")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-pk8der'\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ if (os.pk8der != NULL) {
+ fprintf(stderr,
+ "ERROR: multiple '-pk8der' options\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ os.pk8der = argv[i];
+ argv[i] = NULL;
+ } else if (eqstr(arg, "-pk8pem")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-pk8pem'\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ if (os.pk8pem != NULL) {
+ fprintf(stderr,
+ "ERROR: multiple '-pk8pem' options\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ os.pk8pem = argv[i];
+ argv[i] = NULL;
+ } else if (eqstr(arg, "-gen")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-gen'\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ if (kgen_spec != NULL) {
+ fprintf(stderr,
+ "ERROR: multiple '-gen' options\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+ kgen_spec = argv[i];
+ argv[i] = NULL;
+ } else if (eqstr(arg, "-list")) {
+ list_curves();
+ goto skey_exit;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_skey();
+ goto skey_exit_error;
+ }
+ }
+ if (kgen_spec != NULL) {
+ unsigned rsa_size;
+ uint32_t rsa_pubexp;
+ int curve;
+
+ if (num_files != 0) {
+ fprintf(stderr,
+ "ERROR: key files provided while generating\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+
+ if (parse_rsa_spec(kgen_spec, &rsa_size, &rsa_pubexp)) {
+ if (!keygen_rsa(rsa_size, rsa_pubexp, &os)) {
+ goto skey_exit_error;
+ }
+ } else if (parse_ec_spec(kgen_spec, &curve)) {
+ if (!keygen_ec(curve, &os)) {
+ goto skey_exit_error;
+ }
+ } else {
+ fprintf(stderr,
+ "ERROR: unknown key specification: '%s'\n",
+ kgen_spec);
+ usage_skey();
+ goto skey_exit_error;
+ }
+ } else if (num_files == 0) {
+ fprintf(stderr, "ERROR: no private key provided\n");
+ usage_skey();
+ goto skey_exit_error;
+ }
+
+ for (i = 0; i < argc; i ++) {
+ const char *fname;
+
+ fname = argv[i];
+ if (fname == NULL) {
+ continue;
+ }
+ buf = read_file(fname, &len);
+ if (buf == NULL) {
+ goto skey_exit_error;
+ }
+ if (looks_like_DER(buf, len)) {
+ if (verbose) {
+ fprintf(stderr, "File '%s': ASN.1/DER object\n",
+ fname);
+ }
+ if (!decode_key(buf, len, &os)) {
+ goto skey_exit_error;
+ }
+ } else {
+ size_t u, num;
+
+ if (verbose) {
+ fprintf(stderr, "File '%s': decoding as PEM\n",
+ fname);
+ }
+ pos = decode_pem(buf, len, &num);
+ if (pos == NULL) {
+ goto skey_exit_error;
+ }
+ for (u = 0; pos[u].name; u ++) {
+ const char *name;
+
+ name = pos[u].name;
+ if (eqstr(name, "RSA PRIVATE KEY")
+ || eqstr(name, "EC PRIVATE KEY")
+ || eqstr(name, "PRIVATE KEY"))
+ {
+ if (!decode_key(pos[u].data,
+ pos[u].data_len, &os))
+ {
+ goto skey_exit_error;
+ }
+ } else {
+ if (verbose) {
+ fprintf(stderr,
+ "(skipping '%s')\n",
+ name);
+ }
+ }
+ }
+ for (u = 0; pos[u].name; u ++) {
+ free_pem_object_contents(&pos[u]);
+ }
+ xfree(pos);
+ pos = NULL;
+ }
+ xfree(buf);
+ buf = NULL;
+ }
+
+ /*
+ * Release allocated structures.
+ */
+skey_exit:
+ xfree(buf);
+ if (pos != NULL) {
+ size_t u;
+
+ for (u = 0; pos[u].name; u ++) {
+ free_pem_object_contents(&pos[u]);
+ }
+ xfree(pos);
+ }
+ return retcode;
+
+skey_exit_error:
+ retcode = -1;
+ goto skey_exit;
+}
diff --git a/contrib/bearssl/tools/sslio.c b/contrib/bearssl/tools/sslio.c
new file mode 100644
index 000000000000..ef7dd3f6765a
--- /dev/null
+++ b/contrib/bearssl/tools/sslio.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#define SOCKET int
+#define INVALID_SOCKET (-1)
+#endif
+
+#include "brssl.h"
+
+static void
+dump_blob(const char *name, const void *data, size_t len)
+{
+ const unsigned char *buf;
+ size_t u;
+
+ buf = data;
+ fprintf(stderr, "%s (len = %lu)", name, (unsigned long)len);
+ for (u = 0; u < len; u ++) {
+ if ((u & 15) == 0) {
+ fprintf(stderr, "\n%08lX ", (unsigned long)u);
+ } else if ((u & 7) == 0) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, " %02x", buf[u]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Inspect the provided data in case it is a "command" to trigger a
+ * special behaviour. If the command is recognised, then it is executed
+ * and this function returns 1. Otherwise, this function returns 0.
+ */
+static int
+run_command(br_ssl_engine_context *cc, unsigned char *buf, size_t len)
+{
+ /*
+ * A single static slot for saving session parameters.
+ */
+ static br_ssl_session_parameters slot;
+ static int slot_used = 0;
+
+ size_t u;
+
+ if (len < 2 || len > 3) {
+ return 0;
+ }
+ if (len == 3 && (buf[1] != '\r' || buf[2] != '\n')) {
+ return 0;
+ }
+ if (len == 2 && buf[1] != '\n') {
+ return 0;
+ }
+ switch (buf[0]) {
+ case 'Q':
+ fprintf(stderr, "closing...\n");
+ br_ssl_engine_close(cc);
+ return 1;
+ case 'R':
+ if (br_ssl_engine_renegotiate(cc)) {
+ fprintf(stderr, "renegotiating...\n");
+ } else {
+ fprintf(stderr, "not renegotiating.\n");
+ }
+ return 1;
+ case 'F':
+ /*
+ * Session forget is nominally client-only. But the
+ * session parameters are in the engine structure, which
+ * is the first field of the client context, so the cast
+ * still works properly. On the server, this forgetting
+ * has no effect.
+ */
+ fprintf(stderr, "forgetting session...\n");
+ br_ssl_client_forget_session((br_ssl_client_context *)cc);
+ return 1;
+ case 'S':
+ fprintf(stderr, "saving session parameters...\n");
+ br_ssl_engine_get_session_parameters(cc, &slot);
+ fprintf(stderr, " id = ");
+ for (u = 0; u < slot.session_id_len; u ++) {
+ fprintf(stderr, "%02X", slot.session_id[u]);
+ }
+ fprintf(stderr, "\n");
+ slot_used = 1;
+ return 1;
+ case 'P':
+ if (slot_used) {
+ fprintf(stderr, "restoring session parameters...\n");
+ fprintf(stderr, " id = ");
+ for (u = 0; u < slot.session_id_len; u ++) {
+ fprintf(stderr, "%02X", slot.session_id[u]);
+ }
+ fprintf(stderr, "\n");
+ br_ssl_engine_set_session_parameters(cc, &slot);
+ return 1;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+#ifdef _WIN32
+
+typedef struct {
+ unsigned char buf[1024];
+ size_t ptr, len;
+} in_buffer;
+
+static int
+in_return_bytes(in_buffer *bb, unsigned char *buf, size_t len)
+{
+ if (bb->ptr < bb->len) {
+ size_t clen;
+
+ if (buf == NULL) {
+ return 1;
+ }
+ clen = bb->len - bb->ptr;
+ if (clen > len) {
+ clen = len;
+ }
+ memcpy(buf, bb->buf + bb->ptr, clen);
+ bb->ptr += clen;
+ if (bb->ptr == bb->len) {
+ bb->ptr = bb->len = 0;
+ }
+ return (int)clen;
+ }
+ return 0;
+}
+
+/*
+ * A buffered version of in_read(), using a buffer to return only
+ * full lines when feasible.
+ */
+static int
+in_read_buffered(HANDLE h_in, in_buffer *bb, unsigned char *buf, size_t len)
+{
+ int n;
+
+ if (len == 0) {
+ return 0;
+ }
+ n = in_return_bytes(bb, buf, len);
+ if (n != 0) {
+ return n;
+ }
+ for (;;) {
+ INPUT_RECORD inrec;
+ DWORD v;
+
+ if (!PeekConsoleInput(h_in, &inrec, 1, &v)) {
+ fprintf(stderr, "ERROR: PeekConsoleInput()"
+ " failed with 0x%08lX\n",
+ (unsigned long)GetLastError());
+ return -1;
+ }
+ if (v == 0) {
+ return 0;
+ }
+ if (!ReadConsoleInput(h_in, &inrec, 1, &v)) {
+ fprintf(stderr, "ERROR: ReadConsoleInput()"
+ " failed with 0x%08lX\n",
+ (unsigned long)GetLastError());
+ return -1;
+ }
+ if (v == 0) {
+ return 0;
+ }
+ if (inrec.EventType == KEY_EVENT
+ && inrec.Event.KeyEvent.bKeyDown)
+ {
+ int c;
+
+ c = inrec.Event.KeyEvent.uChar.AsciiChar;
+ if (c == '\n' || c == '\r' || c == '\t'
+ || (c >= 32 && c != 127))
+ {
+ if (c == '\r') {
+ c = '\n';
+ }
+ bb->buf[bb->ptr ++] = (unsigned char)c;
+ printf("%c", c);
+ fflush(stdout);
+ bb->len = bb->ptr;
+ if (bb->len == sizeof bb->buf || c == '\n') {
+ bb->ptr = 0;
+ return in_return_bytes(bb, buf, len);
+ }
+ }
+ }
+ }
+}
+
+static int
+in_avail_buffered(HANDLE h_in, in_buffer *bb)
+{
+ return in_read_buffered(h_in, bb, NULL, 1);
+}
+
+#endif
+
+/* see brssl.h */
+int
+run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags)
+{
+ int hsdetails;
+ int retcode;
+ int verbose;
+ int trace;
+#ifdef _WIN32
+ WSAEVENT fd_event;
+ int can_send, can_recv;
+ HANDLE h_in, h_out;
+ in_buffer bb;
+#endif
+
+ hsdetails = 0;
+ retcode = 0;
+ verbose = (flags & RUN_ENGINE_VERBOSE) != 0;
+ trace = (flags & RUN_ENGINE_TRACE) != 0;
+
+ /*
+ * Print algorithm details.
+ */
+ if (verbose) {
+ const char *rngname;
+
+ fprintf(stderr, "Algorithms:\n");
+ br_prng_seeder_system(&rngname);
+ fprintf(stderr, " RNG: %s\n", rngname);
+ if (cc->iaes_cbcenc != 0) {
+ fprintf(stderr, " AES/CBC (enc): %s\n",
+ get_algo_name(cc->iaes_cbcenc, 0));
+ }
+ if (cc->iaes_cbcdec != 0) {
+ fprintf(stderr, " AES/CBC (dec): %s\n",
+ get_algo_name(cc->iaes_cbcdec, 0));
+ }
+ if (cc->iaes_ctr != 0) {
+ fprintf(stderr, " AES/CTR: %s\n",
+ get_algo_name(cc->iaes_cbcdec, 0));
+ }
+ if (cc->iaes_ctrcbc != 0) {
+ fprintf(stderr, " AES/CCM: %s\n",
+ get_algo_name(cc->iaes_ctrcbc, 0));
+ }
+ if (cc->ides_cbcenc != 0) {
+ fprintf(stderr, " DES/CBC (enc): %s\n",
+ get_algo_name(cc->ides_cbcenc, 0));
+ }
+ if (cc->ides_cbcdec != 0) {
+ fprintf(stderr, " DES/CBC (dec): %s\n",
+ get_algo_name(cc->ides_cbcdec, 0));
+ }
+ if (cc->ighash != 0) {
+ fprintf(stderr, " GHASH (GCM): %s\n",
+ get_algo_name(cc->ighash, 0));
+ }
+ if (cc->ichacha != 0) {
+ fprintf(stderr, " ChaCha20: %s\n",
+ get_algo_name(cc->ichacha, 0));
+ }
+ if (cc->ipoly != 0) {
+ fprintf(stderr, " Poly1305: %s\n",
+ get_algo_name(cc->ipoly, 0));
+ }
+ if (cc->iec != 0) {
+ fprintf(stderr, " EC: %s\n",
+ get_algo_name(cc->iec, 0));
+ }
+ if (cc->iecdsa != 0) {
+ fprintf(stderr, " ECDSA: %s\n",
+ get_algo_name(cc->iecdsa, 0));
+ }
+ if (cc->irsavrfy != 0) {
+ fprintf(stderr, " RSA (vrfy): %s\n",
+ get_algo_name(cc->irsavrfy, 0));
+ }
+ }
+
+#ifdef _WIN32
+ fd_event = WSA_INVALID_EVENT;
+ can_send = 0;
+ can_recv = 0;
+ bb.ptr = bb.len = 0;
+#endif
+
+ /*
+ * On Unix systems, we need to follow three descriptors:
+ * standard input (0), standard output (1), and the socket
+ * itself (for both read and write). This is done with a poll()
+ * call.
+ *
+ * On Windows systems, we use WSAEventSelect() to associate
+ * an event handle with the network activity, and we use
+ * WaitForMultipleObjectsEx() on that handle and the standard
+ * input handle, when appropriate. Standard output is assumed
+ * to be always writeable, and standard input to be the console;
+ * this does not work well (or at all) with redirections (to
+ * pipes or files) but it should be enough for a debug tool
+ * (TODO: make something that handles redirections as well).
+ */
+
+#ifdef _WIN32
+ fd_event = WSACreateEvent();
+ if (fd_event == WSA_INVALID_EVENT) {
+ fprintf(stderr, "ERROR: WSACreateEvent() failed with %d\n",
+ WSAGetLastError());
+ retcode = -2;
+ goto engine_exit;
+ }
+ WSAEventSelect(fd, fd_event, FD_READ | FD_WRITE | FD_CLOSE);
+ h_in = GetStdHandle(STD_INPUT_HANDLE);
+ h_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleMode(h_in, ENABLE_ECHO_INPUT
+ | ENABLE_LINE_INPUT
+ | ENABLE_PROCESSED_INPUT
+ | ENABLE_PROCESSED_OUTPUT
+ | ENABLE_WRAP_AT_EOL_OUTPUT);
+#else
+ /*
+ * Make sure that stdin and stdout are non-blocking.
+ */
+ fcntl(0, F_SETFL, O_NONBLOCK);
+ fcntl(1, F_SETFL, O_NONBLOCK);
+#endif
+
+ /*
+ * Perform the loop.
+ */
+ for (;;) {
+ unsigned st;
+ int sendrec, recvrec, sendapp, recvapp;
+#ifdef _WIN32
+ HANDLE pfd[2];
+ DWORD wt;
+#else
+ struct pollfd pfd[3];
+ int n;
+#endif
+ size_t u, k_fd, k_in, k_out;
+ int sendrec_ok, recvrec_ok, sendapp_ok, recvapp_ok;
+
+ /*
+ * Get current engine state.
+ */
+ st = br_ssl_engine_current_state(cc);
+ if (st == BR_SSL_CLOSED) {
+ int err;
+
+ err = br_ssl_engine_last_error(cc);
+ if (err == BR_ERR_OK) {
+ if (verbose) {
+ fprintf(stderr,
+ "SSL closed normally\n");
+ }
+ retcode = 0;
+ goto engine_exit;
+ } else {
+ fprintf(stderr, "ERROR: SSL error %d", err);
+ retcode = err;
+ if (err >= BR_ERR_SEND_FATAL_ALERT) {
+ err -= BR_ERR_SEND_FATAL_ALERT;
+ fprintf(stderr,
+ " (sent alert %d)\n", err);
+ } else if (err >= BR_ERR_RECV_FATAL_ALERT) {
+ err -= BR_ERR_RECV_FATAL_ALERT;
+ fprintf(stderr,
+ " (received alert %d)\n", err);
+ } else {
+ const char *ename;
+
+ ename = find_error_name(err, NULL);
+ if (ename == NULL) {
+ ename = "unknown";
+ }
+ fprintf(stderr, " (%s)\n", ename);
+ }
+ goto engine_exit;
+ }
+ }
+
+ /*
+ * Compute descriptors that must be polled, depending
+ * on engine state.
+ */
+ sendrec = ((st & BR_SSL_SENDREC) != 0);
+ recvrec = ((st & BR_SSL_RECVREC) != 0);
+ sendapp = ((st & BR_SSL_SENDAPP) != 0);
+ recvapp = ((st & BR_SSL_RECVAPP) != 0);
+ if (verbose && sendapp && !hsdetails) {
+ char csn[80];
+ const char *pname;
+
+ fprintf(stderr, "Handshake completed\n");
+ fprintf(stderr, " version: ");
+ switch (cc->session.version) {
+ case BR_SSL30:
+ fprintf(stderr, "SSL 3.0");
+ break;
+ case BR_TLS10:
+ fprintf(stderr, "TLS 1.0");
+ break;
+ case BR_TLS11:
+ fprintf(stderr, "TLS 1.1");
+ break;
+ case BR_TLS12:
+ fprintf(stderr, "TLS 1.2");
+ break;
+ default:
+ fprintf(stderr, "unknown (0x%04X)",
+ (unsigned)cc->session.version);
+ break;
+ }
+ fprintf(stderr, "\n");
+ get_suite_name_ext(
+ cc->session.cipher_suite, csn, sizeof csn);
+ fprintf(stderr, " cipher suite: %s\n", csn);
+ if (uses_ecdhe(cc->session.cipher_suite)) {
+ get_curve_name_ext(
+ br_ssl_engine_get_ecdhe_curve(cc),
+ csn, sizeof csn);
+ fprintf(stderr,
+ " ECDHE curve: %s\n", csn);
+ }
+ fprintf(stderr, " secure renegotiation: %s\n",
+ cc->reneg == 1 ? "no" : "yes");
+ pname = br_ssl_engine_get_selected_protocol(cc);
+ if (pname != NULL) {
+ fprintf(stderr,
+ " protocol name (ALPN): %s\n",
+ pname);
+ }
+ hsdetails = 1;
+ }
+
+ k_fd = (size_t)-1;
+ k_in = (size_t)-1;
+ k_out = (size_t)-1;
+
+ u = 0;
+#ifdef _WIN32
+ /*
+ * If we recorded that we can send or receive data, and we
+ * want to do exactly that, then we don't wait; we just do
+ * it.
+ */
+ recvapp_ok = 0;
+ sendrec_ok = 0;
+ recvrec_ok = 0;
+ sendapp_ok = 0;
+
+ if (sendrec && can_send) {
+ sendrec_ok = 1;
+ } else if (recvrec && can_recv) {
+ recvrec_ok = 1;
+ } else if (recvapp) {
+ recvapp_ok = 1;
+ } else if (sendapp && in_avail_buffered(h_in, &bb)) {
+ sendapp_ok = 1;
+ } else {
+ /*
+ * If we cannot do I/O right away, then we must
+ * wait for some event, and try again.
+ */
+ pfd[u] = (HANDLE)fd_event;
+ k_fd = u;
+ u ++;
+ if (sendapp) {
+ pfd[u] = h_in;
+ k_in = u;
+ u ++;
+ }
+ wt = WaitForMultipleObjectsEx(u, pfd,
+ FALSE, INFINITE, FALSE);
+ if (wt == WAIT_FAILED) {
+ fprintf(stderr, "ERROR:"
+ " WaitForMultipleObjectsEx()"
+ " failed with 0x%08lX",
+ (unsigned long)GetLastError());
+ retcode = -2;
+ goto engine_exit;
+ }
+ if (wt == k_fd) {
+ WSANETWORKEVENTS e;
+
+ if (WSAEnumNetworkEvents(fd, fd_event, &e)) {
+ fprintf(stderr, "ERROR:"
+ " WSAEnumNetworkEvents()"
+ " failed with %d\n",
+ WSAGetLastError());
+ retcode = -2;
+ goto engine_exit;
+ }
+ if (e.lNetworkEvents & (FD_WRITE | FD_CLOSE)) {
+ can_send = 1;
+ }
+ if (e.lNetworkEvents & (FD_READ | FD_CLOSE)) {
+ can_recv = 1;
+ }
+ }
+ continue;
+ }
+#else
+ if (sendrec || recvrec) {
+ pfd[u].fd = fd;
+ pfd[u].revents = 0;
+ pfd[u].events = 0;
+ if (sendrec) {
+ pfd[u].events |= POLLOUT;
+ }
+ if (recvrec) {
+ pfd[u].events |= POLLIN;
+ }
+ k_fd = u;
+ u ++;
+ }
+ if (sendapp) {
+ pfd[u].fd = 0;
+ pfd[u].revents = 0;
+ pfd[u].events = POLLIN;
+ k_in = u;
+ u ++;
+ }
+ if (recvapp) {
+ pfd[u].fd = 1;
+ pfd[u].revents = 0;
+ pfd[u].events = POLLOUT;
+ k_out = u;
+ u ++;
+ }
+ n = poll(pfd, u, -1);
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("ERROR: poll()");
+ retcode = -2;
+ goto engine_exit;
+ }
+ if (n == 0) {
+ continue;
+ }
+
+ /*
+ * We transform closures/errors into read+write accesses
+ * so as to force the read() or write() call that will
+ * detect the situation.
+ */
+ while (u -- > 0) {
+ if (pfd[u].revents & (POLLERR | POLLHUP)) {
+ pfd[u].revents |= POLLIN | POLLOUT;
+ }
+ }
+
+ recvapp_ok = recvapp && (pfd[k_out].revents & POLLOUT) != 0;
+ sendrec_ok = sendrec && (pfd[k_fd].revents & POLLOUT) != 0;
+ recvrec_ok = recvrec && (pfd[k_fd].revents & POLLIN) != 0;
+ sendapp_ok = sendapp && (pfd[k_in].revents & POLLIN) != 0;
+#endif
+
+ /*
+ * We give preference to outgoing data, on stdout and on
+ * the socket.
+ */
+ if (recvapp_ok) {
+ unsigned char *buf;
+ size_t len;
+#ifdef _WIN32
+ DWORD wlen;
+#else
+ ssize_t wlen;
+#endif
+
+ buf = br_ssl_engine_recvapp_buf(cc, &len);
+#ifdef _WIN32
+ if (!WriteFile(h_out, buf, len, &wlen, NULL)) {
+ if (verbose) {
+ fprintf(stderr, "stdout closed...\n");
+ }
+ retcode = -2;
+ goto engine_exit;
+ }
+#else
+ wlen = write(1, buf, len);
+ if (wlen <= 0) {
+ if (verbose) {
+ fprintf(stderr, "stdout closed...\n");
+ }
+ retcode = -2;
+ goto engine_exit;
+ }
+#endif
+ br_ssl_engine_recvapp_ack(cc, wlen);
+ continue;
+ }
+ if (sendrec_ok) {
+ unsigned char *buf;
+ size_t len;
+ int wlen;
+
+ buf = br_ssl_engine_sendrec_buf(cc, &len);
+ wlen = send(fd, buf, len, 0);
+ if (wlen <= 0) {
+#ifdef _WIN32
+ int err;
+
+ err = WSAGetLastError();
+ if (err == EWOULDBLOCK
+ || err == WSAEWOULDBLOCK)
+ {
+ can_send = 0;
+ continue;
+ }
+#else
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ continue;
+ }
+#endif
+ if (verbose) {
+ fprintf(stderr, "socket closed...\n");
+ }
+ retcode = -1;
+ goto engine_exit;
+ }
+ if (trace) {
+ dump_blob("Outgoing bytes", buf, wlen);
+ }
+ br_ssl_engine_sendrec_ack(cc, wlen);
+ continue;
+ }
+ if (recvrec_ok) {
+ unsigned char *buf;
+ size_t len;
+ int rlen;
+
+ buf = br_ssl_engine_recvrec_buf(cc, &len);
+ rlen = recv(fd, buf, len, 0);
+ if (rlen == 0) {
+ if (verbose) {
+ fprintf(stderr, "socket closed...\n");
+ }
+ retcode = -1;
+ goto engine_exit;
+ }
+ if (rlen < 0) {
+#ifdef _WIN32
+ int err;
+
+ err = WSAGetLastError();
+ if (err == EWOULDBLOCK
+ || err == WSAEWOULDBLOCK)
+ {
+ can_recv = 0;
+ continue;
+ }
+#else
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ continue;
+ }
+#endif
+ if (verbose) {
+ fprintf(stderr, "socket broke...\n");
+ }
+ retcode = -1;
+ goto engine_exit;
+ }
+ if (trace) {
+ dump_blob("Incoming bytes", buf, rlen);
+ }
+ br_ssl_engine_recvrec_ack(cc, rlen);
+ continue;
+ }
+ if (sendapp_ok) {
+ unsigned char *buf;
+ size_t len;
+#ifdef _WIN32
+ int rlen;
+#else
+ ssize_t rlen;
+#endif
+
+ buf = br_ssl_engine_sendapp_buf(cc, &len);
+#ifdef _WIN32
+ rlen = in_read_buffered(h_in, &bb, buf, len);
+#else
+ rlen = read(0, buf, len);
+#endif
+ if (rlen <= 0) {
+ if (verbose) {
+ fprintf(stderr, "stdin closed...\n");
+ }
+ br_ssl_engine_close(cc);
+ } else if (!run_command(cc, buf, rlen)) {
+ br_ssl_engine_sendapp_ack(cc, rlen);
+ }
+ br_ssl_engine_flush(cc, 0);
+ continue;
+ }
+
+ /* We should never reach that point. */
+ fprintf(stderr, "ERROR: poll() misbehaves\n");
+ retcode = -2;
+ goto engine_exit;
+ }
+
+ /*
+ * Release allocated structures.
+ */
+engine_exit:
+#ifdef _WIN32
+ if (fd_event != WSA_INVALID_EVENT) {
+ WSACloseEvent(fd_event);
+ }
+#endif
+ return retcode;
+}
diff --git a/contrib/bearssl/tools/ta.c b/contrib/bearssl/tools/ta.c
new file mode 100644
index 000000000000..df72e2bf57ce
--- /dev/null
+++ b/contrib/bearssl/tools/ta.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static const char *
+curve_to_sym(int curve)
+{
+ switch (curve) {
+ case BR_EC_sect163k1: return "BR_EC_sect163k1";
+ case BR_EC_sect163r1: return "BR_EC_sect163r1";
+ case BR_EC_sect163r2: return "BR_EC_sect163r2";
+ case BR_EC_sect193r1: return "BR_EC_sect193r1";
+ case BR_EC_sect193r2: return "BR_EC_sect193r2";
+ case BR_EC_sect233k1: return "BR_EC_sect233k1";
+ case BR_EC_sect233r1: return "BR_EC_sect233r1";
+ case BR_EC_sect239k1: return "BR_EC_sect239k1";
+ case BR_EC_sect283k1: return "BR_EC_sect283k1";
+ case BR_EC_sect283r1: return "BR_EC_sect283r1";
+ case BR_EC_sect409k1: return "BR_EC_sect409k1";
+ case BR_EC_sect409r1: return "BR_EC_sect409r1";
+ case BR_EC_sect571k1: return "BR_EC_sect571k1";
+ case BR_EC_sect571r1: return "BR_EC_sect571r1";
+ case BR_EC_secp160k1: return "BR_EC_secp160k1";
+ case BR_EC_secp160r1: return "BR_EC_secp160r1";
+ case BR_EC_secp160r2: return "BR_EC_secp160r2";
+ case BR_EC_secp192k1: return "BR_EC_secp192k1";
+ case BR_EC_secp192r1: return "BR_EC_secp192r1";
+ case BR_EC_secp224k1: return "BR_EC_secp224k1";
+ case BR_EC_secp224r1: return "BR_EC_secp224r1";
+ case BR_EC_secp256k1: return "BR_EC_secp256k1";
+ case BR_EC_secp256r1: return "BR_EC_secp256r1";
+ case BR_EC_secp384r1: return "BR_EC_secp384r1";
+ case BR_EC_secp521r1: return "BR_EC_secp521r1";
+ case BR_EC_brainpoolP256r1: return "BR_EC_brainpoolP256r1";
+ case BR_EC_brainpoolP384r1: return "BR_EC_brainpoolP384r1";
+ case BR_EC_brainpoolP512r1: return "BR_EC_brainpoolP512r1";
+ }
+ return NULL;
+}
+
+static void
+print_blob(const char *name, const unsigned char *buf, size_t len)
+{
+ size_t u;
+
+ printf("\nstatic const unsigned char %s[] = {", name);
+ for (u = 0; u < len; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", buf[u]);
+ }
+ printf("\n};\n");
+}
+
+static int
+print_ta_internals(br_x509_trust_anchor *ta, long ctr)
+{
+ char tmp[25];
+
+ sprintf(tmp, "TA%ld_DN", ctr);
+ print_blob(tmp, ta->dn.data, ta->dn.len);
+ switch (ta->pkey.key_type) {
+ case BR_KEYTYPE_RSA:
+ sprintf(tmp, "TA%ld_RSA_N", ctr);
+ print_blob(tmp, ta->pkey.key.rsa.n, ta->pkey.key.rsa.nlen);
+ sprintf(tmp, "TA%ld_RSA_E", ctr);
+ print_blob(tmp, ta->pkey.key.rsa.e, ta->pkey.key.rsa.elen);
+ break;
+ case BR_KEYTYPE_EC:
+ sprintf(tmp, "TA%ld_EC_Q", ctr);
+ print_blob(tmp, ta->pkey.key.ec.q, ta->pkey.key.ec.qlen);
+ break;
+ default:
+ fprintf(stderr, "ERROR: unknown anchor key type '%d'\n",
+ ta->pkey.key_type);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+print_ta(br_x509_trust_anchor *ta, long ctr)
+{
+ char tmp[25];
+
+ printf("\t{\n");
+ printf("\t\t{ (unsigned char *)TA%ld_DN, sizeof TA%ld_DN },\n",
+ ctr, ctr);
+ printf("\t\t%s,\n", (ta->flags & BR_X509_TA_CA)
+ ? "BR_X509_TA_CA" : "0");
+ printf("\t\t{\n");
+ switch (ta->pkey.key_type) {
+ const char *cname;
+
+ case BR_KEYTYPE_RSA:
+ printf("\t\t\tBR_KEYTYPE_RSA,\n");
+ printf("\t\t\t{ .rsa = {\n");
+ printf("\t\t\t\t(unsigned char *)TA%ld_RSA_N,"
+ " sizeof TA%ld_RSA_N,\n", ctr, ctr);
+ printf("\t\t\t\t(unsigned char *)TA%ld_RSA_E,"
+ " sizeof TA%ld_RSA_E,\n", ctr, ctr);
+ printf("\t\t\t} }\n");
+ break;
+ case BR_KEYTYPE_EC:
+ printf("\t\t\tBR_KEYTYPE_EC,\n");
+ printf("\t\t\t{ .ec = {\n");
+ cname = curve_to_sym(ta->pkey.key.ec.curve);
+ if (cname == NULL) {
+ sprintf(tmp, "%d", ta->pkey.key.ec.curve);
+ cname = tmp;
+ }
+ printf("\t\t\t\t%s,\n", cname);
+ printf("\t\t\t\t(unsigned char *)TA%ld_EC_Q,"
+ " sizeof TA%ld_EC_Q,\n", ctr, ctr);
+ printf("\t\t\t} }\n");
+ }
+ printf("\t\t}\n");
+ printf("\t}");
+}
+
+static void
+usage_ta(void)
+{
+ fprintf(stderr,
+"usage: brssl ta [ options ] file...\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+}
+
+/* see brssl.h */
+int
+do_ta(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int i, num_files;
+ anchor_list tas = VEC_INIT;
+ size_t u, num;
+
+ retcode = 0;
+ verbose = 1;
+ num_files = 0;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ num_files ++;
+ continue;
+ }
+ argv[i] = NULL;
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_ta();
+ goto ta_exit_error;
+ }
+ }
+ if (num_files == 0) {
+ fprintf(stderr, "ERROR: no certificate file provided\n");
+ usage_ta();
+ goto ta_exit_error;
+ }
+
+ for (i = 0; i < argc; i ++) {
+ const char *fname;
+ size_t len1, len2;
+
+ fname = argv[i];
+ if (fname == NULL) {
+ continue;
+ }
+ if (verbose) {
+ fprintf(stderr, "Reading file '%s': ", fname);
+ fflush(stderr);
+ }
+ len1 = VEC_LEN(tas);
+ if (read_trust_anchors(&tas, fname) == 0) {
+ goto ta_exit_error;
+ }
+ len2 = VEC_LEN(tas) - len1;
+ if (verbose) {
+ fprintf(stderr, "%lu trust anchor%s\n",
+ (unsigned long)len2, len2 > 1 ? "s" : "");
+ }
+ }
+ num = VEC_LEN(tas);
+ for (u = 0; u < num; u ++) {
+ if (print_ta_internals(&VEC_ELT(tas, u), u) < 0) {
+ goto ta_exit_error;
+ }
+ }
+ printf("\nstatic const br_x509_trust_anchor TAs[%ld] = {", (long)num);
+ for (u = 0; u < num; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ printf("\n");
+ print_ta(&VEC_ELT(tas, u), u);
+ }
+ printf("\n};\n");
+ printf("\n#define TAs_NUM %ld\n", (long)num);
+
+ /*
+ * Release allocated structures.
+ */
+ta_exit:
+ VEC_CLEAREXT(tas, free_ta_contents);
+ return retcode;
+
+ta_exit_error:
+ retcode = -1;
+ goto ta_exit;
+}
diff --git a/contrib/bearssl/tools/twrch.c b/contrib/bearssl/tools/twrch.c
new file mode 100644
index 000000000000..9cce03e504cc
--- /dev/null
+++ b/contrib/bearssl/tools/twrch.c
@@ -0,0 +1,1069 @@
+/*
+ * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+#include "brssl.h"
+
+static int verbose = 0;
+
+static void
+usage_twrch(void)
+{
+ fprintf(stderr,
+"usage: brssl twrch [ options ]\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -trace dump all packets on stderr\n");
+ fprintf(stderr,
+" -v verbose error messages on stderr\n");
+ fprintf(stderr,
+" -server act as an SSL server\n");
+ fprintf(stderr,
+" -client act as an SSL client\n");
+ fprintf(stderr,
+" -sni name use specified name for SNI\n");
+ fprintf(stderr,
+" -mono use monodirectional buffering\n");
+ fprintf(stderr,
+" -buf length set the I/O buffer length (in bytes)\n");
+ fprintf(stderr,
+" -cache length set the session cache storage length (in bytes)\n");
+ fprintf(stderr,
+" -cert fname read certificate chain from file 'fname'\n");
+ fprintf(stderr,
+" -key fname read private key from file 'fname'\n");
+ fprintf(stderr,
+" -CA file add trust anchors from 'file' (for peer auth)\n");
+ fprintf(stderr,
+" -anon_ok request but do not require a client certificate\n");
+ fprintf(stderr,
+" -nostaticecdh prohibit full-static ECDH (client only)\n");
+ fprintf(stderr,
+" -list list supported names (protocols, algorithms...)\n");
+ fprintf(stderr,
+" -vmin name set minimum supported version (default: TLS-1.0)\n");
+ fprintf(stderr,
+" -vmax name set maximum supported version (default: TLS-1.2)\n");
+ fprintf(stderr,
+" -cs names set list of supported cipher suites (comma-separated)\n");
+ fprintf(stderr,
+" -hf names add support for some hash functions (comma-separated)\n");
+ fprintf(stderr,
+" -minhello len set minimum ClientHello length (in bytes)\n");
+ fprintf(stderr,
+" -serverpref enforce server's preferences for cipher suites\n");
+ fprintf(stderr,
+" -noreneg prohibit renegotiations\n");
+ fprintf(stderr,
+" -alpn name add protocol name to list of protocols (ALPN extension)\n");
+ fprintf(stderr,
+" -strictalpn fail on ALPN mismatch\n");
+}
+
+static void
+free_alpn(void *alpn)
+{
+ xfree(*(char **)alpn);
+}
+
+static void
+dump_blob(const char *name, const void *data, size_t len)
+{
+ const unsigned char *buf;
+ size_t u;
+
+ buf = data;
+ fprintf(stderr, "%s (len = %lu)", name, (unsigned long)len);
+ for (u = 0; u < len; u ++) {
+ if ((u & 15) == 0) {
+ fprintf(stderr, "\n%08lX ", (unsigned long)u);
+ } else if ((u & 7) == 0) {
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, " %02x", buf[u]);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Callback for reading bytes from standard input.
+ */
+static int
+stdin_read(void *ctx, unsigned char *buf, size_t len)
+{
+ for (;;) {
+#ifdef _WIN32
+ DWORD rlen;
+#else
+ ssize_t rlen;
+#endif
+ int eof;
+
+#ifdef _WIN32
+ eof = !ReadFile(GetStdHandle(STD_INPUT_HANDLE),
+ buf, len, &rlen, NULL) || rlen == 0;
+#else
+ rlen = read(0, buf, len);
+ if (rlen <= 0) {
+ if (rlen < 0 && errno == EINTR) {
+ continue;
+ }
+ eof = 1;
+ } else {
+ eof = 0;
+ }
+#endif
+ if (eof) {
+ if (*(int *)ctx) {
+ if (verbose) {
+ fprintf(stderr, "recv: EOF\n");
+ }
+ }
+ return -1;
+ }
+ if (*(int *)ctx) {
+ dump_blob("recv", buf, (size_t)rlen);
+ }
+ return (int)rlen;
+ }
+}
+
+/*
+ * Callback for writing bytes on standard output.
+ */
+static int
+stdout_write(void *ctx, const unsigned char *buf, size_t len)
+{
+ for (;;) {
+#ifdef _WIN32
+ DWORD wlen;
+#else
+ ssize_t wlen;
+#endif
+ int eof;
+
+#ifdef _WIN32
+ eof = !WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+ buf, len, &wlen, NULL);
+#else
+ wlen = write(1, buf, len);
+ if (wlen <= 0) {
+ if (wlen < 0 && errno == EINTR) {
+ continue;
+ }
+ eof = 1;
+ } else {
+ eof = 0;
+ }
+#endif
+ if (eof) {
+ if (*(int *)ctx) {
+ if (verbose) {
+ fprintf(stderr, "send: EOF\n");
+ }
+ }
+ return -1;
+ }
+ if (*(int *)ctx) {
+ dump_blob("send", buf, (size_t)wlen);
+ }
+ return (int)wlen;
+ }
+}
+
+static void
+print_error(int err)
+{
+ const char *name, *comment;
+
+ name = find_error_name(err, &comment);
+ if (name != NULL) {
+ fprintf(stderr, "ERR %d: %s\n %s\n", err, name, comment);
+ return;
+ }
+ if (err >= BR_ERR_RECV_FATAL_ALERT
+ && err < BR_ERR_RECV_FATAL_ALERT + 256)
+ {
+ fprintf(stderr, "ERR %d: received fatal alert %d\n",
+ err, err - BR_ERR_RECV_FATAL_ALERT);
+ return;
+ }
+ if (err >= BR_ERR_SEND_FATAL_ALERT
+ && err < BR_ERR_SEND_FATAL_ALERT + 256)
+ {
+ fprintf(stderr, "ERR %d: sent fatal alert %d\n",
+ err, err - BR_ERR_SEND_FATAL_ALERT);
+ return;
+ }
+ fprintf(stderr, "ERR %d: UNKNOWN\n", err);
+}
+
+/* see brssl.h */
+int
+do_twrch(int argc, char *argv[])
+{
+ int retcode;
+ int trace;
+ int is_client;
+ int is_server;
+ const char *sni;
+ int i, bidi;
+ unsigned vmin, vmax;
+ cipher_suite *suites;
+ size_t num_suites;
+ uint16_t *suite_ids;
+ unsigned hfuns;
+ br_x509_certificate *chain;
+ size_t chain_len;
+ int cert_signer_algo;
+ private_key *sk;
+ int nostaticecdh;
+ anchor_list anchors = VEC_INIT;
+ VECTOR(char *) alpn_names = VEC_INIT;
+ br_x509_minimal_context xc;
+ x509_noanchor_context xwc;
+ const br_hash_class *dnhash;
+ size_t u;
+ union {
+ br_ssl_engine_context eng;
+ br_ssl_server_context srv;
+ br_ssl_client_context cnt;
+ } cc;
+ br_ssl_session_cache_lru lru;
+ unsigned char *iobuf, *cache;
+ size_t iobuf_len, cache_len, minhello_len;
+ br_sslio_context ioc;
+ uint32_t flags;
+ int reconnect;
+
+ retcode = 0;
+ trace = 0;
+ is_client = 0;
+ is_server = 0;
+ sni = NULL;
+ bidi = 1;
+ vmin = 0;
+ vmax = 0;
+ suites = NULL;
+ num_suites = 0;
+ suite_ids = NULL;
+ hfuns = 0;
+ chain = NULL;
+ chain_len = 0;
+ cert_signer_algo = 0;
+ sk = NULL;
+ nostaticecdh = 0;
+ iobuf = NULL;
+ iobuf_len = 0;
+ cache = NULL;
+ cache_len = (size_t)-1;
+ minhello_len = (size_t)-1;
+ flags = 0;
+ reconnect = 0;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (eqstr(arg, "-trace")) {
+ trace = 1;
+ } else if (eqstr(arg, "-v")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-server")) {
+ is_server = 1;
+ } else if (eqstr(arg, "-client")) {
+ is_client = 1;
+ } else if (eqstr(arg, "-sni")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-sni'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (sni != NULL) {
+ fprintf(stderr, "ERROR: duplicate SNI\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ sni = arg;
+ } else if (eqstr(arg, "-mono")) {
+ bidi = 0;
+ } else if (eqstr(arg, "-buf")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-buf'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (iobuf_len != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate I/O buffer length\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ iobuf_len = parse_size(arg);
+ if (iobuf_len == (size_t)-1) {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-cache")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cache'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (cache_len != (size_t)-1) {
+ fprintf(stderr, "ERROR: duplicate session"
+ " cache length\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ cache_len = parse_size(arg);
+ if (cache_len == (size_t)-1) {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-cert")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cert'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (chain != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate certificate chain\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ chain = read_certificates(arg, &chain_len);
+ if (chain == NULL || chain_len == 0) {
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-key")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-key'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (sk != NULL) {
+ fprintf(stderr,
+ "ERROR: duplicate private key\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ sk = read_private_key(arg);
+ if (sk == NULL) {
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-CA")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-CA'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (read_trust_anchors(&anchors, arg) == 0) {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-anon_ok")) {
+ flags |= BR_OPT_TOLERATE_NO_CLIENT_AUTH;
+ } else if (eqstr(arg, "-nostaticecdh")) {
+ nostaticecdh = 1;
+ } else if (eqstr(arg, "-list")) {
+ list_names();
+ goto twrch_exit;
+ } else if (eqstr(arg, "-vmin")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmin'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (vmin != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate minimum version\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ vmin = parse_version(arg, strlen(arg));
+ if (vmin == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-vmax")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-vmax'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (vmax != 0) {
+ fprintf(stderr,
+ "ERROR: duplicate maximum version\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ vmax = parse_version(arg, strlen(arg));
+ if (vmax == 0) {
+ fprintf(stderr,
+ "ERROR: unrecognised version '%s'\n",
+ arg);
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-cs")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-cs'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (suites != NULL) {
+ fprintf(stderr, "ERROR: duplicate list"
+ " of cipher suites\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ suites = parse_suites(arg, &num_suites);
+ if (suites == NULL) {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-hf")) {
+ unsigned x;
+
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-hf'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ x = parse_hash_functions(arg);
+ if (x == 0) {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ hfuns |= x;
+ } else if (eqstr(arg, "-minhello")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-minhello'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ arg = argv[i];
+ if (minhello_len != (size_t)-1) {
+ fprintf(stderr, "ERROR: duplicate minimum"
+ " ClientHello length\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ minhello_len = parse_size(arg);
+ /*
+ * Minimum ClientHello length must fit on 16 bits.
+ */
+ if (minhello_len == (size_t)-1
+ || (((minhello_len >> 12) >> 4) != 0))
+ {
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else if (eqstr(arg, "-serverpref")) {
+ flags |= BR_OPT_ENFORCE_SERVER_PREFERENCES;
+ } else if (eqstr(arg, "-noreneg")) {
+ flags |= BR_OPT_NO_RENEGOTIATION;
+ } else if (eqstr(arg, "-alpn")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-alpn'\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ VEC_ADD(alpn_names, xstrdup(argv[i]));
+ } else if (eqstr(arg, "-strictalpn")) {
+ flags |= BR_OPT_FAIL_ON_ALPN_MISMATCH;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ }
+
+ /*
+ * Verify consistency of options.
+ */
+ if (!is_client && !is_server) {
+ fprintf(stderr, "ERROR:"
+ " one of -server and -client must be specified\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (is_client && is_server) {
+ fprintf(stderr, "ERROR:"
+ " -server and -client may not be both specified\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+
+ if (vmin == 0) {
+ vmin = BR_TLS10;
+ }
+ if (vmax == 0) {
+ vmax = BR_TLS12;
+ }
+ if (vmax < vmin) {
+ fprintf(stderr, "ERROR: impossible minimum/maximum protocol"
+ " version combination\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (is_server) {
+ if (chain == NULL) {
+ fprintf(stderr, "ERROR: no certificate specified"
+ " for server (-cert)\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (sk == NULL) {
+ fprintf(stderr, "ERROR: no private key specified"
+ " for server (-key)\n");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ } else {
+ if (chain == NULL && sk != NULL) {
+ fprintf(stderr, "ERROR: private key (-key)"
+ " but no certificate (-cert)");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ if (chain != NULL && sk == NULL) {
+ fprintf(stderr, "ERROR: certificate (-cert)"
+ " but no private key (-key)");
+ usage_twrch();
+ goto twrch_exit_error;
+ }
+ }
+ if (suites == NULL) {
+ num_suites = 0;
+
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ num_suites ++;
+ }
+ }
+ suites = xmalloc(num_suites * sizeof *suites);
+ num_suites = 0;
+ for (u = 0; cipher_suites[u].name; u ++) {
+ if ((cipher_suites[u].req & REQ_TLS12) == 0
+ || vmax >= BR_TLS12)
+ {
+ suites[num_suites ++] = cipher_suites[u];
+ }
+ }
+ }
+ if (hfuns == 0) {
+ hfuns = (unsigned)-1;
+ }
+ if (sk != NULL) {
+ switch (sk->key_type) {
+ int curve;
+ uint32_t supp;
+
+ case BR_KEYTYPE_RSA:
+ break;
+ case BR_KEYTYPE_EC:
+ curve = sk->key.ec.curve;
+ supp = br_ec_get_default()->supported_curves;
+ if (curve > 31 || !((supp >> curve) & 1)) {
+ fprintf(stderr, "ERROR: private key curve (%d)"
+ " is not supported\n", curve);
+ goto twrch_exit_error;
+ }
+ break;
+ default:
+ fprintf(stderr, "ERROR: unsupported"
+ " private key type (%d)\n", sk->key_type);
+ goto twrch_exit_error;
+ }
+ }
+ if (chain != NULL) {
+ cert_signer_algo = get_cert_signer_algo(chain);
+ if (cert_signer_algo == 0) {
+ goto twrch_exit_error;
+ }
+ }
+ if (iobuf_len == 0) {
+ if (bidi) {
+ iobuf_len = BR_SSL_BUFSIZE_BIDI;
+ } else {
+ iobuf_len = BR_SSL_BUFSIZE_MONO;
+ }
+ }
+ iobuf = xmalloc(iobuf_len);
+ if (is_server) {
+ if (cache_len == (size_t)-1) {
+ cache_len = 5000;
+ }
+ cache = xmalloc(cache_len);
+ }
+
+ /*
+ * Initialise the relevant context.
+ */
+ if (is_client) {
+ br_ssl_client_zero(&cc.cnt);
+ } else {
+ br_ssl_server_zero(&cc.srv);
+ }
+
+ /*
+ * Compute implementation requirements and inject implementations.
+ */
+ suite_ids = xmalloc(num_suites * sizeof *suite_ids);
+ br_ssl_engine_set_versions(&cc.eng, vmin, vmax);
+ br_ssl_engine_set_all_flags(&cc.eng, flags);
+ if (vmin <= BR_TLS11) {
+ if (!(hfuns & (1 << br_md5_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need MD5\n");
+ goto twrch_exit_error;
+ }
+ if (!(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr, "ERROR: TLS 1.0 and 1.1 need SHA-1\n");
+ goto twrch_exit_error;
+ }
+ }
+ for (u = 0; u < num_suites; u ++) {
+ unsigned req;
+
+ req = suites[u].req;
+ suite_ids[u] = suites[u].suite;
+ if ((req & REQ_TLS12) != 0 && vmax < BR_TLS12) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires TLS 1.2\n",
+ suites[u].name);
+ goto twrch_exit_error;
+ }
+ if ((req & REQ_SHA1) != 0 && !(hfuns & (1 << br_sha1_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-1\n",
+ suites[u].name);
+ goto twrch_exit_error;
+ }
+ if ((req & REQ_SHA256) != 0 && !(hfuns & (1 << br_sha256_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-256\n",
+ suites[u].name);
+ goto twrch_exit_error;
+ }
+ if ((req & REQ_SHA384) != 0 && !(hfuns & (1 << br_sha384_ID))) {
+ fprintf(stderr,
+ "ERROR: cipher suite %s requires SHA-384\n",
+ suites[u].name);
+ goto twrch_exit_error;
+ }
+ /* TODO: algorithm implementation selection */
+ if ((req & REQ_AESCBC) != 0) {
+ br_ssl_engine_set_default_aes_cbc(&cc.eng);
+ }
+ if ((req & REQ_AESCCM) != 0) {
+ br_ssl_engine_set_default_aes_ccm(&cc.eng);
+ }
+ if ((req & REQ_AESGCM) != 0) {
+ br_ssl_engine_set_default_aes_gcm(&cc.eng);
+ }
+ if ((req & REQ_CHAPOL) != 0) {
+ br_ssl_engine_set_default_chapol(&cc.eng);
+ }
+ if ((req & REQ_3DESCBC) != 0) {
+ br_ssl_engine_set_default_des_cbc(&cc.eng);
+ }
+ if (is_client && (req & REQ_RSAKEYX) != 0) {
+ br_ssl_client_set_default_rsapub(&cc.cnt);
+ }
+ if (is_client && (req & REQ_ECDHE_RSA) != 0) {
+ br_ssl_engine_set_default_rsavrfy(&cc.eng);
+ }
+ if (is_client && (req & REQ_ECDH) != 0) {
+ br_ssl_engine_set_default_ec(&cc.eng);
+ }
+ if ((req & (REQ_ECDHE_RSA | REQ_ECDHE_ECDSA)) != 0) {
+ br_ssl_engine_set_default_ec(&cc.eng);
+ }
+ }
+ br_ssl_engine_set_suites(&cc.eng, suite_ids, num_suites);
+
+ dnhash = NULL;
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF) & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ dnhash = hc;
+ br_ssl_engine_set_hash(&cc.eng, id, hc);
+ }
+ }
+ if (vmin <= BR_TLS11) {
+ br_ssl_engine_set_prf10(&cc.eng, &br_tls10_prf);
+ }
+ if (vmax >= BR_TLS12) {
+ if ((hfuns & ((unsigned)1 << br_sha256_ID)) != 0) {
+ br_ssl_engine_set_prf_sha256(&cc.eng,
+ &br_tls12_sha256_prf);
+ }
+ if ((hfuns & ((unsigned)1 << br_sha384_ID)) != 0) {
+ br_ssl_engine_set_prf_sha384(&cc.eng,
+ &br_tls12_sha384_prf);
+ }
+ }
+ if (VEC_LEN(alpn_names) != 0) {
+ br_ssl_engine_set_protocol_names(&cc.eng,
+ (const char **)&VEC_ELT(alpn_names, 0),
+ VEC_LEN(alpn_names));
+ }
+
+ /*
+ * In server role, we use a session cache (size can be
+ * specified; if size is zero, then no cache is set).
+ */
+ if (is_server && cache != NULL) {
+ br_ssl_session_cache_lru_init(&lru, cache, cache_len);
+ br_ssl_server_set_cache(&cc.srv, &lru.vtable);
+ }
+
+ /*
+ * For a server, set the policy handler.
+ */
+ if (is_server) {
+ switch (sk->key_type) {
+ case BR_KEYTYPE_RSA:
+ br_ssl_server_set_single_rsa(&cc.srv,
+ chain, chain_len, &sk->key.rsa,
+ BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+ br_rsa_private_get_default(),
+ br_rsa_pkcs1_sign_get_default());
+ break;
+ case BR_KEYTYPE_EC:
+ br_ssl_server_set_single_ec(&cc.srv,
+ chain, chain_len, &sk->key.ec,
+ BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN,
+ cert_signer_algo,
+ br_ec_get_default(),
+ br_ecdsa_sign_asn1_get_default());
+ break;
+ default:
+ fprintf(stderr, "ERROR: unsupported"
+ " private key type (%d)\n", sk->key_type);
+ goto twrch_exit_error;
+ }
+ }
+
+ /*
+ * For a client, if a certificate was specified, use it.
+ */
+ if (is_client && chain != NULL) {
+ switch (sk->key_type) {
+ unsigned usages;
+
+ case BR_KEYTYPE_RSA:
+ br_ssl_client_set_single_rsa(&cc.cnt,
+ chain, chain_len, &sk->key.rsa,
+ br_rsa_pkcs1_sign_get_default());
+ break;
+ case BR_KEYTYPE_EC:
+ if (nostaticecdh) {
+ cert_signer_algo = 0;
+ usages = BR_KEYTYPE_SIGN;
+ } else {
+ usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN;
+ }
+ br_ssl_client_set_single_ec(&cc.cnt,
+ chain, chain_len, &sk->key.ec,
+ usages, cert_signer_algo,
+ br_ec_get_default(),
+ br_ecdsa_sign_asn1_get_default());
+ break;
+ default:
+ fprintf(stderr, "ERROR: unsupported"
+ " private key type (%d)\n", sk->key_type);
+ goto twrch_exit_error;
+ }
+ }
+
+ /*
+ * On a client, or if trust anchors have been configured, then
+ * set an X.509 validation engine. If there are no trust anchors
+ * (client only), then a "no anchor" wrapper will be applied.
+ */
+ if (is_client || VEC_LEN(anchors) != 0) {
+ br_x509_minimal_init(&xc, dnhash,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ for (u = 0; hash_functions[u].name; u ++) {
+ const br_hash_class *hc;
+ int id;
+
+ hc = hash_functions[u].hclass;
+ id = (hc->desc >> BR_HASHDESC_ID_OFF)
+ & BR_HASHDESC_ID_MASK;
+ if ((hfuns & ((unsigned)1 << id)) != 0) {
+ br_x509_minimal_set_hash(&xc, id, hc);
+ }
+ }
+ br_ssl_engine_set_default_rsavrfy(&cc.eng);
+ br_ssl_engine_set_default_ecdsa(&cc.eng);
+ br_x509_minimal_set_rsa(&xc, br_rsa_pkcs1_vrfy_get_default());
+ br_x509_minimal_set_ecdsa(&xc,
+ br_ec_get_default(), br_ecdsa_vrfy_asn1_get_default());
+ br_ssl_engine_set_x509(&cc.eng, &xc.vtable);
+
+ if (VEC_LEN(anchors) == 0) {
+ x509_noanchor_init(&xwc, &xc.vtable);
+ br_ssl_engine_set_x509(&cc.eng, &xwc.vtable);
+ } else {
+ br_ssl_engine_set_x509(&cc.eng, &xc.vtable);
+ }
+ if (is_server) {
+ br_ssl_server_set_trust_anchor_names_alt(&cc.srv,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ }
+ }
+
+ /*
+ * Set I/O buffer.
+ */
+ br_ssl_engine_set_buffer(&cc.eng, iobuf, iobuf_len, bidi);
+
+ /*
+ * Start the engine.
+ */
+ if (is_client) {
+ br_ssl_client_reset(&cc.cnt, sni, 0);
+ }
+ if (is_server) {
+ br_ssl_server_reset(&cc.srv);
+ }
+
+ /*
+ * On Unix systems, we want to ignore SIGPIPE: if the peer
+ * closes the connection abruptly, then we want to report it
+ * as a "normal" error (exit code = 1).
+ */
+#ifndef _WIN32
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ /*
+ * Initialize the callbacks for exchanging data over stdin and
+ * stdout.
+ */
+ br_sslio_init(&ioc, &cc.eng, stdin_read, &trace, stdout_write, &trace);
+
+ /*
+ * Run the Twrch protocol.
+ */
+ for (;;) {
+ br_sha1_context sc;
+ unsigned char hv[20], tmp[41];
+ uint64_t count;
+ int fb, i;
+
+ /*
+ * Read line, byte by byte, hashing it on the fly.
+ */
+ br_sha1_init(&sc);
+ count = 0;
+ fb = 0;
+ for (;;) {
+ unsigned char x;
+
+ if (br_sslio_read(&ioc, &x, 1) < 0) {
+ if (count == 0 && reconnect) {
+ reconnect = 0;
+ if (br_sslio_close(&ioc) < 0) {
+ goto twrch_loop_finished;
+ }
+ if (is_client) {
+ br_ssl_client_reset(
+ &cc.cnt, sni, 1);
+ }
+ if (is_server) {
+ br_ssl_server_reset(&cc.srv);
+ }
+ br_sslio_init(&ioc, &cc.eng,
+ stdin_read, &trace,
+ stdout_write, &trace);
+ continue;
+ }
+ goto twrch_loop_finished;
+ }
+ if (count == 0) {
+ fb = x;
+ }
+ if (x == 0x0A) {
+ break;
+ }
+ br_sha1_update(&sc, &x, 1);
+ count ++;
+ }
+ if (count == 1) {
+ switch (fb) {
+ case 'C':
+ br_sslio_close(&ioc);
+ goto twrch_loop_finished;
+ case 'T':
+ if (br_sslio_close(&ioc) < 0) {
+ goto twrch_loop_finished;
+ }
+ if (is_client) {
+ br_ssl_client_reset(&cc.cnt, sni, 1);
+ }
+ if (is_server) {
+ br_ssl_server_reset(&cc.srv);
+ }
+ br_sslio_init(&ioc, &cc.eng,
+ stdin_read, &trace,
+ stdout_write, &trace);
+ continue;
+ case 'G':
+ if (!br_ssl_engine_renegotiate(&cc.eng)) {
+ br_sslio_write_all(&ioc, "DENIED\n", 7);
+ br_sslio_flush(&ioc);
+ } else {
+ br_sslio_write_all(&ioc, "OK\n", 3);
+ br_sslio_flush(&ioc);
+ }
+ continue;
+ case 'R':
+ reconnect = 1;
+ br_sslio_write_all(&ioc, "OK\n", 3);
+ br_sslio_flush(&ioc);
+ continue;
+ case 'U':
+ if (is_client) {
+ br_ssl_client_forget_session(&cc.cnt);
+ }
+ if (is_server && cache != NULL) {
+ br_ssl_session_parameters pp;
+
+ br_ssl_engine_get_session_parameters(
+ &cc.eng, &pp);
+ if (pp.session_id_len == 32) {
+ br_ssl_session_cache_lru_forget(
+ &lru, pp.session_id);
+ }
+ }
+ br_sslio_write_all(&ioc, "DONE\n", 5);
+ br_sslio_flush(&ioc);
+ continue;
+ }
+ }
+ br_sha1_out(&sc, hv);
+ for (i = 0; i < 20; i ++) {
+ int x;
+
+ x = hv[i];
+ tmp[(i << 1) + 0] = "0123456789abcdef"[x >> 4];
+ tmp[(i << 1) + 1] = "0123456789abcdef"[x & 15];
+ }
+ tmp[40] = 0x0A;
+ br_sslio_write_all(&ioc, tmp, 41);
+ br_sslio_flush(&ioc);
+ }
+
+twrch_loop_finished:
+ if (br_ssl_engine_current_state(&cc.eng) == BR_SSL_CLOSED) {
+ int err;
+
+ err = br_ssl_engine_last_error(&cc.eng);
+ if (err == 0) {
+ retcode = 0;
+ } else {
+ if (verbose) {
+ print_error(err);
+ }
+ retcode = 1;
+ }
+ } else {
+ if (verbose) {
+ fprintf(stderr, "Engine not closed!\n");
+ }
+ retcode = 1;
+ }
+
+ /*
+ * Release allocated structures.
+ */
+twrch_exit:
+ xfree(suites);
+ xfree(suite_ids);
+ free_certificates(chain, chain_len);
+ free_private_key(sk);
+ VEC_CLEAREXT(anchors, &free_ta_contents);
+ VEC_CLEAREXT(alpn_names, &free_alpn);
+ xfree(iobuf);
+ xfree(cache);
+ return retcode;
+
+twrch_exit_error:
+ retcode = -1;
+ goto twrch_exit;
+}
diff --git a/contrib/bearssl/tools/vector.c b/contrib/bearssl/tools/vector.c
new file mode 100644
index 000000000000..96df3072b01b
--- /dev/null
+++ b/contrib/bearssl/tools/vector.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+
+/*
+ * Prepare a vector buffer for adding 'extra' elements.
+ * buf current buffer
+ * esize size of a vector element
+ * ptr pointer to the 'ptr' vector field
+ * len pointer to the 'len' vector field
+ * extra number of elements to add
+ *
+ * If the buffer must be enlarged, then this function allocates the new
+ * buffer and releases the old one. The new buffer address is then returned.
+ * If the buffer needs not be enlarged, then the buffer address is returned.
+ *
+ * In case of enlargement, the 'len' field is adjusted accordingly. The
+ * 'ptr' field is not modified.
+ */
+void *
+vector_expand(void *buf,
+ size_t esize, size_t *ptr, size_t *len, size_t extra)
+{
+ size_t nlen;
+ void *nbuf;
+
+ if (*len - *ptr >= extra) {
+ return buf;
+ }
+ nlen = (*len << 1);
+ if (nlen - *ptr < extra) {
+ nlen = extra + *ptr;
+ if (nlen < 8) {
+ nlen = 8;
+ }
+ }
+ nbuf = xmalloc(nlen * esize);
+ if (buf != NULL) {
+ memcpy(nbuf, buf, *len * esize);
+ xfree(buf);
+ }
+ *len = nlen;
+ return nbuf;
+}
diff --git a/contrib/bearssl/tools/verify.c b/contrib/bearssl/tools/verify.c
new file mode 100644
index 000000000000..74055784c769
--- /dev/null
+++ b/contrib/bearssl/tools/verify.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "brssl.h"
+#include "bearssl.h"
+
+static unsigned
+rsa_bit_length(const br_rsa_public_key *pk)
+{
+ size_t u;
+ unsigned x, bl;
+
+ for (u = 0; u < pk->nlen; u ++) {
+ if (pk->n[u] != 0) {
+ break;
+ }
+ }
+ if (u == pk->nlen) {
+ return 0;
+ }
+ bl = (unsigned)(pk->nlen - u - 1) << 3;
+ x = pk->n[u];
+ while (x != 0) {
+ bl ++;
+ x >>= 1;
+ }
+ return bl;
+}
+
+static void
+print_rsa(const br_rsa_public_key *pk, int print_text, int print_C)
+{
+ if (print_text) {
+ size_t u;
+
+ printf("n = ");
+ for (u = 0; u < pk->nlen; u ++) {
+ printf("%02X", pk->n[u]);
+ }
+ printf("\n");
+ printf("e = ");
+ for (u = 0; u < pk->elen; u ++) {
+ printf("%02X", pk->e[u]);
+ }
+ printf("\n");
+ }
+ if (print_C) {
+ size_t u;
+
+ printf("\nstatic const unsigned char RSA_N[] = {");
+ for (u = 0; u < pk->nlen; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", pk->n[u]);
+ }
+ printf("\n};\n");
+ printf("\nstatic const unsigned char RSA_E[] = {");
+ for (u = 0; u < pk->elen; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", pk->e[u]);
+ }
+ printf("\n};\n");
+ printf("\nstatic const br_rsa_public_key RSA = {\n");
+ printf("\t(unsigned char *)RSA_N, sizeof RSA_N,\n");
+ printf("\t(unsigned char *)RSA_E, sizeof RSA_E\n");
+ printf("};\n");
+ }
+}
+
+static void
+print_ec(const br_ec_public_key *pk, int print_text, int print_C)
+{
+ if (print_text) {
+ size_t u;
+
+ printf("Q = ");
+ for (u = 0; u < pk->qlen; u ++) {
+ printf("%02X", pk->q[u]);
+ }
+ printf("\n");
+ }
+ if (print_C) {
+ size_t u;
+
+ printf("\nstatic const unsigned char EC_Q[] = {");
+ for (u = 0; u < pk->qlen; u ++) {
+ if (u != 0) {
+ printf(",");
+ }
+ if (u % 12 == 0) {
+ printf("\n\t");
+ } else {
+ printf(" ");
+ }
+ printf("0x%02X", pk->q[u]);
+ }
+ printf("\n};\n");
+ printf("\nstatic const br_ec_public_key EC = {\n");
+ printf("\t%d,\n", pk->curve);
+ printf("\t(unsigned char *)EC_Q, sizeof EC_Q\n");
+ printf("};\n");
+ }
+}
+
+static void
+usage_verify(void)
+{
+ fprintf(stderr,
+"usage: brssl verify [ options ] file...\n");
+ fprintf(stderr,
+"options:\n");
+ fprintf(stderr,
+" -q suppress verbose messages\n");
+ fprintf(stderr,
+" -sni name check presence of a specific server name\n");
+ fprintf(stderr,
+" -CA file add certificates in 'file' to trust anchors\n");
+ fprintf(stderr,
+" -text print public key details (human-readable)\n");
+ fprintf(stderr,
+" -C print public key details (C code)\n");
+}
+
+typedef VECTOR(br_x509_certificate) cert_list;
+
+static void
+free_cert_contents(br_x509_certificate *xc)
+{
+ xfree(xc->data);
+}
+
+/* see brssl.h */
+int
+do_verify(int argc, char *argv[])
+{
+ int retcode;
+ int verbose;
+ int i;
+ const char *sni;
+ anchor_list anchors = VEC_INIT;
+ cert_list chain = VEC_INIT;
+ size_t u;
+ br_x509_minimal_context mc;
+ int err;
+ int print_text, print_C;
+ br_x509_pkey *pk;
+ const br_x509_pkey *tpk;
+ unsigned usages;
+
+ retcode = 0;
+ verbose = 1;
+ sni = NULL;
+ print_text = 0;
+ print_C = 0;
+ pk = NULL;
+ for (i = 0; i < argc; i ++) {
+ const char *arg;
+
+ arg = argv[i];
+ if (arg[0] != '-') {
+ br_x509_certificate *xcs;
+ size_t num;
+
+ xcs = read_certificates(arg, &num);
+ if (xcs == NULL) {
+ usage_verify();
+ goto verify_exit_error;
+ }
+ VEC_ADDMANY(chain, xcs, num);
+ xfree(xcs);
+ continue;
+ }
+ if (eqstr(arg, "-v") || eqstr(arg, "-verbose")) {
+ verbose = 1;
+ } else if (eqstr(arg, "-q") || eqstr(arg, "-quiet")) {
+ verbose = 0;
+ } else if (eqstr(arg, "-sni")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-sni'\n");
+ usage_verify();
+ goto verify_exit_error;
+ }
+ if (sni != NULL) {
+ fprintf(stderr, "ERROR: duplicate SNI\n");
+ usage_verify();
+ goto verify_exit_error;
+ }
+ sni = argv[i];
+ continue;
+ } else if (eqstr(arg, "-CA")) {
+ if (++ i >= argc) {
+ fprintf(stderr,
+ "ERROR: no argument for '-CA'\n");
+ usage_verify();
+ goto verify_exit_error;
+ }
+ arg = argv[i];
+ if (read_trust_anchors(&anchors, arg) == 0) {
+ usage_verify();
+ goto verify_exit_error;
+ }
+ continue;
+ } else if (eqstr(arg, "-text")) {
+ print_text = 1;
+ } else if (eqstr(arg, "-C")) {
+ print_C = 1;
+ } else {
+ fprintf(stderr, "ERROR: unknown option: '%s'\n", arg);
+ usage_verify();
+ goto verify_exit_error;
+ }
+ }
+ if (VEC_LEN(chain) == 0) {
+ fprintf(stderr, "ERROR: no certificate chain provided\n");
+ usage_verify();
+ goto verify_exit_error;
+ }
+ br_x509_minimal_init(&mc, &br_sha256_vtable,
+ &VEC_ELT(anchors, 0), VEC_LEN(anchors));
+ br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
+ br_x509_minimal_set_hash(&mc, br_sha224_ID, &br_sha224_vtable);
+ br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
+ br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
+ br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
+ br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
+ br_x509_minimal_set_ecdsa(&mc,
+ &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
+
+ mc.vtable->start_chain(&mc.vtable, sni);
+ for (u = 0; u < VEC_LEN(chain); u ++) {
+ br_x509_certificate *xc;
+
+ xc = &VEC_ELT(chain, u);
+ mc.vtable->start_cert(&mc.vtable, xc->data_len);
+ mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
+ mc.vtable->end_cert(&mc.vtable);
+ }
+ err = mc.vtable->end_chain(&mc.vtable);
+ tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
+ if (tpk != NULL) {
+ pk = xpkeydup(tpk);
+ }
+
+ if (err == 0) {
+ if (verbose) {
+ int hkx;
+
+ fprintf(stderr, "Validation success; usages:");
+ hkx = 0;
+ if (usages & BR_KEYTYPE_KEYX) {
+ fprintf(stderr, " key exchange");
+ hkx = 1;
+ }
+ if (usages & BR_KEYTYPE_SIGN) {
+ if (hkx) {
+ fprintf(stderr, ",");
+ }
+ fprintf(stderr, " signature");
+ }
+ fprintf(stderr, "\n");
+ }
+ } else {
+ if (verbose) {
+ const char *errname, *errmsg;
+
+ fprintf(stderr, "Validation failed, err = %d", err);
+ errname = find_error_name(err, &errmsg);
+ if (errname != NULL) {
+ fprintf(stderr, " (%s): %s\n", errname, errmsg);
+ } else {
+ fprintf(stderr, " (unknown)\n");
+ }
+ }
+ retcode = -1;
+ }
+ if (pk != NULL) {
+ switch (pk->key_type) {
+ case BR_KEYTYPE_RSA:
+ if (verbose) {
+ fprintf(stderr, "Key type: RSA (%u bits)\n",
+ rsa_bit_length(&pk->key.rsa));
+ }
+ print_rsa(&pk->key.rsa, print_text, print_C);
+ break;
+ case BR_KEYTYPE_EC:
+ if (verbose) {
+ fprintf(stderr, "Key type: EC (%s)\n",
+ ec_curve_name(pk->key.ec.curve));
+ }
+ print_ec(&pk->key.ec, print_text, print_C);
+ break;
+ default:
+ if (verbose) {
+ fprintf(stderr, "Unknown key type\n");
+ break;
+ }
+ }
+ }
+
+ /*
+ * Release allocated structures.
+ */
+verify_exit:
+ VEC_CLEAREXT(anchors, &free_ta_contents);
+ VEC_CLEAREXT(chain, &free_cert_contents);
+ xfreepkey(pk);
+ return retcode;
+
+verify_exit_error:
+ retcode = -1;
+ goto verify_exit;
+}
diff --git a/contrib/bearssl/tools/xmem.c b/contrib/bearssl/tools/xmem.c
new file mode 100644
index 000000000000..ad192a4b5712
--- /dev/null
+++ b/contrib/bearssl/tools/xmem.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "brssl.h"
+
+/* see brssl.h */
+void *
+xmalloc(size_t len)
+{
+ void *buf;
+
+ if (len == 0) {
+ return NULL;
+ }
+ buf = malloc(len);
+ if (buf == NULL) {
+#ifndef _STANDALONE
+ fprintf(stderr, "ERROR: could not allocate %lu byte(s)\n",
+ (unsigned long)len);
+ exit(EXIT_FAILURE);
+#else
+;
+#endif
+ }
+ return buf;
+}
+
+/* see brssl.h */
+void
+xfree(void *buf)
+{
+ if (buf != NULL) {
+ free(buf);
+ }
+}
+
+/* see brssl.h */
+void *
+xblobdup(const void *src, size_t len)
+{
+ void *buf;
+
+ buf = xmalloc(len);
+ memcpy(buf, src, len);
+ return buf;
+}
+
+/* see brssl.h */
+char *
+xstrdup(const void *src)
+{
+ return xblobdup(src, strlen(src) + 1);
+}
+
+/* see brssl.h */
+br_x509_pkey *
+xpkeydup(const br_x509_pkey *pk)
+{
+ br_x509_pkey *pk2;
+
+ pk2 = xmalloc(sizeof *pk2);
+ pk2->key_type = pk->key_type;
+ switch (pk->key_type) {
+ case BR_KEYTYPE_RSA:
+ pk2->key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
+ pk2->key.rsa.nlen = pk->key.rsa.nlen;
+ pk2->key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
+ pk2->key.rsa.elen = pk->key.rsa.elen;
+ break;
+ case BR_KEYTYPE_EC:
+ pk2->key.ec.curve = pk->key.ec.curve;
+ pk2->key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
+ pk2->key.ec.qlen = pk->key.ec.qlen;
+ break;
+ default:
+#ifndef _STANDALONE
+ fprintf(stderr, "Unknown public key type: %u\n",
+ (unsigned)pk->key_type);
+ exit(EXIT_FAILURE);
+#else
+;
+#endif
+ }
+ return pk2;
+}
+
+/* see brssl.h */
+void
+xfreepkey(br_x509_pkey *pk)
+{
+ if (pk != NULL) {
+ switch (pk->key_type) {
+ case BR_KEYTYPE_RSA:
+ xfree(pk->key.rsa.n);
+ xfree(pk->key.rsa.e);
+ break;
+ case BR_KEYTYPE_EC:
+ xfree(pk->key.ec.q);
+ break;
+ default:
+#ifndef _STANDALONE
+ fprintf(stderr, "Unknown public key type: %u\n",
+ (unsigned)pk->key_type);
+ exit(EXIT_FAILURE);
+#else
+;
+#endif
+ }
+ xfree(pk);
+ }
+}