1323129Sdes/* $OpenBSD: ssh-ecdsa.c,v 1.13 2016/04/21 06:08:02 djm Exp $ */ 2218767Sdes/* 3218767Sdes * Copyright (c) 2000 Markus Friedl. All rights reserved. 4218767Sdes * Copyright (c) 2010 Damien Miller. All rights reserved. 5218767Sdes * 6218767Sdes * Redistribution and use in source and binary forms, with or without 7218767Sdes * modification, are permitted provided that the following conditions 8218767Sdes * are met: 9218767Sdes * 1. Redistributions of source code must retain the above copyright 10218767Sdes * notice, this list of conditions and the following disclaimer. 11218767Sdes * 2. Redistributions in binary form must reproduce the above copyright 12218767Sdes * notice, this list of conditions and the following disclaimer in the 13218767Sdes * documentation and/or other materials provided with the distribution. 14218767Sdes * 15218767Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16218767Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17218767Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18218767Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19218767Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20218767Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21218767Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22218767Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23218767Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24218767Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25218767Sdes */ 26218767Sdes 27218767Sdes#include "includes.h" 28218767Sdes 29294332Sdes#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) 30218767Sdes 31218767Sdes#include <sys/types.h> 32218767Sdes 33218767Sdes#include <openssl/bn.h> 34218767Sdes#include <openssl/ec.h> 35218767Sdes#include <openssl/ecdsa.h> 36218767Sdes#include <openssl/evp.h> 37218767Sdes 38218767Sdes#include <string.h> 39218767Sdes 40294328Sdes#include "sshbuf.h" 41294328Sdes#include "ssherr.h" 42261320Sdes#include "digest.h" 43294328Sdes#define SSHKEY_INTERNAL 44294328Sdes#include "sshkey.h" 45218767Sdes 46294328Sdes/* ARGSUSED */ 47218767Sdesint 48294328Sdesssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 49294328Sdes const u_char *data, size_t datalen, u_int compat) 50218767Sdes{ 51294328Sdes ECDSA_SIG *sig = NULL; 52261320Sdes int hash_alg; 53261320Sdes u_char digest[SSH_DIGEST_MAX_LENGTH]; 54294328Sdes size_t len, dlen; 55294328Sdes struct sshbuf *b = NULL, *bb = NULL; 56294328Sdes int ret = SSH_ERR_INTERNAL_ERROR; 57218767Sdes 58294328Sdes if (lenp != NULL) 59294328Sdes *lenp = 0; 60294328Sdes if (sigp != NULL) 61294328Sdes *sigp = NULL; 62218767Sdes 63294328Sdes if (key == NULL || key->ecdsa == NULL || 64294328Sdes sshkey_type_plain(key->type) != KEY_ECDSA) 65294328Sdes return SSH_ERR_INVALID_ARGUMENT; 66261320Sdes 67294328Sdes if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 68294328Sdes (dlen = ssh_digest_bytes(hash_alg)) == 0) 69294328Sdes return SSH_ERR_INTERNAL_ERROR; 70294328Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 71294328Sdes digest, sizeof(digest))) != 0) 72294328Sdes goto out; 73218767Sdes 74294328Sdes if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { 75294328Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 76294328Sdes goto out; 77218767Sdes } 78218767Sdes 79294328Sdes if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { 80294328Sdes ret = SSH_ERR_ALLOC_FAIL; 81294328Sdes goto out; 82294328Sdes } 83294328Sdes if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || 84294328Sdes (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) 85294328Sdes goto out; 86294328Sdes if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || 87294328Sdes (ret = sshbuf_put_stringb(b, bb)) != 0) 88294328Sdes goto out; 89294328Sdes len = sshbuf_len(b); 90294328Sdes if (sigp != NULL) { 91294328Sdes if ((*sigp = malloc(len)) == NULL) { 92294328Sdes ret = SSH_ERR_ALLOC_FAIL; 93294328Sdes goto out; 94294328Sdes } 95294328Sdes memcpy(*sigp, sshbuf_ptr(b), len); 96294328Sdes } 97218767Sdes if (lenp != NULL) 98218767Sdes *lenp = len; 99294328Sdes ret = 0; 100294328Sdes out: 101294328Sdes explicit_bzero(digest, sizeof(digest)); 102296633Sdes sshbuf_free(b); 103296633Sdes sshbuf_free(bb); 104294328Sdes if (sig != NULL) 105294328Sdes ECDSA_SIG_free(sig); 106294328Sdes return ret; 107294328Sdes} 108218767Sdes 109294328Sdes/* ARGSUSED */ 110218767Sdesint 111294328Sdesssh_ecdsa_verify(const struct sshkey *key, 112294328Sdes const u_char *signature, size_t signaturelen, 113294328Sdes const u_char *data, size_t datalen, u_int compat) 114218767Sdes{ 115294328Sdes ECDSA_SIG *sig = NULL; 116261320Sdes int hash_alg; 117294328Sdes u_char digest[SSH_DIGEST_MAX_LENGTH]; 118294328Sdes size_t dlen; 119294328Sdes int ret = SSH_ERR_INTERNAL_ERROR; 120294328Sdes struct sshbuf *b = NULL, *sigbuf = NULL; 121294328Sdes char *ktype = NULL; 122218767Sdes 123294328Sdes if (key == NULL || key->ecdsa == NULL || 124323129Sdes sshkey_type_plain(key->type) != KEY_ECDSA || 125323129Sdes signature == NULL || signaturelen == 0) 126294328Sdes return SSH_ERR_INVALID_ARGUMENT; 127218767Sdes 128294328Sdes if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 129294328Sdes (dlen = ssh_digest_bytes(hash_alg)) == 0) 130294328Sdes return SSH_ERR_INTERNAL_ERROR; 131294328Sdes 132218767Sdes /* fetch signature */ 133294328Sdes if ((b = sshbuf_from(signature, signaturelen)) == NULL) 134294328Sdes return SSH_ERR_ALLOC_FAIL; 135294328Sdes if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 136294328Sdes sshbuf_froms(b, &sigbuf) != 0) { 137294328Sdes ret = SSH_ERR_INVALID_FORMAT; 138294328Sdes goto out; 139218767Sdes } 140294328Sdes if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 141294328Sdes ret = SSH_ERR_KEY_TYPE_MISMATCH; 142294328Sdes goto out; 143218767Sdes } 144294328Sdes if (sshbuf_len(b) != 0) { 145294328Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 146294328Sdes goto out; 147294328Sdes } 148218767Sdes 149218767Sdes /* parse signature */ 150294328Sdes if ((sig = ECDSA_SIG_new()) == NULL) { 151294328Sdes ret = SSH_ERR_ALLOC_FAIL; 152294328Sdes goto out; 153261320Sdes } 154294328Sdes if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || 155294328Sdes sshbuf_get_bignum2(sigbuf, sig->s) != 0) { 156294328Sdes ret = SSH_ERR_INVALID_FORMAT; 157294328Sdes goto out; 158261320Sdes } 159294328Sdes if (sshbuf_len(sigbuf) != 0) { 160294328Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 161294328Sdes goto out; 162294328Sdes } 163294328Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 164294328Sdes digest, sizeof(digest))) != 0) 165294328Sdes goto out; 166218767Sdes 167294328Sdes switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { 168294328Sdes case 1: 169294328Sdes ret = 0; 170294328Sdes break; 171294328Sdes case 0: 172294328Sdes ret = SSH_ERR_SIGNATURE_INVALID; 173294328Sdes goto out; 174294328Sdes default: 175294328Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 176294328Sdes goto out; 177294328Sdes } 178294328Sdes 179294328Sdes out: 180263712Sdes explicit_bzero(digest, sizeof(digest)); 181296633Sdes sshbuf_free(sigbuf); 182296633Sdes sshbuf_free(b); 183294328Sdes if (sig != NULL) 184294328Sdes ECDSA_SIG_free(sig); 185294328Sdes free(ktype); 186218767Sdes return ret; 187218767Sdes} 188218767Sdes 189294332Sdes#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ 190