Merge branch 'kazuho/pr/335' into kazuho/server-cipher-preference
diff --git a/.travis.yml b/.travis.yml
index 49feef0..2a9d1c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,19 +4,29 @@
matrix:
include:
- - name: Linux (gcc-8)
+ - name: Linux (gcc)
os: linux
- compiler: gcc-8
- addons:
- apt:
- sources: ['ubuntu-toolchain-r-test']
- packages: ['gcc-8']
+ dist: focal
+ compiler: gcc
before_install: &bs_linux
- sudo apt-get install faketime libscope-guard-perl libtest-tcp-perl
- name: Linux (clang)
os: linux
+ dist: focal
compiler: clang
before_install: *bs_linux
+ - name: Linux (OpenSSL 1.1.0)
+ os: linux
+ before_install:
+ - sudo apt-get install faketime libscope-guard-perl libtest-tcp-perl
+ - curl https://www.openssl.org/source/old/1.1.0/openssl-1.1.0l.tar.gz | tar xzf -
+ - cd openssl-1.1.0l
+ - ./config --prefix=/usr/local/openssl-1.1.0
+ - make
+ - sudo make install
+ - cd ..
+ env:
+ - PKG_CONFIG_PATH=/usr/local/openssl-1.1.0/lib/pkgconfig
- name: Linux (OpenSSL 1.0.2)
os: linux
before_install:
@@ -29,6 +39,18 @@
- cd ..
env:
- PKG_CONFIG_PATH=/usr/local/openssl-1.0.2/lib/pkgconfig
+ - name: Linux (libressl 3.2)
+ os: linux
+ before_install:
+ - sudo apt-get install faketime libscope-guard-perl libtest-tcp-perl
+ - curl https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.2.5.tar.gz | tar xzf -
+ - cd libressl-3.2.5
+ - ./configure --prefix=/usr/local/libressl-3.2
+ - make
+ - sudo make install
+ - cd ..
+ env:
+ - PKG_CONFIG_PATH=/usr/local/libressl-3.2/lib/pkgconfig
- name: macOS (Xcode)
os: osx
addons: &addons_macos
diff --git a/README.md b/README.md
index afbc60e..d7c5dbc 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,10 @@
* ["fusion" AES-GCM engine, optimized for QUIC and other protocols that use short AEAD blocks](https://github.com/h2o/picotls/pull/310)
* support for PSK, PSK-DHE resumption using 0-RTT
* API for dealing directly with TLS handshake messages (essential for QUIC)
-* support for new extensions: Encrypted SNI (wg-draft-02), Certificate Compression (wg-draft-10)
+* supported extensions:
+ * RFC 7250 (raw public keys)
+ * RFC 8879 (certificate compression)
+ * Encrypted SNI (wg-draft-02)
Primary goal of the project is to create a fast, tiny, low-latency TLS 1.3 implementation that can be used with the HTTP/2 protocol stack and the upcoming QUIC stack of the [H2O HTTP/2 server](https://h2o.examp1e.net).
@@ -20,8 +23,8 @@
| Binding | License | Key Exchange | Certificate | AEAD cipher |
|:-----:|:-----:|:-----:|:-----:|:-----:|
-| minicrypto | [CC0](https://github.com/ctz/cifra/) / [2-clause BSD](https://github.com/kmackay/micro-ecc) | secp256r1, x25519 | ECDSA (P256)<sup>1</sup> | AES-128-GCM, chacha20-poly1305 |
-| OpenSSL | OpenSSL | secp256r1, secp384r1, secp521r1, x25519 | RSA, ECDSA (P256) | AES-128-GCM, AES-256-GCM, chacha20-poly1305 |
+| minicrypto | [CC0](https://github.com/ctz/cifra/) / [2-clause BSD](https://github.com/kmackay/micro-ecc) | secp256r1, x25519 | ECDSA (secp256r1)<sup>1</sup> | AES-128-GCM, chacha20-poly1305 |
+| OpenSSL | OpenSSL | secp256r1, secp384r1, secp521r1, x25519 | RSA, ECDSA (secp256r1, secp384r1, secp521r1), ed25519 | AES-128-GCM, AES-256-GCM, chacha20-poly1305 |
Note 1: Minicrypto binding is capable of signing a handshake using the certificate's key, but cannot verify a signature sent by the peer.
diff --git a/include/picotls.h b/include/picotls.h
index b61a5e8..b7f13d2 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -124,6 +124,7 @@
#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256 0x0804
#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384 0x0805
#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806
+#define PTLS_SIGNATURE_ED25519 0x0807
/* ESNI */
#define PTLS_ESNI_VERSION_DRAFT03 0xff02
@@ -153,6 +154,7 @@
#define PTLS_ALERT_BAD_RECORD_MAC 20
#define PTLS_ALERT_HANDSHAKE_FAILURE 40
#define PTLS_ALERT_BAD_CERTIFICATE 42
+#define PTLS_ALERT_UNSUPPORTED_CERTIFICATE 43
#define PTLS_ALERT_CERTIFICATE_REVOKED 44
#define PTLS_ALERT_CERTIFICATE_EXPIRED 45
#define PTLS_ALERT_CERTIFICATE_UNKNOWN 46
@@ -210,6 +212,9 @@
#define PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE 25
#define PTLS_HANDSHAKE_TYPE_MESSAGE_HASH 254
+#define PTLS_CERTIFICATE_TYPE_X509 0
+#define PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY 2
+
#define PTLS_ZERO_DIGEST_SHA256 \
{ \
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, \
@@ -531,6 +536,10 @@
const uint16_t *list;
size_t count;
} cipher_suites;
+ struct {
+ const uint8_t *list;
+ size_t count;
+ } server_certificate_types;
/**
* if ESNI was used
*/
@@ -567,9 +576,15 @@
* The implementor of the callback should use that as the opportunity to free any temporary data allocated for the verify_sign
* callback.
*/
-PTLS_CALLBACK_TYPE(int, verify_certificate, ptls_t *tls,
- int (**verify_sign)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data,
- ptls_iovec_t *certs, size_t num_certs);
+typedef struct st_ptls_verify_certificate_t {
+ int (*cb)(struct st_ptls_verify_certificate_t *self, ptls_t *tls,
+ int (**verify_sign)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data,
+ ptls_iovec_t *certs, size_t num_certs);
+ /**
+ * list of signature algorithms being supported, terminated by UINT16_MAX
+ */
+ const uint16_t *algos;
+} ptls_verify_certificate_t;
/**
* Encrypt-and-signs (or verify-and-decrypts) a ticket (server-only).
* When used for encryption (i.e., is_encrypt being set), the function should return 0 if successful, or else a non-zero value.
@@ -708,7 +723,20 @@
*/
unsigned omit_end_of_early_data : 1;
/**
- * server cipher preference
+ * This option turns on support for Raw Public Keys (RFC 7250).
+ *
+ * When running as a client, this option instructs the client to request the server to send raw public keys in place of X.509
+ * certificate chain. The client should set its `certificate_verify` callback to one that is capable of validating the raw
+ * public key that will be sent by the server.
+ *
+ * When running as a server, this option instructs the server to only handle clients requesting the use of raw public keys. If
+ * the client does not, the handshake is rejected. Note however that the rejection happens only after the `on_client_hello`
+ * callback is being called. Therefore, applications can support both X.509 and raw public keys by swapping `ptls_context_t` to
+ * the correct one when that callback is being called (like handling swapping the contexts based on the value of SNI).
+ */
+ unsigned use_raw_public_keys : 1;
+ /**
+ * boolean indicating if the cipher-suite should be chosen based on server's preference
*/
unsigned server_cipher_preference : 1;
/**
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 2bfe1b9..8e26cf8 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -50,9 +50,12 @@
#define PTLS_OPENSSL_HAS_SECP521R1 1 /* deprecated; use HAVE_ */
extern ptls_key_exchange_algorithm_t ptls_openssl_secp521r1;
#endif
+#ifdef EVP_PKEY_ED25519
+#define PTLS_OPENSSL_HAVE_ED25519 1
+#endif
#if defined(NID_X25519) && !defined(LIBRESSL_VERSION_NUMBER)
#define PTLS_OPENSSL_HAVE_X25519 1
-#define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
+#define PTLS_OPENSSL_HAS_X25519 1 /* deprecated; use HAVE_ */
extern ptls_key_exchange_algorithm_t ptls_openssl_x25519;
#endif
#ifndef OPENSSL_NO_BF
@@ -91,19 +94,24 @@
struct st_ptls_openssl_signature_scheme_t {
uint16_t scheme_id;
- const EVP_MD *scheme_md;
+ const EVP_MD *(*scheme_md)(void);
};
typedef struct st_ptls_openssl_sign_certificate_t {
ptls_sign_certificate_t super;
EVP_PKEY *key;
- struct st_ptls_openssl_signature_scheme_t schemes[4]; /* terminated by .scheme_id == UINT16_MAX */
+ const struct st_ptls_openssl_signature_scheme_t *schemes; /* terminated by .scheme_id == UINT16_MAX */
} ptls_openssl_sign_certificate_t;
int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key);
void ptls_openssl_dispose_sign_certificate(ptls_openssl_sign_certificate_t *self);
int ptls_openssl_load_certificates(ptls_context_t *ctx, X509 *cert, STACK_OF(X509) * chain);
+typedef struct st_ptls_openssl_raw_pubkey_verify_certificate_t {
+ ptls_verify_certificate_t super;
+ EVP_PKEY *expected_pubkey;
+} ptls_openssl_raw_pubkey_verify_certificate_t;
+
typedef struct st_ptls_openssl_verify_certificate_t {
ptls_verify_certificate_t super;
X509_STORE *cert_store;
@@ -113,6 +121,9 @@
void ptls_openssl_dispose_verify_certificate(ptls_openssl_verify_certificate_t *self);
X509_STORE *ptls_openssl_create_default_certificate_store(void);
+int ptls_openssl_raw_pubkey_init_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self, EVP_PKEY *pubkey);
+void ptls_openssl_raw_pubkey_dispose_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self);
+
int ptls_openssl_encrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
int (*cb)(unsigned char *, unsigned char *, EVP_CIPHER_CTX *, HMAC_CTX *, int));
int ptls_openssl_decrypt_ticket(ptls_buffer_t *dst, ptls_iovec_t src,
diff --git a/lib/openssl.c b/lib/openssl.c
index 00c7777..80613bb 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -88,6 +88,85 @@
#endif
+static const struct st_ptls_openssl_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256},
+ {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384},
+ {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512},
+ {UINT16_MAX, NULL}};
+static const struct st_ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {
+ {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256}, {UINT16_MAX, NULL}};
+#if PTLS_OPENSSL_HAVE_SECP384R1
+static const struct st_ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {
+ {PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384}, {UINT16_MAX, NULL}};
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+static const struct st_ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
+ {PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512}, {UINT16_MAX, NULL}};
+#endif
+#if PTLS_OPENSSL_HAVE_ED25519
+static const struct st_ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL},
+ {UINT16_MAX, NULL}};
+#endif
+
+/**
+ * The default list sent in ClientHello.signature_algorithms. ECDSA certificates are preferred.
+ */
+static const uint16_t default_signature_schemes[] = {
+#if PTLS_OPENSSL_HAVE_ED25519
+ PTLS_SIGNATURE_ED25519,
+#endif
+ PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
+#if PTLS_OPENSSL_HAVE_SECP384R1
+ PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384,
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+ PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512,
+#endif
+ PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512,
+ PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384,
+ PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256,
+ UINT16_MAX};
+
+static const struct st_ptls_openssl_signature_scheme_t *lookup_signature_schemes(EVP_PKEY *key)
+{
+ const struct st_ptls_openssl_signature_scheme_t *schemes = NULL;
+
+ switch (EVP_PKEY_id(key)) {
+ case EVP_PKEY_RSA:
+ schemes = rsa_signature_schemes;
+ break;
+ case EVP_PKEY_EC: {
+ EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(key);
+ switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) {
+ case NID_X9_62_prime256v1:
+ schemes = secp256r1_signature_schemes;
+ break;
+#if PTLS_OPENSSL_HAVE_SECP384R1
+ case NID_secp384r1:
+ schemes = secp384r1_signature_schemes;
+ break;
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+ case NID_secp521r1:
+ schemes = secp521r1_signature_schemes;
+ break;
+#endif
+ default:
+ break;
+ }
+ EC_KEY_free(eckey);
+ } break;
+#if PTLS_OPENSSL_HAVE_ED25519
+ case EVP_PKEY_ED25519:
+ schemes = ed25519_signature_schemes;
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return schemes;
+}
+
void ptls_openssl_random_bytes(void *buf, size_t len)
{
int ret = RAND_bytes(buf, (int)len);
@@ -613,9 +692,11 @@
}
}
-static int do_sign(EVP_PKEY *key, ptls_buffer_t *outbuf, ptls_iovec_t input, const EVP_MD *md)
+static int do_sign(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
+ ptls_iovec_t input)
{
EVP_MD_CTX *ctx = NULL;
+ const EVP_MD *md = scheme->scheme_md != NULL ? scheme->scheme_md() : NULL;
EVP_PKEY_CTX *pkey_ctx;
size_t siglen;
int ret;
@@ -624,38 +705,58 @@
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
+
if (EVP_DigestSignInit(ctx, &pkey_ctx, md, NULL, key) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
- if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
- if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+
+#if PTLS_OPENSSL_HAVE_ED25519
+ if (EVP_PKEY_id(key) == EVP_PKEY_ED25519) {
+ /* ED25519 requires the use of the all-at-once function that appeared in OpenSSL 1.1.1, hence different path */
+ if (EVP_DigestSign(ctx, NULL, &siglen, input.base, input.len) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
- if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+ if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
+ goto Exit;
+ if (EVP_DigestSign(ctx, outbuf->base + outbuf->off, &siglen, input.base, input.len) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
- if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) {
+ } else
+#endif
+ {
+ if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
+ if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, md) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ }
+ if (EVP_DigestSignUpdate(ctx, input.base, input.len) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_DigestSignFinal(ctx, NULL, &siglen) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
+ goto Exit;
+ if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
}
- if (EVP_DigestSignUpdate(ctx, input.base, input.len) != 1) {
- ret = PTLS_ERROR_LIBRARY;
- goto Exit;
- }
- if (EVP_DigestSignFinal(ctx, NULL, &siglen) != 1) {
- ret = PTLS_ERROR_LIBRARY;
- goto Exit;
- }
- if ((ret = ptls_buffer_reserve(outbuf, siglen)) != 0)
- goto Exit;
- if (EVP_DigestSignFinal(ctx, outbuf->base + outbuf->off, &siglen) != 1) {
- ret = PTLS_ERROR_LIBRARY;
- goto Exit;
- }
+
outbuf->off += siglen;
ret = 0;
@@ -944,7 +1045,7 @@
ptls_openssl_sign_certificate_t *self = (ptls_openssl_sign_certificate_t *)_self;
const struct st_ptls_openssl_signature_scheme_t *scheme;
- /* select the algorithm */
+ /* select the algorithm (driven by server-side preference of `self->schemes`) */
for (scheme = self->schemes; scheme->scheme_id != UINT16_MAX; ++scheme) {
size_t i;
for (i = 0; i != num_algorithms; ++i)
@@ -955,7 +1056,7 @@
Found:
*selected_algorithm = scheme->scheme_id;
- return do_sign(self->key, outbuf, input, scheme->scheme_md);
+ return do_sign(self->key, scheme, outbuf, input);
}
static X509 *to_x509(ptls_iovec_t vec)
@@ -964,9 +1065,10 @@
return d2i_X509(NULL, &p, (long)vec.len);
}
-static int verify_sign(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature)
+static int verify_sign(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature)
{
EVP_PKEY *key = verify_ctx;
+ const struct st_ptls_openssl_signature_scheme_t *scheme;
EVP_MD_CTX *ctx = NULL;
EVP_PKEY_CTX *pkey_ctx = NULL;
int ret = 0;
@@ -974,36 +1076,65 @@
if (data.base == NULL)
goto Exit;
+ if ((scheme = lookup_signature_schemes(key)) == NULL) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ for (; scheme->scheme_id != UINT16_MAX; ++scheme)
+ if (scheme->scheme_id == algo)
+ goto SchemeFound;
+ ret = PTLS_ALERT_ILLEGAL_PARAMETER;
+ goto Exit;
+
+SchemeFound:
if ((ctx = EVP_MD_CTX_create()) == NULL) {
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
- if (EVP_DigestVerifyInit(ctx, &pkey_ctx, EVP_sha256(), NULL, key) != 1) {
- ret = PTLS_ERROR_LIBRARY;
- goto Exit;
- }
- if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
- if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+
+#if PTLS_OPENSSL_HAVE_ED25519
+ if (EVP_PKEY_id(key) == EVP_PKEY_ED25519) {
+ /* ED25519 requires the use of the all-at-once function that appeared in OpenSSL 1.1.1, hence different path */
+ if (EVP_DigestVerifyInit(ctx, &pkey_ctx, NULL, NULL, key) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
- if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+ if (EVP_DigestVerify(ctx, signature.base, signature.len, data.base, data.len) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
- if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256()) != 1) {
+ } else
+#endif
+ {
+ if (EVP_DigestVerifyInit(ctx, &pkey_ctx, scheme->scheme_md(), NULL, key) != 1) {
ret = PTLS_ERROR_LIBRARY;
goto Exit;
}
+
+ if (EVP_PKEY_id(key) == EVP_PKEY_RSA) {
+ if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, -1) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, scheme->scheme_md()) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ }
+ if (EVP_DigestVerifyUpdate(ctx, data.base, data.len) != 1) {
+ ret = PTLS_ERROR_LIBRARY;
+ goto Exit;
+ }
+ if (EVP_DigestVerifyFinal(ctx, signature.base, signature.len) != 1) {
+ ret = PTLS_ALERT_DECRYPT_ERROR;
+ goto Exit;
+ }
}
- if (EVP_DigestVerifyUpdate(ctx, data.base, data.len) != 1) {
- ret = PTLS_ERROR_LIBRARY;
- goto Exit;
- }
- if (EVP_DigestVerifyFinal(ctx, signature.base, signature.len) != 1) {
- ret = PTLS_ALERT_DECRYPT_ERROR;
- goto Exit;
- }
+
ret = 0;
Exit:
@@ -1016,50 +1147,9 @@
int ptls_openssl_init_sign_certificate(ptls_openssl_sign_certificate_t *self, EVP_PKEY *key)
{
*self = (ptls_openssl_sign_certificate_t){{sign_certificate}};
- size_t scheme_index = 0;
-#define PUSH_SCHEME(id, md) \
- self->schemes[scheme_index++] = (struct st_ptls_openssl_signature_scheme_t) \
- { \
- id, md \
- }
-
- switch (EVP_PKEY_id(key)) {
- case EVP_PKEY_RSA:
- PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256());
- PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384());
- PUSH_SCHEME(PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512());
- break;
- case EVP_PKEY_EC: {
- EC_KEY *eckey = EVP_PKEY_get1_EC_KEY(key);
- switch (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey))) {
- case NID_X9_62_prime256v1:
- PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256());
- break;
-#if defined(NID_secp384r1) && !OPENSSL_NO_SHA384
- case NID_secp384r1:
- PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384());
- break;
-#endif
-#if defined(NID_secp384r1) && !OPENSSL_NO_SHA512
- case NID_secp521r1:
- PUSH_SCHEME(PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512());
- break;
-#endif
- default:
- EC_KEY_free(eckey);
- return PTLS_ERROR_INCOMPATIBLE_KEY;
- }
- EC_KEY_free(eckey);
- } break;
- default:
+ if ((self->schemes = lookup_signature_schemes(key)) == NULL)
return PTLS_ERROR_INCOMPATIBLE_KEY;
- }
- PUSH_SCHEME(UINT16_MAX, NULL);
- assert(scheme_index <= PTLS_ELEMENTSOF(self->schemes));
-
-#undef PUSH_SCHEME
-
EVP_PKEY_up_ref(key);
self->key = key;
@@ -1200,8 +1290,9 @@
return ret;
}
-static int verify_cert(ptls_verify_certificate_t *_self, ptls_t *tls, int (**verifier)(void *, ptls_iovec_t, ptls_iovec_t),
- void **verify_data, ptls_iovec_t *certs, size_t num_certs)
+static int verify_cert(ptls_verify_certificate_t *_self, ptls_t *tls,
+ int (**verifier)(void *, uint16_t, ptls_iovec_t, ptls_iovec_t), void **verify_data, ptls_iovec_t *certs,
+ size_t num_certs)
{
ptls_openssl_verify_certificate_t *self = (ptls_openssl_verify_certificate_t *)_self;
X509 *cert = NULL;
@@ -1246,7 +1337,7 @@
int ptls_openssl_init_verify_certificate(ptls_openssl_verify_certificate_t *self, X509_STORE *store)
{
- *self = (ptls_openssl_verify_certificate_t){{verify_cert}};
+ *self = (ptls_openssl_verify_certificate_t){{verify_cert, default_signature_schemes}};
if (store != NULL) {
X509_STORE_up_ref(store);
@@ -1286,6 +1377,52 @@
return NULL;
}
+static int verify_raw_cert(ptls_verify_certificate_t *_self, ptls_t *tls,
+ int (**verifier)(void *, uint16_t algo, ptls_iovec_t, ptls_iovec_t), void **verify_data,
+ ptls_iovec_t *certs, size_t num_certs)
+{
+ ptls_openssl_raw_pubkey_verify_certificate_t *self = (ptls_openssl_raw_pubkey_verify_certificate_t *)_self;
+ int ret = PTLS_ALERT_BAD_CERTIFICATE;
+ ptls_iovec_t expected_pubkey = {};
+
+ assert(num_certs != 0);
+
+ if (num_certs != 1)
+ goto Exit;
+
+ int r = i2d_PUBKEY(self->expected_pubkey, &expected_pubkey.base);
+ if (r <= 0) {
+ ret = PTLS_ALERT_BAD_CERTIFICATE;
+ goto Exit;
+ }
+
+ expected_pubkey.len = r;
+ if (certs[0].len != expected_pubkey.len)
+ goto Exit;
+
+ if (!ptls_mem_equal(expected_pubkey.base, certs[0].base, certs[0].len))
+ goto Exit;
+
+ EVP_PKEY_up_ref(self->expected_pubkey);
+ *verify_data = self->expected_pubkey;
+ *verifier = verify_sign;
+ ret = 0;
+Exit:
+ free(expected_pubkey.base);
+ return ret;
+}
+
+int ptls_openssl_raw_pubkey_init_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self, EVP_PKEY *expected_pubkey)
+{
+ EVP_PKEY_up_ref(expected_pubkey);
+ *self = (ptls_openssl_raw_pubkey_verify_certificate_t){{verify_raw_cert, default_signature_schemes}, expected_pubkey};
+ return 0;
+}
+void ptls_openssl_raw_pubkey_dispose_verify_certificate(ptls_openssl_raw_pubkey_verify_certificate_t *self)
+{
+ EVP_PKEY_free(self->expected_pubkey);
+}
+
#define TICKET_LABEL_SIZE 16
#define TICKET_IV_SIZE EVP_MAX_IV_LENGTH
diff --git a/lib/picotls.c b/lib/picotls.c
index 2428239..e2894fb 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -56,6 +56,7 @@
#define PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS 10
#define PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS 13
#define PTLS_EXTENSION_TYPE_ALPN 16
+#define PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE 20
#define PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE 27
#define PTLS_EXTENSION_TYPE_PRE_SHARED_KEY 41
#define PTLS_EXTENSION_TYPE_EARLY_DATA 42
@@ -256,7 +257,7 @@
* will be used by the client and the server (if require_client_authentication is set).
*/
struct {
- int (*cb)(void *verify_ctx, ptls_iovec_t data, ptls_iovec_t signature);
+ int (*cb)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature);
void *verify_ctx;
} certificate_verify;
/**
@@ -284,6 +285,7 @@
#define MAX_UNKNOWN_EXTENSIONS 16
#define MAX_CLIENT_CIPHERS 32
+#define MAX_CERTIFICATE_TYPES 8
struct st_ptls_client_hello_t {
uint16_t legacy_version;
@@ -335,6 +337,10 @@
unsigned early_data_indication : 1;
unsigned is_last_extension : 1;
} psk;
+ struct {
+ uint8_t list[MAX_CERTIFICATE_TYPES];
+ size_t count;
+ } server_certificate_types;
ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];
unsigned status_request : 1;
};
@@ -462,6 +468,10 @@
ALLOW(CLIENT_HELLO);
ALLOW(SERVER_HELLO);
});
+ EXT(SERVER_CERTIFICATE_TYPE, {
+ ALLOW(CLIENT_HELLO);
+ ALLOW(ENCRYPTED_EXTENSIONS);
+ });
#undef ALLOW
#undef EXT
@@ -1509,15 +1519,16 @@
return ret;
}
-static int push_signature_algorithms(ptls_buffer_t *sendbuf)
+static int push_signature_algorithms(ptls_verify_certificate_t *vc, ptls_buffer_t *sendbuf)
{
+ /* The list sent when verify callback is not registered */
+ static const uint16_t default_algos[] = {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,
+ PTLS_SIGNATURE_RSA_PKCS1_SHA256, PTLS_SIGNATURE_RSA_PKCS1_SHA1, UINT16_MAX};
int ret;
ptls_buffer_push_block(sendbuf, 2, {
- ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256);
- ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256);
- ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA256);
- ptls_buffer_push16(sendbuf, PTLS_SIGNATURE_RSA_PKCS1_SHA1);
+ for (const uint16_t *p = vc != NULL ? vc->algos : default_algos; *p != UINT16_MAX; ++p)
+ ptls_buffer_push16(sendbuf, *p);
});
ret = 0;
@@ -2064,7 +2075,7 @@
});
});
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {
- if ((ret = push_signature_algorithms(sendbuf)) != 0)
+ if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)
goto Exit;
});
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, {
@@ -2079,6 +2090,11 @@
ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, cookie->base, cookie->len); });
});
}
+ if (tls->ctx->use_raw_public_keys) {
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE, {
+ ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });
+ });
+ }
if ((ret = push_additional_extensions(properties, sendbuf)) != 0)
goto Exit;
if (tls->ctx->save_ticket != NULL || resumption_secret.base != NULL) {
@@ -2439,6 +2455,7 @@
static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX};
ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions;
int ret, skip_early_data = 1;
+ uint8_t server_offered_cert_type = PTLS_CERTIFICATE_TYPE_X509;
decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, {
if (tls->ctx->on_extension != NULL &&
@@ -2493,6 +2510,14 @@
}
skip_early_data = 0;
break;
+ case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:
+ if (end - src != 1) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+ server_offered_cert_type = *src;
+ src = end;
+ break;
default:
if (should_collect_unknown_extension(tls, properties, type)) {
if (unknown_extensions == &no_unknown_extensions) {
@@ -2510,6 +2535,12 @@
src = end;
});
+ if (server_offered_cert_type !=
+ (tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY : PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ goto Exit;
+ }
+
if (tls->esni != NULL) {
if (esni_nonce == NULL || !ptls_mem_equal(esni_nonce, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) {
ret = PTLS_ALERT_ILLEGAL_PARAMETER;
@@ -2852,19 +2883,10 @@
src = end;
});
- /* validate */
- switch (algo) {
- case PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256:
- case PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256:
- /* ok */
- break;
- default:
- ret = PTLS_ALERT_ILLEGAL_PARAMETER;
- goto Exit;
- }
signdata_size = build_certificate_verify_signdata(signdata, tls->key_schedule, context_string);
if (tls->certificate_verify.cb != NULL) {
- ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(signdata, signdata_size), signature);
+ ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, algo, ptls_iovec_init(signdata, signdata_size),
+ signature);
} else {
ret = 0;
}
@@ -3300,6 +3322,23 @@
} while (src != end);
});
break;
+ case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:
+ ptls_decode_block(src, end, 1, {
+ size_t list_size = end - src;
+
+ /* RFC7250 4.1: No empty list, no list with single x509 element */
+ if (list_size == 0 || (list_size == 1 && *src == PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_DECODE_ERROR;
+ goto Exit;
+ }
+
+ do {
+ if (ch->server_certificate_types.count < PTLS_ELEMENTSOF(ch->server_certificate_types.list))
+ ch->server_certificate_types.list[ch->server_certificate_types.count++] = *src;
+ src++;
+ } while (src != end);
+ });
+ break;
case PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE:
ptls_decode_block(src, end, 1, {
do {
@@ -3489,7 +3528,7 @@
int64_t delta = (now - issue_at) - (identity->obfuscated_ticket_age - age_add);
if (delta < 0)
delta = -delta;
- if (delta <= PTLS_EARLY_DATA_MAX_DELAY)
+ if (tls->ctx->max_early_data_size != 0 && delta <= PTLS_EARLY_DATA_MAX_DELAY)
*accept_early_data = 1;
}
/* check server-name */
@@ -3598,6 +3637,18 @@
return 0;
}
+static int certificate_type_exists(uint8_t *list, size_t count, uint8_t desired_type)
+{
+ /* empty type list means that we default to x509 */
+ if (desired_type == PTLS_CERTIFICATE_TYPE_X509 && count == 0)
+ return 1;
+ for (size_t i = 0; i < count; i++) {
+ if (list[i] == desired_type)
+ return 1;
+ }
+ return 0;
+}
+
static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message,
ptls_handshake_properties_t *properties)
{
@@ -3649,8 +3700,9 @@
ret = PTLS_ERROR_NO_MEMORY;
goto Exit;
}
- *ch = (struct st_ptls_client_hello_t){0, NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
- {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{UINT16_MAX}}};
+
+ *ch = (struct st_ptls_client_hello_t){0, NULL, {NULL}, {NULL}, 0, {NULL}, {NULL}, {NULL}, {{0}},
+ {NULL}, {NULL}, {{{NULL}}}, {{0}}, {{0}}, {{NULL}}, {NULL}, {{0}}, {{UINT16_MAX}}};
/* decode ClientHello */
if ((ret = decode_client_hello(tls, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) !=
@@ -3736,15 +3788,24 @@
{ch->signature_algorithms.list, ch->signature_algorithms.count},
{ch->cert_compression_algos.list, ch->cert_compression_algos.count},
{ch->client_ciphers.list, ch->client_ciphers.count},
+ {ch->server_certificate_types.list, ch->server_certificate_types.count},
is_esni};
ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms);
} else {
ret = 0;
}
+
if (is_esni)
free(server_name.base);
if (ret != 0)
goto Exit;
+
+ if (!certificate_type_exists(ch->server_certificate_types.list, ch->server_certificate_types.count,
+ tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY
+ : PTLS_CERTIFICATE_TYPE_X509)) {
+ ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;
+ goto Exit;
+ }
} else {
if (ch->psk.early_data_indication) {
ret = PTLS_ALERT_DECODE_ERROR;
@@ -4008,6 +4069,10 @@
* The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {});
}
+ if (tls->ctx->use_raw_public_keys) {
+ buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE,
+ { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });
+ }
if (tls->negotiated_protocol != NULL) {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, {
ptls_buffer_push_block(sendbuf, 2, {
@@ -4036,7 +4101,7 @@
/* extensions */
ptls_buffer_push_block(sendbuf, 2, {
buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {
- if ((ret = push_signature_algorithms(sendbuf)) != 0)
+ if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)
goto Exit;
});
});
@@ -4329,7 +4394,7 @@
free(tls->client.certificate_request.context.base);
}
if (tls->certificate_verify.cb != NULL) {
- tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0));
+ tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, 0, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0));
}
if (tls->pending_handshake_secret != NULL) {
ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE);
diff --git a/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 3fe64e9..27453aa 100644
--- a/picotls.xcodeproj/project.pbxproj
+++ b/picotls.xcodeproj/project.pbxproj
@@ -1019,10 +1019,10 @@
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
@@ -1033,10 +1033,10 @@
EXECUTABLE_PREFIX = lib;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
@@ -1143,10 +1143,10 @@
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
OTHER_LDFLAGS = "-lcrypto";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1157,10 +1157,10 @@
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
OTHER_LDFLAGS = "-lcrypto";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1193,11 +1193,11 @@
);
HEADER_SEARCH_PATHS = (
include,
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
/usr/local/include,
);
LIBRARY_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/lib",
+ /usr/local/opt/openssl/lib,
/usr/local/lib,
);
OTHER_LDFLAGS = (
@@ -1219,11 +1219,11 @@
);
HEADER_SEARCH_PATHS = (
include,
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
/usr/local/include,
);
LIBRARY_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/lib",
+ /usr/local/opt/openssl/lib,
/usr/local/lib,
);
OTHER_LDFLAGS = (
@@ -1259,10 +1259,10 @@
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
OTHER_LDFLAGS = "-lcrypto";
PRODUCT_NAME = "$(TARGET_NAME)";
};
@@ -1273,10 +1273,10 @@
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
HEADER_SEARCH_PATHS = (
- "/usr/local/openssl-1.1.0/include",
+ /usr/local/opt/openssl/include,
include,
);
- LIBRARY_SEARCH_PATHS = "/usr/local/openssl-1.1.0/lib";
+ LIBRARY_SEARCH_PATHS = /usr/local/opt/openssl/lib;
OTHER_LDFLAGS = "-lcrypto";
PRODUCT_NAME = "$(TARGET_NAME)";
};
diff --git a/t/assets/ed25519/cert.pem b/t/assets/ed25519/cert.pem
new file mode 100644
index 0000000..56e0cf9
--- /dev/null
+++ b/t/assets/ed25519/cert.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICOTCCASGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMjEwMzIxMjMxNjU2WhcNMzEwMzE5MjMxNjU2WjAjMSEw
+HwYDVQQDDBhlZDI1NTE5LnRlc3QuZXhhbXBsZS5jb20wKjAFBgMrZXADIQDKBvBk
+m3KyTPvBdVJTrtmR5V7/OtlJTmZmPg972cKuEaN7MHkwCQYDVR0TBAIwADAsBglg
+hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
+BBYEFD2F2ds0fQ6SbWUHViucoIl/kY8BMB8GA1UdIwQYMBaAFL95ypeyYHgglqpG
+V5zfp7Ij9SVjMA0GCSqGSIb3DQEBCwUAA4IBAQBN6/tmJrdkPDxGQ+kbGuh6KlaT
+FzywvfyaArpOoNWRHuDgzyOYOJ7XCaChW4GeVk+zRLxC1ZVrbn9kL5LDP9oKdnTn
+dPOsj4Zmn3er9zPlRgauvGAd5DuHk1n3fdIKhw/zusB3MK5iQWIsmSx9jFEx+8Sf
+CMe5aFqzZO1JHRaR/yrKLxxhabhqjP7Xad6Dz9uMQpOPEddOi93iun589yTI40d3
+UrB81XJW9Ll1SnCs8qYl99D9Kcq8OWjFjxrKiEFHw0z8oWag+M8bgqnjvYZOZln4
+Mnm9mOJtsGjtuQ+wvUMt3AWhJ4iI0eEebXxDjnZFMoipXa/L3B+h/DnnH2GN
+-----END CERTIFICATE-----
diff --git a/t/assets/ed25519/key.pem b/t/assets/ed25519/key.pem
new file mode 100644
index 0000000..6461749
--- /dev/null
+++ b/t/assets/ed25519/key.pem
@@ -0,0 +1,3 @@
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIFh3irfeqAxMuC/lkrh12Q5Qz/h5JfkvSDhgncoYF1t8
+-----END PRIVATE KEY-----
diff --git a/t/assets/ed25519/pub.pem b/t/assets/ed25519/pub.pem
new file mode 100644
index 0000000..04b8564
--- /dev/null
+++ b/t/assets/ed25519/pub.pem
@@ -0,0 +1,3 @@
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAygbwZJtyskz7wXVSU67ZkeVe/zrZSU5mZj4Pe9nCrhE=
+-----END PUBLIC KEY-----
diff --git a/t/assets/server.crt b/t/assets/rsa/cert.pem
similarity index 100%
rename from t/assets/server.crt
rename to t/assets/rsa/cert.pem
diff --git a/t/assets/server.key b/t/assets/rsa/key.pem
similarity index 100%
rename from t/assets/server.key
rename to t/assets/rsa/key.pem
diff --git a/t/assets/rsa/pub.pem b/t/assets/rsa/pub.pem
new file mode 100644
index 0000000..52b7731
--- /dev/null
+++ b/t/assets/rsa/pub.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7zZheZ4ph98JaedBNv9k
+qsVA9CSmhd69kBc9ZAfVFMA4VQwprOj3ZGrxf20HB3FkvqGvew9ZogUF6NjbPume
+iUObGpP21Y5wcYlPL4aojlrwMB/eOxOCpuRyQTRSSe1hDPvdJABQdmshDP5ZSEBL
+dUSgrNn4KWhIDjFj1AHXIMqeqTXetFuRgNzHdtbXQx+UWBis2B6qZJuqSArb2msV
+OC8D5gNznPPlQw7FbdPCaLNXSb6GnI0E0uj6QmYlAw9s6nkgP/zxjfFldqPNUprG
+cEqTwmAb8VVtd7XbANYrzubZ4Nn6/WXrCrVxWUmh/7Spgdwa/I4Nr1JHv9HHyL2z
+/wIDAQAB
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp256r1/cert.pem b/t/assets/secp256r1/cert.pem
new file mode 100644
index 0000000..ef2ae2c
--- /dev/null
+++ b/t/assets/secp256r1/cert.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICYDCCAUigAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMTgwMjIzMDUzMTA0WhcNMjgwMjIxMDUzMTA0WjAbMRkw
+FwYDVQQDExB0ZXN0LmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD
+QgAE2silQFS6M9oYqUF/SVPfYOamPbaOUzqf3RkUXqsDz7z7NpgWJI8HKW0V2E8w
+6Alk+xT8hnzUBsL9neiZP0iMK6N7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0E
+HxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFO4whhah
+0mmtZOTXd2uy/VxPAaK1MB8GA1UdIwQYMBaAFL95ypeyYHgglqpGV5zfp7Ij9SVj
+MA0GCSqGSIb3DQEBCwUAA4IBAQCPrJwBbYGqjK5dtRZ06ujrJluxZtVr1E15DW2H
+qba/dC3Bsi5StkvKDQFFOFga0mptIJhaUbBvLD8PEojtfAmldAAhPUvSLVSqU4tk
++R7qpYrnYV5WklI2PqBoWZx9s+hcS3du3ijtGJGpnDnSlsyYBYx03B4SWzi9Vsuj
+6OEqWivSMkXBEIUgbGs06maRDi64ZIefB7wjTyOtvonfCphH6WMC00H0LaTO3ePY
+QQj+30fA52OOH/BLxa6rwLo4PuOQnAi9dRy5uFRDHZlC4KK3dbsUA3ma9gfYpasr
+OnCLd4Vwipg4mzUJ9mJrKUqnp/k73tjIkFfydiojCwFoxpry
+-----END CERTIFICATE-----
diff --git a/t/assets/secp256r1/key.pem b/t/assets/secp256r1/key.pem
new file mode 100644
index 0000000..71ba2ed
--- /dev/null
+++ b/t/assets/secp256r1/key.pem
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIMF0tPle/noBDr5K6DOyNhP8Zellkag5npqA+6vR/7o6oAoGCCqGSM49
+AwEHoUQDQgAE2silQFS6M9oYqUF/SVPfYOamPbaOUzqf3RkUXqsDz7z7NpgWJI8H
+KW0V2E8w6Alk+xT8hnzUBsL9neiZP0iMKw==
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp256r1/pub.pem b/t/assets/secp256r1/pub.pem
new file mode 100644
index 0000000..0402946
--- /dev/null
+++ b/t/assets/secp256r1/pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2silQFS6M9oYqUF/SVPfYOamPbaO
+Uzqf3RkUXqsDz7z7NpgWJI8HKW0V2E8w6Alk+xT8hnzUBsL9neiZP0iMKw==
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp384r1/cert.pem b/t/assets/secp384r1/cert.pem
new file mode 100644
index 0000000..8c03b51
--- /dev/null
+++ b/t/assets/secp384r1/cert.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIIChzCCAW+gAwIBAgIBAzANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMjEwMzIyMDAxNDM0WhcNMzEwMzIwMDAxNDM0WjAlMSMw
+IQYDVQQDDBpzZWNwMzg0cjEudGVzdC5leGFtcGxlLmNvbTB2MBAGByqGSM49AgEG
+BSuBBAAiA2IABAEP8gHosiRYm0qAIUQsVhmG2EHz8X4AsgGU/zjF1imDNj5t3DiI
+k1/CEZ/eBGW9Qc+KPPZvC1sHUWzxWSCbFnk10g32fD+Gj8IdVC0r9SVMiahisZ+A
+H/HC3J5LprjFWqN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNT
+TCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFMPSpIyQI1TYn+1ZlIMJ
+DdUig6DxMB8GA1UdIwQYMBaAFL95ypeyYHgglqpGV5zfp7Ij9SVjMA0GCSqGSIb3
+DQEBCwUAA4IBAQA3yL5Ip2hXTqqSuRkN+IRLnZQyJrrUHU4fiGmgaNdPSAXHgCoE
+2Wd2TMBJk2kbIFjORIumVIpUoQCTUtGjraDP5hgwZCCpTIztwnp7esOMwGVPFnqx
+mz4DEGyfbDP8LdWgdZN4xtshXSMgFUY9u3cWQUNxWN7WDL61byeHw5GZPKf+SD99
+fQOstYzO8XAeT3xCKNiVmO1rFztRrP/uy3nBbC45TKv1yaJCa6gcOBdNSXi6EH8l
+XWz0ASAuF7/jQg97QwG31IUtDvI7yIJ2Q8qLS68cBRjv2nw8rTCQ/Uv3ABG9mX+f
+bWcOgMQI6c8vOOPZ/61+mVo5R3vBSrevVODF
+-----END CERTIFICATE-----
diff --git a/t/assets/secp384r1/key.pem b/t/assets/secp384r1/key.pem
new file mode 100644
index 0000000..849b07d
--- /dev/null
+++ b/t/assets/secp384r1/key.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDDKRvwSCQcP5gx5uH0nUkITtNjhXTyWQOBxixOqvBviKE+8ypVluHbg
+Ya/evmF1A52gBwYFK4EEACKhZANiAAQBD/IB6LIkWJtKgCFELFYZhthB8/F+ALIB
+lP84xdYpgzY+bdw4iJNfwhGf3gRlvUHPijz2bwtbB1Fs8VkgmxZ5NdIN9nw/ho/C
+HVQtK/UlTImoYrGfgB/xwtyeS6a4xVo=
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp384r1/pub.pem b/t/assets/secp384r1/pub.pem
new file mode 100644
index 0000000..2238c41
--- /dev/null
+++ b/t/assets/secp384r1/pub.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEAQ/yAeiyJFibSoAhRCxWGYbYQfPxfgCy
+AZT/OMXWKYM2Pm3cOIiTX8IRn94EZb1Bz4o89m8LWwdRbPFZIJsWeTXSDfZ8P4aP
+wh1ULSv1JUyJqGKxn4Af8cLcnkumuMVa
+-----END PUBLIC KEY-----
diff --git a/t/assets/secp521r1/cert.pem b/t/assets/secp521r1/cert.pem
new file mode 100644
index 0000000..28a76c1
--- /dev/null
+++ b/t/assets/secp521r1/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICrTCCAZWgAwIBAgIBBDANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDEw9waWNv
+dGxzIHRlc3QgY2EwHhcNMjEwMzIyMDAzNDQ1WhcNMzEwMzIwMDAzNDQ1WjAlMSMw
+IQYDVQQDDBpzZWNwNTIxcjEudGVzdC5leGFtcGxlLmNvbTCBmzAQBgcqhkjOPQIB
+BgUrgQQAIwOBhgAEACyVCnKujpMyv58r9L/3HL0pM9Pxgx/07c5/f+SC1ukqYo8l
+IuLuC78nB3mn4wAB8PsT8pz8XQZoYBI552lj+HBiAHsTki9Y+amzcDo5QClzoqO6
+GtXN7HOILsqBgFUblf+av6k19OX185X+qnO8Pb1KwwviEWXDoku4QTIWoQjj4VGS
+o3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRl
+ZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUv1vbGGuBcMs5zEdTGHWLkJ5mWwgwHwYD
+VR0jBBgwFoAUv3nKl7JgeCCWqkZXnN+nsiP1JWMwDQYJKoZIhvcNAQELBQADggEB
+ABLBvD2LuyS84AGroVEVCp6qijpJWwzQ7Sk8AZQYr0mPUK+mdX/44/8OC2bKlUye
+LtfRBurbQjLXtn4UC8qGJVcBdnYkvKKna+GaPgD8qduzqeyJot3oMD3lZOvg1cZg
+yUynOmFpDQMR+WGufhaHf68Q2ggR11ShX5fxOniMWgz4ogz6rhfVjBqE1Xe3/cFf
++Vadeb66XaEXGhCU5wlJSOYI1+5oZBkXxZGwsOANQmfenDxfNNcd9ZioZimHZf62
+FatHHkIQYt3Lhg9oPl1RCiLDBz3ly0QriZwz3Y/biLQKjNfaoRlVGIcmwOIu7rcc
+ZDEnl40J122rLwtMlw9a0+s=
+-----END CERTIFICATE-----
diff --git a/t/assets/secp521r1/key.pem b/t/assets/secp521r1/key.pem
new file mode 100644
index 0000000..8ec1749
--- /dev/null
+++ b/t/assets/secp521r1/key.pem
@@ -0,0 +1,10 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIA2fGi1/ysuxTrmnDf6rdqCDzvPomZKLbOCNAckjas5FDIYvpYoYI9
+0gUpNTZWhL/50Wckg1kxmUNR10lRDb4bNKmgBwYFK4EEACOhgYkDgYYABAAslQpy
+ro6TMr+fK/S/9xy9KTPT8YMf9O3Of3/kgtbpKmKPJSLi7gu/Jwd5p+MAAfD7E/Kc
+/F0GaGASOedpY/hwYgB7E5IvWPmps3A6OUApc6KjuhrVzexziC7KgYBVG5X/mr+p
+NfTl9fOV/qpzvD29SsML4hFlw6JLuEEyFqEI4+FRkg==
+-----END EC PRIVATE KEY-----
diff --git a/t/assets/secp521r1/pub.pem b/t/assets/secp521r1/pub.pem
new file mode 100644
index 0000000..656e591
--- /dev/null
+++ b/t/assets/secp521r1/pub.pem
@@ -0,0 +1,6 @@
+-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQALJUKcq6OkzK/nyv0v/ccvSkz0/GD
+H/Ttzn9/5ILW6SpijyUi4u4LvycHeafjAAHw+xPynPxdBmhgEjnnaWP4cGIAexOS
+L1j5qbNwOjlAKXOio7oa1c3sc4guyoGAVRuV/5q/qTX05fXzlf6qc7w9vUrDC+IR
+ZcOiS7hBMhahCOPhUZI=
+-----END PUBLIC KEY-----
diff --git a/t/cli.c b/t/cli.c
index bce9a65..db6bf82 100644
--- a/t/cli.c
+++ b/t/cli.c
@@ -97,6 +97,7 @@
goto Exit;
}
}
+
if (server_name != NULL) {
ptls_set_server_name(tls, server_name, 0);
if ((ret = ptls_handshake(tls, &encbuf, NULL, NULL, hsprop)) != PTLS_ERROR_IN_PROGRESS) {
@@ -358,6 +359,10 @@
" -E esni-file file that stores ESNI data generated by picotls-esni\n"
" -e when resuming a session, send first 8,192 bytes of input\n"
" as early data\n"
+ " -r public-key-file use raw public keys (RFC 7250). When set and running as a\n"
+ " client, the argument specifies the public keys that the\n"
+ " server is expected to use. When running as a server, the\n"
+ " argument is ignored.\n"
" -u update the traffic key when handshake is complete\n"
" -v verify peer using the default certificates\n"
" -y cipher-suite cipher-suite to be used, e.g., aes128gcmsha256 (default:\n"
@@ -374,6 +379,17 @@
#if PTLS_OPENSSL_HAVE_X25519
", X25519"
#endif
+ "\n"
+ "Supported signature algorithms: rsa, secp256r1"
+#if PTLS_OPENSSL_HAVE_SECP384R1
+ ", secp384r1"
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+ ", secp521r1"
+#endif
+#if PTLS_OPENSSL_HAVE_ED25519
+ ", ed25519"
+#endif
"\n\n",
cmd);
}
@@ -404,8 +420,9 @@
struct sockaddr_storage sa;
socklen_t salen;
int family = 0;
+ const char *raw_pub_key_file = NULL, *cert_location = NULL;
- while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:SE:K:l:y:vh")) != -1) {
+ while ((ch = getopt(argc, argv, "46abBC:c:i:Ik:nN:es:Sr:E:K:l:y:vh")) != -1) {
switch (ch) {
case '4':
family = AF_INET;
@@ -429,11 +446,11 @@
break;
case 'C':
case 'c':
- if (ctx.certificates.count != 0) {
+ if (cert_location != NULL) {
fprintf(stderr, "-C/-c can only be specified once\n");
return 1;
}
- load_certificate_chain(&ctx, optarg);
+ cert_location = optarg;
is_server = ch == 'c';
break;
case 'i':
@@ -451,6 +468,9 @@
case 'e':
use_early_data = 1;
break;
+ case 'r':
+ raw_pub_key_file = optarg;
+ break;
case 's':
setup_session_file(&ctx, &hsprop, optarg);
break;
@@ -540,10 +560,36 @@
}
argc -= optind;
argv += optind;
+
+ if (raw_pub_key_file != NULL) {
+ int is_dash = !strcmp(raw_pub_key_file, "-");
+ if (is_server) {
+ ctx.certificates.list = malloc(sizeof(*ctx.certificates.list));
+ load_raw_public_key(ctx.certificates.list, cert_location);
+ ctx.certificates.count = 1;
+ } else if (!is_dash) {
+ ptls_iovec_t raw_pub_key;
+ EVP_PKEY *pubkey;
+ load_raw_public_key(&raw_pub_key, raw_pub_key_file);
+ pubkey = d2i_PUBKEY(NULL, (const unsigned char **)&raw_pub_key.base, raw_pub_key.len);
+ if (pubkey == NULL) {
+ fprintf(stderr, "Failed to create an EVP_PKEY from the key found in %s\n", raw_pub_key_file);
+ return 1;
+ }
+ setup_raw_pubkey_verify_certificate(&ctx, pubkey);
+ EVP_PKEY_free(pubkey);
+ }
+ ctx.use_raw_public_keys = 1;
+ } else {
+ if (cert_location)
+ load_certificate_chain(&ctx, cert_location);
+ }
+
if ((ctx.certificates.count == 0) != (ctx.sign_certificate == NULL)) {
fprintf(stderr, "-C/-c and -k options must be used together\n");
return 1;
}
+
if (is_server) {
if (ctx.certificates.count == 0) {
fprintf(stderr, "-c and -k options must be set\n");
diff --git a/t/e2e.t b/t/e2e.t
index 577bc20..ada3109 100755
--- a/t/e2e.t
+++ b/t/e2e.t
@@ -16,7 +16,7 @@
my $tempdir = tempdir(CLEANUP => 1);
subtest "hello" => sub {
- my $guard = spawn_server(qw(-i t/assets/hello.txt));
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt));
subtest "full-handshake" => sub {
my $resp = `$cli 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
@@ -35,7 +35,7 @@
subtest "success" => sub {
plan skip_all => "faketime not found"
unless system("which faketime > /dev/null 2>&1") == 0;
- my $guard = spawn_server(qw(-i t/assets/hello.txt -l), "$tempdir/events");
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt -l), "$tempdir/events");
my $resp = `$cli -s $tempdir/session 127.0.0.1 $port`;
is $resp, "hello";
$resp = `$cli -e -s $tempdir/session 127.0.0.1 $port`;
@@ -63,17 +63,38 @@
subtest "certificate-compression" => sub {
plan skip_all => "feature disabled"
unless system("$cli -b -h > /dev/null 2>&1") == 0;
- my $guard = spawn_server(qw(-i t/assets/hello.txt -b));
+ my $guard = spawn_server("rsa", qw(-i t/assets/hello.txt -b));
my $resp = `$cli 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
$resp = `$cli -b 127.0.0.1 $port 2> /dev/null`;
is $resp, "hello";
};
+# This test acts as an end-to-end testing of the certificate verifier of the OpenSSL backend.
+subtest "raw-public-keys" => sub {
+ my @key_types = do {
+ my $help = `$cli -h`;
+ $help =~ /^Supported signature algorithms:\s*(.*)\s*$/m
+ or die "failed to extract list of supported signature algorithms from $cli -h";
+ split /,\s*/, $1;
+ };
+ die "unexpected list of supported signature algorithms: @key_types"
+ unless grep /^rsa$/, @key_types;
+ for my $key_type (@key_types) {
+ subtest $key_type => sub {
+ my $guard = spawn_server($key_type, qw(-r - -i t/assets/hello.txt));
+ my $resp = `$cli -v -r t/assets/$key_type/pub.pem 127.0.0.1 $port 2> /dev/null`;
+ is $resp, "hello";
+ };
+ }
+};
+
done_testing;
sub spawn_server {
- my @cmd = ($cli, "-k", "t/assets/server.key", "-c", "t/assets/server.crt", @_, "127.0.0.1", $port);
+ my $key_type = shift;
+ my $cert_type = grep(/^-r$/, @_) ? "pub" : "cert";
+ my @cmd = ($cli, "-k", "t/assets/$key_type/key.pem", "-c", "t/assets/$key_type/$cert_type.pem", @_, "127.0.0.1", $port);
my $pid = fork;
die "fork failed:$!"
unless defined $pid;
diff --git a/t/openssl.c b/t/openssl.c
index fb52775..ff47eee 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -129,47 +129,74 @@
#endif
}
+static void test_sign_verify(EVP_PKEY *key, const struct st_ptls_openssl_signature_scheme_t *schemes)
+{
+ for (size_t i = 0; schemes[i].scheme_id != UINT16_MAX; ++i) {
+ note("scheme 0x%04x", schemes[i].scheme_id);
+ const void *message = "hello world";
+ ptls_buffer_t sigbuf;
+ uint8_t sigbuf_small[1024];
+
+ ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
+ ok(do_sign(key, schemes + i, &sigbuf, ptls_iovec_init(message, strlen(message))) == 0);
+ EVP_PKEY_up_ref(key);
+ ok(verify_sign(key, schemes[i].scheme_id, ptls_iovec_init(message, strlen(message)),
+ ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
+
+ ptls_buffer_dispose(&sigbuf);
+ }
+}
+
static void test_rsa_sign(void)
{
ptls_openssl_sign_certificate_t *sc = (ptls_openssl_sign_certificate_t *)ctx->sign_certificate;
-
- const void *message = "hello world";
- ptls_buffer_t sigbuf;
- uint8_t sigbuf_small[1024];
-
- ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
- ok(do_sign(sc->key, &sigbuf, ptls_iovec_init(message, strlen(message)), EVP_sha256()) == 0);
- EVP_PKEY_up_ref(sc->key);
- ok(verify_sign(sc->key, ptls_iovec_init(message, strlen(message)), ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
-
- ptls_buffer_dispose(&sigbuf);
+ test_sign_verify(sc->key, sc->schemes);
}
-static void test_ecdsa_sign(void)
+static void do_test_ecdsa_sign(int nid, const struct st_ptls_openssl_signature_scheme_t *schemes)
{
EVP_PKEY *pkey;
{ /* create pkey */
- EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ EC_KEY *eckey = EC_KEY_new_by_curve_name(nid);
EC_KEY_generate_key(eckey);
pkey = EVP_PKEY_new();
EVP_PKEY_set1_EC_KEY(pkey, eckey);
EC_KEY_free(eckey);
}
- const char *message = "hello world";
- ptls_buffer_t sigbuf;
- uint8_t sigbuf_small[1024];
-
- ptls_buffer_init(&sigbuf, sigbuf_small, sizeof(sigbuf_small));
- ok(do_sign(pkey, &sigbuf, ptls_iovec_init(message, strlen(message)), EVP_sha256()) == 0);
- EVP_PKEY_up_ref(pkey);
- ok(verify_sign(pkey, ptls_iovec_init(message, strlen(message)), ptls_iovec_init(sigbuf.base, sigbuf.off)) == 0);
-
- ptls_buffer_dispose(&sigbuf);
+ test_sign_verify(pkey, schemes);
EVP_PKEY_free(pkey);
}
+static void test_ecdsa_sign(void)
+{
+ do_test_ecdsa_sign(NID_X9_62_prime256v1, secp256r1_signature_schemes);
+#if PTLS_OPENSSL_HAVE_SECP384R1
+ do_test_ecdsa_sign(NID_secp384r1, secp384r1_signature_schemes);
+#endif
+#if PTLS_OPENSSL_HAVE_SECP521R1
+ do_test_ecdsa_sign(NID_secp521r1, secp521r1_signature_schemes);
+#endif
+}
+
+static void test_ed25519_sign(void)
+{
+#if PTLS_OPENSSL_HAVE_ED25519
+ EVP_PKEY *pkey = NULL;
+
+ { /* create pkey */
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL);
+ EVP_PKEY_keygen_init(pctx);
+ EVP_PKEY_keygen(pctx, &pkey);
+ EVP_PKEY_CTX_free(pctx);
+ }
+
+ test_sign_verify(pkey, ed25519_signature_schemes);
+ EVP_PKEY_free(pkey);
+#endif
+}
+
static X509 *x509_from_pem(const char *pem)
{
BIO *bio = BIO_new_mem_buf((void *)pem, (int)strlen(pem));
@@ -308,6 +335,7 @@
subtest("rsa-sign", test_rsa_sign);
subtest("ecdsa-sign", test_ecdsa_sign);
+ subtest("ed25519-sign", test_ed25519_sign);
subtest("cert-verify", test_cert_verify);
subtest("picotls", test_picotls);
test_picotls_esni(esni_private_keys);
diff --git a/t/util.h b/t/util.h
index db86416..3890737 100644
--- a/t/util.h
+++ b/t/util.h
@@ -48,6 +48,15 @@
}
}
+static inline void load_raw_public_key(ptls_iovec_t *raw_public_key, char const *cert_pem_file)
+{
+ size_t count;
+ if (ptls_load_pem_objects(cert_pem_file, "PUBLIC KEY", raw_public_key, 1, &count) != 0) {
+ fprintf(stderr, "failed to load public key:%s:%s\n", cert_pem_file, strerror(errno));
+ exit(1);
+ }
+}
+
static inline void load_private_key(ptls_context_t *ctx, const char *fn)
{
static ptls_openssl_sign_certificate_t sc;
@@ -122,6 +131,13 @@
ctx->verify_certificate = &vc.super;
}
+static inline void setup_raw_pubkey_verify_certificate(ptls_context_t *ctx, EVP_PKEY *pubkey)
+{
+ static ptls_openssl_raw_pubkey_verify_certificate_t vc;
+ ptls_openssl_raw_pubkey_init_verify_certificate(&vc, pubkey);
+ ctx->verify_certificate = &vc.super;
+}
+
static inline void setup_esni(ptls_context_t *ctx, const char *esni_fn, ptls_key_exchange_context_t **key_exchanges)
{
uint8_t esnikeys[65536];