version 1.3, 2014/02/23 20:03:42 |
version 1.4, 2014/06/24 01:13:21 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
*/ |
*/ |
|
#define SSHKEY_INTERNAL |
#include <sys/types.h> |
#include <sys/types.h> |
|
#include <limits.h> |
|
|
#include "crypto_api.h" |
#include "crypto_api.h" |
|
|
#include <limits.h> |
|
#include <string.h> |
#include <string.h> |
#include <stdarg.h> |
#include <stdarg.h> |
|
|
#include "xmalloc.h" |
#include "xmalloc.h" |
#include "log.h" |
#include "log.h" |
#include "buffer.h" |
#include "buffer.h" |
#include "key.h" |
#include "sshkey.h" |
|
#include "ssherr.h" |
#include "ssh.h" |
#include "ssh.h" |
|
|
int |
int |
ssh_ed25519_sign(const Key *key, u_char **sigp, u_int *lenp, |
ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, |
const u_char *data, u_int datalen) |
const u_char *data, size_t datalen, u_int compat) |
{ |
{ |
u_char *sig; |
u_char *sig = NULL; |
u_int slen, len; |
size_t slen = 0, len; |
unsigned long long smlen; |
unsigned long long smlen; |
int ret; |
int r, ret; |
Buffer b; |
struct sshbuf *b = NULL; |
|
|
if (key == NULL || key_type_plain(key->type) != KEY_ED25519 || |
if (lenp != NULL) |
key->ed25519_sk == NULL) { |
*lenp = 0; |
error("%s: no ED25519 key", __func__); |
if (sigp != NULL) |
return -1; |
*sigp = NULL; |
} |
|
|
|
if (datalen >= UINT_MAX - crypto_sign_ed25519_BYTES) { |
if (key == NULL || |
error("%s: datalen %u too long", __func__, datalen); |
sshkey_type_plain(key->type) != KEY_ED25519 || |
return -1; |
key->ed25519_sk == NULL || |
} |
datalen >= INT_MAX - crypto_sign_ed25519_BYTES) |
|
return SSH_ERR_INVALID_ARGUMENT; |
smlen = slen = datalen + crypto_sign_ed25519_BYTES; |
smlen = slen = datalen + crypto_sign_ed25519_BYTES; |
sig = xmalloc(slen); |
if ((sig = malloc(slen)) == NULL) |
|
return SSH_ERR_ALLOC_FAIL; |
|
|
if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, |
if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen, |
key->ed25519_sk)) != 0 || smlen <= datalen) { |
key->ed25519_sk)) != 0 || smlen <= datalen) { |
error("%s: crypto_sign_ed25519 failed: %d", __func__, ret); |
r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ |
free(sig); |
goto out; |
return -1; |
|
} |
} |
/* encode signature */ |
/* encode signature */ |
buffer_init(&b); |
if ((b = sshbuf_new()) == NULL) { |
buffer_put_cstring(&b, "ssh-ed25519"); |
r = SSH_ERR_ALLOC_FAIL; |
buffer_put_string(&b, sig, smlen - datalen); |
goto out; |
len = buffer_len(&b); |
} |
|
if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 || |
|
(r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) |
|
goto out; |
|
len = sshbuf_len(b); |
|
if (sigp != NULL) { |
|
if ((*sigp = malloc(len)) == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
|
goto out; |
|
} |
|
memcpy(*sigp, sshbuf_ptr(b), len); |
|
} |
if (lenp != NULL) |
if (lenp != NULL) |
*lenp = len; |
*lenp = len; |
if (sigp != NULL) { |
/* success */ |
*sigp = xmalloc(len); |
r = 0; |
memcpy(*sigp, buffer_ptr(&b), len); |
out: |
|
sshbuf_free(b); |
|
if (sig != NULL) { |
|
explicit_bzero(sig, slen); |
|
free(sig); |
} |
} |
buffer_free(&b); |
|
explicit_bzero(sig, slen); |
|
free(sig); |
|
|
|
return 0; |
return r; |
} |
} |
|
|
int |
int |
ssh_ed25519_verify(const Key *key, const u_char *signature, u_int signaturelen, |
ssh_ed25519_verify(const struct sshkey *key, |
const u_char *data, u_int datalen) |
const u_char *signature, size_t signaturelen, |
|
const u_char *data, size_t datalen, u_int compat) |
{ |
{ |
Buffer b; |
struct sshbuf *b = NULL; |
char *ktype; |
char *ktype = NULL; |
u_char *sigblob, *sm, *m; |
const u_char *sigblob; |
u_int len; |
u_char *sm = NULL, *m = NULL; |
unsigned long long smlen, mlen; |
size_t len; |
int rlen, ret; |
unsigned long long smlen = 0, mlen = 0; |
|
int r, ret; |
|
|
if (key == NULL || key_type_plain(key->type) != KEY_ED25519 || |
if (key == NULL || |
key->ed25519_pk == NULL) { |
sshkey_type_plain(key->type) != KEY_ED25519 || |
error("%s: no ED25519 key", __func__); |
key->ed25519_pk == NULL || |
return -1; |
datalen >= INT_MAX - crypto_sign_ed25519_BYTES) |
} |
return SSH_ERR_INVALID_ARGUMENT; |
buffer_init(&b); |
|
buffer_append(&b, signature, signaturelen); |
if ((b = sshbuf_from(signature, signaturelen)) == NULL) |
ktype = buffer_get_cstring(&b, NULL); |
return SSH_ERR_ALLOC_FAIL; |
|
if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || |
|
(r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) |
|
goto out; |
if (strcmp("ssh-ed25519", ktype) != 0) { |
if (strcmp("ssh-ed25519", ktype) != 0) { |
error("%s: cannot handle type %s", __func__, ktype); |
r = SSH_ERR_KEY_TYPE_MISMATCH; |
buffer_free(&b); |
goto out; |
free(ktype); |
|
return -1; |
|
} |
} |
free(ktype); |
if (sshbuf_len(b) != 0) { |
sigblob = buffer_get_string(&b, &len); |
r = SSH_ERR_UNEXPECTED_TRAILING_DATA; |
rlen = buffer_len(&b); |
goto out; |
buffer_free(&b); |
|
if (rlen != 0) { |
|
error("%s: remaining bytes in signature %d", __func__, rlen); |
|
free(sigblob); |
|
return -1; |
|
} |
} |
if (len > crypto_sign_ed25519_BYTES) { |
if (len > crypto_sign_ed25519_BYTES) { |
error("%s: len %u > crypto_sign_ed25519_BYTES %u", __func__, |
r = SSH_ERR_INVALID_FORMAT; |
len, crypto_sign_ed25519_BYTES); |
goto out; |
free(sigblob); |
|
return -1; |
|
} |
} |
|
if (datalen >= SIZE_MAX - len) |
|
return SSH_ERR_INVALID_ARGUMENT; |
smlen = len + datalen; |
smlen = len + datalen; |
sm = xmalloc(smlen); |
mlen = smlen; |
|
if ((sm = malloc(smlen)) == NULL || (m = xmalloc(mlen)) == NULL) { |
|
r = SSH_ERR_ALLOC_FAIL; |
|
goto out; |
|
} |
memcpy(sm, sigblob, len); |
memcpy(sm, sigblob, len); |
memcpy(sm+len, data, datalen); |
memcpy(sm+len, data, datalen); |
mlen = smlen; |
|
m = xmalloc(mlen); |
|
if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, |
if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen, |
key->ed25519_pk)) != 0) { |
key->ed25519_pk)) != 0) { |
debug2("%s: crypto_sign_ed25519_open failed: %d", |
debug2("%s: crypto_sign_ed25519_open failed: %d", |
__func__, ret); |
__func__, ret); |
} |
} |
if (ret == 0 && mlen != datalen) { |
if (ret != 0 || mlen != datalen) { |
debug2("%s: crypto_sign_ed25519_open " |
r = SSH_ERR_SIGNATURE_INVALID; |
"mlen != datalen (%llu != %u)", __func__, mlen, datalen); |
goto out; |
ret = -1; |
|
} |
} |
/* XXX compare 'm' and 'data' ? */ |
/* XXX compare 'm' and 'data' ? */ |
|
/* success */ |
explicit_bzero(sigblob, len); |
r = 0; |
explicit_bzero(sm, smlen); |
out: |
explicit_bzero(m, smlen); /* NB. mlen may be invalid if ret != 0 */ |
if (sm != NULL) { |
free(sigblob); |
explicit_bzero(sm, smlen); |
free(sm); |
free(sm); |
free(m); |
} |
debug("%s: signature %scorrect", __func__, (ret != 0) ? "in" : ""); |
if (m != NULL) { |
|
explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */ |
/* translate return code carefully */ |
free(m); |
return (ret == 0) ? 1 : -1; |
} |
|
sshbuf_free(b); |
|
free(ktype); |
|
return r; |
} |
} |
|
|