On Sun, Jun 16, 2019 at 09:25:45AM +0200, Florian Obser wrote:
> On Sat, Jun 15, 2019 at 06:20:49PM +0200, Florian Obser wrote:
> > OK?
> 
> update to apply cleanly on -current

The acctproc.c change looks ok with some minor comments inline.
I haven't tried to verify the JSON stuff.

> 
> 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)

I would suggest using get0 as in the RSA case ...

> +             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);

...and drop this EC_KEY_free()

> +     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");

same here: get0...

> +     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);

... and omit EC_KEY_free()

> +     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;

To simplify slightly, you could use 

        unsigned char            dig[EVP_MAX_MD_SIZE];

instead of allocating dig dynamically (EVP_MAX_MD_SIZE is 64).

> +     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) {

use get0

> +                     warnx("EVP_PKEY_get1_EC_KEY");
> +                     goto out;
> +             }
> +             degree = EC_GROUP_get_degree(EC_KEY_get0_group(ec));
> +             bn_len = (degree + 7) / 8;

This calculation is fine if you use curves over a prime field GFp (as
you currently do), but it will be incorrect if you should ever want to
use a curve over a binary field GF2m.

> +             EC_KEY_free(ec);

if you switched to get0 above, don't use EC_KEY_free().

> +
> +             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 de9b5e25e25..a49415e1610 100644
> --- main.c
> +++ main.c
> @@ -270,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], authority->account);
> +             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