1323124Sdes/* $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 29295367Sdes#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 40295367Sdes#include "sshbuf.h" 41295367Sdes#include "ssherr.h" 42262566Sdes#include "digest.h" 43295367Sdes#define SSHKEY_INTERNAL 44295367Sdes#include "sshkey.h" 45218767Sdes 46295367Sdes/* ARGSUSED */ 47218767Sdesint 48295367Sdesssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 49295367Sdes const u_char *data, size_t datalen, u_int compat) 50218767Sdes{ 51295367Sdes ECDSA_SIG *sig = NULL; 52262566Sdes int hash_alg; 53262566Sdes u_char digest[SSH_DIGEST_MAX_LENGTH]; 54295367Sdes size_t len, dlen; 55295367Sdes struct sshbuf *b = NULL, *bb = NULL; 56295367Sdes int ret = SSH_ERR_INTERNAL_ERROR; 57218767Sdes 58295367Sdes if (lenp != NULL) 59295367Sdes *lenp = 0; 60295367Sdes if (sigp != NULL) 61295367Sdes *sigp = NULL; 62218767Sdes 63295367Sdes if (key == NULL || key->ecdsa == NULL || 64295367Sdes sshkey_type_plain(key->type) != KEY_ECDSA) 65295367Sdes return SSH_ERR_INVALID_ARGUMENT; 66262566Sdes 67295367Sdes if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 68295367Sdes (dlen = ssh_digest_bytes(hash_alg)) == 0) 69295367Sdes return SSH_ERR_INTERNAL_ERROR; 70295367Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 71295367Sdes digest, sizeof(digest))) != 0) 72295367Sdes goto out; 73218767Sdes 74295367Sdes if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) { 75295367Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 76295367Sdes goto out; 77218767Sdes } 78218767Sdes 79295367Sdes if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) { 80295367Sdes ret = SSH_ERR_ALLOC_FAIL; 81295367Sdes goto out; 82295367Sdes } 83295367Sdes if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || 84295367Sdes (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) 85295367Sdes goto out; 86295367Sdes if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || 87295367Sdes (ret = sshbuf_put_stringb(b, bb)) != 0) 88295367Sdes goto out; 89295367Sdes len = sshbuf_len(b); 90295367Sdes if (sigp != NULL) { 91295367Sdes if ((*sigp = malloc(len)) == NULL) { 92295367Sdes ret = SSH_ERR_ALLOC_FAIL; 93295367Sdes goto out; 94295367Sdes } 95295367Sdes memcpy(*sigp, sshbuf_ptr(b), len); 96295367Sdes } 97218767Sdes if (lenp != NULL) 98218767Sdes *lenp = len; 99295367Sdes ret = 0; 100295367Sdes out: 101295367Sdes explicit_bzero(digest, sizeof(digest)); 102296781Sdes sshbuf_free(b); 103296781Sdes sshbuf_free(bb); 104295367Sdes if (sig != NULL) 105295367Sdes ECDSA_SIG_free(sig); 106295367Sdes return ret; 107295367Sdes} 108218767Sdes 109295367Sdes/* ARGSUSED */ 110218767Sdesint 111295367Sdesssh_ecdsa_verify(const struct sshkey *key, 112295367Sdes const u_char *signature, size_t signaturelen, 113295367Sdes const u_char *data, size_t datalen, u_int compat) 114218767Sdes{ 115295367Sdes ECDSA_SIG *sig = NULL; 116262566Sdes int hash_alg; 117295367Sdes u_char digest[SSH_DIGEST_MAX_LENGTH]; 118295367Sdes size_t dlen; 119295367Sdes int ret = SSH_ERR_INTERNAL_ERROR; 120295367Sdes struct sshbuf *b = NULL, *sigbuf = NULL; 121295367Sdes char *ktype = NULL; 122218767Sdes 123295367Sdes if (key == NULL || key->ecdsa == NULL || 124323124Sdes sshkey_type_plain(key->type) != KEY_ECDSA || 125323124Sdes signature == NULL || signaturelen == 0) 126295367Sdes return SSH_ERR_INVALID_ARGUMENT; 127218767Sdes 128295367Sdes if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 || 129295367Sdes (dlen = ssh_digest_bytes(hash_alg)) == 0) 130295367Sdes return SSH_ERR_INTERNAL_ERROR; 131295367Sdes 132218767Sdes /* fetch signature */ 133295367Sdes if ((b = sshbuf_from(signature, signaturelen)) == NULL) 134295367Sdes return SSH_ERR_ALLOC_FAIL; 135295367Sdes if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 136295367Sdes sshbuf_froms(b, &sigbuf) != 0) { 137295367Sdes ret = SSH_ERR_INVALID_FORMAT; 138295367Sdes goto out; 139218767Sdes } 140295367Sdes if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 141295367Sdes ret = SSH_ERR_KEY_TYPE_MISMATCH; 142295367Sdes goto out; 143218767Sdes } 144295367Sdes if (sshbuf_len(b) != 0) { 145295367Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 146295367Sdes goto out; 147295367Sdes } 148218767Sdes 149218767Sdes /* parse signature */ 150295367Sdes if ((sig = ECDSA_SIG_new()) == NULL) { 151295367Sdes ret = SSH_ERR_ALLOC_FAIL; 152295367Sdes goto out; 153262566Sdes } 154295367Sdes if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || 155295367Sdes sshbuf_get_bignum2(sigbuf, sig->s) != 0) { 156295367Sdes ret = SSH_ERR_INVALID_FORMAT; 157295367Sdes goto out; 158262566Sdes } 159295367Sdes if (sshbuf_len(sigbuf) != 0) { 160295367Sdes ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 161295367Sdes goto out; 162295367Sdes } 163295367Sdes if ((ret = ssh_digest_memory(hash_alg, data, datalen, 164295367Sdes digest, sizeof(digest))) != 0) 165295367Sdes goto out; 166218767Sdes 167295367Sdes switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) { 168295367Sdes case 1: 169295367Sdes ret = 0; 170295367Sdes break; 171295367Sdes case 0: 172295367Sdes ret = SSH_ERR_SIGNATURE_INVALID; 173295367Sdes goto out; 174295367Sdes default: 175295367Sdes ret = SSH_ERR_LIBCRYPTO_ERROR; 176295367Sdes goto out; 177295367Sdes } 178295367Sdes 179295367Sdes out: 180264377Sdes explicit_bzero(digest, sizeof(digest)); 181296781Sdes sshbuf_free(sigbuf); 182296781Sdes sshbuf_free(b); 183295367Sdes if (sig != NULL) 184295367Sdes ECDSA_SIG_free(sig); 185295367Sdes free(ktype); 186218767Sdes return ret; 187218767Sdes} 188218767Sdes 189295367Sdes#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ 190