Merge pull request #517 from h2o/kazuho/pr501

MbedTLS sign certificate
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dc146a8..f42f21a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -204,7 +204,7 @@
     message(STATUS "mbedtls/include: ${MBEDTLS_INCLUDE_DIRS}")
     message(STATUS "mbedtls libraries: ${MBEDTLS_LIBRARIES}")
     INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIRS})
-    ADD_LIBRARY(picotls-mbedtls lib/mbedtls.c)
+    ADD_LIBRARY(picotls-mbedtls lib/mbedtls.c lib/mbedtls_sign.c)
     ADD_EXECUTABLE(test-mbedtls.t
         deps/picotest/picotest.c
         ${CORE_TEST_FILES}
diff --git a/include/picotls/mbedtls.h b/include/picotls/mbedtls.h
index b649246..62fd382 100644
--- a/include/picotls/mbedtls.h
+++ b/include/picotls/mbedtls.h
@@ -60,6 +60,9 @@
 
 void ptls_mbedtls_random_bytes(void *buf, size_t len);
 
+int ptls_mbedtls_load_private_key(ptls_context_t *ctx, char const *pem_fname);
+void ptls_mbedtls_dispose_sign_certificate(ptls_sign_certificate_t *_self);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/mbedtls_sign.c b/lib/mbedtls_sign.c
index 2e167ae..de06175 100644
--- a/lib/mbedtls_sign.c
+++ b/lib/mbedtls_sign.c
@@ -36,7 +36,7 @@
 #include <psa/crypto.h>
 #include <psa/crypto_struct.h>
 #include <psa/crypto_values.h>
-#include "ptls_mbedtls.h"
+/* #include "ptls_mbedtls.h" */
 
 typedef struct st_ptls_mbedtls_signature_scheme_t {
     uint16_t scheme_id;
@@ -58,6 +58,7 @@
                                                                         {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, PSA_ALG_SHA_384},
                                                                         {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, PSA_ALG_SHA_512},
                                                                         {UINT16_MAX, PSA_ALG_NONE}};
+
 static const ptls_mbedtls_signature_scheme_t secp256r1_signature_schemes[] = {
     {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, PSA_ALG_SHA_256}, {UINT16_MAX, PSA_ALG_NONE}};
 static const ptls_mbedtls_signature_scheme_t secp384r1_signature_schemes[] = {
@@ -69,6 +70,8 @@
 
 #if defined(MBEDTLS_PEM_PARSE_C)
 
+/* Mapping of MBEDTLS APIs to Picotls */
+
 static int ptls_mbedtls_parse_der_length(const unsigned char *pem_buf, size_t pem_len, size_t *px, size_t *pl)
 {
     int ret = 0;
@@ -94,8 +97,6 @@
 static int ptls_mbedtls_parse_ecdsa_field(const unsigned char *pem_buf, size_t pem_len, size_t *key_index, size_t *key_length)
 {
     int ret = 0;
-    int param_index_index = -1;
-    int param_length = 0;
     size_t x = 0;
 
     // const unsigned char head = { 0x30, l-2, 0x02, 0x01, 0x01, 0x04 }
@@ -166,6 +167,7 @@
         if (x + l_key != *key_index + *key_length) {
             ret = -1;
         } else {
+
             *key_index = x;
             *key_length = l_key;
         }
@@ -241,7 +243,6 @@
         /* At that point the oid has been identified.
          * The next parameter is an octet string containing the key info.
          */
-        size_t l = 0;
         if (x + 2 > pem_len || pem_buf[x++] != 0x04) {
             ret = -1;
         } else {
@@ -292,6 +293,7 @@
     } else if (ret == MBEDTLS_ERR_PEM_PASSWORD_REQUIRED) {
         return MBEDTLS_ERR_PK_PASSWORD_REQUIRED;
     } else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) {
+
         return ret;
     }
 #endif /* MBEDTLS_RSA_C */
@@ -352,6 +354,7 @@
                                                                             const uint16_t *algorithms, size_t num_algorithms)
 {
     const ptls_mbedtls_signature_scheme_t *scheme;
+
     /* select the algorithm, driven by server-isde preference of `available` */
     for (scheme = available; scheme->scheme_id != UINT16_MAX; ++scheme) {
         for (size_t i = 0; i != num_algorithms; ++i) {
@@ -446,6 +449,7 @@
         /* First prepare the hash */
         unsigned char hash_buffer[PTLS_MAX_DIGEST_SIZE];
         unsigned char *hash_value = NULL;
+
         size_t hash_length = 0;
 
         if (scheme->hash_algo == PSA_ALG_NONE) {
@@ -476,7 +480,6 @@
             }
             if ((ret = ptls_buffer_reserve(outbuf, nb_bytes)) == 0) {
                 size_t signature_length = 0;
-
                 if (psa_sign_hash(self->key_id, sign_algo, hash_value, hash_length, outbuf->base + outbuf->off, nb_bytes,
                                   &signature_length) != 0) {
                     ret = PTLS_ERROR_INCOMPATIBLE_KEY;
@@ -602,7 +605,7 @@
     unsigned char *buf;
     mbedtls_pem_context pem = {0};
     mbedtls_pk_type_t pk_type = 0;
-    mbedtls_svc_key_id_t key_id = 0;
+    /* mbedtls_svc_key_id_t key_id = 0; */
     size_t key_length = 0;
     size_t key_index = 0;
     ptls_mbedtls_sign_certificate_t *signer = (ptls_mbedtls_sign_certificate_t *)malloc(sizeof(ptls_mbedtls_sign_certificate_t));
diff --git a/t/mbedtls.c b/t/mbedtls.c
index af3ec95..1ea3416 100644
--- a/t/mbedtls.c
+++ b/t/mbedtls.c
@@ -34,6 +34,22 @@
 #include "../deps/picotest/picotest.h"
 #include "test.h"
 
+typedef struct st_ptls_mbedtls_signature_scheme_t {
+    uint16_t scheme_id;
+    psa_algorithm_t hash_algo;
+} ptls_mbedtls_signature_scheme_t;
+
+typedef struct st_ptls_mbedtls_sign_certificate_t {
+    ptls_sign_certificate_t super;
+    mbedtls_svc_key_id_t key_id;
+    psa_key_attributes_t attributes;
+    const ptls_mbedtls_signature_scheme_t *schemes;
+} ptls_mbedtls_sign_certificate_t;
+
+int ptls_mbedtls_sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, ptls_async_job_t **async,
+                                  uint16_t *selected_algorithm, ptls_buffer_t *outbuf, ptls_iovec_t input,
+                                  const uint16_t *algorithms, size_t num_algorithms);
+
 static int random_trial()
 {
     /* The random test is just trying to check that we call the API properly.
@@ -89,6 +105,157 @@
     subtest("x25519", test_x25519);
 }
 
+/*
+Sign certificate implements a callback:
+
+if ((ret = tls->ctx->sign_certificate->cb(
+tls->ctx->sign_certificate, tls, tls->is_server ? &tls->server.async_job : NULL, &algo, sendbuf,
+ptls_iovec_init(data, datalen), signature_algorithms != NULL ? signature_algorithms->list : NULL,
+signature_algorithms != NULL ? signature_algorithms->count : 0)) != 0) {
+
+or:
+
+static int sign_certificate(ptls_sign_certificate_t *_self, ptls_t *tls, ptls_async_job_t **async, uint16_t *selected_algorithm,
+ptls_buffer_t *outbuf, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms)
+
+The callback "super" type is ptls_sign_certificate_t, defined by the macro:
+PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, ptls_async_job_t **async, uint16_t *selected_algorithm,
+ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms);
+
+The notation is simple: input buffer and supported algorithms as input, selected algo and output buffer as output.
+Output buffer is already partially filled.
+
+*/
+
+#define ASSET_RSA_KEY "t/assets/rsa/key.pem"
+#define ASSET_RSA_PKCS8_KEY "t/assets/rsa-pkcs8/key.pem"
+#define ASSET_SECP256R1_KEY "t/assets/secp256r1/key.pem"
+#define ASSET_SECP384R1_KEY "t/assets/secp384r1/key.pem"
+#define ASSET_SECP521R1_KEY "t/assets/secp521r1/key.pem"
+#define ASSET_SECP256R1_PKCS8_KEY "t/assets/secp256r1-pkcs8/key.pem"
+
+int test_load_one_der_key(char const *path)
+{
+    int ret = -1;
+    unsigned char hash[32];
+    const unsigned char h0[32] = {1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+                                  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
+    ptls_context_t ctx = {0};
+
+    ret = ptls_mbedtls_load_private_key(&ctx, path);
+    if (ret != 0) {
+        printf("Cannot create sign_certificate from: %s\n", path);
+        ret = -1;
+    } else if (ctx.sign_certificate == NULL) {
+        printf("Sign_certificate not set in ptls context for: %s\n", path);
+        ret = -1;
+    } else {
+        /* Try to sign something */
+        int ret;
+        ptls_mbedtls_sign_certificate_t *signer =
+            (ptls_mbedtls_sign_certificate_t *)(((unsigned char *)ctx.sign_certificate) -
+                                                offsetof(struct st_ptls_mbedtls_sign_certificate_t, super));
+        /* get the key algorithm */
+        ptls_buffer_t outbuf;
+        uint8_t outbuf_smallbuf[256];
+        ptls_iovec_t input = {hash, sizeof(hash)};
+        uint16_t selected_algorithm = 0;
+        int num_algorithms = 0;
+        uint16_t algorithms[16];
+        memcpy(hash, h0, 32);
+        while (signer->schemes[num_algorithms].scheme_id != UINT16_MAX && num_algorithms < 16) {
+            algorithms[num_algorithms] = signer->schemes[num_algorithms].scheme_id;
+            num_algorithms++;
+        }
+
+        ptls_buffer_init(&outbuf, outbuf_smallbuf, sizeof(outbuf_smallbuf));
+
+        ret = ptls_mbedtls_sign_certificate(ctx.sign_certificate, NULL, NULL, &selected_algorithm, &outbuf, input, algorithms,
+                                            num_algorithms);
+        if (ret == 0) {
+            printf("Signed a message, key: %s, scheme: %x, signature size: %zu\n", path, selected_algorithm, outbuf.off);
+        } else {
+            printf("Sign failed, key: %s, scheme: %x, signature size: %zu\n", path, selected_algorithm, outbuf.off);
+        }
+        ptls_buffer_dispose(&outbuf);
+        ptls_mbedtls_dispose_sign_certificate(&signer->super);
+    }
+    return ret;
+}
+
+static void test_load_rsa_key()
+{
+    int ret = test_load_one_der_key(ASSET_RSA_KEY);
+
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_load_secp256r1_key()
+{
+    int ret = test_load_one_der_key(ASSET_SECP256R1_KEY);
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_load_secp384r1_key()
+{
+    int ret = test_load_one_der_key(ASSET_SECP384R1_KEY);
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_load_secp521r1_key()
+{
+    int ret = test_load_one_der_key(ASSET_SECP521R1_KEY);
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_load_secp256r1_pkcs8_key()
+{
+    int ret = test_load_one_der_key(ASSET_SECP256R1_PKCS8_KEY);
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_load_rsa_pkcs8_key()
+{
+    int ret = test_load_one_der_key(ASSET_RSA_PKCS8_KEY);
+    if (ret != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+void test_sign_certificate(void)
+{
+    subtest("load rsa key", test_load_rsa_key);
+    subtest("load secp256r1 key", test_load_secp256r1_key);
+    subtest("load secp384r1 key", test_load_secp384r1_key);
+    subtest("load secp521r1 key", test_load_secp521r1_key);
+    subtest("load secp521r1-pkcs8 key", test_load_secp256r1_pkcs8_key);
+    subtest("load rsa-pkcs8 key", test_load_rsa_pkcs8_key);
+
+    /* we do not test EDDSA keys, because they are not yet supported */
+}
+
 DEFINE_FFX_AES128_ALGORITHMS(mbedtls);
 DEFINE_FFX_CHACHA20_ALGORITHMS(mbedtls);
 
@@ -143,6 +310,9 @@
     ctx_peer = &mbedtls_ctx;
     subtest("minicrypto vs.", test_picotls);
 
+    /* test the sign certificate */
+    subtest("sign certificate", test_sign_certificate);
+
     /* Deinitialize the PSA crypto library. */
     mbedtls_psa_crypto_free();