diff options
Diffstat (limited to 'usr.bin/ssh/ssh-ecdsa.c')
-rw-r--r-- | usr.bin/ssh/ssh-ecdsa.c | 232 |
1 files changed, 123 insertions, 109 deletions
diff --git a/usr.bin/ssh/ssh-ecdsa.c b/usr.bin/ssh/ssh-ecdsa.c index 62baf1e7982..5bb1a8ffde1 100644 --- a/usr.bin/ssh/ssh-ecdsa.c +++ b/usr.bin/ssh/ssh-ecdsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-ecdsa.c,v 1.10 2014/02/03 23:28:00 djm Exp $ */ +/* $OpenBSD: ssh-ecdsa.c,v 1.11 2014/06/24 01:13:21 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2010 Damien Miller. All rights reserved. @@ -33,140 +33,154 @@ #include <string.h> -#include "xmalloc.h" -#include "buffer.h" -#include "compat.h" -#include "log.h" -#include "key.h" +#include "sshbuf.h" +#include "ssherr.h" #include "digest.h" +#define SSHKEY_INTERNAL +#include "sshkey.h" +/* ARGSUSED */ int -ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp, - const u_char *data, u_int datalen) +ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, + const u_char *data, size_t datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; int hash_alg; u_char digest[SSH_DIGEST_MAX_LENGTH]; - u_int len, dlen; - Buffer b, bb; + size_t len, dlen; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; - if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || - key->ecdsa == NULL) { - error("%s: no ECDSA key", __func__); - return -1; + if (lenp != NULL) + *lenp = 0; + if (sigp != NULL) + *sigp = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); - if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; - } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: digest_memory failed", __func__); - return -1; + if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; } - - sig = ECDSA_do_sign(digest, dlen, key->ecdsa); - explicit_bzero(digest, sizeof(digest)); - - if (sig == NULL) { - error("%s: sign failed", __func__); - return -1; + if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || + (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) + goto out; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || + (ret = sshbuf_put_stringb(b, bb)) != 0) + goto out; + len = sshbuf_len(b); + if (sigp != NULL) { + if ((*sigp = malloc(len)) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + memcpy(*sigp, sshbuf_ptr(b), len); } - - buffer_init(&bb); - buffer_put_bignum2(&bb, sig->r); - buffer_put_bignum2(&bb, sig->s); - ECDSA_SIG_free(sig); - - buffer_init(&b); - buffer_put_cstring(&b, key_ssh_name_plain(key)); - buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb)); - buffer_free(&bb); - len = buffer_len(&b); if (lenp != NULL) *lenp = len; - if (sigp != NULL) { - *sigp = xmalloc(len); - memcpy(*sigp, buffer_ptr(&b), len); - } - buffer_free(&b); - - return 0; + ret = 0; + out: + explicit_bzero(digest, sizeof(digest)); + if (b != NULL) + sshbuf_free(b); + if (bb != NULL) + sshbuf_free(bb); + if (sig != NULL) + ECDSA_SIG_free(sig); + return ret; } + +/* ARGSUSED */ int -ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen, - const u_char *data, u_int datalen) +ssh_ecdsa_verify(const struct sshkey *key, + const u_char *signature, size_t signaturelen, + const u_char *data, size_t datalen, u_int compat) { - ECDSA_SIG *sig; + ECDSA_SIG *sig = NULL; int hash_alg; - u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob; - u_int len, dlen; - int rlen, ret; - Buffer b, bb; - char *ktype; - - if (key == NULL || key_type_plain(key->type) != KEY_ECDSA || - key->ecdsa == NULL) { - error("%s: no ECDSA key", __func__); - return -1; - } + u_char digest[SSH_DIGEST_MAX_LENGTH]; + size_t dlen; + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL; + char *ktype = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA) + return SSH_ERR_INVALID_ARGUMENT; + + if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || + (dlen = ssh_digest_bytes(hash_alg)) == 0) + return SSH_ERR_INTERNAL_ERROR; /* fetch signature */ - buffer_init(&b); - buffer_append(&b, signature, signaturelen); - ktype = buffer_get_string(&b, NULL); - if (strcmp(key_ssh_name_plain(key), ktype) != 0) { - error("%s: cannot handle type %s", __func__, ktype); - buffer_free(&b); - free(ktype); - return -1; + if ((b = sshbuf_from(signature, signaturelen)) == NULL) + return SSH_ERR_ALLOC_FAIL; + if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || + sshbuf_froms(b, &sigbuf) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; } - free(ktype); - sigblob = buffer_get_string(&b, &len); - rlen = buffer_len(&b); - buffer_free(&b); - if (rlen != 0) { - error("%s: remaining bytes in signature %d", __func__, rlen); - free(sigblob); - return -1; + if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { + ret = SSH_ERR_KEY_TYPE_MISMATCH; + goto out; + } + if (sshbuf_len(b) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } /* parse signature */ - if ((sig = ECDSA_SIG_new()) == NULL) - fatal("%s: ECDSA_SIG_new failed", __func__); - - buffer_init(&bb); - buffer_append(&bb, sigblob, len); - buffer_get_bignum2(&bb, sig->r); - buffer_get_bignum2(&bb, sig->s); - if (buffer_len(&bb) != 0) - fatal("%s: remaining bytes in inner sigblob", __func__); - buffer_free(&bb); - - /* clean up */ - explicit_bzero(sigblob, len); - free(sigblob); - - /* hash the data */ - hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid); - if ((dlen = ssh_digest_bytes(hash_alg)) == 0) { - error("%s: bad hash algorithm %d", __func__, hash_alg); - return -1; + if ((sig = ECDSA_SIG_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || + sshbuf_get_bignum2(sigbuf, sig->s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; } - if (ssh_digest_memory(hash_alg, data, datalen, - digest, sizeof(digest)) != 0) { - error("%s: digest_memory failed", __func__); - return -1; + if ((ret = ssh_digest_memory(hash_alg, data, datalen, + digest, sizeof(digest))) != 0) + goto out; + + switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { + case 1: + ret = 0; + break; + case 0: + ret = SSH_ERR_SIGNATURE_INVALID; + goto out; + default: + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; } - ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa); + out: explicit_bzero(digest, sizeof(digest)); - - ECDSA_SIG_free(sig); - - debug("%s: signature %s", __func__, - ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error"); + if (sigbuf != NULL) + sshbuf_free(sigbuf); + if (b != NULL) + sshbuf_free(b); + if (sig != NULL) + ECDSA_SIG_free(sig); + free(ktype); return ret; } |