1264377Sdes/* $OpenBSD: ssh-ecdsa.c,v 1.10 2014/02/03 23:28:00 djm Exp $ */
2218767Sdes/*
3218767Sdes * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4218767Sdes * Copyright (c) 2010 Damien Miller.  All rights reserved.
5218767Sdes *
6218767Sdes * Redistribution and use in source and binary forms, with or without
7218767Sdes * modification, are permitted provided that the following conditions
8218767Sdes * are met:
9218767Sdes * 1. Redistributions of source code must retain the above copyright
10218767Sdes *    notice, this list of conditions and the following disclaimer.
11218767Sdes * 2. Redistributions in binary form must reproduce the above copyright
12218767Sdes *    notice, this list of conditions and the following disclaimer in the
13218767Sdes *    documentation and/or other materials provided with the distribution.
14218767Sdes *
15218767Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16218767Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17218767Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18218767Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19218767Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20218767Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21218767Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22218767Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23218767Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24218767Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25218767Sdes */
26218767Sdes
27218767Sdes#include "includes.h"
28218767Sdes
29218767Sdes#ifdef OPENSSL_HAS_ECC
30218767Sdes
31218767Sdes#include <sys/types.h>
32218767Sdes
33218767Sdes#include <openssl/bn.h>
34218767Sdes#include <openssl/ec.h>
35218767Sdes#include <openssl/ecdsa.h>
36218767Sdes#include <openssl/evp.h>
37218767Sdes
38218767Sdes#include <string.h>
39218767Sdes
40218767Sdes#include "xmalloc.h"
41218767Sdes#include "buffer.h"
42218767Sdes#include "compat.h"
43218767Sdes#include "log.h"
44218767Sdes#include "key.h"
45262566Sdes#include "digest.h"
46218767Sdes
47218767Sdesint
48218767Sdesssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp,
49218767Sdes    const u_char *data, u_int datalen)
50218767Sdes{
51218767Sdes	ECDSA_SIG *sig;
52262566Sdes	int hash_alg;
53262566Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH];
54218767Sdes	u_int len, dlen;
55218767Sdes	Buffer b, bb;
56218767Sdes
57262566Sdes	if (key == NULL || key_type_plain(key->type) != KEY_ECDSA ||
58262566Sdes	    key->ecdsa == NULL) {
59218767Sdes		error("%s: no ECDSA key", __func__);
60218767Sdes		return -1;
61218767Sdes	}
62218767Sdes
63262566Sdes	hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid);
64262566Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
65262566Sdes		error("%s: bad hash algorithm %d", __func__, hash_alg);
66262566Sdes		return -1;
67262566Sdes	}
68262566Sdes	if (ssh_digest_memory(hash_alg, data, datalen,
69262566Sdes	    digest, sizeof(digest)) != 0) {
70262566Sdes		error("%s: digest_memory failed", __func__);
71262566Sdes		return -1;
72262566Sdes	}
73262566Sdes
74218767Sdes	sig = ECDSA_do_sign(digest, dlen, key->ecdsa);
75264377Sdes	explicit_bzero(digest, sizeof(digest));
76218767Sdes
77218767Sdes	if (sig == NULL) {
78218767Sdes		error("%s: sign failed", __func__);
79218767Sdes		return -1;
80218767Sdes	}
81218767Sdes
82218767Sdes	buffer_init(&bb);
83218767Sdes	buffer_put_bignum2(&bb, sig->r);
84218767Sdes	buffer_put_bignum2(&bb, sig->s);
85218767Sdes	ECDSA_SIG_free(sig);
86218767Sdes
87218767Sdes	buffer_init(&b);
88218767Sdes	buffer_put_cstring(&b, key_ssh_name_plain(key));
89218767Sdes	buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb));
90218767Sdes	buffer_free(&bb);
91218767Sdes	len = buffer_len(&b);
92218767Sdes	if (lenp != NULL)
93218767Sdes		*lenp = len;
94218767Sdes	if (sigp != NULL) {
95218767Sdes		*sigp = xmalloc(len);
96218767Sdes		memcpy(*sigp, buffer_ptr(&b), len);
97218767Sdes	}
98218767Sdes	buffer_free(&b);
99218767Sdes
100218767Sdes	return 0;
101218767Sdes}
102218767Sdesint
103218767Sdesssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
104218767Sdes    const u_char *data, u_int datalen)
105218767Sdes{
106218767Sdes	ECDSA_SIG *sig;
107262566Sdes	int hash_alg;
108262566Sdes	u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
109218767Sdes	u_int len, dlen;
110218767Sdes	int rlen, ret;
111218767Sdes	Buffer b, bb;
112218767Sdes	char *ktype;
113218767Sdes
114262566Sdes	if (key == NULL || key_type_plain(key->type) != KEY_ECDSA ||
115262566Sdes	    key->ecdsa == NULL) {
116218767Sdes		error("%s: no ECDSA key", __func__);
117218767Sdes		return -1;
118218767Sdes	}
119218767Sdes
120218767Sdes	/* fetch signature */
121218767Sdes	buffer_init(&b);
122218767Sdes	buffer_append(&b, signature, signaturelen);
123218767Sdes	ktype = buffer_get_string(&b, NULL);
124218767Sdes	if (strcmp(key_ssh_name_plain(key), ktype) != 0) {
125218767Sdes		error("%s: cannot handle type %s", __func__, ktype);
126218767Sdes		buffer_free(&b);
127255767Sdes		free(ktype);
128218767Sdes		return -1;
129218767Sdes	}
130255767Sdes	free(ktype);
131218767Sdes	sigblob = buffer_get_string(&b, &len);
132218767Sdes	rlen = buffer_len(&b);
133218767Sdes	buffer_free(&b);
134218767Sdes	if (rlen != 0) {
135218767Sdes		error("%s: remaining bytes in signature %d", __func__, rlen);
136255767Sdes		free(sigblob);
137218767Sdes		return -1;
138218767Sdes	}
139218767Sdes
140218767Sdes	/* parse signature */
141218767Sdes	if ((sig = ECDSA_SIG_new()) == NULL)
142218767Sdes		fatal("%s: ECDSA_SIG_new failed", __func__);
143218767Sdes
144218767Sdes	buffer_init(&bb);
145218767Sdes	buffer_append(&bb, sigblob, len);
146218767Sdes	buffer_get_bignum2(&bb, sig->r);
147218767Sdes	buffer_get_bignum2(&bb, sig->s);
148218767Sdes	if (buffer_len(&bb) != 0)
149218767Sdes		fatal("%s: remaining bytes in inner sigblob", __func__);
150240075Sdes	buffer_free(&bb);
151218767Sdes
152218767Sdes	/* clean up */
153264377Sdes	explicit_bzero(sigblob, len);
154255767Sdes	free(sigblob);
155218767Sdes
156218767Sdes	/* hash the data */
157262566Sdes	hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid);
158262566Sdes	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
159262566Sdes		error("%s: bad hash algorithm %d", __func__, hash_alg);
160262566Sdes		return -1;
161262566Sdes	}
162262566Sdes	if (ssh_digest_memory(hash_alg, data, datalen,
163262566Sdes	    digest, sizeof(digest)) != 0) {
164262566Sdes		error("%s: digest_memory failed", __func__);
165262566Sdes		return -1;
166262566Sdes	}
167218767Sdes
168218767Sdes	ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa);
169264377Sdes	explicit_bzero(digest, sizeof(digest));
170218767Sdes
171218767Sdes	ECDSA_SIG_free(sig);
172218767Sdes
173218767Sdes	debug("%s: signature %s", __func__,
174218767Sdes	    ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error");
175218767Sdes	return ret;
176218767Sdes}
177218767Sdes
178218767Sdes#endif /* OPENSSL_HAS_ECC */
179