1 /* $OpenBSD: sshbuf-getput-crypto.c,v 1.5 2016/01/12 23:42:54 djm Exp $ */ 2 /* 3 * Copyright (c) 2011 Damien Miller 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <stdlib.h> 20 #include <stdio.h> 21 #include <string.h> 22 23 #include <openssl/bn.h> 24 #include <openssl/ec.h> 25 26 #include "ssherr.h" 27 #define SSHBUF_INTERNAL 28 #include "sshbuf.h" 29 30 int 31 sshbuf_get_bignum2(struct sshbuf *buf, BIGNUM *v) 32 { 33 const u_char *d; 34 size_t len; 35 int r; 36 37 if ((r = sshbuf_get_bignum2_bytes_direct(buf, &d, &len)) != 0) 38 return r; 39 if (v != NULL && BN_bin2bn(d, len, v) == NULL) 40 return SSH_ERR_ALLOC_FAIL; 41 return 0; 42 } 43 44 int 45 sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v) 46 { 47 const u_char *d = sshbuf_ptr(buf); 48 u_int16_t len_bits; 49 size_t len_bytes; 50 51 /* Length in bits */ 52 if (sshbuf_len(buf) < 2) 53 return SSH_ERR_MESSAGE_INCOMPLETE; 54 len_bits = PEEK_U16(d); 55 len_bytes = (len_bits + 7) >> 3; 56 if (len_bytes > SSHBUF_MAX_BIGNUM) 57 return SSH_ERR_BIGNUM_TOO_LARGE; 58 if (sshbuf_len(buf) < 2 + len_bytes) 59 return SSH_ERR_MESSAGE_INCOMPLETE; 60 if (v != NULL && BN_bin2bn(d + 2, len_bytes, v) == NULL) 61 return SSH_ERR_ALLOC_FAIL; 62 if (sshbuf_consume(buf, 2 + len_bytes) != 0) { 63 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 64 SSHBUF_ABORT(); 65 return SSH_ERR_INTERNAL_ERROR; 66 } 67 return 0; 68 } 69 70 static int 71 get_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g) 72 { 73 /* Refuse overlong bignums */ 74 if (len == 0 || len > SSHBUF_MAX_ECPOINT) 75 return SSH_ERR_ECPOINT_TOO_LARGE; 76 /* Only handle uncompressed points */ 77 if (*d != POINT_CONVERSION_UNCOMPRESSED) 78 return SSH_ERR_INVALID_FORMAT; 79 if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1) 80 return SSH_ERR_INVALID_FORMAT; /* XXX assumption */ 81 return 0; 82 } 83 84 int 85 sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g) 86 { 87 const u_char *d; 88 size_t len; 89 int r; 90 91 if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) 92 return r; 93 if ((r = get_ec(d, len, v, g)) != 0) 94 return r; 95 /* Skip string */ 96 if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 97 /* Shouldn't happen */ 98 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 99 SSHBUF_ABORT(); 100 return SSH_ERR_INTERNAL_ERROR; 101 } 102 return 0; 103 } 104 105 int 106 sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v) 107 { 108 EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v)); 109 int r; 110 const u_char *d; 111 size_t len; 112 113 if (pt == NULL) { 114 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 115 return SSH_ERR_ALLOC_FAIL; 116 } 117 if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) { 118 EC_POINT_free(pt); 119 return r; 120 } 121 if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) { 122 EC_POINT_free(pt); 123 return r; 124 } 125 if (EC_KEY_set_public_key(v, pt) != 1) { 126 EC_POINT_free(pt); 127 return SSH_ERR_ALLOC_FAIL; /* XXX assumption */ 128 } 129 EC_POINT_free(pt); 130 /* Skip string */ 131 if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 132 /* Shouldn't happen */ 133 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 134 SSHBUF_ABORT(); 135 return SSH_ERR_INTERNAL_ERROR; 136 } 137 return 0; 138 } 139 140 int 141 sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v) 142 { 143 u_char d[SSHBUF_MAX_BIGNUM + 1]; 144 int len = BN_num_bytes(v), prepend = 0, r; 145 146 if (len < 0 || len > SSHBUF_MAX_BIGNUM) 147 return SSH_ERR_INVALID_ARGUMENT; 148 *d = '\0'; 149 if (BN_bn2bin(v, d + 1) != len) 150 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 151 /* If MSB is set, prepend a \0 */ 152 if (len > 0 && (d[1] & 0x80) != 0) 153 prepend = 1; 154 if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) { 155 explicit_bzero(d, sizeof(d)); 156 return r; 157 } 158 explicit_bzero(d, sizeof(d)); 159 return 0; 160 } 161 162 int 163 sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v) 164 { 165 int r, len_bits = BN_num_bits(v); 166 size_t len_bytes = (len_bits + 7) / 8; 167 u_char d[SSHBUF_MAX_BIGNUM], *dp; 168 169 if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM) 170 return SSH_ERR_INVALID_ARGUMENT; 171 if (BN_bn2bin(v, d) != (int)len_bytes) 172 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 173 if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) { 174 explicit_bzero(d, sizeof(d)); 175 return r; 176 } 177 POKE_U16(dp, len_bits); 178 if (len_bytes != 0) 179 memcpy(dp + 2, d, len_bytes); 180 explicit_bzero(d, sizeof(d)); 181 return 0; 182 } 183 184 int 185 sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g) 186 { 187 u_char d[SSHBUF_MAX_ECPOINT]; 188 BN_CTX *bn_ctx; 189 size_t len; 190 int ret; 191 192 if ((bn_ctx = BN_CTX_new()) == NULL) 193 return SSH_ERR_ALLOC_FAIL; 194 if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 195 NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) { 196 BN_CTX_free(bn_ctx); 197 return SSH_ERR_INVALID_ARGUMENT; 198 } 199 if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 200 d, len, bn_ctx) != len) { 201 BN_CTX_free(bn_ctx); 202 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 203 } 204 BN_CTX_free(bn_ctx); 205 ret = sshbuf_put_string(buf, d, len); 206 explicit_bzero(d, len); 207 return ret; 208 } 209 210 int 211 sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v) 212 { 213 return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v), 214 EC_KEY_get0_group(v)); 215 } 216 217