19768ca48f57aaf035f508a473421d210b5145e99Greg Hartman/* $OpenBSD: ssh-ed25519.c,v 1.7 2016/04/21 06:08:02 djm Exp $ */ 2d059297112922cabb0c674840589be8db821fd9aAdam Langley/* 3d059297112922cabb0c674840589be8db821fd9aAdam Langley * Copyright (c) 2013 Markus Friedl <markus@openbsd.org> 4d059297112922cabb0c674840589be8db821fd9aAdam Langley * 5d059297112922cabb0c674840589be8db821fd9aAdam Langley * Permission to use, copy, modify, and distribute this software for any 6d059297112922cabb0c674840589be8db821fd9aAdam Langley * purpose with or without fee is hereby granted, provided that the above 7d059297112922cabb0c674840589be8db821fd9aAdam Langley * copyright notice and this permission notice appear in all copies. 8d059297112922cabb0c674840589be8db821fd9aAdam Langley * 9d059297112922cabb0c674840589be8db821fd9aAdam Langley * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10d059297112922cabb0c674840589be8db821fd9aAdam Langley * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11d059297112922cabb0c674840589be8db821fd9aAdam Langley * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12d059297112922cabb0c674840589be8db821fd9aAdam Langley * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13d059297112922cabb0c674840589be8db821fd9aAdam Langley * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14d059297112922cabb0c674840589be8db821fd9aAdam Langley * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15d059297112922cabb0c674840589be8db821fd9aAdam Langley * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 17d059297112922cabb0c674840589be8db821fd9aAdam Langley 18d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "includes.h" 19d059297112922cabb0c674840589be8db821fd9aAdam Langley 20d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/types.h> 21d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <limits.h> 22d059297112922cabb0c674840589be8db821fd9aAdam Langley 23d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "crypto_api.h" 24d059297112922cabb0c674840589be8db821fd9aAdam Langley 25d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <string.h> 26d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <stdarg.h> 27d059297112922cabb0c674840589be8db821fd9aAdam Langley 28d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "log.h" 29d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshbuf.h" 30d059297112922cabb0c674840589be8db821fd9aAdam Langley#define SSHKEY_INTERNAL 31d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshkey.h" 32d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "ssherr.h" 33d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "ssh.h" 34d059297112922cabb0c674840589be8db821fd9aAdam Langley 35d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 36d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 37d059297112922cabb0c674840589be8db821fd9aAdam Langley const u_char *data, size_t datalen, u_int compat) 38d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 39d059297112922cabb0c674840589be8db821fd9aAdam Langley u_char *sig = NULL; 40d059297112922cabb0c674840589be8db821fd9aAdam Langley size_t slen = 0, len; 41d059297112922cabb0c674840589be8db821fd9aAdam Langley unsigned long long smlen; 42d059297112922cabb0c674840589be8db821fd9aAdam Langley int r, ret; 43d059297112922cabb0c674840589be8db821fd9aAdam Langley struct sshbuf *b = NULL; 44d059297112922cabb0c674840589be8db821fd9aAdam Langley 45d059297112922cabb0c674840589be8db821fd9aAdam Langley if (lenp != NULL) 46d059297112922cabb0c674840589be8db821fd9aAdam Langley *lenp = 0; 47d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sigp != NULL) 48d059297112922cabb0c674840589be8db821fd9aAdam Langley *sigp = NULL; 49d059297112922cabb0c674840589be8db821fd9aAdam Langley 50d059297112922cabb0c674840589be8db821fd9aAdam Langley if (key == NULL || 51d059297112922cabb0c674840589be8db821fd9aAdam Langley sshkey_type_plain(key->type) != KEY_ED25519 || 52d059297112922cabb0c674840589be8db821fd9aAdam Langley key->ed25519_sk == NULL || 53d059297112922cabb0c674840589be8db821fd9aAdam Langley datalen >= INT_MAX - crypto_sign_ed25519_BYTES) 54d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_INVALID_ARGUMENT; 55d059297112922cabb0c674840589be8db821fd9aAdam Langley smlen = slen = datalen + crypto_sign_ed25519_BYTES; 56d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((sig = malloc(slen)) == NULL) 57d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_ALLOC_FAIL; 58d059297112922cabb0c674840589be8db821fd9aAdam Langley 59d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, 60d059297112922cabb0c674840589be8db821fd9aAdam Langley key->ed25519_sk)) != 0 || smlen <= datalen) { 61d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 62d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 63d059297112922cabb0c674840589be8db821fd9aAdam Langley } 64d059297112922cabb0c674840589be8db821fd9aAdam Langley /* encode signature */ 65d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((b = sshbuf_new()) == NULL) { 66d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_ALLOC_FAIL; 67d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 68d059297112922cabb0c674840589be8db821fd9aAdam Langley } 69d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || 70d059297112922cabb0c674840589be8db821fd9aAdam Langley (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) 71d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 72d059297112922cabb0c674840589be8db821fd9aAdam Langley len = sshbuf_len(b); 73d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sigp != NULL) { 74d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((*sigp = malloc(len)) == NULL) { 75d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_ALLOC_FAIL; 76d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 77d059297112922cabb0c674840589be8db821fd9aAdam Langley } 78d059297112922cabb0c674840589be8db821fd9aAdam Langley memcpy(*sigp, sshbuf_ptr(b), len); 79d059297112922cabb0c674840589be8db821fd9aAdam Langley } 80d059297112922cabb0c674840589be8db821fd9aAdam Langley if (lenp != NULL) 81d059297112922cabb0c674840589be8db821fd9aAdam Langley *lenp = len; 82d059297112922cabb0c674840589be8db821fd9aAdam Langley /* success */ 83d059297112922cabb0c674840589be8db821fd9aAdam Langley r = 0; 84d059297112922cabb0c674840589be8db821fd9aAdam Langley out: 85d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_free(b); 86d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sig != NULL) { 87d059297112922cabb0c674840589be8db821fd9aAdam Langley explicit_bzero(sig, slen); 88d059297112922cabb0c674840589be8db821fd9aAdam Langley free(sig); 89d059297112922cabb0c674840589be8db821fd9aAdam Langley } 90d059297112922cabb0c674840589be8db821fd9aAdam Langley 91d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 92d059297112922cabb0c674840589be8db821fd9aAdam Langley} 93d059297112922cabb0c674840589be8db821fd9aAdam Langley 94d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 95d059297112922cabb0c674840589be8db821fd9aAdam Langleyssh_ed25519_verify(const struct sshkey *key, 96d059297112922cabb0c674840589be8db821fd9aAdam Langley const u_char *signature, size_t signaturelen, 97d059297112922cabb0c674840589be8db821fd9aAdam Langley const u_char *data, size_t datalen, u_int compat) 98d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 99d059297112922cabb0c674840589be8db821fd9aAdam Langley struct sshbuf *b = NULL; 100d059297112922cabb0c674840589be8db821fd9aAdam Langley char *ktype = NULL; 101d059297112922cabb0c674840589be8db821fd9aAdam Langley const u_char *sigblob; 102d059297112922cabb0c674840589be8db821fd9aAdam Langley u_char *sm = NULL, *m = NULL; 103d059297112922cabb0c674840589be8db821fd9aAdam Langley size_t len; 104d059297112922cabb0c674840589be8db821fd9aAdam Langley unsigned long long smlen = 0, mlen = 0; 105d059297112922cabb0c674840589be8db821fd9aAdam Langley int r, ret; 106d059297112922cabb0c674840589be8db821fd9aAdam Langley 107d059297112922cabb0c674840589be8db821fd9aAdam Langley if (key == NULL || 108d059297112922cabb0c674840589be8db821fd9aAdam Langley sshkey_type_plain(key->type) != KEY_ED25519 || 109d059297112922cabb0c674840589be8db821fd9aAdam Langley key->ed25519_pk == NULL || 1109768ca48f57aaf035f508a473421d210b5145e99Greg Hartman datalen >= INT_MAX - crypto_sign_ed25519_BYTES || 1119768ca48f57aaf035f508a473421d210b5145e99Greg Hartman signature == NULL || signaturelen == 0) 112d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_INVALID_ARGUMENT; 113d059297112922cabb0c674840589be8db821fd9aAdam Langley 114d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((b = sshbuf_from(signature, signaturelen)) == NULL) 115d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_ALLOC_FAIL; 116d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 117d059297112922cabb0c674840589be8db821fd9aAdam Langley (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 118d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 119d059297112922cabb0c674840589be8db821fd9aAdam Langley if (strcmp("ssh-ed25519", ktype) != 0) { 120d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_KEY_TYPE_MISMATCH; 121d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 122d059297112922cabb0c674840589be8db821fd9aAdam Langley } 123d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_len(b) != 0) { 124d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 125d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 126d059297112922cabb0c674840589be8db821fd9aAdam Langley } 127d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len > crypto_sign_ed25519_BYTES) { 128d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_INVALID_FORMAT; 129d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 130d059297112922cabb0c674840589be8db821fd9aAdam Langley } 131d059297112922cabb0c674840589be8db821fd9aAdam Langley if (datalen >= SIZE_MAX - len) { 132d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_INVALID_ARGUMENT; 133d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 134d059297112922cabb0c674840589be8db821fd9aAdam Langley } 135d059297112922cabb0c674840589be8db821fd9aAdam Langley smlen = len + datalen; 136d059297112922cabb0c674840589be8db821fd9aAdam Langley mlen = smlen; 137d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 138d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_ALLOC_FAIL; 139d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 140d059297112922cabb0c674840589be8db821fd9aAdam Langley } 141d059297112922cabb0c674840589be8db821fd9aAdam Langley memcpy(sm, sigblob, len); 142d059297112922cabb0c674840589be8db821fd9aAdam Langley memcpy(sm+len, data, datalen); 143d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, 144d059297112922cabb0c674840589be8db821fd9aAdam Langley key->ed25519_pk)) != 0) { 145d059297112922cabb0c674840589be8db821fd9aAdam Langley debug2("%s: crypto_sign_ed25519_open failed: %d", 146d059297112922cabb0c674840589be8db821fd9aAdam Langley __func__, ret); 147d059297112922cabb0c674840589be8db821fd9aAdam Langley } 148d059297112922cabb0c674840589be8db821fd9aAdam Langley if (ret != 0 || mlen != datalen) { 149d059297112922cabb0c674840589be8db821fd9aAdam Langley r = SSH_ERR_SIGNATURE_INVALID; 150d059297112922cabb0c674840589be8db821fd9aAdam Langley goto out; 151d059297112922cabb0c674840589be8db821fd9aAdam Langley } 152d059297112922cabb0c674840589be8db821fd9aAdam Langley /* XXX compare 'm' and 'data' ? */ 153d059297112922cabb0c674840589be8db821fd9aAdam Langley /* success */ 154d059297112922cabb0c674840589be8db821fd9aAdam Langley r = 0; 155d059297112922cabb0c674840589be8db821fd9aAdam Langley out: 156d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sm != NULL) { 157d059297112922cabb0c674840589be8db821fd9aAdam Langley explicit_bzero(sm, smlen); 158d059297112922cabb0c674840589be8db821fd9aAdam Langley free(sm); 159d059297112922cabb0c674840589be8db821fd9aAdam Langley } 160d059297112922cabb0c674840589be8db821fd9aAdam Langley if (m != NULL) { 161d059297112922cabb0c674840589be8db821fd9aAdam Langley explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */ 162d059297112922cabb0c674840589be8db821fd9aAdam Langley free(m); 163d059297112922cabb0c674840589be8db821fd9aAdam Langley } 164d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_free(b); 165d059297112922cabb0c674840589be8db821fd9aAdam Langley free(ktype); 166d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 167d059297112922cabb0c674840589be8db821fd9aAdam Langley} 168