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