upstream: sshsig tweaks and improvements from and suggested by

Markus

ok markus/me

OpenBSD-Commit-ID: ea4f46ad5a16b27af96e08c4877423918c4253e9
This commit is contained in:
djm@openbsd.org 2019-09-03 08:35:27 +00:00 committed by Damien Miller
parent 2a9c9f7272
commit d637c4aee6
5 changed files with 122 additions and 104 deletions

View File

@ -8,9 +8,9 @@ At present, only detached and armored signatures are supported.
The Armored SSH signatures consist of a header, a base64
encoded blob, and a footer.
The header is the string “-----BEGIN SSH SIGNATURE-----”
The header is the string "-----BEGIN SSH SIGNATURE-----"
followed by a newline. The footer is the string
“-----END SSH SIGNATURE-----” immediately after a newline.
"-----END SSH SIGNATURE-----" immediately after a newline.
The header MUST be present at the start of every signature.
Files containing the signature MUST start with the header.

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.163 2019/09/03 08:34:19 djm Exp $
.\" $OpenBSD: ssh-keygen.1,v 1.164 2019/09/03 08:35:27 djm Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -986,7 +986,7 @@ Indicates that this key is accepted as a certificate authority (CA) and
that certificates signed by this CA may be accepted for verification.
.It Cm namespaces="namespace-list"
Specifies a pattern-list of namespaces that are accepted for this key.
If this option is present, the the signature namespace embedded in the
If this option is present, the signature namespace embedded in the
signature object and presented on the verification command-line must
match the specified list before the key will be considered acceptable.
.El

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.344 2019/09/03 08:34:19 djm Exp $ */
/* $OpenBSD: ssh-keygen.c,v 1.345 2019/09/03 08:35:27 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -2659,10 +2659,10 @@ verify(const char *signature, const char *sig_namespace, const char *principal,
fp = NULL;
if (revoked_keys != NULL) {
if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) {
debug3("sshkey_check_revoked failed: %s", ssh_err(r));
goto done;
}
if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) {
debug3("sshkey_check_revoked failed: %s", ssh_err(r));
goto done;
}
}
if ((r = sshsig_check_allowed_keys(allowed_keys, sign_key,

180
sshsig.c
View File

@ -230,7 +230,7 @@ sshsig_parse_preamble(struct sshbuf *buf)
return r;
}
if (sversion < SIG_VERSION) {
if (sversion > SIG_VERSION) {
error("Signature version %lu is larger than supported "
"version %u", (unsigned long)sversion, SIG_VERSION);
return SSH_ERR_INVALID_FORMAT;
@ -241,7 +241,8 @@ sshsig_parse_preamble(struct sshbuf *buf)
static int
sshsig_check_hashalg(const char *hashalg)
{
if (match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
if (hashalg == NULL ||
match_pattern_list(hashalg, HASHALG_ALLOWED, 0) == 1)
return 0;
error("%s: unsupported hash algorithm \"%.100s\"", __func__, hashalg);
return SSH_ERR_SIGN_ALG_UNSUPPORTED;
@ -268,8 +269,6 @@ sshsig_peek_hashalg(struct sshbuf *signature, char **hashalgp)
error("Couldn't parse signature blob: %s", ssh_err(r));
goto done;
}
if ((r = sshsig_check_hashalg(hashalg)) != 0)
goto done;
/* success */
r = 0;
@ -293,6 +292,7 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
char *got_namespace = NULL, *sigtype = NULL, *sig_hashalg = NULL;
size_t siglen;
debug("%s: verify message length %zu", __func__, sshbuf_len(h_message));
if (sign_keyp != NULL)
*sign_keyp = NULL;
@ -301,9 +301,6 @@ sshsig_wrap_verify(struct sshbuf *signature, const char *hashalg,
r = SSH_ERR_ALLOC_FAIL;
goto done;
}
if ((r = sshsig_check_hashalg(hashalg)) != 0)
goto done;
if ((r = sshbuf_put(toverify, MAGIC_PREAMBLE,
MAGIC_PREAMBLE_LEN)) != 0 ||
(r = sshbuf_put_cstring(toverify, expect_namespace)) != 0 ||
@ -382,36 +379,65 @@ done:
return r;
}
int
sshsig_sign_message(struct sshkey *key, const char *hashalg,
const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
static int
hash_buffer(const struct sshbuf *m, const char *hashalg, struct sshbuf **bp)
{
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL;
char *hex, hash[SSH_DIGEST_MAX_LENGTH];
int alg, r = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL;
if (out != NULL)
*out = NULL;
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
*bp = NULL;
memset(hash, 0, sizeof(hash));
if ((r = sshsig_check_hashalg(hashalg)) != 0)
return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT);
__func__, hashalg);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = ssh_digest_buffer(alg, message, hash, sizeof(hash))) != 0) {
if ((r = ssh_digest_buffer(alg, m, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
return r;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
debug3("%s: final hash: %s", __func__, hex);
freezero(hex, strlen(hex));
}
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
error("%s: sshbuf_put: %s", __func__, ssh_err(r));
goto out;
}
*bp = b;
b = NULL; /* transferred */
/* success */
r = 0;
out:
sshbuf_free(b);
explicit_bzero(hash, sizeof(hash));
return 0;
}
int
sshsig_signb(struct sshkey *key, const char *hashalg,
const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx)
{
struct sshbuf *b = NULL;
int r = SSH_ERR_INTERNAL_ERROR;
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
if (out != NULL)
*out = NULL;
if ((r = hash_buffer(message, hashalg, &b)) != 0) {
error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
goto out;
}
if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
signer, signer_ctx)) != 0)
goto out;
@ -419,17 +445,15 @@ sshsig_sign_message(struct sshkey *key, const char *hashalg,
r = 0;
out:
sshbuf_free(b);
explicit_bzero(hash, sizeof(hash));
return r;
}
int
sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message,
sshsig_verifyb(struct sshbuf *signature, const struct sshbuf *message,
const char *expect_namespace, struct sshkey **sign_keyp)
{
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR;
int r = SSH_ERR_INTERNAL_ERROR;
char *hashalg = NULL;
if (sign_keyp != NULL)
@ -437,18 +461,9 @@ sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message,
if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = ssh_digest_buffer(alg, message, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_buffer failed: %s", __func__, ssh_err(r));
goto out;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
debug("%s: signature made with hash \"%s\"", __func__, hashalg);
if ((r = hash_buffer(message, hashalg, &b)) != 0) {
error("%s: hash_buffer failed: %s", __func__, ssh_err(r));
goto out;
}
if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
@ -459,20 +474,29 @@ sshsig_verify_message(struct sshbuf *signature, const struct sshbuf *message,
out:
sshbuf_free(b);
free(hashalg);
explicit_bzero(hash, sizeof(hash));
return r;
}
static int
hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
hash_file(int fd, const char *hashalg, struct sshbuf **bp)
{
char *hex, rbuf[8192];
char *hex, rbuf[8192], hash[SSH_DIGEST_MAX_LENGTH];
ssize_t n, total = 0;
struct ssh_digest_ctx *ctx;
int r, oerrno;
int alg, oerrno, r = SSH_ERR_INTERNAL_ERROR;
struct sshbuf *b = NULL;
memset(hash, 0, hashlen);
if ((ctx = ssh_digest_start(hashalg)) == NULL) {
*bp = NULL;
memset(hash, 0, sizeof(hash));
if ((r = sshsig_check_hashalg(hashalg)) != 0)
return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, hashalg);
return SSH_ERR_INTERNAL_ERROR;
}
if ((ctx = ssh_digest_start(alg)) == NULL) {
error("%s: ssh_digest_start failed", __func__);
return SSH_ERR_INTERNAL_ERROR;
}
@ -484,7 +508,8 @@ hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
error("%s: read: %s", __func__, strerror(errno));
ssh_digest_free(ctx);
errno = oerrno;
return SSH_ERR_SYSTEM_ERROR;
r = SSH_ERR_SYSTEM_ERROR;
goto out;
} else if (n == 0) {
debug2("%s: hashed %zu bytes", __func__, total);
break; /* EOF */
@ -493,20 +518,33 @@ hash_file(int fd, int hashalg, u_char *hash, size_t hashlen)
if ((r = ssh_digest_update(ctx, rbuf, (size_t)n)) != 0) {
error("%s: ssh_digest_update: %s",
__func__, ssh_err(r));
ssh_digest_free(ctx);
return r;
goto out;
}
}
if ((r = ssh_digest_final(ctx, hash, hashlen)) != 0) {
if ((r = ssh_digest_final(ctx, hash, sizeof(hash))) != 0) {
error("%s: ssh_digest_final: %s", __func__, ssh_err(r));
ssh_digest_free(ctx);
goto out;
}
if ((hex = tohex(hash, hashlen)) != NULL) {
if ((hex = tohex(hash, ssh_digest_bytes(alg))) != NULL) {
debug3("%s: final hash: %s", __func__, hex);
freezero(hex, strlen(hex));
}
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_put(b, hash, ssh_digest_bytes(alg))) != 0) {
error("%s: sshbuf_put: %s", __func__, ssh_err(r));
goto out;
}
*bp = b;
b = NULL; /* transferred */
/* success */
r = 0;
out:
sshbuf_free(b);
ssh_digest_free(ctx);
explicit_bzero(hash, sizeof(hash));
return 0;
}
@ -515,31 +553,17 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
int fd, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx)
{
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR;
int r = SSH_ERR_INTERNAL_ERROR;
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
if (out != NULL)
*out = NULL;
if (hashalg == NULL)
hashalg = HASHALG_DEFAULT;
if ((r = sshsig_check_hashalg(hashalg)) != 0)
return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = hash_file(fd, alg, hash, sizeof(hash))) != 0) {
if ((r = hash_file(fd, hashalg, &b)) != 0) {
error("%s: hash_file failed: %s", __func__, ssh_err(r));
return r;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshsig_wrap_sign(key, hashalg, b, sig_namespace, out,
signer, signer_ctx)) != 0)
goto out;
@ -547,7 +571,6 @@ sshsig_sign_fd(struct sshkey *key, const char *hashalg,
r = 0;
out:
sshbuf_free(b);
explicit_bzero(hash, sizeof(hash));
return r;
}
@ -555,9 +578,8 @@ int
sshsig_verify_fd(struct sshbuf *signature, int fd,
const char *expect_namespace, struct sshkey **sign_keyp)
{
u_char hash[SSH_DIGEST_MAX_LENGTH];
struct sshbuf *b = NULL;
int alg, r = SSH_ERR_INTERNAL_ERROR;
int r = SSH_ERR_INTERNAL_ERROR;
char *hashalg = NULL;
if (sign_keyp != NULL)
@ -565,18 +587,9 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
if ((r = sshsig_peek_hashalg(signature, &hashalg)) != 0)
return r;
if ((alg = ssh_digest_alg_by_name(hashalg)) == -1) {
error("%s: can't look up hash algorithm %s",
__func__, HASHALG_DEFAULT);
return SSH_ERR_INTERNAL_ERROR;
}
if ((r = hash_file(fd, alg, hash, sizeof(hash))) != 0) {
debug("%s: signature made with hash \"%s\"", __func__, hashalg);
if ((r = hash_file(fd, hashalg, &b)) != 0) {
error("%s: hash_file failed: %s", __func__, ssh_err(r));
return r;
}
if ((b = sshbuf_from(hash, ssh_digest_bytes(alg))) == NULL) {
error("%s: sshbuf_from failed", __func__);
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshsig_wrap_verify(signature, hashalg, b, expect_namespace,
@ -587,7 +600,6 @@ sshsig_verify_fd(struct sshbuf *signature, int fd,
out:
sshbuf_free(b);
free(hashalg);
explicit_bzero(hash, sizeof(hash));
return r;
}
@ -769,14 +781,14 @@ sshsig_check_allowed_keys(const char *path, const struct sshkey *sign_key,
linenum++;
r = check_allowed_keys_line(path, linenum, line, sign_key,
principal, sig_namespace);
free(line);
line = NULL;
if (r == SSH_ERR_KEY_NOT_FOUND)
continue;
else if (r == 0) {
/* success */
fclose(f);
free(line);
return 0;
/* XXX continue and check revocation? */
} else
break;
}

View File

@ -23,15 +23,28 @@ struct sshkey;
typedef int sshsig_signer(struct sshkey *, u_char **, size_t *,
const u_char *, size_t, const char *, u_int, void *);
/* Buffer-oriented API */
/*
* Creates a detached SSH signature for a given message.
* Creates a detached SSH signature for a given buffer.
* Returns 0 on success or a negative SSH_ERR_* error code on failure.
* out is populated with the detached signature, or NULL on failure.
*/
int sshsig_sign_message(struct sshkey *key, const char *hashalg,
int sshsig_signb(struct sshkey *key, const char *hashalg,
const struct sshbuf *message, const char *sig_namespace,
struct sshbuf **out, sshsig_signer *signer, void *signer_ctx);
/*
* Verifies that a detached signature is valid and optionally returns key
* used to sign via argument.
* Returns 0 on success or a negative SSH_ERR_* error code on failure.
*/
int sshsig_verifyb(struct sshbuf *signature,
const struct sshbuf *message, const char *sig_namespace,
struct sshkey **sign_keyp);
/* File/FD-oriented API */
/*
* Creates a detached SSH signature for a given file.
* Returns 0 on success or a negative SSH_ERR_* error code on failure.
@ -41,15 +54,6 @@ int sshsig_sign_fd(struct sshkey *key, const char *hashalg,
int fd, const char *sig_namespace, struct sshbuf **out,
sshsig_signer *signer, void *signer_ctx);
/*
* Verifies that a detached signature is valid and optionally returns key
* used to sign via argument.
* Returns 0 on success or a negative SSH_ERR_* error code on failure.
*/
int sshsig_verify_message(struct sshbuf *signature,
const struct sshbuf *message, const char *sig_namespace,
struct sshkey **sign_keyp);
/*
* Verifies that a detached signature over a file is valid and optionally
* returns key used to sign via argument.
@ -58,6 +62,8 @@ int sshsig_verify_message(struct sshbuf *signature,
int sshsig_verify_fd(struct sshbuf *signature, int fd,
const char *sig_namespace, struct sshkey **sign_keyp);
/* Utility functions */
/*
* Return a base64 encoded "ASCII armoured" version of a raw signature.
*/