1255767Sdes/* $OpenBSD: bufec.c,v 1.2 2013/05/17 00:13:13 djm Exp $ */
2218767Sdes/*
3218767Sdes * Copyright (c) 2010 Damien Miller <djm@mindrot.org>
4218767Sdes *
5218767Sdes * Permission to use, copy, modify, and distribute this software for any
6218767Sdes * purpose with or without fee is hereby granted, provided that the above
7218767Sdes * copyright notice and this permission notice appear in all copies.
8218767Sdes *
9218767Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10218767Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11218767Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12218767Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13218767Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14218767Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15218767Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16218767Sdes */
17218767Sdes
18218767Sdes#include "includes.h"
19218767Sdes
20218767Sdes#ifdef OPENSSL_HAS_ECC
21218767Sdes
22218767Sdes#include <sys/types.h>
23218767Sdes
24218767Sdes#include <openssl/bn.h>
25218767Sdes#include <openssl/ec.h>
26218767Sdes
27218767Sdes#include <string.h>
28218767Sdes#include <stdarg.h>
29218767Sdes
30218767Sdes#include "xmalloc.h"
31218767Sdes#include "buffer.h"
32218767Sdes#include "log.h"
33218767Sdes#include "misc.h"
34218767Sdes
35218767Sdes/*
36218767Sdes * Maximum supported EC GFp field length is 528 bits. SEC1 uncompressed
37218767Sdes * encoding represents this as two bitstring points that should each
38218767Sdes * be no longer than the field length, SEC1 specifies a 1 byte
39218767Sdes * point type header.
40218767Sdes * Being paranoid here may insulate us to parsing problems in
41218767Sdes * EC_POINT_oct2point.
42218767Sdes */
43218767Sdes#define BUFFER_MAX_ECPOINT_LEN ((528*2 / 8) + 1)
44218767Sdes
45218767Sdes/*
46218767Sdes * Append an EC_POINT to the buffer as a string containing a SEC1 encoded
47218767Sdes * uncompressed point. Fortunately OpenSSL handles the gory details for us.
48218767Sdes */
49218767Sdesint
50218767Sdesbuffer_put_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
51218767Sdes    const EC_POINT *point)
52218767Sdes{
53218767Sdes	u_char *buf = NULL;
54218767Sdes	size_t len;
55218767Sdes	BN_CTX *bnctx;
56218767Sdes	int ret = -1;
57218767Sdes
58218767Sdes	/* Determine length */
59218767Sdes	if ((bnctx = BN_CTX_new()) == NULL)
60218767Sdes		fatal("%s: BN_CTX_new failed", __func__);
61218767Sdes	len = EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
62218767Sdes	    NULL, 0, bnctx);
63218767Sdes	if (len > BUFFER_MAX_ECPOINT_LEN) {
64218767Sdes		error("%s: giant EC point: len = %lu (max %u)",
65218767Sdes		    __func__, (u_long)len, BUFFER_MAX_ECPOINT_LEN);
66218767Sdes		goto out;
67218767Sdes	}
68218767Sdes	/* Convert */
69218767Sdes	buf = xmalloc(len);
70218767Sdes	if (EC_POINT_point2oct(curve, point, POINT_CONVERSION_UNCOMPRESSED,
71218767Sdes	    buf, len, bnctx) != len) {
72218767Sdes		error("%s: EC_POINT_point2oct length mismatch", __func__);
73218767Sdes		goto out;
74218767Sdes	}
75218767Sdes	/* Append */
76218767Sdes	buffer_put_string(buffer, buf, len);
77218767Sdes	ret = 0;
78218767Sdes out:
79218767Sdes	if (buf != NULL) {
80218767Sdes		bzero(buf, len);
81255767Sdes		free(buf);
82218767Sdes	}
83218767Sdes	BN_CTX_free(bnctx);
84218767Sdes	return ret;
85218767Sdes}
86218767Sdes
87218767Sdesvoid
88218767Sdesbuffer_put_ecpoint(Buffer *buffer, const EC_GROUP *curve,
89218767Sdes    const EC_POINT *point)
90218767Sdes{
91218767Sdes	if (buffer_put_ecpoint_ret(buffer, curve, point) == -1)
92218767Sdes		fatal("%s: buffer error", __func__);
93218767Sdes}
94218767Sdes
95218767Sdesint
96218767Sdesbuffer_get_ecpoint_ret(Buffer *buffer, const EC_GROUP *curve,
97218767Sdes    EC_POINT *point)
98218767Sdes{
99218767Sdes	u_char *buf;
100218767Sdes	u_int len;
101218767Sdes	BN_CTX *bnctx;
102218767Sdes	int ret = -1;
103218767Sdes
104218767Sdes	if ((buf = buffer_get_string_ret(buffer, &len)) == NULL) {
105218767Sdes		error("%s: invalid point", __func__);
106218767Sdes		return -1;
107218767Sdes	}
108218767Sdes	if ((bnctx = BN_CTX_new()) == NULL)
109218767Sdes		fatal("%s: BN_CTX_new failed", __func__);
110218767Sdes	if (len > BUFFER_MAX_ECPOINT_LEN) {
111218767Sdes		error("%s: EC_POINT too long: %u > max %u", __func__,
112218767Sdes		    len, BUFFER_MAX_ECPOINT_LEN);
113218767Sdes		goto out;
114218767Sdes	}
115218767Sdes	if (len == 0) {
116218767Sdes		error("%s: EC_POINT buffer is empty", __func__);
117218767Sdes		goto out;
118218767Sdes	}
119218767Sdes	if (buf[0] != POINT_CONVERSION_UNCOMPRESSED) {
120218767Sdes		error("%s: EC_POINT is in an incorrect form: "
121218767Sdes		    "0x%02x (want 0x%02x)", __func__, buf[0],
122218767Sdes		    POINT_CONVERSION_UNCOMPRESSED);
123218767Sdes		goto out;
124218767Sdes	}
125218767Sdes	if (EC_POINT_oct2point(curve, point, buf, len, bnctx) != 1) {
126218767Sdes		error("buffer_get_bignum2_ret: BN_bin2bn failed");
127218767Sdes		goto out;
128218767Sdes	}
129218767Sdes	/* EC_POINT_oct2point verifies that the point is on the curve for us */
130218767Sdes	ret = 0;
131218767Sdes out:
132218767Sdes	BN_CTX_free(bnctx);
133218767Sdes	bzero(buf, len);
134255767Sdes	free(buf);
135218767Sdes	return ret;
136218767Sdes}
137218767Sdes
138218767Sdesvoid
139218767Sdesbuffer_get_ecpoint(Buffer *buffer, const EC_GROUP *curve,
140218767Sdes    EC_POINT *point)
141218767Sdes{
142218767Sdes	if (buffer_get_ecpoint_ret(buffer, curve, point) == -1)
143218767Sdes		fatal("%s: buffer error", __func__);
144218767Sdes}
145218767Sdes
146218767Sdes#endif /* OPENSSL_HAS_ECC */
147