version 1.8, 2013/11/08 00:39:15 |
version 1.9, 2014/01/09 23:20:00 |
|
|
#include "log.h" |
#include "log.h" |
|
|
#include "schnorr.h" |
#include "schnorr.h" |
|
#include "digest.h" |
|
|
/* #define SCHNORR_DEBUG */ /* Privacy-violating debugging */ |
/* #define SCHNORR_DEBUG */ /* Privacy-violating debugging */ |
/* #define SCHNORR_MAIN */ /* Include main() selftest */ |
/* #define SCHNORR_MAIN */ /* Include main() selftest */ |
|
|
|
|
/* |
/* |
* Calculate hash component of Schnorr signature H(g || g^v || g^x || id) |
* Calculate hash component of Schnorr signature H(g || g^v || g^x || id) |
* using the hash function defined by "evp_md". Returns signature as |
* using the hash function defined by "hash_alg". Returns signature as |
* bignum or NULL on error. |
* bignum or NULL on error. |
*/ |
*/ |
static BIGNUM * |
static BIGNUM * |
schnorr_hash(const BIGNUM *p, const BIGNUM *q, const BIGNUM *g, |
schnorr_hash(const BIGNUM *p, const BIGNUM *q, const BIGNUM *g, |
const EVP_MD *evp_md, const BIGNUM *g_v, const BIGNUM *g_x, |
int hash_alg, const BIGNUM *g_v, const BIGNUM *g_x, |
const u_char *id, u_int idlen) |
const u_char *id, u_int idlen) |
{ |
{ |
u_char *digest; |
u_char *digest; |
|
|
|
|
SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b), |
SCHNORR_DEBUG_BUF((buffer_ptr(&b), buffer_len(&b), |
"%s: hashblob", __func__)); |
"%s: hashblob", __func__)); |
if (hash_buffer(buffer_ptr(&b), buffer_len(&b), evp_md, |
if (hash_buffer(buffer_ptr(&b), buffer_len(&b), hash_alg, |
&digest, &digest_len) != 0) { |
&digest, &digest_len) != 0) { |
error("%s: hash_buffer", __func__); |
error("%s: hash_buffer", __func__); |
goto out; |
goto out; |
|
|
/* |
/* |
* Generate Schnorr signature to prove knowledge of private value 'x' used |
* Generate Schnorr signature to prove knowledge of private value 'x' used |
* in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g' |
* in public exponent g^x, under group defined by 'grp_p', 'grp_q' and 'grp_g' |
* using the hash function "evp_md". |
* using the hash function "hash_alg". |
* 'idlen' bytes from 'id' will be included in the signature hash as an anti- |
* 'idlen' bytes from 'id' will be included in the signature hash as an anti- |
* replay salt. |
* replay salt. |
* |
* |
|
|
*/ |
*/ |
int |
int |
schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, |
schnorr_sign(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, |
const EVP_MD *evp_md, const BIGNUM *x, const BIGNUM *g_x, |
int hash_alg, const BIGNUM *x, const BIGNUM *g_x, |
const u_char *id, u_int idlen, BIGNUM **r_p, BIGNUM **e_p) |
const u_char *id, u_int idlen, BIGNUM **r_p, BIGNUM **e_p) |
{ |
{ |
int success = -1; |
int success = -1; |
|
|
SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__)); |
SCHNORR_DEBUG_BN((g_v, "%s: g_v = ", __func__)); |
|
|
/* h = H(g || g^v || g^x || id) */ |
/* h = H(g || g^v || g^x || id) */ |
if ((h = schnorr_hash(grp_p, grp_q, grp_g, evp_md, g_v, g_x, |
if ((h = schnorr_hash(grp_p, grp_q, grp_g, hash_alg, g_v, g_x, |
id, idlen)) == NULL) { |
id, idlen)) == NULL) { |
error("%s: schnorr_hash failed", __func__); |
error("%s: schnorr_hash failed", __func__); |
goto out; |
goto out; |
|
|
Buffer b; |
Buffer b; |
BIGNUM *r, *e; |
BIGNUM *r, *e; |
|
|
if (schnorr_sign(grp_p, grp_q, grp_g, EVP_sha256(), |
if (schnorr_sign(grp_p, grp_q, grp_g, SSH_DIGEST_SHA256, |
x, g_x, id, idlen, &r, &e) != 0) |
x, g_x, id, idlen, &r, &e) != 0) |
return -1; |
return -1; |
|
|
|
|
/* |
/* |
* Verify Schnorr signature { r (v - xh mod q), e (g^v mod p) } against |
* Verify Schnorr signature { r (v - xh mod q), e (g^v mod p) } against |
* public exponent g_x (g^x) under group defined by 'grp_p', 'grp_q' and |
* public exponent g_x (g^x) under group defined by 'grp_p', 'grp_q' and |
* 'grp_g' using hash "evp_md". |
* 'grp_g' using hash "hash_alg". |
* Signature hash will be salted with 'idlen' bytes from 'id'. |
* Signature hash will be salted with 'idlen' bytes from 'id'. |
* Returns -1 on failure, 0 on incorrect signature or 1 on matching signature. |
* Returns -1 on failure, 0 on incorrect signature or 1 on matching signature. |
*/ |
*/ |
int |
int |
schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, |
schnorr_verify(const BIGNUM *grp_p, const BIGNUM *grp_q, const BIGNUM *grp_g, |
const EVP_MD *evp_md, const BIGNUM *g_x, const u_char *id, u_int idlen, |
int hash_alg, const BIGNUM *g_x, const u_char *id, u_int idlen, |
const BIGNUM *r, const BIGNUM *e) |
const BIGNUM *r, const BIGNUM *e) |
{ |
{ |
int success = -1; |
int success = -1; |
|
|
|
|
SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__)); |
SCHNORR_DEBUG_BN((g_xh, "%s: g_xh = ", __func__)); |
/* h = H(g || g^v || g^x || id) */ |
/* h = H(g || g^v || g^x || id) */ |
if ((h = schnorr_hash(grp_p, grp_q, grp_g, evp_md, e, g_x, |
if ((h = schnorr_hash(grp_p, grp_q, grp_g, hash_alg, e, g_x, |
id, idlen)) == NULL) { |
id, idlen)) == NULL) { |
error("%s: schnorr_hash failed", __func__); |
error("%s: schnorr_hash failed", __func__); |
goto out; |
goto out; |
|
|
goto out; |
goto out; |
} |
} |
|
|
ret = schnorr_verify(grp_p, grp_q, grp_g, EVP_sha256(), |
ret = schnorr_verify(grp_p, grp_q, grp_g, SSH_DIGEST_SHA256, |
g_x, id, idlen, r, e); |
g_x, id, idlen, r, e); |
out: |
out: |
BN_clear_free(e); |
BN_clear_free(e); |
|
|
return NULL; |
return NULL; |
} |
} |
|
|
|
/* XXX convert all callers of this to use ssh_digest_memory() directly */ |
/* |
/* |
* Hash contents of buffer 'b' with hash 'md'. Returns 0 on success, |
* Hash contents of buffer 'b' with hash 'md'. Returns 0 on success, |
* with digest via 'digestp' (caller to free) and length via 'lenp'. |
* with digest via 'digestp' (caller to free) and length via 'lenp'. |
* Returns -1 on failure. |
* Returns -1 on failure. |
*/ |
*/ |
int |
int |
hash_buffer(const u_char *buf, u_int len, const EVP_MD *md, |
hash_buffer(const u_char *buf, u_int len, int hash_alg, |
u_char **digestp, u_int *lenp) |
u_char **digestp, u_int *lenp) |
{ |
{ |
u_char digest[EVP_MAX_MD_SIZE]; |
u_char digest[SSH_DIGEST_MAX_LENGTH]; |
u_int digest_len; |
u_int digest_len = ssh_digest_bytes(hash_alg); |
EVP_MD_CTX evp_md_ctx; |
|
int success = -1; |
|
|
|
EVP_MD_CTX_init(&evp_md_ctx); |
if (digest_len == 0) { |
|
error("%s: invalid hash", __func__); |
if (EVP_DigestInit_ex(&evp_md_ctx, md, NULL) != 1) { |
return -1; |
error("%s: EVP_DigestInit_ex", __func__); |
|
goto out; |
|
} |
} |
if (EVP_DigestUpdate(&evp_md_ctx, buf, len) != 1) { |
if (ssh_digest_memory(hash_alg, buf, len, digest, digest_len) != 0) { |
error("%s: EVP_DigestUpdate", __func__); |
error("%s: digest_memory failed", __func__); |
goto out; |
return -1; |
} |
} |
if (EVP_DigestFinal_ex(&evp_md_ctx, digest, &digest_len) != 1) { |
|
error("%s: EVP_DigestFinal_ex", __func__); |
|
goto out; |
|
} |
|
*digestp = xmalloc(digest_len); |
*digestp = xmalloc(digest_len); |
*lenp = digest_len; |
*lenp = digest_len; |
memcpy(*digestp, digest, *lenp); |
memcpy(*digestp, digest, *lenp); |
success = 0; |
|
out: |
|
EVP_MD_CTX_cleanup(&evp_md_ctx); |
|
bzero(digest, sizeof(digest)); |
bzero(digest, sizeof(digest)); |
digest_len = 0; |
digest_len = 0; |
return success; |
return 0; |
} |
} |
|
|
/* print formatted string followed by bignum */ |
/* print formatted string followed by bignum */ |