mirror of
https://github.com/oasislinux/oasis.git
synced 2026-10-10 13:57:02 +02:00
1630 lines
42 KiB
Diff
1630 lines
42 KiB
Diff
From 4ad4ffcb7c731ae48c97b93760d7625724a991bc Mon Sep 17 00:00:00 2001
|
|
From: Michael Forney <mforney@mforney.org>
|
|
Date: Fri, 23 Apr 2021 23:14:16 -0700
|
|
Subject: [PATCH] acme-client: Port to BearSSL
|
|
|
|
---
|
|
usr.sbin/acme-client/acctproc.c | 298 +++++++++------------------
|
|
usr.sbin/acme-client/certproc.c | 5 -
|
|
usr.sbin/acme-client/key.c | 329 ++++++++++++++++++++++++------
|
|
usr.sbin/acme-client/key.h | 22 +-
|
|
usr.sbin/acme-client/keyproc.c | 192 ++++++-----------
|
|
usr.sbin/acme-client/revokeproc.c | 319 +++++++++++------------------
|
|
6 files changed, 568 insertions(+), 597 deletions(-)
|
|
|
|
diff --git a/usr.sbin/acme-client/acctproc.c b/usr.sbin/acme-client/acctproc.c
|
|
index 9e97a8bb760..8d66dac49d9 100644
|
|
--- a/usr.sbin/acme-client/acctproc.c
|
|
+++ b/usr.sbin/acme-client/acctproc.c
|
|
@@ -19,73 +19,29 @@
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
-#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
-#include <openssl/bn.h>
|
|
-#include <openssl/ec.h>
|
|
-#include <openssl/evp.h>
|
|
-#include <openssl/rsa.h>
|
|
-#include <openssl/err.h>
|
|
+#include <bearssl.h>
|
|
|
|
#include "extern.h"
|
|
#include "key.h"
|
|
|
|
-/*
|
|
- * Converts a BIGNUM to the form used in JWK.
|
|
- * This is essentially a base64-encoded big-endian binary string
|
|
- * representation of the number.
|
|
- */
|
|
-static char *
|
|
-bn2string(const BIGNUM *bn)
|
|
-{
|
|
- int len;
|
|
- unsigned char *buf;
|
|
- char *bbuf;
|
|
-
|
|
- /* Extract big-endian representation of BIGNUM. */
|
|
-
|
|
- len = BN_num_bytes(bn);
|
|
- if ((buf = malloc(len)) == NULL) {
|
|
- warn("malloc");
|
|
- return NULL;
|
|
- } else if (len != BN_bn2bin(bn, buf)) {
|
|
- warnx("BN_bn2bin");
|
|
- free(buf);
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- /* Convert to base64url. */
|
|
-
|
|
- if ((bbuf = base64buf_url(buf, len)) == NULL) {
|
|
- warnx("base64buf_url");
|
|
- free(buf);
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- free(buf);
|
|
- return bbuf;
|
|
-}
|
|
-
|
|
/*
|
|
* Extract the relevant RSA components from the key and create the JSON
|
|
* thumbprint from them.
|
|
*/
|
|
static char *
|
|
-op_thumb_rsa(EVP_PKEY *pkey)
|
|
+op_thumb_rsa(struct key *key)
|
|
{
|
|
char *exp = NULL, *mod = NULL, *json = NULL;
|
|
- RSA *r;
|
|
-
|
|
- if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
|
|
- warnx("EVP_PKEY_get0_RSA");
|
|
- else if ((mod = bn2string(RSA_get0_n(r))) == NULL)
|
|
- warnx("bn2string");
|
|
- else if ((exp = bn2string(RSA_get0_e(r))) == NULL)
|
|
- warnx("bn2string");
|
|
+
|
|
+ if ((mod = base64buf_url(key->rsa.pk.n, key->rsa.pk.nlen)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
+ else if ((exp = base64buf_url(key->rsa.pk.e, key->rsa.pk.elen)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
else if ((json = json_fmt_thumb_rsa(exp, mod)) == NULL)
|
|
warnx("json_fmt_thumb_rsa");
|
|
|
|
@@ -99,31 +55,23 @@ op_thumb_rsa(EVP_PKEY *pkey)
|
|
* thumbprint from them.
|
|
*/
|
|
static char *
|
|
-op_thumb_ec(EVP_PKEY *pkey)
|
|
+op_thumb_ec(struct key *key)
|
|
{
|
|
- BIGNUM *X = NULL, *Y = NULL;
|
|
- EC_KEY *ec = NULL;
|
|
+ size_t len;
|
|
char *x = NULL, *y = NULL;
|
|
char *json = NULL;
|
|
|
|
- if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
|
|
- warnx("EVP_PKEY_get0_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(EC_KEY_get0_group(ec),
|
|
- EC_KEY_get0_public_key(ec), X, Y, NULL))
|
|
- warnx("EC_POINT_get_affine_coordinates");
|
|
- else if ((x = bn2string(X)) == NULL)
|
|
- warnx("bn2string");
|
|
- else if ((y = bn2string(Y)) == NULL)
|
|
- warnx("bn2string");
|
|
+ /* Points are stored in uncompressed format. */
|
|
+ len = key->ec.pk.qlen / 2;
|
|
+ if (key->ec.pk.qlen % 2 != 1 || key->ec.pk.q[0] != 0x04)
|
|
+ warnx("invalid EC public key");
|
|
+ else if ((x = base64buf_url(key->ec.pk.q + 1, len)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
+ else if ((y = base64buf_url(key->ec.pk.q + 1 + len, len)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
else if ((json = json_fmt_thumb_ec(x, y)) == NULL)
|
|
warnx("json_fmt_thumb_ec");
|
|
|
|
- BN_free(X);
|
|
- BN_free(Y);
|
|
free(x);
|
|
free(y);
|
|
return json;
|
|
@@ -133,26 +81,26 @@ op_thumb_ec(EVP_PKEY *pkey)
|
|
* The thumbprint operation is used for the challenge sequence.
|
|
*/
|
|
static int
|
|
-op_thumbprint(int fd, EVP_PKEY *pkey)
|
|
+op_thumbprint(int fd, struct key *pkey)
|
|
{
|
|
- char *thumb = NULL, *dig64 = NULL;
|
|
- unsigned char dig[EVP_MAX_MD_SIZE];
|
|
- unsigned int digsz;
|
|
- int rc = 0;
|
|
+ char *thumb = NULL, *dig64 = NULL;
|
|
+ br_sha256_context ctx;
|
|
+ unsigned char dig[br_sha256_SIZE];
|
|
+ int rc = 0;
|
|
|
|
/* Construct the thumbprint input itself. */
|
|
|
|
- switch (EVP_PKEY_base_id(pkey)) {
|
|
- case EVP_PKEY_RSA:
|
|
+ switch (pkey->type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
if ((thumb = op_thumb_rsa(pkey)) != NULL)
|
|
break;
|
|
goto out;
|
|
- case EVP_PKEY_EC:
|
|
+ case BR_KEYTYPE_EC:
|
|
if ((thumb = op_thumb_ec(pkey)) != NULL)
|
|
break;
|
|
goto out;
|
|
default:
|
|
- warnx("EVP_PKEY_base_id: unknown key type");
|
|
+ warnx("unknown key type");
|
|
goto out;
|
|
}
|
|
|
|
@@ -163,12 +111,10 @@ op_thumbprint(int fd, EVP_PKEY *pkey)
|
|
* it up in the read loop).
|
|
*/
|
|
|
|
- if (!EVP_Digest(thumb, strlen(thumb), dig, &digsz, EVP_sha256(),
|
|
- NULL)) {
|
|
- warnx("EVP_Digest");
|
|
- goto out;
|
|
- }
|
|
- if ((dig64 = base64buf_url(dig, digsz)) == NULL) {
|
|
+ br_sha256_init(&ctx);
|
|
+ br_sha256_update(&ctx, thumb, strlen(thumb));
|
|
+ br_sha256_out(&ctx, dig);
|
|
+ if ((dig64 = base64buf_url(dig, sizeof(dig))) == NULL) {
|
|
warnx("base64buf_url");
|
|
goto out;
|
|
}
|
|
@@ -183,11 +129,10 @@ out:
|
|
}
|
|
|
|
static int
|
|
-op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
|
|
+op_sign_rsa(char **prot, struct key *key, const char *nonce, const char *url)
|
|
{
|
|
char *exp = NULL, *mod = NULL;
|
|
int rc = 0;
|
|
- RSA *r;
|
|
|
|
*prot = NULL;
|
|
|
|
@@ -196,12 +141,10 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
|
|
* Finally, format the header combined with the nonce.
|
|
*/
|
|
|
|
- if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL)
|
|
- warnx("EVP_PKEY_get0_RSA");
|
|
- else if ((mod = bn2string(RSA_get0_n(r))) == NULL)
|
|
- warnx("bn2string");
|
|
- else if ((exp = bn2string(RSA_get0_e(r))) == NULL)
|
|
- warnx("bn2string");
|
|
+ if ((mod = base64buf_url(key->rsa.pk.n, key->rsa.pk.nlen)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
+ else if ((exp = base64buf_url(key->rsa.pk.e, key->rsa.pk.elen)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
else if ((*prot = json_fmt_protected_rsa(exp, mod, nonce, url)) == NULL)
|
|
warnx("json_fmt_protected_rsa");
|
|
else
|
|
@@ -213,35 +156,27 @@ op_sign_rsa(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
|
|
}
|
|
|
|
static int
|
|
-op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
|
|
+op_sign_ec(char **prot, struct key *key, const char *nonce, const char *url)
|
|
{
|
|
- BIGNUM *X = NULL, *Y = NULL;
|
|
- EC_KEY *ec = NULL;
|
|
+ size_t len;
|
|
char *x = NULL, *y = NULL;
|
|
int rc = 0;
|
|
|
|
*prot = NULL;
|
|
|
|
- if ((ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL)
|
|
- warnx("EVP_PKEY_get0_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(EC_KEY_get0_group(ec),
|
|
- EC_KEY_get0_public_key(ec), X, Y, NULL))
|
|
- warnx("EC_POINT_get_affine_coordinates");
|
|
- else if ((x = bn2string(X)) == NULL)
|
|
- warnx("bn2string");
|
|
- else if ((y = bn2string(Y)) == NULL)
|
|
- warnx("bn2string");
|
|
+ /* Points are stored in uncompressed format. */
|
|
+ len = key->ec.pk.qlen / 2;
|
|
+ if (key->ec.pk.qlen % 2 != 1 || key->ec.pk.q[0] != 0x04)
|
|
+ warnx("invalid EC public key");
|
|
+ else if ((x = base64buf_url(key->ec.pk.q + 1, len)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
+ else if ((y = base64buf_url(key->ec.pk.q + 1 + len, len)) == NULL)
|
|
+ warnx("base64buf_url");
|
|
else if ((*prot = json_fmt_protected_ec(x, y, nonce, url)) == NULL)
|
|
warnx("json_fmt_protected_ec");
|
|
else
|
|
rc = 1;
|
|
|
|
- BN_free(X);
|
|
- BN_free(Y);
|
|
free(x);
|
|
free(y);
|
|
return rc;
|
|
@@ -252,20 +187,18 @@ op_sign_ec(char **prot, EVP_PKEY *pkey, const char *nonce, const char *url)
|
|
* This requires the sender ("fd") to provide the payload and a nonce.
|
|
*/
|
|
static int
|
|
-op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
+op_sign(int fd, struct key *key, enum acctop op)
|
|
{
|
|
- EVP_MD_CTX *ctx = NULL;
|
|
- const EVP_MD *evp_md = NULL;
|
|
- ECDSA_SIG *ec_sig = NULL;
|
|
- const BIGNUM *ec_sig_r = NULL, *ec_sig_s = NULL;
|
|
- int bn_len, sign_len, rc = 0;
|
|
+ br_hash_compat_context ctx;
|
|
+ int sign_len, rc = 0;
|
|
+ unsigned int digsz, sigsz;
|
|
char *nonce = NULL, *pay = NULL, *pay64 = NULL;
|
|
char *prot = NULL, *prot64 = NULL;
|
|
- char *sign = NULL, *dig64 = NULL, *fin = NULL;
|
|
+ char *sign = NULL, *sig64 = NULL, *fin = NULL;
|
|
char *url = NULL, *kid = NULL, *alg = NULL;
|
|
- const unsigned char *digp;
|
|
- unsigned char *dig = NULL, *buf = NULL;
|
|
- size_t digsz;
|
|
+ unsigned char dig[64];
|
|
+ unsigned char *sig = NULL;
|
|
+ const unsigned char *oid = NULL;
|
|
|
|
/* Read our payload and nonce from the requestor. */
|
|
|
|
@@ -282,19 +215,22 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
|
|
/* Base64-encode the payload. */
|
|
|
|
- if ((pay64 = base64buf_url((unsigned char *)pay, strlen(pay))) == NULL) {
|
|
+ if ((pay64 = base64buf_url(pay, strlen(pay))) == NULL) {
|
|
warnx("base64buf_url");
|
|
goto out;
|
|
}
|
|
|
|
- switch (EVP_PKEY_base_id(pkey)) {
|
|
- case EVP_PKEY_RSA:
|
|
+ switch (key->type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
alg = "RS256";
|
|
- evp_md = EVP_sha256();
|
|
+ ctx.vtable = &br_sha256_vtable;
|
|
+ oid = BR_HASH_OID_SHA256;
|
|
+ sigsz = (key->rsa.sk.n_bitlen + 7) / 8;
|
|
break;
|
|
- case EVP_PKEY_EC:
|
|
+ case BR_KEYTYPE_EC:
|
|
alg = "ES384";
|
|
- evp_md = EVP_sha384();
|
|
+ ctx.vtable = &br_sha384_vtable;
|
|
+ sigsz = 96;
|
|
break;
|
|
default:
|
|
warnx("unknown account key type");
|
|
@@ -308,17 +244,17 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
goto out;
|
|
}
|
|
} else {
|
|
- switch (EVP_PKEY_base_id(pkey)) {
|
|
- case EVP_PKEY_RSA:
|
|
- if (!op_sign_rsa(&prot, pkey, nonce, url))
|
|
+ switch (key->type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
+ if (!op_sign_rsa(&prot, key, nonce, url))
|
|
goto out;
|
|
break;
|
|
- case EVP_PKEY_EC:
|
|
- if (!op_sign_ec(&prot, pkey, nonce, url))
|
|
+ case BR_KEYTYPE_EC:
|
|
+ if (!op_sign_ec(&prot, key, nonce, url))
|
|
goto out;
|
|
break;
|
|
default:
|
|
- warnx("EVP_PKEY_base_id");
|
|
+ warnx("unknown key type");
|
|
goto out;
|
|
}
|
|
}
|
|
@@ -341,76 +277,34 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
|
|
/* Sign the message. */
|
|
|
|
- if ((ctx = EVP_MD_CTX_new()) == NULL) {
|
|
- warnx("EVP_MD_CTX_new");
|
|
- goto out;
|
|
- }
|
|
- if (!EVP_DigestSignInit(ctx, NULL, evp_md, NULL, pkey)) {
|
|
- warnx("EVP_DigestSignInit");
|
|
- goto out;
|
|
- }
|
|
- if (!EVP_DigestSign(ctx, NULL, &digsz, sign, sign_len)) {
|
|
- warnx("EVP_DigestSign");
|
|
- goto out;
|
|
- }
|
|
- if ((dig = malloc(digsz)) == NULL) {
|
|
+ ctx.vtable->init(&ctx.vtable);
|
|
+ ctx.vtable->update(&ctx.vtable, sign, sign_len);
|
|
+ ctx.vtable->out(&ctx.vtable, dig);
|
|
+ digsz = ctx.vtable->desc >> BR_HASHDESC_OUT_OFF & BR_HASHDESC_OUT_MASK;
|
|
+
|
|
+ if ((sig = malloc(sigsz)) == NULL) {
|
|
warn("malloc");
|
|
goto out;
|
|
}
|
|
- if (!EVP_DigestSign(ctx, dig, &digsz, sign, sign_len)) {
|
|
- warnx("EVP_DigestSign");
|
|
- goto out;
|
|
- }
|
|
|
|
- switch (EVP_PKEY_base_id(pkey)) {
|
|
- case EVP_PKEY_RSA:
|
|
- if ((dig64 = base64buf_url(dig, digsz)) == NULL) {
|
|
- warnx("base64buf_url");
|
|
+ switch (key->type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
+ if (!br_rsa_pkcs1_sign_get_default()(oid, dig, digsz,
|
|
+ &key->rsa.sk, sig)) {
|
|
+ warnx("br_rsa_pkcs1_sign");
|
|
goto out;
|
|
}
|
|
break;
|
|
- case EVP_PKEY_EC:
|
|
- if (digsz > LONG_MAX) {
|
|
- warnx("EC signature too long");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- digp = dig;
|
|
- if ((ec_sig = d2i_ECDSA_SIG(NULL, &digp, digsz)) == NULL) {
|
|
- warnx("d2i_ECDSA_SIG");
|
|
+ case BR_KEYTYPE_EC:
|
|
+ sigsz = br_ecdsa_sign_raw_get_default()(br_ec_get_default(),
|
|
+ ctx.vtable, dig, &key->ec.sk, sig);
|
|
+ if (sigsz == 0 || sigsz % 2 != 0) {
|
|
+ warnx("br_ecdsa_sign_raw");
|
|
goto out;
|
|
}
|
|
-
|
|
- if ((ec_sig_r = ECDSA_SIG_get0_r(ec_sig)) == NULL ||
|
|
- (ec_sig_s = ECDSA_SIG_get0_s(ec_sig)) == NULL) {
|
|
- warnx("ECDSA_SIG_get0");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if ((bn_len = (EVP_PKEY_bits(pkey) + 7) / 8) <= 0) {
|
|
- warnx("EVP_PKEY_bits");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if ((buf = calloc(2, bn_len)) == NULL) {
|
|
- warnx("calloc");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if (BN_bn2binpad(ec_sig_r, buf, bn_len) != bn_len ||
|
|
- BN_bn2binpad(ec_sig_s, buf + bn_len, bn_len) != bn_len) {
|
|
- warnx("BN_bn2binpad");
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if ((dig64 = base64buf_url(buf, 2 * bn_len)) == NULL) {
|
|
- warnx("base64buf_url");
|
|
- goto out;
|
|
- }
|
|
-
|
|
break;
|
|
default:
|
|
- warnx("EVP_PKEY_base_id");
|
|
+ warnx("unknown key type");
|
|
goto out;
|
|
}
|
|
|
|
@@ -420,7 +314,11 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
* when we next enter the read loop).
|
|
*/
|
|
|
|
- if ((fin = json_fmt_signed(prot64, pay64, dig64)) == NULL) {
|
|
+ if ((sig64 = base64buf_url(sig, sigsz)) == NULL) {
|
|
+ warnx("base64buf_url");
|
|
+ goto out;
|
|
+ }
|
|
+ if ((fin = json_fmt_signed(prot64, pay64, sig64)) == NULL) {
|
|
warnx("json_fmt_signed");
|
|
goto out;
|
|
} else if (writestr(fd, COMM_REQ, fin) < 0)
|
|
@@ -428,8 +326,6 @@ op_sign(int fd, EVP_PKEY *pkey, enum acctop op)
|
|
|
|
rc = 1;
|
|
out:
|
|
- ECDSA_SIG_free(ec_sig);
|
|
- EVP_MD_CTX_free(ctx);
|
|
free(pay);
|
|
free(sign);
|
|
free(pay64);
|
|
@@ -438,10 +334,9 @@ out:
|
|
free(kid);
|
|
free(prot);
|
|
free(prot64);
|
|
- free(dig);
|
|
- free(dig64);
|
|
+ free(sig);
|
|
+ free(sig64);
|
|
free(fin);
|
|
- free(buf);
|
|
return rc;
|
|
}
|
|
|
|
@@ -449,7 +344,7 @@ int
|
|
acctproc(int netsock, const char *acctkey, enum keytype keytype)
|
|
{
|
|
FILE *f = NULL;
|
|
- EVP_PKEY *pkey = NULL;
|
|
+ struct key *pkey = NULL;
|
|
long lval;
|
|
enum acctop op;
|
|
int rc = 0, cc, newacct = 0;
|
|
@@ -475,8 +370,6 @@ acctproc(int netsock, const char *acctkey, enum keytype keytype)
|
|
|
|
/* File-system, user, and sandbox jailing. */
|
|
|
|
- ERR_load_crypto_strings();
|
|
-
|
|
if (pledge("stdio", NULL) == -1) {
|
|
warn("pledge");
|
|
goto out;
|
|
@@ -554,8 +447,7 @@ out:
|
|
close(netsock);
|
|
if (f != NULL)
|
|
fclose(f);
|
|
- EVP_PKEY_free(pkey);
|
|
- ERR_print_errors_fp(stderr);
|
|
- ERR_free_strings();
|
|
+ if (pkey != NULL)
|
|
+ freezero(pkey, sizeof(*pkey) + pkey->datasz);
|
|
return rc;
|
|
}
|
|
diff --git a/usr.sbin/acme-client/certproc.c b/usr.sbin/acme-client/certproc.c
|
|
index f443d573675..85c3897a4b8 100644
|
|
--- a/usr.sbin/acme-client/certproc.c
|
|
+++ b/usr.sbin/acme-client/certproc.c
|
|
@@ -21,11 +21,6 @@
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
-#include <openssl/pem.h>
|
|
-#include <openssl/x509.h>
|
|
-#include <openssl/x509v3.h>
|
|
-#include <openssl/err.h>
|
|
-
|
|
#include "extern.h"
|
|
|
|
#define BEGIN_MARKER "-----BEGIN CERTIFICATE-----"
|
|
diff --git a/usr.sbin/acme-client/key.c b/usr.sbin/acme-client/key.c
|
|
index 9ece3059d4e..9599a7fdbd5 100644
|
|
--- a/usr.sbin/acme-client/key.c
|
|
+++ b/usr.sbin/acme-client/key.c
|
|
@@ -17,14 +17,11 @@
|
|
*/
|
|
|
|
#include <err.h>
|
|
+#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
-#include <openssl/evp.h>
|
|
-#include <openssl/pem.h>
|
|
-#include <openssl/rsa.h>
|
|
-#include <openssl/ec.h>
|
|
-#include <openssl/obj_mac.h>
|
|
+#include <bearssl.h>
|
|
|
|
#include "key.h"
|
|
|
|
@@ -33,102 +30,320 @@
|
|
*/
|
|
#define KBITS 4096
|
|
|
|
+static void
|
|
+prng_init(const br_prng_class **ctx, const void *params, const void *seed, size_t len)
|
|
+{
|
|
+}
|
|
+
|
|
+static void
|
|
+prng_generate(const br_prng_class **ctx, void *out, size_t len)
|
|
+{
|
|
+ arc4random_buf(out, len);
|
|
+}
|
|
+
|
|
+static void
|
|
+prng_update(const br_prng_class **ctx, const void *seed, size_t len)
|
|
+{
|
|
+}
|
|
+
|
|
+static const br_prng_class prng_class = {
|
|
+ 0, prng_init, prng_generate, prng_update
|
|
+}, *prng = &prng_class;
|
|
+
|
|
/*
|
|
* Create an RSA key with the default KBITS number of bits.
|
|
*/
|
|
-EVP_PKEY *
|
|
+struct key *
|
|
rsa_key_create(FILE *f, const char *fname)
|
|
{
|
|
- EVP_PKEY_CTX *ctx = NULL;
|
|
- EVP_PKEY *pkey = NULL;
|
|
+ struct key *key = NULL;
|
|
+ size_t slen, plen;
|
|
+ unsigned char *sbuf, *pbuf;
|
|
+ unsigned char d[KBITS / 8];
|
|
+ unsigned char *der = NULL, *pem = NULL;
|
|
+ size_t derlen, pemlen;
|
|
|
|
- if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL)) == NULL) {
|
|
- warnx("EVP_PKEY_CTX_new_id");
|
|
- goto err;
|
|
- }
|
|
- if (EVP_PKEY_keygen_init(ctx) <= 0) {
|
|
- warnx("EVP_PKEY_keygen_init");
|
|
+ slen = BR_RSA_KBUF_PRIV_SIZE(KBITS);
|
|
+ plen = BR_RSA_KBUF_PUB_SIZE(KBITS);
|
|
+ if ((key = malloc(sizeof(*key) + slen + plen)) == NULL) {
|
|
+ warnx("malloc");
|
|
goto err;
|
|
}
|
|
- if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KBITS) <= 0) {
|
|
- warnx("EVP_PKEY_set_rsa_keygen_bits");
|
|
+ key->type = BR_KEYTYPE_RSA;
|
|
+ key->datasz = slen + plen;
|
|
+ sbuf = key->data;
|
|
+ pbuf = key->data + slen;
|
|
+ if (!br_rsa_keygen_get_default()(&prng, &key->rsa.sk, sbuf,
|
|
+ &key->rsa.pk, pbuf, KBITS, 0x10001)) {
|
|
+ warnx("br_rsa_keygen");
|
|
goto err;
|
|
}
|
|
- if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
|
|
- warnx("EVP_PKEY_keygen");
|
|
+
|
|
+ /* Compute the private exponent. */
|
|
+
|
|
+ if (!br_rsa_compute_privexp_get_default()(d, &key->rsa.sk, 0x10001)) {
|
|
+ warnx("br_rsa_compute_modulus");
|
|
goto err;
|
|
}
|
|
|
|
- /* Serialise the key to the disc. */
|
|
+ /* Serialise the key to the disk. */
|
|
|
|
- if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
|
|
- warnx("%s: PEM_write_PrivateKey", fname);
|
|
+ derlen = br_encode_rsa_raw_der(NULL, &key->rsa.sk, &key->rsa.pk,
|
|
+ d, sizeof(d));
|
|
+ if ((der = malloc(derlen)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ br_encode_rsa_raw_der(der, &key->rsa.sk, &key->rsa.pk, d, sizeof(d));
|
|
+ pemlen = br_pem_encode(NULL, der, derlen, BR_ENCODE_PEM_RSA_RAW, 0);
|
|
+ if ((pem = malloc(pemlen + 1)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ br_pem_encode(pem, der, derlen, BR_ENCODE_PEM_RSA_RAW, 0);
|
|
+ if (fwrite(pem, 1, pemlen, f) != pemlen) {
|
|
+ warn("write private key");
|
|
goto err;
|
|
}
|
|
|
|
- EVP_PKEY_CTX_free(ctx);
|
|
- return pkey;
|
|
+ free(der);
|
|
+ free(pem);
|
|
+ return key;
|
|
|
|
err:
|
|
- EVP_PKEY_free(pkey);
|
|
- EVP_PKEY_CTX_free(ctx);
|
|
+ free(der);
|
|
+ free(pem);
|
|
+ free(key);
|
|
return NULL;
|
|
}
|
|
|
|
-EVP_PKEY *
|
|
+struct key *
|
|
ec_key_create(FILE *f, const char *fname)
|
|
{
|
|
- EVP_PKEY_CTX *ctx = NULL;
|
|
- EVP_PKEY *pkey = NULL;
|
|
+ struct key *key = NULL;
|
|
+ const br_ec_impl *ec;
|
|
+ size_t slen, plen;
|
|
+ unsigned char *sbuf, *pbuf;
|
|
+ unsigned char *der = NULL, *pem = NULL;
|
|
+ size_t derlen, pemlen;
|
|
|
|
- if ((ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL) {
|
|
- warnx("EVP_PKEY_CTX_new_id");
|
|
- goto err;
|
|
- }
|
|
- if (EVP_PKEY_keygen_init(ctx) <= 0) {
|
|
- warnx("EVP_PKEY_keygen_init");
|
|
+ slen = BR_EC_KBUF_PRIV_MAX_SIZE;
|
|
+ plen = BR_EC_KBUF_PUB_MAX_SIZE;
|
|
+ if ((key = malloc(sizeof(*key) + slen + plen)) == NULL) {
|
|
+ warn("malloc");
|
|
goto err;
|
|
}
|
|
- if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, NID_secp384r1) <= 0) {
|
|
- warnx("EVP_PKEY_CTX_set_ec_paramgen_curve_nid");
|
|
+ key->type = BR_KEYTYPE_EC;
|
|
+ key->datasz = slen + plen;
|
|
+ sbuf = key->data;
|
|
+ pbuf = key->data + slen;
|
|
+
|
|
+ ec = br_ec_get_default();
|
|
+ if (br_ec_keygen(&prng, ec, &key->ec.sk, sbuf, BR_EC_secp384r1) == 0) {
|
|
+ warnx("br_ec_keygen");
|
|
goto err;
|
|
}
|
|
- if (EVP_PKEY_keygen(ctx, &pkey) <= 0) {
|
|
- warnx("EVP_PKEY_keygen");
|
|
+ if (br_ec_compute_pub(ec, &key->ec.pk, pbuf, &key->ec.sk) == 0) {
|
|
+ warnx("br_ec_compute_pub");
|
|
goto err;
|
|
}
|
|
|
|
- /* Serialise the key to the disc. */
|
|
+ /* Serialise the key to the disk in EC format */
|
|
|
|
- if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
|
|
- warnx("%s: PEM_write_PrivateKey", fname);
|
|
+ if ((derlen = br_encode_ec_raw_der(NULL, &key->ec.sk,
|
|
+ &key->ec.pk)) == 0) {
|
|
+ warnx("br_encode_ec_raw_der");
|
|
+ goto err;
|
|
+ }
|
|
+ if ((der = malloc(derlen)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ br_encode_ec_raw_der(der, &key->ec.sk, &key->ec.pk);
|
|
+ pemlen = br_pem_encode(NULL, der, derlen, BR_ENCODE_PEM_EC_RAW, 0);
|
|
+ if ((pem = malloc(pemlen + 1)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ br_pem_encode(pem, der, derlen, BR_ENCODE_PEM_EC_RAW, 0);
|
|
+ if (fwrite(pem, 1, pemlen, f) != pemlen) {
|
|
+ warn("write private key");
|
|
goto err;
|
|
}
|
|
|
|
- EVP_PKEY_CTX_free(ctx);
|
|
- return pkey;
|
|
+ free(der);
|
|
+ free(pem);
|
|
+ return key;
|
|
|
|
err:
|
|
- EVP_PKEY_free(pkey);
|
|
- EVP_PKEY_CTX_free(ctx);
|
|
+ free(der);
|
|
+ free(pem);
|
|
+ free(key);
|
|
return NULL;
|
|
}
|
|
|
|
-EVP_PKEY *
|
|
+static void
|
|
+append_skey(void *ctx, const void *src, size_t len)
|
|
+{
|
|
+ br_skey_decoder_push(ctx, src, len);
|
|
+}
|
|
+
|
|
+struct key *
|
|
key_load(FILE *f, const char *fname)
|
|
{
|
|
- EVP_PKEY *pkey;
|
|
+ struct key *key = NULL;
|
|
+ size_t datasz, len = 0, n;
|
|
+ int type = 0, err;
|
|
+ unsigned char buf[8192], *pos;
|
|
+ br_pem_decoder_context pemctx;
|
|
+ br_skey_decoder_context keyctx;
|
|
+ br_rsa_compute_modulus compute_modulus;
|
|
+ br_rsa_compute_pubexp compute_pubexp;
|
|
+ const br_ec_impl *ecimpl;
|
|
+ const br_rsa_private_key *rsa;
|
|
+ const br_ec_private_key *ec;
|
|
+ const char *name = NULL;
|
|
+ uint32_t pubexp;
|
|
|
|
- pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
|
|
- if (pkey == NULL) {
|
|
- warnx("%s: PEM_read_PrivateKey", fname);
|
|
- return NULL;
|
|
+ br_pem_decoder_init(&pemctx);
|
|
+ br_skey_decoder_init(&keyctx);
|
|
+ while (type == 0) {
|
|
+ if (len == 0) {
|
|
+ if (feof(f)) {
|
|
+ warnx("%s: missing private key", fname);
|
|
+ break;
|
|
+ }
|
|
+ len = fread(buf, 1, sizeof(buf), f);
|
|
+ if (ferror(f)) {
|
|
+ warn("%s: read", fname);
|
|
+ goto err;
|
|
+ }
|
|
+ pos = buf;
|
|
+ }
|
|
+ n = br_pem_decoder_push(&pemctx, pos, len);
|
|
+ pos += n;
|
|
+ len -= n;
|
|
+ switch (br_pem_decoder_event(&pemctx)) {
|
|
+ case BR_PEM_BEGIN_OBJ:
|
|
+ name = br_pem_decoder_name(&pemctx);
|
|
+ if (strcmp(name, BR_ENCODE_PEM_PKCS8) != 0 &&
|
|
+ strcmp(name, BR_ENCODE_PEM_RSA_RAW) != 0 &&
|
|
+ strcmp(name, BR_ENCODE_PEM_EC_RAW) != 0) {
|
|
+ name = NULL;
|
|
+ break;
|
|
+ }
|
|
+ br_pem_decoder_setdest(&pemctx, append_skey, &keyctx);
|
|
+ break;
|
|
+ case BR_PEM_END_OBJ:
|
|
+ if (name == NULL)
|
|
+ break;
|
|
+ if ((err = br_skey_decoder_last_error(&keyctx)) != 0) {
|
|
+ warnx("%s: br_skey_decoder: %d", fname, err);
|
|
+ goto err;
|
|
+ }
|
|
+ type = br_skey_decoder_key_type(&keyctx);
|
|
+ break;
|
|
+ case 0:
|
|
+ break;
|
|
+ default:
|
|
+ warnx("%s: PEM decoding failed", fname);
|
|
+ goto err;
|
|
+ }
|
|
}
|
|
- if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA ||
|
|
- EVP_PKEY_base_id(pkey) == EVP_PKEY_EC)
|
|
- return pkey;
|
|
|
|
- warnx("%s: unsupported key type", fname);
|
|
- EVP_PKEY_free(pkey);
|
|
- return NULL;
|
|
+ switch (type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
+ rsa = br_skey_decoder_get_rsa(&keyctx);
|
|
+ compute_modulus = br_rsa_compute_modulus_get_default();
|
|
+ compute_pubexp = br_rsa_compute_pubexp_get_default();
|
|
+
|
|
+ /* Compute public modulus size. This will fail if
|
|
+ * p or q is not 3 mod 4. */
|
|
+ if ((datasz = compute_modulus(NULL, rsa)) == 0) {
|
|
+ warnx("%s: br_rsa_compute_modulus", fname);
|
|
+ goto err;
|
|
+ }
|
|
+ datasz += 4 + rsa->plen + rsa->qlen + rsa->dplen + rsa->dqlen +
|
|
+ rsa->iqlen;
|
|
+
|
|
+ if ((key = malloc(sizeof(*key) + datasz)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ key->type = BR_KEYTYPE_RSA;
|
|
+ key->datasz = datasz;
|
|
+
|
|
+ if ((pubexp = compute_pubexp(rsa)) == 0) {
|
|
+ warnx("%s: br_rsa_compute_pubexp", fname);
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ /* Copy private key. */
|
|
+ key->rsa.sk.n_bitlen = rsa->n_bitlen;
|
|
+ key->rsa.sk.p = key->data;
|
|
+ key->rsa.sk.plen = rsa->plen;
|
|
+ key->rsa.sk.q = key->rsa.sk.p + rsa->plen;
|
|
+ key->rsa.sk.qlen = rsa->qlen;
|
|
+ key->rsa.sk.dp = key->rsa.sk.q + rsa->qlen;
|
|
+ key->rsa.sk.dplen = rsa->dplen;
|
|
+ key->rsa.sk.dq = key->rsa.sk.dp + rsa->dplen;
|
|
+ key->rsa.sk.dqlen = rsa->dqlen;
|
|
+ key->rsa.sk.iq = key->rsa.sk.dq + rsa->dqlen;
|
|
+ key->rsa.sk.iqlen = rsa->iqlen;
|
|
+ memcpy(key->rsa.sk.p, rsa->p, rsa->plen);
|
|
+ memcpy(key->rsa.sk.q, rsa->q, rsa->qlen);
|
|
+ memcpy(key->rsa.sk.dp, rsa->dp, rsa->dplen);
|
|
+ memcpy(key->rsa.sk.dq, rsa->dq, rsa->dqlen);
|
|
+ memcpy(key->rsa.sk.iq, rsa->iq, rsa->iqlen);
|
|
+
|
|
+ /* Compute public modulus and encode public exponent. */
|
|
+ key->rsa.pk.n = key->rsa.sk.iq + rsa->iqlen;
|
|
+ key->rsa.pk.nlen = compute_modulus(key->rsa.pk.n, rsa);
|
|
+ key->rsa.pk.elen = 4;
|
|
+ key->rsa.pk.e = key->rsa.pk.n + key->rsa.pk.nlen;
|
|
+ key->rsa.pk.e[0] = pubexp >> 24;
|
|
+ key->rsa.pk.e[1] = pubexp >> 16;
|
|
+ key->rsa.pk.e[2] = pubexp >> 8;
|
|
+ key->rsa.pk.e[3] = pubexp;
|
|
+
|
|
+ /* Trim leading zeros. */
|
|
+ while (key->rsa.pk.elen > 0 && key->rsa.pk.e[0] == 0) {
|
|
+ --key->rsa.pk.elen;
|
|
+ ++key->rsa.pk.e;
|
|
+ }
|
|
+ goto out;
|
|
+ case BR_KEYTYPE_EC:
|
|
+ ec = br_skey_decoder_get_ec(&keyctx);
|
|
+ ecimpl = br_ec_get_default();
|
|
+ if ((datasz = br_ec_compute_pub(ecimpl, NULL, NULL, ec)) == 0) {
|
|
+ warnx("%s: br_ec_compute_pub", fname);
|
|
+ goto err;
|
|
+ }
|
|
+ datasz += ec->xlen;
|
|
+
|
|
+ if ((key = malloc(sizeof(*key) + datasz)) == NULL) {
|
|
+ warn("malloc");
|
|
+ goto err;
|
|
+ }
|
|
+ key->type = BR_KEYTYPE_EC;
|
|
+ key->datasz = datasz;
|
|
+
|
|
+ key->ec.sk.curve = ec->curve;
|
|
+ key->ec.sk.x = key->data;
|
|
+ key->ec.sk.xlen = ec->xlen;
|
|
+ memcpy(key->ec.sk.x, ec->x, ec->xlen);
|
|
+ br_ec_compute_pub(ecimpl, &key->ec.pk,
|
|
+ key->ec.sk.x + key->ec.sk.xlen, &key->ec.sk);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ warnx("%s: missing private key", fname);
|
|
+
|
|
+err:
|
|
+ free(key);
|
|
+ key = NULL;
|
|
+out:
|
|
+ explicit_bzero(&pemctx, sizeof(pemctx));
|
|
+ explicit_bzero(&keyctx, sizeof(keyctx));
|
|
+ return key;
|
|
}
|
|
diff --git a/usr.sbin/acme-client/key.h b/usr.sbin/acme-client/key.h
|
|
index 272d36eb09a..12abdec813c 100644
|
|
--- a/usr.sbin/acme-client/key.h
|
|
+++ b/usr.sbin/acme-client/key.h
|
|
@@ -18,8 +18,24 @@
|
|
#ifndef KEY_H
|
|
#define KEY_H
|
|
|
|
-EVP_PKEY *rsa_key_create(FILE *, const char *);
|
|
-EVP_PKEY *ec_key_create(FILE *, const char *);
|
|
-EVP_PKEY *key_load(FILE *, const char *);
|
|
+struct key {
|
|
+ int type;
|
|
+ union {
|
|
+ struct {
|
|
+ br_rsa_public_key pk;
|
|
+ br_rsa_private_key sk;
|
|
+ } rsa;
|
|
+ struct {
|
|
+ br_ec_public_key pk;
|
|
+ br_ec_private_key sk;
|
|
+ } ec;
|
|
+ };
|
|
+ size_t datasz;
|
|
+ unsigned char data[];
|
|
+};
|
|
+
|
|
+struct key *rsa_key_create(FILE *, const char *);
|
|
+struct key *ec_key_create(FILE *, const char *);
|
|
+struct key *key_load(FILE *, const char *);
|
|
|
|
#endif /* ! KEY_H */
|
|
diff --git a/usr.sbin/acme-client/keyproc.c b/usr.sbin/acme-client/keyproc.c
|
|
index 4360156b9c0..19b8077a8fb 100644
|
|
--- a/usr.sbin/acme-client/keyproc.c
|
|
+++ b/usr.sbin/acme-client/keyproc.c
|
|
@@ -18,55 +18,18 @@
|
|
#include <sys/stat.h>
|
|
|
|
#include <err.h>
|
|
+#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
-#include <openssl/pem.h>
|
|
-#include <openssl/err.h>
|
|
-#include <openssl/rand.h>
|
|
-#include <openssl/x509.h>
|
|
-#include <openssl/x509v3.h>
|
|
+#include <bearssl.h>
|
|
+#include <x509cert.h>
|
|
|
|
#include "extern.h"
|
|
#include "key.h"
|
|
|
|
-/*
|
|
- * This was lifted more or less directly from demos/x509/mkreq.c of the
|
|
- * OpenSSL source code.
|
|
- */
|
|
-static int
|
|
-add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, const char *value)
|
|
-{
|
|
- X509_EXTENSION *ex;
|
|
- char *cp;
|
|
-
|
|
- /*
|
|
- * XXX: I don't like this at all.
|
|
- * There's no documentation for X509V3_EXT_conf_nid, so I'm not
|
|
- * sure if the "value" parameter is ever written to, touched,
|
|
- * etc.
|
|
- * The 'official' examples suggest not (they use a string
|
|
- * literal as the input), but to be safe, I'm doing an
|
|
- * allocation here and just letting it go.
|
|
- * This leaks memory, but bounded to the number of SANs.
|
|
- */
|
|
-
|
|
- if ((cp = strdup(value)) == NULL) {
|
|
- warn("strdup");
|
|
- return (0);
|
|
- }
|
|
- ex = X509V3_EXT_conf_nid(NULL, NULL, nid, cp);
|
|
- if (ex == NULL) {
|
|
- warnx("X509V3_EXT_conf_nid");
|
|
- free(cp);
|
|
- return (0);
|
|
- }
|
|
- sk_X509_EXTENSION_push(sk, ex);
|
|
- return (1);
|
|
-}
|
|
-
|
|
/*
|
|
* Create an X509 certificate from the private key we have on file.
|
|
* To do this, we first open the key file, then jail ourselves.
|
|
@@ -76,19 +39,20 @@ add_ext(STACK_OF(X509_EXTENSION) *sk, int nid, const char *value)
|
|
int
|
|
keyproc(int netsock, struct domain_c *domain)
|
|
{
|
|
- char *der64 = NULL;
|
|
- unsigned char *der = NULL, *dercp;
|
|
- char *sans = NULL, *san = NULL;
|
|
- FILE *f;
|
|
- size_t sansz;
|
|
- void *pp;
|
|
- EVP_PKEY *pkey = NULL;
|
|
- X509_REQ *x = NULL;
|
|
- int len, rc = 0, cc, nid, newkey = 0, first;
|
|
- mode_t prev;
|
|
- STACK_OF(X509_EXTENSION) *exts = NULL;
|
|
- struct altname_c *ac;
|
|
- const char *keyfile = domain->key;
|
|
+ char *der64 = NULL;
|
|
+ unsigned char *der = NULL;
|
|
+ FILE *f;
|
|
+ struct key *pkey = NULL;
|
|
+ struct x509cert_req req;
|
|
+ struct x509cert_skey skey;
|
|
+ struct x509cert_dn dn;
|
|
+ struct x509cert_item item;
|
|
+ int len, rc = 0, newkey = 0, i;
|
|
+ mode_t prev;
|
|
+ struct altname_c *ac;
|
|
+ const char *keyfile = domain->key;
|
|
+
|
|
+ req.alts = NULL;
|
|
|
|
/*
|
|
* First, open our private key file read-only or write-only if
|
|
@@ -110,8 +74,6 @@ keyproc(int netsock, struct domain_c *domain)
|
|
|
|
/* File-system, user, and sandbox jail. */
|
|
|
|
- ERR_load_crypto_strings();
|
|
-
|
|
if (pledge("stdio", NULL) == -1) {
|
|
warn("pledge");
|
|
goto out;
|
|
@@ -145,98 +107,65 @@ keyproc(int netsock, struct domain_c *domain)
|
|
* Then set it as the X509 requester's key.
|
|
*/
|
|
|
|
- if ((x = X509_REQ_new()) == NULL) {
|
|
- warnx("X509_REQ_new");
|
|
- goto out;
|
|
- } else if (!X509_REQ_set_version(x, 0)) {
|
|
- warnx("X509_REQ_set_version");
|
|
- goto out;
|
|
- } else if (!X509_REQ_set_pubkey(x, pkey)) {
|
|
- warnx("X509_REQ_set_pubkey");
|
|
- goto out;
|
|
+ req.pkey.key_type = pkey->type;
|
|
+ skey.type = pkey->type;
|
|
+ switch (pkey->type) {
|
|
+ case BR_KEYTYPE_RSA:
|
|
+ req.pkey.key.rsa = pkey->rsa.pk;
|
|
+ skey.u.rsa = &pkey->rsa.sk;
|
|
+ break;
|
|
+ case BR_KEYTYPE_EC:
|
|
+ req.pkey.key.ec = pkey->ec.pk;
|
|
+ skey.u.ec = &pkey->ec.sk;
|
|
+ break;
|
|
}
|
|
|
|
- /*
|
|
- * Now add the SAN extensions.
|
|
- * This was lifted more or less directly from demos/x509/mkreq.c
|
|
- * of the OpenSSL source code.
|
|
- * (The zeroth altname is the domain name.)
|
|
- * TODO: is this the best way of doing this?
|
|
- */
|
|
+ /* Use an empty subject. */
|
|
|
|
- nid = NID_subject_alt_name;
|
|
- if ((exts = sk_X509_EXTENSION_new_null()) == NULL) {
|
|
- warnx("sk_X509_EXTENSION_new_null");
|
|
- goto out;
|
|
- }
|
|
- /* Initialise to empty string. */
|
|
- if ((sans = strdup("")) == NULL) {
|
|
- warn("strdup");
|
|
+ dn.rdn = NULL;
|
|
+ dn.rdn_len = 0;
|
|
+ req.subject.enc = x509cert_dn_encoder;
|
|
+ req.subject.val = &dn;
|
|
+
|
|
+ /* Now add the SAN extension. */
|
|
+
|
|
+ req.alts_len = domain->altname_count;
|
|
+ req.alts = calloc(domain->altname_count, sizeof(req.alts[0]));
|
|
+ if (req.alts == NULL) {
|
|
+ warn("calloc");
|
|
goto out;
|
|
}
|
|
- sansz = strlen(sans) + 1;
|
|
|
|
- /*
|
|
- * For each SAN entry, append it to the string.
|
|
- * We need a single SAN entry for all of the SAN
|
|
- * domains: NOT an entry per domain!
|
|
- */
|
|
+ /* Add a dNSName SAN entry for each alternate name. */
|
|
|
|
- first = 1;
|
|
+ i = 0;
|
|
TAILQ_FOREACH(ac, &domain->altname_list, entry) {
|
|
switch (ac->idtype) {
|
|
case ID_DNS:
|
|
- cc = asprintf(&san, "%sDNS:%s", first ? "" : ",",
|
|
- ac->domain);
|
|
+ req.alts[i].tag = X509CERT_SAN_DNSNAME;
|
|
+ req.alts[i].val = ac->domain;
|
|
+ req.alts[i].len = strlen(ac->domain);
|
|
break;
|
|
case ID_IP:
|
|
- cc = asprintf(&san, "%sIP:%s", first ? "" : ",",
|
|
- ac->domain);
|
|
- break;
|
|
- }
|
|
- first = 0;
|
|
- if (cc == -1) {
|
|
- warn("asprintf");
|
|
- goto out;
|
|
- }
|
|
- pp = recallocarray(sans, sansz, sansz + strlen(san), 1);
|
|
- if (pp == NULL) {
|
|
- warn("recallocarray");
|
|
+ warnx("ID_IP is not supported");
|
|
goto out;
|
|
}
|
|
- sans = pp;
|
|
- sansz += strlen(san);
|
|
- strlcat(sans, san, sansz);
|
|
- free(san);
|
|
- san = NULL;
|
|
- }
|
|
-
|
|
- if (!add_ext(exts, nid, sans)) {
|
|
- warnx("add_ext");
|
|
- goto out;
|
|
- } else if (!X509_REQ_add_extensions(x, exts)) {
|
|
- warnx("X509_REQ_add_extensions");
|
|
- goto out;
|
|
- }
|
|
- sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
|
-
|
|
- /* Sign the X509 request using SHA256. */
|
|
-
|
|
- if (!X509_REQ_sign(x, pkey, EVP_sha256())) {
|
|
- warnx("X509_sign");
|
|
- goto out;
|
|
+ i++;
|
|
}
|
|
|
|
- /* Now, serialise to DER, then base64. */
|
|
+ /* Sign the X.509 request using SHA256, and serialise to
|
|
+ * DER then base64. */
|
|
|
|
- if ((len = i2d_X509_REQ(x, NULL)) < 0) {
|
|
- warnx("i2d_X509_REQ");
|
|
+ item.enc = x509cert_req_encoder;
|
|
+ item.val = &req;
|
|
+ if ((len = x509cert_sign(&item, &skey, &br_sha256_vtable, NULL)) == 0) {
|
|
+ warnx("x509cert_sign");
|
|
goto out;
|
|
- } else if ((der = dercp = malloc(len)) == NULL) {
|
|
+ } else if ((der = malloc(len)) == NULL) {
|
|
warn("malloc");
|
|
goto out;
|
|
- } else if (len != i2d_X509_REQ(x, &dercp)) {
|
|
- warnx("i2d_X509_REQ");
|
|
+ } else if ((len = x509cert_sign(&item, &skey, &br_sha256_vtable, der)) == 0) {
|
|
+ warnx("x509cert_sign");
|
|
goto out;
|
|
} else if ((der64 = base64buf_url(der, len)) == NULL) {
|
|
warnx("base64buf_url");
|
|
@@ -261,11 +190,8 @@ out:
|
|
fclose(f);
|
|
free(der);
|
|
free(der64);
|
|
- free(sans);
|
|
- free(san);
|
|
- X509_REQ_free(x);
|
|
- EVP_PKEY_free(pkey);
|
|
- ERR_print_errors_fp(stderr);
|
|
- ERR_free_strings();
|
|
+ free(req.alts);
|
|
+ if (pkey != NULL)
|
|
+ freezero(pkey, pkey->datasz);
|
|
return rc;
|
|
}
|
|
diff --git a/usr.sbin/acme-client/revokeproc.c b/usr.sbin/acme-client/revokeproc.c
|
|
index c0963a278fb..2109d7a8799 100644
|
|
--- a/usr.sbin/acme-client/revokeproc.c
|
|
+++ b/usr.sbin/acme-client/revokeproc.c
|
|
@@ -15,8 +15,6 @@
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
-#include <sys/socket.h>
|
|
-#include <arpa/inet.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
@@ -24,71 +22,54 @@
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
+#include <time.h>
|
|
#include <unistd.h>
|
|
#include <vis.h>
|
|
|
|
-#include <openssl/pem.h>
|
|
-#include <openssl/x509.h>
|
|
-#include <openssl/x509v3.h>
|
|
-#include <openssl/err.h>
|
|
+#include <bearssl.h>
|
|
|
|
#include "extern.h"
|
|
|
|
-/*
|
|
- * Convert the X509's notAfter time into a time_t value.
|
|
- */
|
|
-static time_t
|
|
-X509notafter(X509 *x)
|
|
-{
|
|
- ASN1_TIME *atim;
|
|
- struct tm t;
|
|
-
|
|
- if ((atim = X509_getm_notAfter(x)) == NULL)
|
|
- return -1;
|
|
-
|
|
- memset(&t, 0, sizeof(t));
|
|
-
|
|
- if (!ASN1_TIME_to_tm(atim, &t))
|
|
- return -1;
|
|
-
|
|
- return timegm(&t);
|
|
-}
|
|
-
|
|
-/*
|
|
- * Convert the X509's notBefore time into a time_t value.
|
|
- */
|
|
-static time_t
|
|
-X509notbefore(X509 *x)
|
|
+static void
|
|
+append_cert(void *ctx, const void *buf, size_t len)
|
|
{
|
|
- ASN1_TIME *atim;
|
|
- struct tm t;
|
|
-
|
|
- if ((atim = X509_getm_notBefore(x)) == NULL)
|
|
- return -1;
|
|
-
|
|
- memset(&t, 0, sizeof(t));
|
|
-
|
|
- if (!ASN1_TIME_to_tm(atim, &t))
|
|
- return -1;
|
|
-
|
|
- return timegm(&t);
|
|
+ br_x509_certificate *cert = ctx;
|
|
+ size_t newlen;
|
|
+ unsigned char *newdata;
|
|
+
|
|
+ if (cert->data_len == -1)
|
|
+ return;
|
|
+ newlen = cert->data_len + len;
|
|
+ if ((newdata = realloc(cert->data, newlen)) != NULL) {
|
|
+ memcpy(newdata + cert->data_len, buf, len);
|
|
+ cert->data = newdata;
|
|
+ cert->data_len = newlen;
|
|
+ } else {
|
|
+ warn("realloc");
|
|
+ cert->data_len = -1;
|
|
+ }
|
|
}
|
|
|
|
int
|
|
revokeproc(int fd, const char *certfile, int force,
|
|
int revocate, struct domain_c *domain)
|
|
{
|
|
- GENERAL_NAMES *sans = NULL;
|
|
- unsigned char *der = NULL, *dercp;
|
|
- char *der64 = NULL;
|
|
- int rc = 0, cc, sanidx, len, j, k;
|
|
- int *found_altnames = NULL;
|
|
+ static const unsigned char dnsname[] = {0, 2};
|
|
+ char buf[8192], *pos, *sans = NULL, *der64 = NULL;
|
|
+ int rc = 0, cc, i, eltsz, state, err;
|
|
+ size_t n, len = 0, altlen, altmax;
|
|
FILE *f = NULL;
|
|
- X509 *x = NULL;
|
|
+ br_pem_decoder_context pc;
|
|
+ br_x509_decoder_context xd;
|
|
+ br_x509_minimal_context xc;
|
|
+ br_x509_certificate cert = {0};
|
|
+ br_name_element *elts = NULL;
|
|
+ uint32_t days, secs;
|
|
long lval;
|
|
enum revokeop op, rop;
|
|
time_t notafter, notbefore, cert_validity;
|
|
time_t remaining_validity, renew_allow;
|
|
+ struct altname_c *ac;
|
|
|
|
/*
|
|
* First try to open the certificate before we drop privileges
|
|
@@ -103,8 +84,6 @@ revokeproc(int fd, const char *certfile, int force,
|
|
|
|
/* File-system and sandbox jailing. */
|
|
|
|
- ERR_load_crypto_strings();
|
|
-
|
|
if (pledge("stdio", NULL) == -1) {
|
|
warn("pledge");
|
|
goto out;
|
|
@@ -128,45 +107,89 @@ revokeproc(int fd, const char *certfile, int force,
|
|
goto out;
|
|
}
|
|
|
|
- if ((x = PEM_read_X509(f, NULL, NULL, NULL)) == NULL) {
|
|
- warnx("PEM_read_X509");
|
|
- goto out;
|
|
+ br_pem_decoder_init(&pc);
|
|
+ for (state = 0; state != 2;) {
|
|
+ if (len == 0) {
|
|
+ if (feof(f)) {
|
|
+ warnx("%s: truncated certificate", certfile);
|
|
+ goto out;
|
|
+ }
|
|
+ len = fread(buf, 1, sizeof(buf), f);
|
|
+ if (ferror(f)) {
|
|
+ warn("fread");
|
|
+ goto out;
|
|
+ }
|
|
+ pos = buf;
|
|
+ }
|
|
+ n = br_pem_decoder_push(&pc, pos, len);
|
|
+ pos += n;
|
|
+ len -= n;
|
|
+ switch (br_pem_decoder_event(&pc)) {
|
|
+ case BR_PEM_BEGIN_OBJ:
|
|
+ if (strcmp(br_pem_decoder_name(&pc), "CERTIFICATE") == 0) {
|
|
+ br_pem_decoder_setdest(&pc, append_cert, &cert);
|
|
+ state = 1;
|
|
+ }
|
|
+ break;
|
|
+ case BR_PEM_END_OBJ:
|
|
+ if (state == 1)
|
|
+ state = 2;
|
|
+ break;
|
|
+ case 0:
|
|
+ break;
|
|
+ default:
|
|
+ warnx("%s: PEM decoding error", certfile);
|
|
+ goto out;
|
|
+ }
|
|
}
|
|
-
|
|
- /* Cache and sanity check X509v3 extensions. */
|
|
-
|
|
- if (X509_check_purpose(x, -1, -1) <= 0) {
|
|
- warnx("%s: invalid X509v3 extensions", certfile);
|
|
+ if (cert.data_len == -1)
|
|
goto out;
|
|
- }
|
|
|
|
/* Read out the expiration date. */
|
|
|
|
- if ((notafter = X509notafter(x)) == -1) {
|
|
- warnx("X509notafter");
|
|
+ br_x509_decoder_init(&xd, NULL, NULL);
|
|
+ br_x509_decoder_push(&xd, cert.data, cert.data_len);
|
|
+ if ((err = br_x509_decoder_last_error(&xd)) != 0) {
|
|
+ warnx("%s: X.509 decoding error %d", certfile, err);
|
|
goto out;
|
|
}
|
|
-
|
|
- if ((notbefore = X509notbefore(x)) == -1) {
|
|
- warnx("X509notbefore");
|
|
+ br_x509_decoder_get_notafter(&xd, &days, &secs);
|
|
+ notafter = 86400ll * (days - 719528) + 86400;
|
|
+ br_x509_decoder_get_notbefore(&xd, &days, &secs);
|
|
+ notbefore = 86400ll * (days - 719528) + 86400;
|
|
+
|
|
+ altmax = 0;
|
|
+ TAILQ_FOREACH(ac, &domain->altname_list, entry) {
|
|
+ altlen = strlen(ac->domain) + 1;
|
|
+ if (altlen > altmax)
|
|
+ altmax = altlen;
|
|
+ }
|
|
+ eltsz = domain->altname_count + 1;
|
|
+ if ((elts = calloc(eltsz, sizeof(elts[0]))) == NULL ||
|
|
+ (sans = calloc(eltsz, altmax)) == NULL) {
|
|
+ warn("calloc");
|
|
goto out;
|
|
}
|
|
-
|
|
- /* Extract list of SAN entries from the certificate. */
|
|
-
|
|
- sans = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
|
|
- if (sans == NULL) {
|
|
- warnx("%s: does not have a SAN entry", certfile);
|
|
- if (revocate)
|
|
- goto out;
|
|
- force = 2;
|
|
+ for (i = 0; i < eltsz; ++i) {
|
|
+ elts[i].oid = dnsname;
|
|
+ elts[i].buf = sans + i * altmax;
|
|
+ elts[i].len = altmax;
|
|
}
|
|
|
|
- /* An array of buckets: the number of entries found. */
|
|
+ /* Extract list of SAN entries from the certificate. */
|
|
|
|
- if ((found_altnames = (int *)calloc(domain->altname_count,
|
|
- sizeof(int))) == NULL) {
|
|
- warn("calloc");
|
|
+ br_x509_minimal_init(&xc, &br_sha256_vtable, NULL, 0);
|
|
+ br_x509_minimal_set_hash(&xc, br_sha256_ID, &br_sha256_vtable);
|
|
+ br_x509_minimal_set_hash(&xc, br_sha384_ID, &br_sha384_vtable);
|
|
+ br_x509_minimal_set_hash(&xc, br_sha512_ID, &br_sha512_vtable);
|
|
+ br_x509_minimal_set_name_elements(&xc, elts, eltsz);
|
|
+ xc.vtable->start_chain(&xc.vtable, NULL);
|
|
+ xc.vtable->start_cert(&xc.vtable, cert.data_len);
|
|
+ xc.vtable->append(&xc.vtable, cert.data, cert.data_len);
|
|
+ xc.vtable->end_cert(&xc.vtable);
|
|
+ err = xc.vtable->end_chain(&xc.vtable);
|
|
+ if (err != BR_ERR_X509_NOT_TRUSTED && err != BR_ERR_X509_EXPIRED) {
|
|
+ warnx("%s: X.509 engine error %d", certfile, err);
|
|
goto out;
|
|
}
|
|
|
|
@@ -175,121 +198,37 @@ revokeproc(int fd, const char *certfile, int force,
|
|
* configuration file and that all domains are represented only once.
|
|
*/
|
|
|
|
- for (sanidx = 0; sanidx < sk_GENERAL_NAME_num(sans); sanidx++) {
|
|
- GENERAL_NAME *gen_name;
|
|
- char *name_buf = NULL;
|
|
- int name_len;
|
|
- struct altname_c *ac;
|
|
-
|
|
- gen_name = sk_GENERAL_NAME_value(sans, sanidx);
|
|
- assert(gen_name != NULL);
|
|
-
|
|
- if (gen_name->type == GEN_IPADD) {
|
|
- char ip_buf[INET6_ADDRSTRLEN];
|
|
- const char *ip;
|
|
-
|
|
- name_len = ASN1_STRING_length(gen_name->d.iPAddress);
|
|
- switch (name_len) {
|
|
- case 4:
|
|
- ip = inet_ntop(AF_INET,
|
|
- ASN1_STRING_get0_data(gen_name->d.iPAddress),
|
|
- ip_buf, INET6_ADDRSTRLEN);
|
|
- break;
|
|
- case 16:
|
|
- ip = inet_ntop(AF_INET6,
|
|
- ASN1_STRING_get0_data(gen_name->d.iPAddress),
|
|
- ip_buf, INET6_ADDRSTRLEN);
|
|
- break;
|
|
- default:
|
|
- ip = NULL;
|
|
+ TAILQ_FOREACH(ac, &domain->altname_list, entry) {
|
|
+ for (i = 0; i < eltsz; i++) {
|
|
+ if (elts[i].status == 1 &&
|
|
+ strcmp(ac->domain, elts[i].buf) == 0) {
|
|
+ elts[i].status = 0;
|
|
break;
|
|
}
|
|
- if (ip == NULL) {
|
|
- warnx("invalid IP address");
|
|
- continue;
|
|
- }
|
|
- name_len = asprintf(&name_buf, "%s", ip);
|
|
- } else if (gen_name->type == GEN_DNS) {
|
|
- name_len = ASN1_STRING_length(gen_name->d.dNSName);
|
|
- name_len = asprintf(&name_buf, "%.*s",
|
|
- name_len,
|
|
- ASN1_STRING_get0_data(gen_name->d.dNSName));
|
|
- } else
|
|
- continue;
|
|
-
|
|
- if (name_len == -1) {
|
|
- warn("asprintf");
|
|
- continue;
|
|
}
|
|
-
|
|
- j = 0;
|
|
- TAILQ_FOREACH(ac, &domain->altname_list, entry) {
|
|
- if (strcmp(name_buf, ac->domain) == 0) {
|
|
- found_altnames[j]++;
|
|
- break;
|
|
- }
|
|
- /* increment if didn't match */
|
|
- j++;
|
|
- }
|
|
- if (j >= domain->altname_count) {
|
|
- /* we haven't matched any */
|
|
- if (revocate) {
|
|
- char *visbuf;
|
|
-
|
|
- visbuf = calloc(4, name_len + 1);
|
|
- if (visbuf == NULL) {
|
|
- warn("%s: unexpected SAN in "
|
|
- "certificate", certfile);
|
|
- free(name_buf);
|
|
- goto out;
|
|
- }
|
|
- strvisx(visbuf, name_buf, name_len, VIS_SAFE);
|
|
- warnx("%s: unexpected SAN entry in "
|
|
- "certificate: %s", certfile, visbuf);
|
|
- free(visbuf);
|
|
- free(name_buf);
|
|
- goto out;
|
|
- }
|
|
- force = 2;
|
|
- continue;
|
|
- }
|
|
- /* should not reach here if j is out of bounds */
|
|
- if (found_altnames[j] > 1) {
|
|
+ if (i == eltsz) {
|
|
if (revocate) {
|
|
- char *visbuf;
|
|
- visbuf = calloc(4, name_len + 1);
|
|
- if (visbuf == NULL) {
|
|
- warn("%s: duplicate SAN in "
|
|
- "certificate", certfile);
|
|
- free(name_buf);
|
|
- goto out;
|
|
- }
|
|
- warnx("%s: duplicate SAN entry in "
|
|
- "certificate: %s", certfile, visbuf);
|
|
- free(name_buf);
|
|
- free(visbuf);
|
|
+ warnx("%s: domain not listed: %s", certfile, ac->domain);
|
|
goto out;
|
|
}
|
|
force = 2;
|
|
}
|
|
-
|
|
- free(name_buf);
|
|
}
|
|
|
|
- for (j = 0; j < domain->altname_count; j++) {
|
|
- struct altname_c *ac;
|
|
-
|
|
- if (found_altnames[j])
|
|
+ for (i = 0; i < eltsz; i++) {
|
|
+ if (elts[i].status == 0)
|
|
continue;
|
|
if (revocate) {
|
|
- k = 0;
|
|
- TAILQ_FOREACH(ac, &domain->altname_list, entry) {
|
|
- if (j == k)
|
|
- break;
|
|
- k++;
|
|
+ char *visbuf;
|
|
+
|
|
+ if (elts[i].status != 1 ||
|
|
+ stravis(&visbuf, elts[i].buf, VIS_SAFE) < 0) {
|
|
+ warnx("%s: unexpected SAN", certfile);
|
|
+ goto out;
|
|
}
|
|
- warnx("%s: domain not listed: %s", certfile,
|
|
- ac->domain);
|
|
+ warnx("%s: unexpected SAN entry: %s",
|
|
+ certfile, visbuf);
|
|
+ free(visbuf);
|
|
goto out;
|
|
}
|
|
force = 2;
|
|
@@ -315,16 +254,7 @@ revokeproc(int fd, const char *certfile, int force,
|
|
if (cc <= 0)
|
|
goto out;
|
|
|
|
- if ((len = i2d_X509(x, NULL)) < 0) {
|
|
- warnx("i2d_X509");
|
|
- goto out;
|
|
- } else if ((der = dercp = malloc(len)) == NULL) {
|
|
- warn("malloc");
|
|
- goto out;
|
|
- } else if (len != i2d_X509(x, &dercp)) {
|
|
- warnx("i2d_X509");
|
|
- goto out;
|
|
- } else if ((der64 = base64buf_url(der, len)) == NULL) {
|
|
+ if ((der64 = base64buf_url(cert.data, cert.data_len)) == NULL) {
|
|
warnx("base64buf_url");
|
|
goto out;
|
|
} else if (writestr(fd, COMM_CSR, der64) >= 0)
|
|
@@ -398,12 +328,9 @@ out:
|
|
close(fd);
|
|
if (f != NULL)
|
|
fclose(f);
|
|
- X509_free(x);
|
|
- GENERAL_NAMES_free(sans);
|
|
- free(der);
|
|
- free(found_altnames);
|
|
+ free(cert.data);
|
|
+ free(sans);
|
|
+ free(elts);
|
|
free(der64);
|
|
- ERR_print_errors_fp(stderr);
|
|
- ERR_free_strings();
|
|
return rc;
|
|
}
|
|
--
|
|
2.54.0
|
|
|