mirror of
https://github.com/urbit/shrub.git
synced 2024-12-23 02:41:35 +03:00
316 lines
9.4 KiB
C
316 lines
9.4 KiB
C
|
/* j/5/secp.c
|
||
|
**
|
||
|
*/
|
||
|
#include "all.h"
|
||
|
#include "../include/secp256k1.h"
|
||
|
#include "../include/secp256k1_recovery.h"
|
||
|
|
||
|
/* util funcs
|
||
|
*/
|
||
|
|
||
|
/* no guarantees if 'in' and 'out' overlap / are the same */
|
||
|
static void byte_reverse(c3_y *i_y, /* in */
|
||
|
c3_y *o_y, /* out */
|
||
|
c3_w n_w) /* size */
|
||
|
{
|
||
|
c3_w j_w;
|
||
|
for (j_w = 0; j_w < n_w; j_w++){
|
||
|
o_y[n_w - 1 - j_w] = i_y[j_w];
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Identical to u3r_bytes, but reverses bytes in place.
|
||
|
could be cleaner if we modified u3r_bytes(), but not gonna do that.
|
||
|
|
||
|
This func exists bc Urbit code base is explicitly little-endian,
|
||
|
and secp256k1 library is explicitly big-endian.
|
||
|
|
||
|
Several times below we do the pattern of (1) invoke u3r_bytes, (2) invert. Do it in a func.
|
||
|
*/
|
||
|
|
||
|
|
||
|
static void u3r_bytes_reverse(c3_w a_w,
|
||
|
c3_w b_w,
|
||
|
c3_y* c_y, /* out */
|
||
|
u3_atom d) /* in */
|
||
|
{
|
||
|
u3r_bytes(a_w, b_w, c_y, d);
|
||
|
c3_w i_w;
|
||
|
for (i_w = 0; i_w < ((b_w - a_w) / 2) ; i_w++) {
|
||
|
c3_y lo = c_y[i_w];
|
||
|
c3_y hi = c_y[b_w - i_w - 1];
|
||
|
c_y[i_w] = hi;
|
||
|
c_y[b_w - i_w - 1] = lo;
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* sign hash with priv key
|
||
|
*/
|
||
|
|
||
|
u3_noun
|
||
|
u3we_sign(u3_noun cor)
|
||
|
{
|
||
|
|
||
|
u3_noun has, prv;
|
||
|
|
||
|
if ( (c3n == u3r_mean(cor,
|
||
|
u3x_sam_2, &has,
|
||
|
u3x_sam_3, &prv,
|
||
|
0)) ||
|
||
|
(c3n == u3ud(has)) ||
|
||
|
(c3n == u3ud(prv))) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
} else {
|
||
|
return (u3qe_sign(has, prv));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
u3_noun
|
||
|
u3qe_sign(u3_atom has,
|
||
|
u3_atom prv)
|
||
|
{
|
||
|
/* build library context object once (and only once) */
|
||
|
static secp256k1_context * ctx_u = NULL;
|
||
|
if (NULL == ctx_u) {
|
||
|
ctx_u = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||
|
}
|
||
|
|
||
|
/* parse arguments, convert endianness */
|
||
|
c3_y has_y[32]; /* hash */
|
||
|
c3_y prv_y[32]; /* private key */
|
||
|
u3r_bytes_reverse(0, 32, has_y, has);
|
||
|
u3r_bytes_reverse(0, 32, prv_y, prv);
|
||
|
|
||
|
|
||
|
/* sign
|
||
|
N.B. if we want the 'v' field we can't use default secp256k1_ecdsa_sign(),
|
||
|
but must use secp256k1_ecdsa_sign_recoverable() */
|
||
|
c3_ws ret;
|
||
|
secp256k1_ecdsa_recoverable_signature sig_u;
|
||
|
ret = secp256k1_ecdsa_sign_recoverable(ctx_u, /* IN: context object */
|
||
|
& sig_u, /* OUT: signature */
|
||
|
(const c3_y *) has_y, /* IN: 32 byte hash to be signed */
|
||
|
(const c3_y *) prv_y, /* IN: 32 byte secret key */
|
||
|
(secp256k1_nonce_function) NULL, /* IN: nonce-function ptr ; NULL = default */
|
||
|
(const void *) NULL); /* IN: data for nonce function; not used */
|
||
|
if (1 != ret) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
/* convert opaque 65 byte signature into v + [r + s]
|
||
|
convert endianness while we're at it */
|
||
|
c3_y rec_y[64];
|
||
|
c3_ws v = 0;
|
||
|
ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx_u,
|
||
|
rec_y, /* OUT: 64 byte sig (r,s) */
|
||
|
& v, /* OUT: v */
|
||
|
& sig_u); /* IN: 65 byte sig */
|
||
|
if (1 != ret) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
c3_y s_y[32];
|
||
|
c3_y r_y[32];
|
||
|
byte_reverse(rec_y, r_y, 32);
|
||
|
byte_reverse(rec_y + 32, s_y, 32);
|
||
|
|
||
|
/* package s,r,v signature for return */
|
||
|
u3_noun s = u3i_words(8, (const c3_w*) s_y);
|
||
|
u3_noun r = u3i_words(8, (const c3_w*) r_y);
|
||
|
return (u3nt(v, r, s));
|
||
|
}
|
||
|
|
||
|
|
||
|
/* recover pubkey from signature (which is how we verify signatures)
|
||
|
*/
|
||
|
|
||
|
u3_noun
|
||
|
u3we_reco(u3_noun cor)
|
||
|
{
|
||
|
u3_noun has, /* hash */
|
||
|
siv, sir, sis; /* signature: v, r, s */
|
||
|
|
||
|
if ( (c3n == u3r_mean(cor,
|
||
|
u3x_sam_2, &has,
|
||
|
u3x_sam_6, &siv,
|
||
|
u3x_sam_14, &sir,
|
||
|
u3x_sam_15, &sis,
|
||
|
0)) ||
|
||
|
(c3n == u3ud(has)) ||
|
||
|
(c3n == u3ud(siv)) ||
|
||
|
(c3n == u3ud(sir)) ||
|
||
|
(c3n == u3ud(sis)) )
|
||
|
{
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
} else {
|
||
|
return u3qe_reco(has, siv, sir, sis);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
u3_noun
|
||
|
u3qe_reco(u3_atom has,
|
||
|
u3_atom siv, /* signature: v */
|
||
|
u3_atom sir, /* signature: r */
|
||
|
u3_atom sis) /* signature: s */
|
||
|
{
|
||
|
|
||
|
/* build library context object once (and only once) */
|
||
|
static secp256k1_context * ctx_u = NULL;
|
||
|
if (NULL == ctx_u) {
|
||
|
ctx_u = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||
|
}
|
||
|
|
||
|
/* parse arguments, convert endianness */
|
||
|
c3_y has_y[32];
|
||
|
c3_y sir_y[32];
|
||
|
c3_y sis_y[32];
|
||
|
c3_y siv_y[1];
|
||
|
u3r_bytes_reverse(0, 32, has_y, has);
|
||
|
u3r_bytes_reverse(0, 32, sir_y, sir);
|
||
|
u3r_bytes_reverse(0, 32, sis_y, sis);
|
||
|
u3r_bytes_reverse(0, 1, siv_y, siv);
|
||
|
|
||
|
/* build the signature object */
|
||
|
c3_y ras_y[64]; /* priv key: r and s components */
|
||
|
c3_ws i_ws;
|
||
|
for (i_ws = 0; i_ws < 32; i_ws++) {
|
||
|
ras_y[i_ws] = sir_y[i_ws] ;
|
||
|
}
|
||
|
for (i_ws = 0; i_ws < 32; i_ws++) {
|
||
|
ras_y[i_ws + 32] = sis_y[i_ws] ;
|
||
|
}
|
||
|
|
||
|
c3_ws siv_ws = siv_y[0];
|
||
|
secp256k1_ecdsa_recoverable_signature sig_u;
|
||
|
memset( (void *) & sig_u, 0, sizeof(secp256k1_ecdsa_recoverable_signature) );
|
||
|
c3_ws ret = secp256k1_ecdsa_recoverable_signature_parse_compact(ctx_u, /* IN: context */
|
||
|
& sig_u, /* OUT: sig */
|
||
|
ras_y, /* IN: r/s */
|
||
|
siv_ws); /* IN: v */
|
||
|
if (1 != ret) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
/* turn sign into puk_u */
|
||
|
secp256k1_pubkey puk_u;
|
||
|
memset((void *) & puk_u, 0, sizeof(secp256k1_pubkey) );
|
||
|
ret = secp256k1_ecdsa_recover(ctx_u, /* IN: context */
|
||
|
& puk_u, /* OUT: pub key */
|
||
|
& sig_u, /* IN: signature */
|
||
|
(const c3_y *) & has); /* IN: message has */
|
||
|
|
||
|
if (1 != ret) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
/* convert puk_u into serialized form that we can get x,y out of */
|
||
|
c3_y puk_y[65];
|
||
|
size_t outputlen = 65;
|
||
|
memset((void *) puk_y, 0, 65);
|
||
|
|
||
|
ret = secp256k1_ec_pubkey_serialize( ctx_u, /* IN: */
|
||
|
puk_y, /* OUT: */
|
||
|
& outputlen, /* OUT: */
|
||
|
& puk_u, /* IN: */
|
||
|
SECP256K1_EC_UNCOMPRESSED); /* IN: flags */
|
||
|
|
||
|
if (1 != ret) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
/* in file
|
||
|
subprojects/secp256k1/src/eckey_impl.h
|
||
|
func
|
||
|
secp256k1_eckey_puk_u_parse()
|
||
|
we can see
|
||
|
byte 0: signal bits (???)
|
||
|
bytes 1-32: x
|
||
|
bytes 33-64: y
|
||
|
|
||
|
convert endianness while we're at it */
|
||
|
|
||
|
c3_y x_y[32];
|
||
|
for (i_ws = 0; i_ws < 32; i_ws++) {
|
||
|
x_y[i_ws] = puk_y[32 - i_ws];
|
||
|
}
|
||
|
u3_noun x = u3i_bytes(32, x_y);
|
||
|
|
||
|
c3_y y_y[32];
|
||
|
for (i_ws = 0; i_ws < 32; i_ws++) {
|
||
|
y_y[i_ws] = puk_y[64 - i_ws];
|
||
|
}
|
||
|
u3_noun y = u3i_bytes(32, y_y);
|
||
|
|
||
|
/* returns x,y */
|
||
|
return(u3nc(x, y));
|
||
|
}
|
||
|
|
||
|
|
||
|
u3_noun
|
||
|
u3we_make(u3_noun cor)
|
||
|
{
|
||
|
u3_noun has, prv;
|
||
|
if ( (c3n == u3r_mean(cor,
|
||
|
u3x_sam_2, &has,
|
||
|
u3x_sam_3, &prv,
|
||
|
0)) ||
|
||
|
(c3n == u3ud(has)) ||
|
||
|
(c3n == u3ud(prv)) )
|
||
|
{
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
} else {
|
||
|
return u3qe_make(has, prv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
u3_noun
|
||
|
u3qe_make(u3_atom has,
|
||
|
u3_atom prv)
|
||
|
{
|
||
|
|
||
|
c3_y hel_y[32]; /* hash, little endian */
|
||
|
c3_y heb_y[32]; /* hash, big endian */
|
||
|
u3r_bytes(0, 32, hel_y, has);
|
||
|
byte_reverse(hel_y, heb_y, 32);
|
||
|
|
||
|
c3_y pel_y[32]; /* priv key, little endian */
|
||
|
c3_y peb_y[32]; /* priv key, big endian */
|
||
|
u3r_bytes(0, 32, pel_y, prv);
|
||
|
byte_reverse(pel_y, peb_y, 32);
|
||
|
|
||
|
|
||
|
c3_ws ret_ws;
|
||
|
c3_y neb_y[32]; /* nonce */
|
||
|
ret_ws = secp256k1_nonce_function_rfc6979(neb_y, /* OUT: return arg for nonce */
|
||
|
(const c3_y *) heb_y, /* IN: message / hash */
|
||
|
(const c3_y *) peb_y, /* IN: key32 */
|
||
|
NULL, /* IN: algorithm (NULL == ECDSA) */
|
||
|
(void *) NULL, /* IN: arbitrary data pointer (unused) */
|
||
|
0); /* IN: attempt number (0 == normal) */
|
||
|
if (1 != ret_ws) {
|
||
|
fprintf(stderr, "\rsecp jet: crypto package error\n");
|
||
|
return u3m_bail(c3__exit);
|
||
|
}
|
||
|
|
||
|
c3_y nel_y[32];
|
||
|
byte_reverse(neb_y, nel_y, 32);
|
||
|
u3_noun non = u3i_words(8, (const c3_w*) nel_y);
|
||
|
return(non);
|
||
|
}
|
||
|
|