1295367Sdes/* $OpenBSD: sshbuf-getput-crypto.c,v 1.5 2016/01/12 23:42:54 djm Exp $ */ 2276707Sdes/* 3276707Sdes * Copyright (c) 2011 Damien Miller 4276707Sdes * 5276707Sdes * Permission to use, copy, modify, and distribute this software for any 6276707Sdes * purpose with or without fee is hereby granted, provided that the above 7276707Sdes * copyright notice and this permission notice appear in all copies. 8276707Sdes * 9276707Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10276707Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11276707Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12276707Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13276707Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14276707Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15276707Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16276707Sdes */ 17276707Sdes 18276707Sdes#define SSHBUF_INTERNAL 19276707Sdes#include "includes.h" 20276707Sdes 21276707Sdes#include <sys/types.h> 22276707Sdes#include <stdlib.h> 23276707Sdes#include <stdio.h> 24276707Sdes#include <string.h> 25276707Sdes 26276707Sdes#include <openssl/bn.h> 27276707Sdes#ifdef OPENSSL_HAS_ECC 28276707Sdes# include <openssl/ec.h> 29276707Sdes#endif /* OPENSSL_HAS_ECC */ 30276707Sdes 31276707Sdes#include "ssherr.h" 32276707Sdes#include "sshbuf.h" 33276707Sdes 34276707Sdesint 35276707Sdessshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v) 36276707Sdes{ 37276707Sdes const u_char *d; 38276707Sdes size_t len; 39276707Sdes int r; 40276707Sdes 41295367Sdes if ((r = sshbuf_get_bignum2_bytes_direct(buf, &d, &len)) != 0) 42276707Sdes return r; 43276707Sdes if (v != NULL && BN_bin2bn(d, len, v) == NULL) 44276707Sdes return SSH_ERR_ALLOC_FAIL; 45276707Sdes return 0; 46276707Sdes} 47276707Sdes 48276707Sdesint 49276707Sdessshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v) 50276707Sdes{ 51276707Sdes const u_char *d = sshbuf_ptr(buf); 52276707Sdes u_int16_t len_bits; 53276707Sdes size_t len_bytes; 54276707Sdes 55276707Sdes /* Length in bits */ 56276707Sdes if (sshbuf_len(buf) < 2) 57276707Sdes return SSH_ERR_MESSAGE_INCOMPLETE; 58276707Sdes len_bits = PEEK_U16(d); 59276707Sdes len_bytes = (len_bits + 7) >> 3; 60276707Sdes if (len_bytes > SSHBUF_MAX_BIGNUM) 61276707Sdes return SSH_ERR_BIGNUM_TOO_LARGE; 62276707Sdes if (sshbuf_len(buf) < 2 + len_bytes) 63276707Sdes return SSH_ERR_MESSAGE_INCOMPLETE; 64276707Sdes if (v != NULL && BN_bin2bn(d + 2, len_bytes, v) == NULL) 65276707Sdes return SSH_ERR_ALLOC_FAIL; 66276707Sdes if (sshbuf_consume(buf, 2 + len_bytes) != 0) { 67276707Sdes SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 68276707Sdes SSHBUF_ABORT(); 69276707Sdes return SSH_ERR_INTERNAL_ERROR; 70276707Sdes } 71276707Sdes return 0; 72276707Sdes} 73276707Sdes 74276707Sdes#ifdef OPENSSL_HAS_ECC 75276707Sdesstatic int 76276707Sdesget_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g) 77276707Sdes{ 78276707Sdes /* Refuse overlong bignums */ 79276707Sdes if (len == 0 || len > SSHBUF_MAX_ECPOINT) 80276707Sdes return SSH_ERR_ECPOINT_TOO_LARGE; 81276707Sdes /* Only handle uncompressed points */ 82276707Sdes if (*d != POINT_CONVERSION_UNCOMPRESSED) 83276707Sdes return SSH_ERR_INVALID_FORMAT; 84276707Sdes if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1) 85276707Sdes return SSH_ERR_INVALID_FORMAT; /* XXX assumption */ 86276707Sdes return 0; 87276707Sdes} 88276707Sdes 89276707Sdesint 90276707Sdessshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g) 91276707Sdes{ 92276707Sdes const u_char *d; 93276707Sdes size_t len; 94276707Sdes int r; 95276707Sdes 96276707Sdes if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) 97276707Sdes return r; 98276707Sdes if ((r = get_ec(d, len, v, g)) != 0) 99276707Sdes return r; 100276707Sdes /* Skip string */ 101276707Sdes if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 102276707Sdes /* Shouldn't happen */ 103276707Sdes SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 104276707Sdes SSHBUF_ABORT(); 105276707Sdes return SSH_ERR_INTERNAL_ERROR; 106276707Sdes } 107276707Sdes return 0; 108276707Sdes} 109276707Sdes 110276707Sdesint 111276707Sdessshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v) 112276707Sdes{ 113276707Sdes EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v)); 114276707Sdes int r; 115276707Sdes const u_char *d; 116276707Sdes size_t len; 117276707Sdes 118276707Sdes if (pt == NULL) { 119276707Sdes SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 120276707Sdes return SSH_ERR_ALLOC_FAIL; 121276707Sdes } 122276707Sdes if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) { 123276707Sdes EC_POINT_free(pt); 124276707Sdes return r; 125276707Sdes } 126276707Sdes if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) { 127276707Sdes EC_POINT_free(pt); 128276707Sdes return r; 129276707Sdes } 130276707Sdes if (EC_KEY_set_public_key(v, pt) != 1) { 131276707Sdes EC_POINT_free(pt); 132276707Sdes return SSH_ERR_ALLOC_FAIL; /* XXX assumption */ 133276707Sdes } 134276707Sdes EC_POINT_free(pt); 135276707Sdes /* Skip string */ 136276707Sdes if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 137276707Sdes /* Shouldn't happen */ 138276707Sdes SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 139276707Sdes SSHBUF_ABORT(); 140276707Sdes return SSH_ERR_INTERNAL_ERROR; 141276707Sdes } 142276707Sdes return 0; 143276707Sdes} 144276707Sdes#endif /* OPENSSL_HAS_ECC */ 145276707Sdes 146276707Sdesint 147276707Sdessshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v) 148276707Sdes{ 149276707Sdes u_char d[SSHBUF_MAX_BIGNUM + 1]; 150276707Sdes int len = BN_num_bytes(v), prepend = 0, r; 151276707Sdes 152276707Sdes if (len < 0 || len > SSHBUF_MAX_BIGNUM) 153276707Sdes return SSH_ERR_INVALID_ARGUMENT; 154276707Sdes *d = '\0'; 155276707Sdes if (BN_bn2bin(v, d + 1) != len) 156276707Sdes return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 157276707Sdes /* If MSB is set, prepend a \0 */ 158276707Sdes if (len > 0 && (d[1] & 0x80) != 0) 159276707Sdes prepend = 1; 160276707Sdes if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) { 161295367Sdes explicit_bzero(d, sizeof(d)); 162276707Sdes return r; 163276707Sdes } 164295367Sdes explicit_bzero(d, sizeof(d)); 165276707Sdes return 0; 166276707Sdes} 167276707Sdes 168276707Sdesint 169276707Sdessshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v) 170276707Sdes{ 171276707Sdes int r, len_bits = BN_num_bits(v); 172276707Sdes size_t len_bytes = (len_bits + 7) / 8; 173276707Sdes u_char d[SSHBUF_MAX_BIGNUM], *dp; 174276707Sdes 175276707Sdes if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM) 176276707Sdes return SSH_ERR_INVALID_ARGUMENT; 177276707Sdes if (BN_bn2bin(v, d) != (int)len_bytes) 178276707Sdes return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 179276707Sdes if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) { 180295367Sdes explicit_bzero(d, sizeof(d)); 181276707Sdes return r; 182276707Sdes } 183276707Sdes POKE_U16(dp, len_bits); 184295367Sdes if (len_bytes != 0) 185295367Sdes memcpy(dp + 2, d, len_bytes); 186295367Sdes explicit_bzero(d, sizeof(d)); 187276707Sdes return 0; 188276707Sdes} 189276707Sdes 190276707Sdes#ifdef OPENSSL_HAS_ECC 191276707Sdesint 192276707Sdessshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g) 193276707Sdes{ 194276707Sdes u_char d[SSHBUF_MAX_ECPOINT]; 195276707Sdes BN_CTX *bn_ctx; 196276707Sdes size_t len; 197276707Sdes int ret; 198276707Sdes 199276707Sdes if ((bn_ctx = BN_CTX_new()) == NULL) 200276707Sdes return SSH_ERR_ALLOC_FAIL; 201276707Sdes if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 202276707Sdes NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) { 203276707Sdes BN_CTX_free(bn_ctx); 204276707Sdes return SSH_ERR_INVALID_ARGUMENT; 205276707Sdes } 206276707Sdes if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 207276707Sdes d, len, bn_ctx) != len) { 208276707Sdes BN_CTX_free(bn_ctx); 209276707Sdes return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 210276707Sdes } 211276707Sdes BN_CTX_free(bn_ctx); 212276707Sdes ret = sshbuf_put_string(buf, d, len); 213295367Sdes explicit_bzero(d, len); 214276707Sdes return ret; 215276707Sdes} 216276707Sdes 217276707Sdesint 218276707Sdessshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v) 219276707Sdes{ 220276707Sdes return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v), 221276707Sdes EC_KEY_get0_group(v)); 222276707Sdes} 223276707Sdes#endif /* OPENSSL_HAS_ECC */ 224276707Sdes 225