OK?

diff --git acctproc.c acctproc.c
index 52309b765d5..09ac8c83372 100644
--- acctproc.c
+++ acctproc.c
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include <openssl/pem.h>
+#include <openssl/evp.h>
 #include <openssl/rsa.h>
 #include <openssl/rand.h>
 #include <openssl/err.h>
@@ -90,6 +91,42 @@ op_thumb_rsa(EVP_PKEY *pkey)
        return json;
 }
 
+/*
+ * Extract the relevant EC components from the key and create the JSON
+ * thumbprint from them.
+ */
+static char *
+op_thumb_ec(EVP_PKEY *pkey)
+{
+       BIGNUM  *X = NULL, *Y = NULL;
+       EC_KEY  *ec = NULL;
+       char    *x = NULL, *y = NULL;
+       char    *json = NULL;
+
+       if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
+               warnx("EVP_PKEY_get1_EC_KEY");
+       else if ((X = BN_new()) == NULL)
+               warnx("BN_new");
+       else if ((Y = BN_new()) == NULL)
+               warnx("BN_new");
+       else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
+           EC_KEY_get0_public_key(ec), X, Y, NULL))
+               warnx("EC_POINT_get_affine_coordinates_GFp");
+       else if ((x = bn2string(X)) == NULL)
+               warnx("bn2string");
+       else if ((y = bn2string(Y)) == NULL)
+               warnx("bn2string");
+       else if ((json = json_fmt_thumb_ec(x, y)) == NULL)
+               warnx("json_fmt_thumb_rsa");
+
+       EC_KEY_free(ec);
+       BN_free(X);
+       BN_free(Y);
+       free(x);
+       free(y);
+       return json;
+}
+
 /*
  * The thumbprint operation is used for the challenge sequence.
  */
@@ -109,6 +146,10 @@ op_thumbprint(int fd, EVP_PKEY *pkey)
                if ((thumb = op_thumb_rsa(pkey)) != NULL)
                        break;
                goto out;
+       case EVP_PKEY_EC:
+               if ((thumb = op_thumb_ec(pkey)) != NULL)
+                       break;
+               goto out;
        default:
                warnx("EVP_PKEY_type: unknown key type");
                goto out;
@@ -183,6 +224,42 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, const char 
*nonce, const char *url)
        return rc;
 }
 
+static int
+op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
+{
+       BIGNUM  *X = NULL, *Y = NULL;
+       EC_KEY  *ec = NULL;
+       char    *x = NULL, *y = NULL;
+       int     rc = 0;
+
+       *prot = NULL;
+
+       if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
+               warnx("EVP_PKEY_get1_EC_KEY");
+       else if ((X = BN_new()) == NULL)
+               warnx("BN_new");
+       else if ((Y = BN_new()) == NULL)
+               warnx("BN_new");
+       else if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
+           EC_KEY_get0_public_key(ec), X, Y, NULL))
+               warnx("EC_POINT_get_affine_coordinates_GFp");
+       else if ((x = bn2string(X)) == NULL)
+               warnx("bn2string");
+       else if ((y = bn2string(Y)) == NULL)
+               warnx("bn2string");
+       else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL)
+               warnx("json_fmt_protected_ec");
+       else
+               rc = 1;
+
+       EC_KEY_free(ec);
+       BN_free(X);
+       BN_free(Y);
+       free(x);
+       free(y);
+       return rc;
+}
+
 /*
  * Operation to sign a message with the account key.
  * This requires the sender ("fd") to provide the payload and a nonce.
@@ -190,14 +267,19 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, const char 
*nonce, const char *url)
 static int
 op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
 {
-       char            *nonce = NULL, *pay = NULL, *pay64 = NULL;
-       char            *prot = NULL, *prot64 = NULL;
-       char            *sign = NULL, *dig64 = NULL, *fin = NULL;
-       char            *url = NULL, *kid = NULL;
-       unsigned char   *dig = NULL;
-       EVP_MD_CTX      *ctx = NULL;
-       int              cc, rc = 0;
-       unsigned int     digsz;
+       EVP_MD_CTX              *ctx = NULL;
+       const EVP_MD            *evp_md = NULL;
+       EC_KEY                  *ec;
+       ECDSA_SIG               *ec_sig = NULL;
+       const BIGNUM            *ec_sig_r = NULL, *ec_sig_s = NULL;
+       int                      cc, rc = 0;
+       unsigned int             digsz, bufsz, degree, bn_len, r_len, s_len;
+       char                    *nonce = NULL, *pay = NULL, *pay64 = NULL;
+       char                    *prot = NULL, *prot64 = NULL;
+       char                    *sign = NULL, *dig64 = NULL, *fin = NULL;
+       char                    *url = NULL, *kid = NULL, *alg = NULL;
+       unsigned char           *dig = NULL, *buf = NULL;
+       const unsigned char     *digp;
 
        /* Read our payload and nonce from the requestor. */
 
@@ -219,8 +301,23 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
                goto out;
        }
 
+       switch (EVP_PKEY_type(pkey->type)) {
+       case EVP_PKEY_RSA:
+               alg = "RS256";
+               evp_md = EVP_sha256();
+               break;
+       case EVP_PKEY_EC:
+               alg = "ES384";
+               evp_md = EVP_sha384();
+               break;
+       default:
+               warnx("unknown account key type");
+               goto out;
+       }
+
        if (op == ACCT_KID_SIGN) {
-               if ((prot = json_fmt_protected_kid(kid, nonce, url)) == NULL) {
+               if ((prot = json_fmt_protected_kid(alg, kid, nonce, url)) ==
+                   NULL) {
                        warnx("json_fmt_protected_kid");
                        goto out;
                }
@@ -230,6 +327,10 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
                        if (!op_sign_rsa(&prot, pkey, nonce, url))
                                goto out;
                        break;
+               case EVP_PKEY_EC:
+                       if (!op_sign_ec(&prot, pkey, nonce, url))
+                               goto out;
+                       break;
                default:
                        warnx("EVP_PKEY_type");
                        goto out;
@@ -265,7 +366,7 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
        if ((ctx = EVP_MD_CTX_create()) == NULL) {
                warnx("EVP_MD_CTX_create");
                goto out;
-       } else if (!EVP_SignInit_ex(ctx, EVP_sha256(), NULL)) {
+       } else if (!EVP_SignInit_ex(ctx, evp_md, NULL)) {
                warnx("EVP_SignInit_ex");
                goto out;
        } else if (!EVP_SignUpdate(ctx, sign, strlen(sign))) {
@@ -274,8 +375,58 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
        } else if (!EVP_SignFinal(ctx, dig, &digsz, pkey)) {
                warnx("EVP_SignFinal");
                goto out;
-       } else if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
-               warnx("base64buf_url");
+       }
+
+       switch (EVP_PKEY_type(pkey->type)) {
+       case EVP_PKEY_RSA:
+               if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
+                       warnx("base64buf_url");
+                       goto out;
+               }
+               break;
+       case EVP_PKEY_EC:
+               if ((ec = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) {
+                       warnx("EVP_PKEY_get1_EC_KEY");
+                       goto out;
+               }
+               degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec));
+               bn_len = (degree + 7) / 8;
+               EC_KEY_free(ec);
+
+               digp = dig; /* d2i_ECDSA_SIG advances digp */
+               if ((ec_sig = d2i_ECDSA_SIG(NULL, &digp, digsz)) == NULL) {
+                       warnx("d2i_ECDSA_SIG");
+                       goto out;
+               }
+
+               ECDSA_SIG_get0(ec_sig, &ec_sig_r, &ec_sig_s);
+
+               r_len = BN_num_bytes(ec_sig_r);
+               s_len = BN_num_bytes(ec_sig_s);
+
+               if((r_len > bn_len) || (s_len > bn_len)) {
+                       warnx("ECDSA_SIG_get0");
+                       goto out;
+               }
+
+               bufsz = 2 * bn_len;
+               if ((buf = calloc(1, bufsz)) == NULL) {
+                       warnx("calloc");
+                       goto out;
+               }
+
+               /* put r and s in with leading zeros if any */
+               BN_bn2bin(ec_sig_r, buf + bn_len - r_len);
+               BN_bn2bin(ec_sig_s, buf + bufsz - s_len);
+
+               if ((dig64 = base64buf_url((char *)buf, bufsz)) == NULL) {
+                       warnx("base64buf_url");
+                       goto out;
+               }
+
+               break;
+       default:
+               warnx("EVP_PKEY_type");
                goto out;
        }
 
@@ -307,11 +458,12 @@ out:
        free(dig);
        free(dig64);
        free(fin);
+       free(buf);
        return rc;
 }
 
 int
-acctproc(int netsock, const char *acctkey)
+acctproc(int netsock, const char *acctkey, enum keytype keytype)
 {
        FILE            *f = NULL;
        EVP_PKEY        *pkey = NULL;
@@ -348,15 +500,23 @@ acctproc(int netsock, const char *acctkey)
        }
 
        if (newacct) {
-               if ((pkey = rsa_key_create(f, acctkey)) == NULL)
-                       goto out;
-               dodbg("%s: generated RSA account key", acctkey);
+               switch (keytype) {
+               case KT_ECDSA:
+                       if ((pkey = ec_key_create(f, acctkey)) == NULL)
+                               goto out;
+                       dodbg("%s: generated ECDSA account key", acctkey);
+                       break;
+               case KT_RSA:
+                       if ((pkey = rsa_key_create(f, acctkey)) == NULL)
+                               goto out;
+                       dodbg("%s: generated RSA account key", acctkey);
+                       break;
+               }
        } else {
                if ((pkey = key_load(f, acctkey)) == NULL)
                        goto out;
-               if (EVP_PKEY_type(pkey->type) != EVP_PKEY_RSA) 
-                       goto out;
-               doddbg("%s: loaded RSA account key", acctkey);
+               /* XXX check if account key type equals configured key type */
+               doddbg("%s: loaded account key", acctkey);
        }
 
        fclose(f);
diff --git acme-client.conf.5 acme-client.conf.5
index 3df0ca38915..5852d8da839 100644
--- acme-client.conf.5
+++ acme-client.conf.5
@@ -83,10 +83,17 @@ is a string used to reference this certificate authority.
 .Pp
 It is followed by a block of options enclosed in curly brackets:
 .Bl -tag -width Ds
-.It Ic account key Ar file
+.It Ic account key Ar file Op Ar keytype
 Specify a
 .Ar file
 used to identify the user of this certificate authority.
+.Ar keytype
+can be
+.Cm rsa
+or
+.Cm ecdsa .
+It defaults to
+.Cm rsa .
 .It Ic api url Ar url
 Specify the
 .Ar url
diff --git extern.h extern.h
index d533466fbe6..7fcbf77d88f 100644
--- extern.h
+++ extern.h
@@ -199,7 +199,7 @@ __BEGIN_DECLS
  * Start with our components.
  * These are all isolated and talk to each other using sockets.
  */
-int             acctproc(int, const char *);
+int             acctproc(int, const char *, enum keytype);
 int             certproc(int, int);
 int             chngproc(int, const char *);
 int             dnsproc(int);
@@ -265,10 +265,13 @@ char              *json_fmt_newacc(void);
 char           *json_fmt_neworder(const char *const *, size_t);
 char           *json_fmt_protected_rsa(const char *,
                        const char *, const char *, const char *);
-char           *json_fmt_protected_kid(const char *, const char *,
+char           *json_fmt_protected_ec(const char *, const char *, const char *,
+                       const char *);
+char           *json_fmt_protected_kid(const char*, const char *, const char *,
                        const char *);
 char           *json_fmt_revokecert(const char *);
 char           *json_fmt_thumb_rsa(const char *, const char *);
+char           *json_fmt_thumb_ec(const char *, const char *);
 char           *json_fmt_signed(const char *, const char *, const char *);
 
 /*
diff --git json.c json.c
index bee5c83c724..baae3c3a4a9 100644
--- json.c
+++ json.c
@@ -733,18 +733,43 @@ json_fmt_protected_rsa(const char *exp, const char *mod, 
const char *nce,
  * Protected component of json_fmt_signed().
  */
 char *
-json_fmt_protected_kid(const char *kid, const char *nce, const char *url)
+json_fmt_protected_ec(const char *x, const char *y, const char *nce,
+    const char *url)
 {
        int      c;
        char    *p;
 
        c = asprintf(&p, "{"
-           "\"alg\": \"RS256\", "
+           "\"alg\": \"ES384\", "
+           "\"jwk\": "
+           "{\"crv\": \"P-384\", \"kty\": \"EC\", \"x\": \"%s\", "
+           "\"y\": \"%s\"}, \"nonce\": \"%s\", \"url\": \"%s\""
+           "}",
+           x, y, nce, url);
+       if (c == -1) {
+               warn("asprintf");
+               p = NULL;
+       }
+       return p;
+}
+
+/*
+ * Protected component of json_fmt_signed().
+ */
+char *
+json_fmt_protected_kid(const char *alg, const char *kid, const char *nce,
+    const char *url)
+{
+       int      c;
+       char    *p;
+
+       c = asprintf(&p, "{"
+           "\"alg\": \"%s\", "
            "\"kid\": \"%s\", "
            "\"nonce\": \"%s\", "
            "\"url\": \"%s\""
            "}",
-           kid, nce, url);
+           alg, kid, nce, url);
        if (c == -1) {
                warn("asprintf");
                p = NULL;
@@ -796,3 +821,27 @@ json_fmt_thumb_rsa(const char *exp, const char *mod)
        }
        return p;
 }
+
+/*
+ * Produce thumbprint input.
+ * This isn't technically a JSON string--it's the input we'll use for
+ * hashing and digesting.
+ * However, it's in the form of a JSON string, so do it here.
+ */
+char *
+json_fmt_thumb_ec(const char *x, const char *y)
+{
+       int      c;
+       char    *p;
+
+       /*NOTE: WHITESPACE IS IMPORTANT. */
+
+       c = asprintf(&p, "{\"crv\":\"P-384\",\"kty\":\"EC\",\"x\":\"%s\","
+           "\"y\":\"%s\"}",
+           x, y);
+       if (c == -1) {
+               warn("asprintf");
+               p = NULL;
+       }
+       return p;
+}
diff --git main.c main.c
index 1a2c1749e49..a49415e1610 100644
--- main.c
+++ main.c
@@ -38,7 +38,6 @@ main(int argc, char *argv[])
        const char       **alts = NULL;
        char             *certdir = NULL, *certfile = NULL;
        char             *chainfile = NULL, *fullchainfile = NULL;
-       char             *acctkey = NULL;
        char             *chngdir = NULL, *auth = NULL;
        char             *conffile = CONF_FILE;
        char             *tmps, *tmpsd;
@@ -147,8 +146,6 @@ main(int argc, char *argv[])
                        errx(EXIT_FAILURE, "authority %s not found", auth);
        }
 
-       acctkey = authority->account;
-
        if ((chngdir = domain->challengedir) == NULL)
                if ((chngdir = strdup(WWW_DIR)) == NULL)
                        err(EXIT_FAILURE, "strdup");
@@ -273,7 +270,8 @@ main(int argc, char *argv[])
                close(chng_fds[0]);
                close(file_fds[0]);
                close(file_fds[1]);
-               c = acctproc(acct_fds[0], acctkey);
+               c = acctproc(acct_fds[0], authority->account,
+                   authority->keytype);
                exit(c ? EXIT_SUCCESS : EXIT_FAILURE);
        }
 
diff --git parse.h parse.h
index 95229a42441..d7bd3b2a7b4 100644
--- parse.h
+++ parse.h
@@ -34,9 +34,10 @@ enum keytype {
 
 struct authority_c {
        TAILQ_ENTRY(authority_c)         entry;
-       char                    *name;
-       char                    *api;
-       char                    *account;
+       char                            *name;
+       char                            *api;
+       char                            *account;
+       enum keytype                     keytype;
 };
 
 struct domain_c {
diff --git parse.y parse.y
index 0fdab0d7435..2b6bdb3135a 100644
--- parse.y
+++ parse.y
@@ -219,7 +219,7 @@ authorityoptsl      : API URL STRING {
                                err(EXIT_FAILURE, "strdup");
                        auth->api = s;
                }
-               | ACCOUNT KEY STRING {
+               | ACCOUNT KEY STRING keytype{
                        char *s;
                        if (auth->account != NULL) {
                                yyerror("duplicate account");
@@ -228,6 +228,7 @@ authorityoptsl      : API URL STRING {
                        if ((s = strdup($3)) == NULL)
                                err(EXIT_FAILURE, "strdup");
                        auth->account = s;
+                       auth->keytype = $4;
                }
                ;
 
@@ -1020,7 +1021,8 @@ print_config(struct acme_conf *xconf)
                if (a->api != NULL)
                        printf("\tapi url \"%s\"\n", a->api);
                if (a->account != NULL)
-                       printf("\taccount key \"%s\"\n", a->account);
+                       printf("\taccount key \"%s\" %s\n", a->account,
+                           kt2txt(a->keytype));
                printf("}\n\n");
        }
        TAILQ_FOREACH(d, &xconf->domain_list, entry) {

-- 
I'm not entirely sure you are real.

Reply via email to