Download raw body.
acme-client: support for external account binding
On Sat, May 02, 2026 at 07:53:50PM +0200, Theo Buehler wrote:
> On Sat, May 02, 2026 at 07:09:37PM +0200, Florian Obser wrote:
> > On 2026-05-01 14:09 +10, Jonathan Matthew <jonathan@d14n.org> wrote:
> > > The ACME protocol includes a scheme allowing a client to bind an ACME
> > > account key with a account in some non-ACME ("external") system run by
> > > the CA. This is described in section 7.3.4 of RFC 8555. In short, the
> > > CA gives you a key out-of-band and your ACME client HMACs your account
> > > details with that key and sends that to the ACME server.
> >
> > I know some of these words! My Joo Janta 200 conveniently turned black
> > when looking at the acctproc changes. That stuff is certainly over my
> > head. In the past tb@ pointed out the errors of my ways, maybe we can
> > trick him into reviewing those bits.
>
> I haven't checked against the spec, but it looks fine to me, just two
> things:
>
> > > + /* sign with the EAB key */
> > > + dig = malloc(eab_key_len);
> > > + HMAC(EVP_sha256(), eab_key, eab_key_len, sign, sign_len, dig, &digsz);
>
> Both malloc and HMAC should be error checked (against NULL).
>
> > > @@ -630,6 +630,14 @@ json_fmt_newacc(const char *contact)
> > > return NULL;
> > > }
> > > }
> > > + if (eab != NULL) {
> > > + char *ecnt = NULL;
> > > + c = asprintf(&ecnt, "%s\"externalAccountBinding\": %s, ",
> > > + cnt == NULL ? "" : cnt, eab);
>
> Pretty sure this should return NULL if c == -1.
Thanks, here's an updated version with comments addressed.
Index: acctproc.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/acctproc.c,v
diff -u -p -u -p -r1.32 acctproc.c
--- acctproc.c 29 Aug 2023 14:44:53 -0000 1.32
+++ acctproc.c 6 May 2026 03:58:31 -0000
@@ -28,6 +28,7 @@
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
+#include <openssl/hmac.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
@@ -182,18 +183,13 @@ out:
}
static int
-op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
+op_sign_rsa(char **jwk, EVP_PKEY *pkey)
{
char *exp = NULL, *mod = NULL;
int rc = 0;
RSA *r;
- *prot = NULL;
-
- /*
- * First, extract relevant portions of our private key.
- * Finally, format the header combined with the nonce.
- */
+ *jwk = NULL;
if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
warnx("EVP_PKEY_get0_RSA");
@@ -201,8 +197,8 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey,
warnx("bn2string");
else if ((exp = bn2string(RSA_get0_e(r))) == NULL)
warnx("bn2string");
- else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL)
- warnx("json_fmt_protected_rsa");
+ else if ((*jwk = json_fmt_jwk_rsa(exp, mod)) == NULL)
+ warnx("json_fmt_jwk_rsa");
else
rc = 1;
@@ -212,14 +208,14 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey,
}
static int
-op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
+op_sign_ec(char **jwk, EVP_PKEY *pkey)
{
BIGNUM *X = NULL, *Y = NULL;
EC_KEY *ec = NULL;
char *x = NULL, *y = NULL;
int rc = 0;
- *prot = NULL;
+ *jwk = NULL;
if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
warnx("EVP_PKEY_get0_EC_KEY");
@@ -234,8 +230,8 @@ op_sign_ec(char **prot, EVP_PKEY *pkey,
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 if ((*jwk = json_fmt_jwk_ec(x, y)) == NULL)
+ warnx("json_fmt_jwk_ec");
else
rc = 1;
@@ -262,6 +258,7 @@ op_sign(int fd, EVP_PKEY *pkey, enum acc
char *prot = NULL, *prot64 = NULL;
char *sign = NULL, *dig64 = NULL, *fin = NULL;
char *url = NULL, *kid = NULL, *alg = NULL;
+ char *jwk = NULL;
const unsigned char *digp;
unsigned char *dig = NULL, *buf = NULL;
size_t digsz;
@@ -309,17 +306,21 @@ op_sign(int fd, EVP_PKEY *pkey, enum acc
} else {
switch (EVP_PKEY_base_id(pkey)) {
case EVP_PKEY_RSA:
- if (!op_sign_rsa(&prot, pkey, nonce, url))
+ if (!op_sign_rsa(&jwk, pkey))
goto out;
break;
case EVP_PKEY_EC:
- if (!op_sign_ec(&prot, pkey, nonce, url))
+ if (!op_sign_ec(&jwk, pkey))
goto out;
break;
default:
warnx("EVP_PKEY_base_id");
goto out;
}
+
+ prot = json_fmt_protected_jwk(alg, jwk, nonce, url);
+ if (prot == NULL)
+ goto out;
}
/* The header combined with the nonce, base64. */
@@ -434,6 +435,7 @@ out:
free(pay64);
free(url);
free(nonce);
+ free(jwk);
free(kid);
free(prot);
free(prot64);
@@ -444,8 +446,96 @@ out:
return rc;
}
+static int
+op_eab(int fd, EVP_PKEY *pkey, const char *eab_kid,
+ const unsigned char *eab_key, int eab_key_len)
+{
+ char *url = NULL;
+ char *prot = NULL, *prot64 = NULL;
+ char *jwk = NULL, *pay64 = NULL;
+ char *sign = NULL;
+ char *dig64 = NULL, *fin = NULL;
+ unsigned char dig[EVP_MAX_MD_SIZE];
+ unsigned int digsz;
+ int sign_len, rc = 0;
+
+ if ((url = readstr(fd, COMM_URL)) == NULL)
+ goto out;
+
+ /* protected component: algorithm, EAB key ID, new account URL */
+ if ((prot = json_fmt_protected_eab(eab_kid, url)) == NULL) {
+ warnx("json_fmt_protected_eab");
+ goto out;
+ }
+
+ if ((prot64 = base64buf_url(prot, strlen(prot))) == NULL) {
+ warnx("base64buf_url");
+ goto out;
+ }
+
+ /* payload component: account public key */
+ switch (EVP_PKEY_base_id(pkey)) {
+ case EVP_PKEY_RSA:
+ if (!op_sign_rsa(&jwk, pkey))
+ goto out;
+ break;
+ case EVP_PKEY_EC:
+ if (!op_sign_ec(&jwk, pkey))
+ goto out;
+ break;
+ default:
+ warnx("EVP_PKEY_base_id");
+ goto out;
+ }
+
+ if ((pay64 = base64buf_url(jwk, strlen(jwk))) == NULL) {
+ warnx("base64buf_url");
+ goto out;
+ }
+
+ sign_len = asprintf(&sign, "%s.%s", prot64, pay64);
+ if (sign_len == -1) {
+ warn("asprintf");
+ sign = NULL;
+ goto out;
+ }
+
+ /* sign with the EAB key */
+ if (HMAC(EVP_sha256(), eab_key, eab_key_len, sign, sign_len, dig,
+ &digsz) == NULL) {
+ warnx("HMAC");
+ goto out;
+ }
+
+ if ((dig64 = base64buf_url((char *)dig, digsz)) == NULL) {
+ warnx("base64buf_url");
+ goto out;
+ }
+
+ if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) {
+ warnx("json_fmt_signed");
+ goto out;
+ } else if (writestr(fd, COMM_REQ, fin) < 0) {
+ goto out;
+ }
+
+ rc = 1;
+out:
+
+ free(fin);
+ free(dig64);
+ free(sign);
+ free(pay64);
+ free(jwk);
+ free(prot64);
+ free(prot);
+ free(url);
+ return rc;
+}
+
int
-acctproc(int netsock, const char *acctkey, enum keytype keytype)
+acctproc(int netsock, const char *acctkey, enum keytype keytype,
+ const char *eab_kid, const unsigned char *eab_key, int eab_key_len)
{
FILE *f = NULL;
EVP_PKEY *pkey = NULL;
@@ -522,7 +612,7 @@ acctproc(int netsock, const char *acctke
if ((lval = readop(netsock, COMM_ACCT)) == 0)
op = ACCT_STOP;
else if (lval == ACCT_SIGN || lval == ACCT_KID_SIGN ||
- lval == ACCT_THUMBPRINT)
+ lval == ACCT_THUMBPRINT || lval == ACCT_EAB)
op = lval;
if (ACCT__MAX == op) {
@@ -542,6 +632,12 @@ acctproc(int netsock, const char *acctke
if (op_thumbprint(netsock, pkey))
break;
warnx("op_thumbprint");
+ goto out;
+ case ACCT_EAB:
+ if (op_eab(netsock, pkey, eab_kid, eab_key,
+ eab_key_len))
+ break;
+ warnx("op_eab");
goto out;
default:
abort();
Index: acme-client.1
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/acme-client.1,v
diff -u -p -u -p -r1.43 acme-client.1
--- acme-client.1 18 Sep 2025 14:13:27 -0000 1.43
+++ acme-client.1 6 May 2026 03:58:31 -0000
@@ -24,6 +24,7 @@
.Nm acme-client
.Op Fl Fnrv
.Op Fl f Ar configfile
+.Op Fl e Ar keyid:key
.Ar handle
.Sh DESCRIPTION
.Nm
@@ -67,10 +68,27 @@ location "/.well-known/acme-challenge/*"
}
.Ed
.Pp
+Some signing authorities require the client to perform external account
+binding to associate the ACME account key generated by
+.Nm
+with an existing account in a non-ACME system such as a CA customer database.
+To enable ACME account binding, the CA operating the ACME server needs to
+provide the ACME client with a base64url-encoded MAC key and a key identifier
+string, using some mechanism outside of ACME.
+These can be supplied to
+.Nm
+using the
+.Fl e
+command line argument.
+.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl F
Force certificate renewal, regardless of remaining lifetime.
+.It Fl e Ar key-id:key
+Specify external account binding parameters.
+This consists of the key identifier string and the base64url-encoded MAC key
+separated by a colon.
.It Fl f Ar configfile
Specify an alternative configuration file.
.It Fl n
Index: base64.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/base64.c,v
diff -u -p -u -p -r1.9 base64.c
--- base64.c 24 Jan 2017 13:32:55 -0000 1.9
+++ base64.c 6 May 2026 03:58:31 -0000
@@ -19,6 +19,7 @@
#include <resolv.h>
#include <stdlib.h>
+#include <string.h>
#include "extern.h"
@@ -64,4 +65,70 @@ base64buf_url(const char *data, size_t l
}
return buf;
+}
+
+static const u_int8_t index_64[128] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 62, 255, 255, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 255, 255, 255, 255, 0, 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, 255, 255, 255, 255, 63, 255, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255
+};
+#define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)])
+
+int
+unbase64buf_url(const unsigned char *data, unsigned char **decoded)
+{
+ unsigned char *out, *bp;
+ const unsigned char *p;
+ unsigned char c1, c2, c3, c4;
+ int len;
+
+ len = strlen(data);
+ out = malloc(len);
+ if (out == NULL)
+ return -1;
+
+ p = data;
+ bp = out;
+ while (p < data + len) {
+ c1 = CHAR64(*p);
+ /* Invalid data */
+ if (c1 == 255)
+ return -1;
+
+ c2 = CHAR64(*(p + 1));
+ if (c2 == 255)
+ return -1;
+
+ *bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
+ if ((p + 2) >= data + len)
+ break;
+
+ c3 = CHAR64(*(p + 2));
+ if (c3 == 255)
+ return -1;
+
+ *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
+ if ((p + 3) >= data + len)
+ break;
+
+ c4 = CHAR64(*(p + 3));
+ if (c4 == 255)
+ return -1;
+ *bp++ = ((c3 & 0x03) << 6) | c4;
+
+ p += 4;
+ }
+
+ *decoded = out;
+ return (bp - out);
}
Index: extern.h
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/extern.h,v
diff -u -p -u -p -r1.23 extern.h
--- extern.h 23 Feb 2026 10:27:49 -0000 1.23
+++ extern.h 6 May 2026 03:58:31 -0000
@@ -34,6 +34,7 @@ enum acctop {
ACCT_SIGN,
ACCT_KID_SIGN,
ACCT_THUMBPRINT,
+ ACCT_EAB,
ACCT__MAX
};
@@ -200,7 +201,8 @@ __BEGIN_DECLS
* Start with our components.
* These are all isolated and talk to each other using sockets.
*/
-int acctproc(int, const char *, enum keytype);
+int acctproc(int, const char *, enum keytype, const char *,
+ const unsigned char *, int);
int certproc(int, int);
int chngproc(int, const char *);
int dnsproc(int);
@@ -209,7 +211,7 @@ int fileproc(int, const char *, const
const char *);
int keyproc(int, struct domain_c *);
int netproc(int, int, int, int, int, int, int,
- struct authority_c *, struct domain_c *);
+ struct authority_c *, struct domain_c *, int);
/*
* Debugging functions.
@@ -240,6 +242,7 @@ int checkexit_ext(int *, pid_t, enum c
*/
size_t base64len(size_t);
char *base64buf_url(const char *, size_t);
+int unbase64buf_url(const unsigned char *, unsigned char **);
/*
* JSON parsing routines.
@@ -259,18 +262,19 @@ char *json_getstr(struct jsmnn *, const
char *json_fmt_newcert(const char *);
char *json_fmt_chkacc(void);
-char *json_fmt_newacc(const char *);
+char *json_fmt_newacc(const char *, const char *);
char *json_fmt_neworder(struct domain_c *);
-char *json_fmt_protected_rsa(const char *,
- const char *, 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_jwk_rsa(const char *, const char *);
+char *json_fmt_jwk_ec(const char *, const char *);
+char *json_fmt_protected_jwk(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 *);
+char *json_fmt_protected_eab(const char *, const char *);
/*
* Should we print debugging messages?
Index: json.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/json.c,v
diff -u -p -u -p -r1.23 json.c
--- json.c 23 Feb 2026 10:27:49 -0000 1.23
+++ json.c 6 May 2026 03:58:31 -0000
@@ -618,7 +618,7 @@ json_fmt_chkacc(void)
* Format the "newAccount" resource request.
*/
char *
-json_fmt_newacc(const char *contact)
+json_fmt_newacc(const char *contact, const char *eab)
{
int c;
char *p, *cnt = NULL;
@@ -630,6 +630,18 @@ json_fmt_newacc(const char *contact)
return NULL;
}
}
+ if (eab != NULL) {
+ char *ecnt = NULL;
+ c = asprintf(&ecnt, "%s\"externalAccountBinding\": %s, ",
+ cnt == NULL ? "" : cnt, eab);
+ if (c == -1) {
+ warn("asprintf");
+ return NULL;
+ }
+
+ free(cnt);
+ cnt = ecnt;
+ }
c = asprintf(&p, "{"
"%s"
@@ -737,23 +749,35 @@ json_fmt_newcert(const char *cert)
}
/*
- * Protected component of json_fmt_signed().
+ * Format an RSA public key in JWK format.
*/
char *
-json_fmt_protected_rsa(const char *exp, const char *mod, const char *nce,
- const char *url)
+json_fmt_jwk_rsa(const char *exp, const char *mod)
{
int c;
char *p;
- c = asprintf(&p, "{"
- "\"alg\": \"RS256\", "
- "\"jwk\": "
- "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}, "
- "\"nonce\": \"%s\", "
- "\"url\": \"%s\""
- "}",
- exp, mod, nce, url);
+ c = asprintf(&p, "{\"e\": \"%s\", \"kty\": \"RSA\", \"n\": \"%s\"}",
+ exp, mod);
+ if (c == -1) {
+ warn("asprintf");
+ p = NULL;
+ }
+ return p;
+}
+
+/*
+ * Format an EC public key in JWK format.
+ */
+char *
+json_fmt_jwk_ec(const char *x, const char *y)
+{
+ int c;
+ char *p;
+
+ c = asprintf(&p, "{\"crv\": \"P-384\", \"kty\": \"EC\","
+ " \"x\": \"%s\", \"y\": \"%s\"}",
+ x, y);
if (c == -1) {
warn("asprintf");
p = NULL;
@@ -765,19 +789,19 @@ json_fmt_protected_rsa(const char *exp,
* Protected component of json_fmt_signed().
*/
char *
-json_fmt_protected_ec(const char *x, const char *y, const char *nce,
+json_fmt_protected_jwk(const char *alg, const char *jwk, const char *nce,
const char *url)
{
int c;
char *p;
c = asprintf(&p, "{"
- "\"alg\": \"ES384\", "
- "\"jwk\": "
- "{\"crv\": \"P-384\", \"kty\": \"EC\", \"x\": \"%s\", "
- "\"y\": \"%s\"}, \"nonce\": \"%s\", \"url\": \"%s\""
+ "\"alg\": \"%s\", "
+ "\"jwk\": %s, "
+ "\"nonce\": \"%s\", "
+ "\"url\": \"%s\""
"}",
- x, y, nce, url);
+ alg, jwk, nce, url);
if (c == -1) {
warn("asprintf");
p = NULL;
@@ -871,6 +895,28 @@ json_fmt_thumb_ec(const char *x, const c
c = asprintf(&p, "{\"crv\":\"P-384\",\"kty\":\"EC\",\"x\":\"%s\","
"\"y\":\"%s\"}",
x, y);
+ if (c == -1) {
+ warn("asprintf");
+ p = NULL;
+ }
+ return p;
+}
+
+/*
+ * Protected component of external account binding.
+ */
+char *
+json_fmt_protected_eab(const char *keyid, const char *url)
+{
+ int c;
+ char *p;
+
+ c = asprintf(&p, "{"
+ "\"alg\": \"HS256\", "
+ "\"kid\": \"%s\", "
+ "\"url\": \"%s\""
+ "}",
+ keyid, url);
if (c == -1) {
warn("asprintf");
p = NULL;
Index: main.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/main.c,v
diff -u -p -u -p -r1.58 main.c
--- main.c 23 Feb 2026 10:27:49 -0000 1.58
+++ main.c 6 May 2026 03:58:31 -0000
@@ -43,12 +43,14 @@ main(int argc, char *argv[])
char *certdir = NULL;
char *chngdir = NULL, *auth = NULL;
char *conffile = CONF_FILE;
+ char *eab = NULL, *eab_key_enc = NULL;
char *tmps, *tmpsd;
+ unsigned char *eab_key = NULL;
int key_fds[2], acct_fds[2], chng_fds[2], cert_fds[2];
int file_fds[2], dns_fds[2], rvk_fds[2];
int force = 0;
int c, rc, revocate = 0;
- int popts = 0;
+ int popts = 0, eab_key_len;
pid_t pids[COMP__MAX];
size_t ne;
@@ -60,11 +62,14 @@ main(int argc, char *argv[])
if (setlocale(LC_CTYPE, "C") == NULL)
errx(1, "setlocale");
- while ((c = getopt(argc, argv, "Fnrvf:")) != -1)
+ while ((c = getopt(argc, argv, "Fe:nrvf:")) != -1)
switch (c) {
case 'F':
force = 1;
break;
+ case 'e':
+ eab = strdup(optarg);
+ break;
case 'f':
if ((conffile = strdup(optarg)) == NULL)
err(EXIT_FAILURE, "strdup");
@@ -166,6 +171,22 @@ main(int argc, char *argv[])
ne++;
}
+ if (eab != NULL) {
+ eab_key_enc = eab;
+ eab = strsep(&eab_key_enc, ":");
+ if (eab_key_enc == NULL) {
+ warnx("EAB parameters must be in the format keyid:key");
+ ne++;
+ } else {
+ eab_key_len = unbase64buf_url(
+ (unsigned char *)eab_key_enc, &eab_key);
+ if (eab_key_len == -1) {
+ warnx("unable to decode EAB key");
+ ne++;
+ }
+ }
+ }
+
if (ne > 0)
return EXIT_FAILURE;
@@ -222,7 +243,7 @@ main(int argc, char *argv[])
c = netproc(key_fds[1], acct_fds[1],
chng_fds[1], cert_fds[1],
dns_fds[1], rvk_fds[1],
- revocate, authority, domain);
+ revocate, authority, domain, eab != NULL);
exit(c ? EXIT_SUCCESS : EXIT_FAILURE);
}
@@ -267,7 +288,7 @@ main(int argc, char *argv[])
close(file_fds[0]);
close(file_fds[1]);
c = acctproc(acct_fds[0], authority->account,
- authority->keytype);
+ authority->keytype, eab, eab_key, eab_key_len);
exit(c ? EXIT_SUCCESS : EXIT_FAILURE);
}
@@ -378,6 +399,6 @@ main(int argc, char *argv[])
return rc != COMP__MAX ? EXIT_FAILURE : (c == 2 ? EXIT_SUCCESS : 2);
usage:
fprintf(stderr,
- "usage: acme-client [-Fnrv] [-f configfile] handle\n");
+ "usage: acme-client [-Fnrv] [-f configfile] [-e kid:key] handle\n");
return EXIT_FAILURE;
}
Index: netproc.c
===================================================================
RCS file: /cvs/src/usr.sbin/acme-client/netproc.c,v
diff -u -p -u -p -r1.46 netproc.c
--- netproc.c 23 Feb 2026 10:27:49 -0000 1.46
+++ netproc.c 6 May 2026 03:58:31 -0000
@@ -408,14 +408,30 @@ sreq(struct conn *c, const char *addr, i
* Returns non-zero on success.
*/
static int
-donewacc(struct conn *c, const struct capaths *p, const char *contact)
+donewacc(struct conn *c, const struct capaths *p, const char *contact,
+ int eab)
{
struct jsmnn *j = NULL;
int rc = 0;
char *req, *detail, *error = NULL, *accturi = NULL;
+ char *eab_json = NULL;
long lc;
- if ((req = json_fmt_newacc(contact)) == NULL)
+ if (eab) {
+ /* ask acct proc to produce eab json */
+ if (writeop(c->fd, COMM_ACCT, ACCT_EAB) <= 0) {
+ return -1;
+ } else if (writestr(c->fd, COMM_URL, p->newaccount) <= 0) {
+ return -1;
+ }
+
+ /* Now read back the signed payload. */
+ if ((eab_json = readstr(c->fd, COMM_REQ)) == NULL) {
+ return -1;
+ }
+ }
+
+ if ((req = json_fmt_newacc(contact, eab_json)) == NULL)
warnx("json_fmt_newacc");
else if ((lc = sreq(c, p->newaccount, 0, req, &c->kid)) < 0)
warnx("%s: bad comm", p->newaccount);
@@ -455,7 +471,8 @@ donewacc(struct conn *c, const struct ca
* Returns non-zero on success.
*/
static int
-dochkacc(struct conn *c, const struct capaths *p, const char *contact)
+dochkacc(struct conn *c, const struct capaths *p, const char *contact,
+ int eab)
{
int rc = 0;
char *req, *accturi = NULL;
@@ -470,7 +487,7 @@ dochkacc(struct conn *c, const struct ca
else if (c->buf.buf == NULL || c->buf.sz == 0)
warnx("%s: empty response", p->newaccount);
else if (lc == 400)
- rc = donewacc(c, p, contact);
+ rc = donewacc(c, p, contact, eab);
else
rc = 1;
@@ -723,7 +740,7 @@ dodirs(struct conn *c, const char *addr,
int
netproc(int kfd, int afd, int Cfd, int cfd, int dfd, int rfd,
int revocate, struct authority_c *authority,
- struct domain_c *domain)
+ struct domain_c *domain, int eab)
{
int rc = 0, retries = 0;
size_t i;
@@ -805,7 +822,7 @@ netproc(int kfd, int afd, int Cfd, int c
c.newnonce = paths.newnonce;
/* Check if our account already exists or create it. */
- if (!dochkacc(&c, &paths, authority->contact))
+ if (!dochkacc(&c, &paths, authority->contact, eab))
goto out;
/*
acme-client: support for external account binding