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