1*0cbfa66cSDaniel Fojt /* $OpenBSD: ssh-ecdsa-sk.c,v 1.5 2019/11/26 03:04:27 djm Exp $ */ 2*0cbfa66cSDaniel Fojt /* 3*0cbfa66cSDaniel Fojt * Copyright (c) 2000 Markus Friedl. All rights reserved. 4*0cbfa66cSDaniel Fojt * Copyright (c) 2010 Damien Miller. All rights reserved. 5*0cbfa66cSDaniel Fojt * Copyright (c) 2019 Google Inc. All rights reserved. 6*0cbfa66cSDaniel Fojt * 7*0cbfa66cSDaniel Fojt * Redistribution and use in source and binary forms, with or without 8*0cbfa66cSDaniel Fojt * modification, are permitted provided that the following conditions 9*0cbfa66cSDaniel Fojt * are met: 10*0cbfa66cSDaniel Fojt * 1. Redistributions of source code must retain the above copyright 11*0cbfa66cSDaniel Fojt * notice, this list of conditions and the following disclaimer. 12*0cbfa66cSDaniel Fojt * 2. Redistributions in binary form must reproduce the above copyright 13*0cbfa66cSDaniel Fojt * notice, this list of conditions and the following disclaimer in the 14*0cbfa66cSDaniel Fojt * documentation and/or other materials provided with the distribution. 15*0cbfa66cSDaniel Fojt * 16*0cbfa66cSDaniel Fojt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17*0cbfa66cSDaniel Fojt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18*0cbfa66cSDaniel Fojt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19*0cbfa66cSDaniel Fojt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20*0cbfa66cSDaniel Fojt * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21*0cbfa66cSDaniel Fojt * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22*0cbfa66cSDaniel Fojt * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23*0cbfa66cSDaniel Fojt * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24*0cbfa66cSDaniel Fojt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25*0cbfa66cSDaniel Fojt * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*0cbfa66cSDaniel Fojt */ 27*0cbfa66cSDaniel Fojt 28*0cbfa66cSDaniel Fojt /* #define DEBUG_SK 1 */ 29*0cbfa66cSDaniel Fojt 30*0cbfa66cSDaniel Fojt #include "includes.h" 31*0cbfa66cSDaniel Fojt 32*0cbfa66cSDaniel Fojt #include <sys/types.h> 33*0cbfa66cSDaniel Fojt 34*0cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL 35*0cbfa66cSDaniel Fojt #include <openssl/bn.h> 36*0cbfa66cSDaniel Fojt #include <openssl/ec.h> 37*0cbfa66cSDaniel Fojt #include <openssl/ecdsa.h> 38*0cbfa66cSDaniel Fojt #include <openssl/evp.h> 39*0cbfa66cSDaniel Fojt #endif 40*0cbfa66cSDaniel Fojt 41*0cbfa66cSDaniel Fojt #include <string.h> 42*0cbfa66cSDaniel Fojt #include <stdio.h> /* needed for DEBUG_SK only */ 43*0cbfa66cSDaniel Fojt 44*0cbfa66cSDaniel Fojt #include "openbsd-compat/openssl-compat.h" 45*0cbfa66cSDaniel Fojt 46*0cbfa66cSDaniel Fojt #include "sshbuf.h" 47*0cbfa66cSDaniel Fojt #include "ssherr.h" 48*0cbfa66cSDaniel Fojt #include "digest.h" 49*0cbfa66cSDaniel Fojt #define SSHKEY_INTERNAL 50*0cbfa66cSDaniel Fojt #include "sshkey.h" 51*0cbfa66cSDaniel Fojt 52*0cbfa66cSDaniel Fojt /* ARGSUSED */ 53*0cbfa66cSDaniel Fojt int 54*0cbfa66cSDaniel Fojt ssh_ecdsa_sk_verify(const struct sshkey *key, 55*0cbfa66cSDaniel Fojt const u_char *signature, size_t signaturelen, 56*0cbfa66cSDaniel Fojt const u_char *data, size_t datalen, u_int compat, 57*0cbfa66cSDaniel Fojt struct sshkey_sig_details **detailsp) 58*0cbfa66cSDaniel Fojt { 59*0cbfa66cSDaniel Fojt #ifdef OPENSSL_HAS_ECC 60*0cbfa66cSDaniel Fojt ECDSA_SIG *sig = NULL; 61*0cbfa66cSDaniel Fojt BIGNUM *sig_r = NULL, *sig_s = NULL; 62*0cbfa66cSDaniel Fojt u_char sig_flags; 63*0cbfa66cSDaniel Fojt u_char msghash[32], apphash[32], sighash[32]; 64*0cbfa66cSDaniel Fojt u_int sig_counter; 65*0cbfa66cSDaniel Fojt int ret = SSH_ERR_INTERNAL_ERROR; 66*0cbfa66cSDaniel Fojt struct sshbuf *b = NULL, *sigbuf = NULL, *original_signed = NULL; 67*0cbfa66cSDaniel Fojt char *ktype = NULL; 68*0cbfa66cSDaniel Fojt struct sshkey_sig_details *details = NULL; 69*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK 70*0cbfa66cSDaniel Fojt char *tmp = NULL; 71*0cbfa66cSDaniel Fojt #endif 72*0cbfa66cSDaniel Fojt 73*0cbfa66cSDaniel Fojt if (detailsp != NULL) 74*0cbfa66cSDaniel Fojt *detailsp = NULL; 75*0cbfa66cSDaniel Fojt if (key == NULL || key->ecdsa == NULL || 76*0cbfa66cSDaniel Fojt sshkey_type_plain(key->type) != KEY_ECDSA_SK || 77*0cbfa66cSDaniel Fojt signature == NULL || signaturelen == 0) 78*0cbfa66cSDaniel Fojt return SSH_ERR_INVALID_ARGUMENT; 79*0cbfa66cSDaniel Fojt 80*0cbfa66cSDaniel Fojt if (key->ecdsa_nid != NID_X9_62_prime256v1) 81*0cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 82*0cbfa66cSDaniel Fojt 83*0cbfa66cSDaniel Fojt /* fetch signature */ 84*0cbfa66cSDaniel Fojt if ((b = sshbuf_from(signature, signaturelen)) == NULL) 85*0cbfa66cSDaniel Fojt return SSH_ERR_ALLOC_FAIL; 86*0cbfa66cSDaniel Fojt if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 87*0cbfa66cSDaniel Fojt sshbuf_froms(b, &sigbuf) != 0 || 88*0cbfa66cSDaniel Fojt sshbuf_get_u8(b, &sig_flags) != 0 || 89*0cbfa66cSDaniel Fojt sshbuf_get_u32(b, &sig_counter) != 0) { 90*0cbfa66cSDaniel Fojt ret = SSH_ERR_INVALID_FORMAT; 91*0cbfa66cSDaniel Fojt goto out; 92*0cbfa66cSDaniel Fojt } 93*0cbfa66cSDaniel Fojt if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) { 94*0cbfa66cSDaniel Fojt ret = SSH_ERR_KEY_TYPE_MISMATCH; 95*0cbfa66cSDaniel Fojt goto out; 96*0cbfa66cSDaniel Fojt } 97*0cbfa66cSDaniel Fojt if (sshbuf_len(b) != 0) { 98*0cbfa66cSDaniel Fojt ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 99*0cbfa66cSDaniel Fojt goto out; 100*0cbfa66cSDaniel Fojt } 101*0cbfa66cSDaniel Fojt 102*0cbfa66cSDaniel Fojt /* parse signature */ 103*0cbfa66cSDaniel Fojt if (sshbuf_get_bignum2(sigbuf, &sig_r) != 0 || 104*0cbfa66cSDaniel Fojt sshbuf_get_bignum2(sigbuf, &sig_s) != 0) { 105*0cbfa66cSDaniel Fojt ret = SSH_ERR_INVALID_FORMAT; 106*0cbfa66cSDaniel Fojt goto out; 107*0cbfa66cSDaniel Fojt } 108*0cbfa66cSDaniel Fojt if ((sig = ECDSA_SIG_new()) == NULL) { 109*0cbfa66cSDaniel Fojt ret = SSH_ERR_ALLOC_FAIL; 110*0cbfa66cSDaniel Fojt goto out; 111*0cbfa66cSDaniel Fojt } 112*0cbfa66cSDaniel Fojt if (!ECDSA_SIG_set0(sig, sig_r, sig_s)) { 113*0cbfa66cSDaniel Fojt ret = SSH_ERR_LIBCRYPTO_ERROR; 114*0cbfa66cSDaniel Fojt goto out; 115*0cbfa66cSDaniel Fojt } 116*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK 117*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: data: (len %zu)\n", __func__, datalen); 118*0cbfa66cSDaniel Fojt /* sshbuf_dump_data(data, datalen, stderr); */ 119*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_r: %s\n", __func__, (tmp = BN_bn2hex(sig_r))); 120*0cbfa66cSDaniel Fojt free(tmp); 121*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_s: %s\n", __func__, (tmp = BN_bn2hex(sig_s))); 122*0cbfa66cSDaniel Fojt free(tmp); 123*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n", 124*0cbfa66cSDaniel Fojt __func__, sig_flags, sig_counter); 125*0cbfa66cSDaniel Fojt #endif 126*0cbfa66cSDaniel Fojt sig_r = sig_s = NULL; /* transferred */ 127*0cbfa66cSDaniel Fojt 128*0cbfa66cSDaniel Fojt if (sshbuf_len(sigbuf) != 0) { 129*0cbfa66cSDaniel Fojt ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 130*0cbfa66cSDaniel Fojt goto out; 131*0cbfa66cSDaniel Fojt } 132*0cbfa66cSDaniel Fojt 133*0cbfa66cSDaniel Fojt /* Reconstruct data that was supposedly signed */ 134*0cbfa66cSDaniel Fojt if ((original_signed = sshbuf_new()) == NULL) { 135*0cbfa66cSDaniel Fojt ret = SSH_ERR_ALLOC_FAIL; 136*0cbfa66cSDaniel Fojt goto out; 137*0cbfa66cSDaniel Fojt } 138*0cbfa66cSDaniel Fojt if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen, 139*0cbfa66cSDaniel Fojt msghash, sizeof(msghash))) != 0) 140*0cbfa66cSDaniel Fojt goto out; 141*0cbfa66cSDaniel Fojt /* Application value is hashed before signature */ 142*0cbfa66cSDaniel Fojt if ((ret = ssh_digest_memory(SSH_DIGEST_SHA256, key->sk_application, 143*0cbfa66cSDaniel Fojt strlen(key->sk_application), apphash, sizeof(apphash))) != 0) 144*0cbfa66cSDaniel Fojt goto out; 145*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK 146*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: hashed application:\n", __func__); 147*0cbfa66cSDaniel Fojt sshbuf_dump_data(apphash, sizeof(apphash), stderr); 148*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: hashed message:\n", __func__); 149*0cbfa66cSDaniel Fojt sshbuf_dump_data(msghash, sizeof(msghash), stderr); 150*0cbfa66cSDaniel Fojt #endif 151*0cbfa66cSDaniel Fojt if ((ret = sshbuf_put(original_signed, 152*0cbfa66cSDaniel Fojt apphash, sizeof(apphash))) != 0 || 153*0cbfa66cSDaniel Fojt (ret = sshbuf_put_u8(original_signed, sig_flags)) != 0 || 154*0cbfa66cSDaniel Fojt (ret = sshbuf_put_u32(original_signed, sig_counter)) != 0 || 155*0cbfa66cSDaniel Fojt (ret = sshbuf_put(original_signed, msghash, sizeof(msghash))) != 0) 156*0cbfa66cSDaniel Fojt goto out; 157*0cbfa66cSDaniel Fojt /* Signature is over H(original_signed) */ 158*0cbfa66cSDaniel Fojt if ((ret = ssh_digest_buffer(SSH_DIGEST_SHA256, original_signed, 159*0cbfa66cSDaniel Fojt sighash, sizeof(sighash))) != 0) 160*0cbfa66cSDaniel Fojt goto out; 161*0cbfa66cSDaniel Fojt if ((details = calloc(1, sizeof(*details))) == NULL) { 162*0cbfa66cSDaniel Fojt ret = SSH_ERR_ALLOC_FAIL; 163*0cbfa66cSDaniel Fojt goto out; 164*0cbfa66cSDaniel Fojt } 165*0cbfa66cSDaniel Fojt details->sk_counter = sig_counter; 166*0cbfa66cSDaniel Fojt details->sk_flags = sig_flags; 167*0cbfa66cSDaniel Fojt #ifdef DEBUG_SK 168*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: signed buf:\n", __func__); 169*0cbfa66cSDaniel Fojt sshbuf_dump(original_signed, stderr); 170*0cbfa66cSDaniel Fojt fprintf(stderr, "%s: signed hash:\n", __func__); 171*0cbfa66cSDaniel Fojt sshbuf_dump_data(sighash, sizeof(sighash), stderr); 172*0cbfa66cSDaniel Fojt #endif 173*0cbfa66cSDaniel Fojt 174*0cbfa66cSDaniel Fojt /* Verify it */ 175*0cbfa66cSDaniel Fojt switch (ECDSA_do_verify(sighash, sizeof(sighash), sig, key->ecdsa)) { 176*0cbfa66cSDaniel Fojt case 1: 177*0cbfa66cSDaniel Fojt ret = 0; 178*0cbfa66cSDaniel Fojt break; 179*0cbfa66cSDaniel Fojt case 0: 180*0cbfa66cSDaniel Fojt ret = SSH_ERR_SIGNATURE_INVALID; 181*0cbfa66cSDaniel Fojt goto out; 182*0cbfa66cSDaniel Fojt default: 183*0cbfa66cSDaniel Fojt ret = SSH_ERR_LIBCRYPTO_ERROR; 184*0cbfa66cSDaniel Fojt goto out; 185*0cbfa66cSDaniel Fojt } 186*0cbfa66cSDaniel Fojt /* success */ 187*0cbfa66cSDaniel Fojt if (detailsp != NULL) { 188*0cbfa66cSDaniel Fojt *detailsp = details; 189*0cbfa66cSDaniel Fojt details = NULL; 190*0cbfa66cSDaniel Fojt } 191*0cbfa66cSDaniel Fojt out: 192*0cbfa66cSDaniel Fojt explicit_bzero(&sig_flags, sizeof(sig_flags)); 193*0cbfa66cSDaniel Fojt explicit_bzero(&sig_counter, sizeof(sig_counter)); 194*0cbfa66cSDaniel Fojt explicit_bzero(msghash, sizeof(msghash)); 195*0cbfa66cSDaniel Fojt explicit_bzero(sighash, sizeof(msghash)); 196*0cbfa66cSDaniel Fojt explicit_bzero(apphash, sizeof(apphash)); 197*0cbfa66cSDaniel Fojt sshkey_sig_details_free(details); 198*0cbfa66cSDaniel Fojt sshbuf_free(original_signed); 199*0cbfa66cSDaniel Fojt sshbuf_free(sigbuf); 200*0cbfa66cSDaniel Fojt sshbuf_free(b); 201*0cbfa66cSDaniel Fojt ECDSA_SIG_free(sig); 202*0cbfa66cSDaniel Fojt BN_clear_free(sig_r); 203*0cbfa66cSDaniel Fojt BN_clear_free(sig_s); 204*0cbfa66cSDaniel Fojt free(ktype); 205*0cbfa66cSDaniel Fojt return ret; 206*0cbfa66cSDaniel Fojt #else 207*0cbfa66cSDaniel Fojt return SSH_ERR_INTERNAL_ERROR; 208*0cbfa66cSDaniel Fojt #endif 209*0cbfa66cSDaniel Fojt } 210