Add a signer

we also reduce the number of tests to 1/10th due to the signing
computation required.
parent e6c7a016
......@@ -529,6 +529,20 @@ crypto_ctx_task_md_ctx(VRT_CTX, const void *id, EVP_MD_CTX *evpctx, int reset)
return (vcvt->evpctx);
}
/*
* ------------------------------------------------------------
* misc
*/
static int
crypto_err_cb(const char *s, size_t l, void *u)
{
VRT_CTX;
CAST_OBJ_NOTNULL(ctx, u, VRT_CTX_MAGIC);
VSLb(ctx->vsl, SLT_Debug, "crypto %.*s", l, s);
return (0);
}
/*
* ------------------------------------------------------------
* $Object verfier()
......@@ -697,16 +711,6 @@ VCL_BOOL vmod_verifier_reset(VRT_CTX,
return (!! crypto_ctx_task_md_ctx(ctx, vcv, vcv->evpctx, 1));
}
static int
crypto_err_cb(const char *s, size_t l, void *u)
{
VRT_CTX;
CAST_OBJ_NOTNULL(ctx, u, VRT_CTX_MAGIC);
VSLb(ctx->vsl, SLT_Debug, "crypto %.*s", l, s);
return (0);
}
VCL_BOOL vmod_verifier_valid(VRT_CTX,
struct vmod_crypto_verifier *vcv, VCL_BLOB sig)
{
......@@ -728,3 +732,205 @@ VCL_BOOL vmod_verifier_valid(VRT_CTX,
}
return (r);
}
/*
* ------------------------------------------------------------
* $Object signer()
*
* XXX structurally this is very similar to the verifier. Using common functions
* has been considered, but the benefits seemed quite marginal
*/
struct vmod_crypto_signer {
unsigned magic;
#define VMOD_CRYPTO_SIGNER_MAGIC 0x6bba960e
char *vcl_name;
EVP_MD_CTX *evpctx;
};
VCL_VOID
vmod_signer__init(VRT_CTX,
struct vmod_crypto_signer **vcsp, const char *vcl_name,
struct VARGS(signer__init) *args)
{
struct vmod_crypto_signer *vcs;
const EVP_MD *md = md_evp(md_parse(args->digest));
EVP_PKEY *pkey;
if (md == NULL) {
VRT_fail(ctx, "digest %s not supported", args->digest);
return;
}
if (args->valid_pem ^ args->valid_key == 0) {
VRT_fail(ctx, "Need either pem or key, but not both");
return;
}
AN(vcsp);
AZ(*vcsp);
ALLOC_OBJ(vcs, VMOD_CRYPTO_SIGNER_MAGIC);
if (vcs == NULL) {
VRT_fail(ctx, "obj alloc failed");
return;
}
REPLACE(vcs->vcl_name, vcl_name);
if (vcs->vcl_name == NULL) {
VRT_fail(ctx, "dup vcl_name failed");
goto err_dup;
}
ERR_clear_error();
vcs->evpctx = EVP_MD_CTX_new();
if (vcs->evpctx == NULL) {
VRT_fail(ctx, "EVP_MD_CTX_new failed, error 0x%lx",
ERR_get_error());
goto err_evpctx;
}
if (EVP_DigestInit_ex(vcs->evpctx, md, NULL) != 1) {
VRT_fail(ctx, "EVP_DigestInit_ex failed, error 0x%lx",
ERR_get_error());
goto err_digest;
}
if (args->valid_pem)
pkey = privkey_pem(ctx, args->pem, NULL);
else if (args->valid_key)
pkey = pkey_blob(ctx, args->key);
else
INCOMPL();
if (pkey == NULL)
goto err_digest;
if (EVP_DigestSignInit(vcs->evpctx, NULL, md, NULL, pkey) !=1) {
VRT_fail(ctx, "EVP_DigestSignInit failed, error 0x%lx",
ERR_get_error());
EVP_PKEY_free(pkey);
goto err_digest;
}
if (args->valid_pem)
EVP_PKEY_free(pkey);
*vcsp = vcs;
return;
err_digest:
EVP_MD_CTX_free(vcs->evpctx);
vcs->evpctx = NULL;
err_evpctx:
free(vcs->vcl_name);
err_dup:
FREE_OBJ(vcs);
}
VCL_VOID
vmod_signer__fini(struct vmod_crypto_signer **vcsp)
{
struct vmod_crypto_signer *vcs = *vcsp;
*vcsp = NULL;
if (vcs == NULL)
return;
CHECK_OBJ(vcs, VMOD_CRYPTO_SIGNER_MAGIC);
EVP_MD_CTX_free(vcs->evpctx);
vcs->evpctx = NULL;
free(vcs->vcl_name);
FREE_OBJ(vcs);
}
VCL_BOOL
vmod_signer_update(VRT_CTX, struct vmod_crypto_signer *vcs,
VCL_STRANDS str)
{
EVP_MD_CTX *evpctx = crypto_ctx_task_md_ctx(ctx, vcs, vcs->evpctx, 0);
const char *s;
int i;
if (evpctx == NULL)
return (0);
AN(str);
ERR_clear_error();
for (i = 0; i < str->n; i++) {
s = str->p[i];
if (s == NULL || *s == '\0')
continue;
if (EVP_DigestSignUpdate(evpctx, s, strlen(s)) != 1) {
VRT_fail(ctx, "EVP_DigestSignUpdate"
" failed, error 0x%lx", ERR_get_error());
return (0);
}
}
return (1);
}
VCL_BOOL
vmod_signer_update_blob(VRT_CTX, struct vmod_crypto_signer *vcs,
VCL_BLOB blob)
{
EVP_MD_CTX *evpctx = crypto_ctx_task_md_ctx(ctx, vcs, vcs->evpctx, 0);
if (evpctx == NULL)
return (0);
ERR_clear_error();
if (blob && blob->len > 0) {
AN(blob->blob);
if (EVP_DigestSignUpdate(evpctx,
blob->blob, blob->len) != 1) {
VRT_fail(ctx, "EVP_DigestSignUpdate"
" failed, error 0x%lx", ERR_get_error());
return (0);
}
}
return (1);
}
VCL_BOOL vmod_signer_reset(VRT_CTX,
struct vmod_crypto_signer *vcs)
{
return (!! crypto_ctx_task_md_ctx(ctx, vcs, vcs->evpctx, 1));
}
VCL_BLOB vmod_signer_final(VRT_CTX, struct vmod_crypto_signer *vcs)
{
EVP_MD_CTX *evpctx = crypto_ctx_task_md_ctx(ctx, vcs, vcs->evpctx, 0);
unsigned char *sig;
size_t siglen;
if (evpctx == NULL)
return (0);
ERR_clear_error();
if (EVP_DigestSignFinal(evpctx, NULL, &siglen) != 1)
goto err;
sig = WS_Alloc(ctx->ws, siglen);
if (sig == NULL) {
VRT_fail(ctx, "%s.final() out of workspace", vcs->vcl_name);
return (NULL);
}
ERR_clear_error();
if (EVP_DigestSignFinal(evpctx, sig, &siglen) != 1)
goto err;
return (VRT_blob(ctx, "xsigner.final()", sig, siglen, 0x6bba960e));
err:
VSLb(ctx->vsl, SLT_Debug, "%s.final() failed: %s",
vcs->vcl_name, ERR_get_error());
ERR_print_errors_cb(crypto_err_cb, (void *)ctx);
return (NULL);
}
......@@ -40,6 +40,16 @@ SYNOPSIS
:ref:`xverifier.valid()`
:ref:`crypto.signer()`
:ref:`xsigner.update()`
:ref:`xsigner.update_blob()`
:ref:`xsigner.reset()`
:ref:`xsigner.final()`
DESCRIPTION
===========
......@@ -180,6 +190,65 @@ Note that after calling .valid(), .update can be called again to add
additional data, which can then be validated against a (different)
signature using another call to .valid().
.. _crypto.signer():
new xsigner = crypto.signer(ENUM digest, [STRING pem], [BLOB key])
------------------------------------------------------------------
::
new xsigner = crypto.signer(
ENUM {md_null, md4, md5, sha1, sha224, sha256, sha384, sha512, ripemd160, rmd160, whirlpool} digest,
[STRING pem],
[BLOB key]
)
Create an object to create signatures using _digest_ and _key_.
The _key_ argument should be a call to `xkey.use()`_ on the respective
`crypto.key()`_ private key object.
Alternatively to _key_, the _pem_ argument may be used to pass a
PEM-encoded private key specification. Password protection is not
supported with a _pem_ argument. Use of the _pem_ argument is
deprecated.
Either the _key_ or the _pem_ argument must be given.
.. _xsigner.update():
BOOL xsigner.update(STRING)
---------------------------
Add strings to the data to be signed.
.. _xsigner.update_blob():
BOOL xsigner.update_blob(BLOB)
------------------------------
Add a blob to the data to be signed.
.. _xsigner.reset():
BOOL xsigner.reset()
--------------------
Reset the signer state as if previous calls to the update methods had
not happened.
.. _xsigner.final():
BLOB xsigner.final()
--------------------
Return the signature for data added using `xsigner.update()` and
`xsigner.update_blob()`.
Note that after calling `xsigner.final()`,
`xsigner.update()`/`xsigner.update_blob()` can be called again to add
additional data, and more signatures can be generated with
`xsigner.final()`.
SEE ALSO
========vcl\(7),varnishd\(1)
......
......@@ -113,6 +113,44 @@ Note that after calling .valid(), .update can be called again to add
additional data, which can then be validated against a (different)
signature using another call to .valid().
$Object signer(ENUM {md_null, md4, md5, sha1, sha224,
sha256, sha384, sha512, ripemd160, rmd160, whirlpool} digest,
[STRING pem], [BLOB key])
Create an object to create signatures using _digest_ and _key_.
The _key_ argument should be a call to `xkey.use()`_ on the respective
`crypto.key()`_ private key object.
Alternatively to _key_, the _pem_ argument may be used to pass a
PEM-encoded private key specification. Password protection is not
supported with a _pem_ argument. Use of the _pem_ argument is
deprecated.
Either the _key_ or the _pem_ argument must be given.
$Method BOOL .update(STRANDS)
Add strings to the data to be signed.
$Method BOOL .update_blob(BLOB)
Add a blob to the data to be signed.
$Method BOOL .reset()
Reset the signer state as if previous calls to the update methods had
not happened.
$Method BLOB .final()
Return the signature for data added using `xsigner.update()` and
`xsigner.update_blob()`.
Note that after calling `xsigner.final()`,
`xsigner.update()`/`xsigner.update_blob()` can be called again to add
additional data, and more signatures can be generated with
`xsigner.final()`.
SEE ALSO
========vcl\(7),varnishd\(1)
......@@ -13,6 +13,8 @@ varnish v1 -vcl+backend {
sub vcl_init {
new v = crypto.verifier(§{MD},
std.fileread("${vtc_dir}/keys/§{ALG}{BITS}.pub.pem"));
new s = crypto.signer(§{MD},
std.fileread("${vtc_dir}/keys/§{ALG}{BITS}.pem"));
new sig = blob.blob(
BASE64, encoded=regsuball(
std.fileread(
......@@ -30,13 +32,24 @@ varnish v1 -vcl+backend {
}
sub vcl_deliver {
# signature of first half
# verify first half
set resp.http.up1a = v.update_blob(
blob.sub(data.get(), §{HALF}B));
if (v.valid(sig_part.get())) {
set resp.http.ok1a = "true";
} else {
return (synth(400));
return (synth(400, "verify 1st half failed"));
}
# sign first half
set resp.http.sup1a = s.update_blob(
blob.sub(data.get(), §{HALF}B));
if (blob.equal(s.final(), sig_part.get())) {
set resp.http.sok1a = "true";
} else if (v.valid(s.final())) {
# DSA has entropy in the signature
set resp.http.sok1a = "true";
} else {
return (synth(400, "own sig 1st half does not match/verify"));
}
# update and check full sig
set resp.http.up1b = v.update_blob(
......@@ -44,7 +57,18 @@ varnish v1 -vcl+backend {
if (v.valid(sig.get())) {
set resp.http.ok1b = "true";
} else {
return (synth(400));
return (synth(400, "verify 2nd half failed"));
}
# update and sign full
set resp.http.sup1b = s.update_blob(
blob.sub(data.get(), §{LEN}B - §{HALF}B, §{HALF}B));
if (blob.equal(s.final(), sig.get())) {
set resp.http.sok1b = "true";
} else if (v.valid(s.final())) {
# DSA has entropy in the signature
set resp.http.sok1b = "true";
} else {
return (synth(400, "own sig 2nd half does not match/verify"));
}
# check full sig in one go
v.reset();
......@@ -52,12 +76,12 @@ varnish v1 -vcl+backend {
if (v.valid(sig.get())) {
set resp.http.ok = "true";
} else {
return (synth(400));
return (synth(400, "verify full failed"));
}
}
} -start
client c0 -repeat 100 -keepalive {
client c0 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -68,7 +92,7 @@ client c0 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c1 -repeat 100 -keepalive {
client c1 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -79,7 +103,7 @@ client c1 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c10 -repeat 100 -keepalive {
client c10 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -90,7 +114,7 @@ client c10 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c11 -repeat 100 -keepalive {
client c11 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -101,7 +125,7 @@ client c11 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c110 -repeat 100 -keepalive {
client c110 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -112,7 +136,7 @@ client c110 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c111 -repeat 100 -keepalive {
client c111 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -123,7 +147,7 @@ client c111 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c1010 -repeat 100 -keepalive {
client c1010 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......@@ -134,7 +158,7 @@ client c1010 -repeat 100 -keepalive {
expect resp.http.up == true
expect resp.http.ok == true
} -run
client c1011 -repeat 100 -keepalive {
client c1011 -repeat 10 -keepalive {
txreq
rxresp
expect resp.status == 200
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment