1 /* $OpenBSD: sshbuf-getput-crypto.c,v 1.2 2014/06/18 15:42:09 naddy 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_peek_string_direct(buf, &d, &len)) < 0) 38 return r; 39 /* Refuse negative (MSB set) bignums */ 40 if ((len != 0 && (*d & 0x80) != 0)) 41 return SSH_ERR_BIGNUM_IS_NEGATIVE; 42 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 43 if (len > SSHBUF_MAX_BIGNUM + 1 || 44 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 45 return SSH_ERR_BIGNUM_TOO_LARGE; 46 if (v != NULL && BN_bin2bn(d, len, v) == NULL) 47 return SSH_ERR_ALLOC_FAIL; 48 /* Consume the string */ 49 if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 50 /* Shouldn't happen */ 51 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 52 SSHBUF_ABORT(); 53 return SSH_ERR_INTERNAL_ERROR; 54 } 55 return 0; 56 } 57 58 int 59 sshbuf_get_bignum1(struct sshbuf *buf, BIGNUM *v) 60 { 61 const u_char *d = sshbuf_ptr(buf); 62 u_int16_t len_bits; 63 size_t len_bytes; 64 65 /* Length in bits */ 66 if (sshbuf_len(buf) < 2) 67 return SSH_ERR_MESSAGE_INCOMPLETE; 68 len_bits = PEEK_U16(d); 69 len_bytes = (len_bits + 7) >> 3; 70 if (len_bytes > SSHBUF_MAX_BIGNUM) 71 return SSH_ERR_BIGNUM_TOO_LARGE; 72 if (sshbuf_len(buf) < 2 + len_bytes) 73 return SSH_ERR_MESSAGE_INCOMPLETE; 74 if (v != NULL && BN_bin2bn(d + 2, len_bytes, v) == NULL) 75 return SSH_ERR_ALLOC_FAIL; 76 if (sshbuf_consume(buf, 2 + len_bytes) != 0) { 77 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 78 SSHBUF_ABORT(); 79 return SSH_ERR_INTERNAL_ERROR; 80 } 81 return 0; 82 } 83 84 static int 85 get_ec(const u_char *d, size_t len, EC_POINT *v, const EC_GROUP *g) 86 { 87 /* Refuse overlong bignums */ 88 if (len == 0 || len > SSHBUF_MAX_ECPOINT) 89 return SSH_ERR_ECPOINT_TOO_LARGE; 90 /* Only handle uncompressed points */ 91 if (*d != POINT_CONVERSION_UNCOMPRESSED) 92 return SSH_ERR_INVALID_FORMAT; 93 if (v != NULL && EC_POINT_oct2point(g, v, d, len, NULL) != 1) 94 return SSH_ERR_INVALID_FORMAT; /* XXX assumption */ 95 return 0; 96 } 97 98 int 99 sshbuf_get_ec(struct sshbuf *buf, EC_POINT *v, const EC_GROUP *g) 100 { 101 const u_char *d; 102 size_t len; 103 int r; 104 105 if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) 106 return r; 107 if ((r = get_ec(d, len, v, g)) != 0) 108 return r; 109 /* Skip string */ 110 if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 111 /* Shouldn't happen */ 112 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 113 SSHBUF_ABORT(); 114 return SSH_ERR_INTERNAL_ERROR; 115 } 116 return 0; 117 } 118 119 int 120 sshbuf_get_eckey(struct sshbuf *buf, EC_KEY *v) 121 { 122 EC_POINT *pt = EC_POINT_new(EC_KEY_get0_group(v)); 123 int r; 124 const u_char *d; 125 size_t len; 126 127 if (pt == NULL) { 128 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 129 return SSH_ERR_ALLOC_FAIL; 130 } 131 if ((r = sshbuf_peek_string_direct(buf, &d, &len)) < 0) { 132 EC_POINT_free(pt); 133 return r; 134 } 135 if ((r = get_ec(d, len, pt, EC_KEY_get0_group(v))) != 0) { 136 EC_POINT_free(pt); 137 return r; 138 } 139 if (EC_KEY_set_public_key(v, pt) != 1) { 140 EC_POINT_free(pt); 141 return SSH_ERR_ALLOC_FAIL; /* XXX assumption */ 142 } 143 EC_POINT_free(pt); 144 /* Skip string */ 145 if (sshbuf_get_string_direct(buf, NULL, NULL) != 0) { 146 /* Shouldn't happen */ 147 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 148 SSHBUF_ABORT(); 149 return SSH_ERR_INTERNAL_ERROR; 150 } 151 return 0; 152 } 153 154 int 155 sshbuf_put_bignum2(struct sshbuf *buf, const BIGNUM *v) 156 { 157 u_char d[SSHBUF_MAX_BIGNUM + 1]; 158 int len = BN_num_bytes(v), prepend = 0, r; 159 160 if (len < 0 || len > SSHBUF_MAX_BIGNUM) 161 return SSH_ERR_INVALID_ARGUMENT; 162 *d = '\0'; 163 if (BN_bn2bin(v, d + 1) != len) 164 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 165 /* If MSB is set, prepend a \0 */ 166 if (len > 0 && (d[1] & 0x80) != 0) 167 prepend = 1; 168 if ((r = sshbuf_put_string(buf, d + 1 - prepend, len + prepend)) < 0) { 169 bzero(d, sizeof(d)); 170 return r; 171 } 172 bzero(d, sizeof(d)); 173 return 0; 174 } 175 176 int 177 sshbuf_put_bignum1(struct sshbuf *buf, const BIGNUM *v) 178 { 179 int r, len_bits = BN_num_bits(v); 180 size_t len_bytes = (len_bits + 7) / 8; 181 u_char d[SSHBUF_MAX_BIGNUM], *dp; 182 183 if (len_bits < 0 || len_bytes > SSHBUF_MAX_BIGNUM) 184 return SSH_ERR_INVALID_ARGUMENT; 185 if (BN_bn2bin(v, d) != (int)len_bytes) 186 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 187 if ((r = sshbuf_reserve(buf, len_bytes + 2, &dp)) < 0) { 188 bzero(d, sizeof(d)); 189 return r; 190 } 191 POKE_U16(dp, len_bits); 192 memcpy(dp + 2, d, len_bytes); 193 bzero(d, sizeof(d)); 194 return 0; 195 } 196 197 int 198 sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g) 199 { 200 u_char d[SSHBUF_MAX_ECPOINT]; 201 BN_CTX *bn_ctx; 202 size_t len; 203 int ret; 204 205 if ((bn_ctx = BN_CTX_new()) == NULL) 206 return SSH_ERR_ALLOC_FAIL; 207 if ((len = EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 208 NULL, 0, bn_ctx)) > SSHBUF_MAX_ECPOINT) { 209 BN_CTX_free(bn_ctx); 210 return SSH_ERR_INVALID_ARGUMENT; 211 } 212 if (EC_POINT_point2oct(g, v, POINT_CONVERSION_UNCOMPRESSED, 213 d, len, bn_ctx) != len) { 214 BN_CTX_free(bn_ctx); 215 return SSH_ERR_INTERNAL_ERROR; /* Shouldn't happen */ 216 } 217 BN_CTX_free(bn_ctx); 218 ret = sshbuf_put_string(buf, d, len); 219 bzero(d, len); 220 return ret; 221 } 222 223 int 224 sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v) 225 { 226 return sshbuf_put_ec(buf, EC_KEY_get0_public_key(v), 227 EC_KEY_get0_group(v)); 228 } 229 230