xref: /openbsd-src/usr.bin/ssh/sshbuf-getput-crypto.c (revision 5ad04d351680822078003e2b066cfc9680d6157d)
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