2022-09-09 06:47:49 +00:00
|
|
|
#include "fstoken.h"
|
|
|
|
|
|
|
|
void fstoken_keygen(byte* ikm, byte* sk_byte, byte* pk_byte){
|
|
|
|
blst_scalar sk;
|
|
|
|
blst_p2 pk;
|
|
|
|
blst_p2_affine pk_affine;
|
|
|
|
|
|
|
|
blst_keygen(&sk, ikm, 32, 0, 0); // generate a secret key from IKM
|
|
|
|
blst_bendian_from_scalar(sk_byte, &sk); // convert it to 32 big-endian bytes in sk_byte to return
|
|
|
|
|
|
|
|
blst_sk_to_pk_in_g2(&pk, &sk); // get a public key from the secret key
|
|
|
|
blst_p2_to_affine(&pk_affine, &pk); // convert it to an affine point, which is what most uses of the public key use
|
|
|
|
blst_p2_affine_compress(pk_byte, &pk_affine); // compress it to 96 bytes in pk_byte to return
|
|
|
|
}
|
|
|
|
|
|
|
|
void fstoken_get_pk_from_sk(byte* sk_byte, byte* pk_byte){
|
|
|
|
blst_p2 pk;
|
|
|
|
blst_p2_affine pk_affine;
|
|
|
|
blst_scalar sk;
|
|
|
|
|
2022-11-24 06:49:46 +00:00
|
|
|
blst_scalar_from_bendian(&sk, sk_byte); //convert the SK to a blst_scalar
|
|
|
|
blst_sk_to_pk_in_g2(&pk, &sk); // get a PK from it
|
|
|
|
blst_p2_to_affine(&pk_affine, &pk); // convert the PK to affine
|
|
|
|
blst_p2_affine_compress(pk_byte, &pk_affine); // compress the PK
|
|
|
|
}
|
|
|
|
|
|
|
|
void fstoken_gen_request(fstoken_request* req, fstoken_request_descriptor* reqdesc, fstoken_target_descriptor* target, byte* token_id, byte* blinding_factor){
|
|
|
|
byte debug_print_buf[256]; //get rid of this
|
|
|
|
|
|
|
|
int targ_idbytes;
|
|
|
|
blst_p1 hash, blinded_id;
|
|
|
|
blst_scalar blinding_r;
|
|
|
|
|
|
|
|
targ_idbytes = (target->idbits + 7)/8;
|
|
|
|
|
|
|
|
//printf("IDBYTES_MAX: %i, IDBYTES for target: %i\n", IDBYTES_MAX, targ_idbytes);
|
|
|
|
|
|
|
|
memcpy(&reqdesc->blinding_factor, blinding_factor, 32);
|
|
|
|
|
|
|
|
bit_truncated_copy(token_id, reqdesc->token_id, IDBYTES_MAX, target->idbits); //cut down the supplied token ID to match the target's IDBITS value
|
|
|
|
|
|
|
|
//debug_print_bytes("Working with ID: ", reqdesc->token_id, IDBYTES_MAX);
|
|
|
|
//debug_print_bytes("Working with Blinding Factor: ", blinding_factor, 32);
|
|
|
|
|
|
|
|
// Hash the Token ID to a BLST P1
|
|
|
|
blst_hash_to_g1(&hash, token_id, targ_idbytes, FSTOKEN_DST, strlen((char *) FSTOKEN_DST), target->pk, 96);
|
|
|
|
|
|
|
|
// Get the blinding factor as a BLST scalar
|
|
|
|
blst_scalar_from_bendian(&blinding_r, blinding_factor);
|
|
|
|
|
|
|
|
blst_p1_serialize(debug_print_buf, &hash);
|
|
|
|
//debug_print_bytes("HASH: ", debug_print_buf, 96);
|
|
|
|
|
|
|
|
// Sign the hash as if the blinding factor was a private key
|
|
|
|
blst_sign_pk_in_g2(&blinded_id, &hash, &blinding_r);
|
|
|
|
|
|
|
|
// Compress the resulting signature and put it in the signature request
|
|
|
|
blst_p1_compress(req->id_blind, &blinded_id);
|
|
|
|
|
|
|
|
//debug_print_bytes("Blinded and compressed for wire: ", req->id_blind, 48);
|
|
|
|
}
|
|
|
|
|
|
|
|
int fstoken_sign_request(fstoken_request* req, fstoken_signature* sig, fstoken_target_descriptor* targ){
|
|
|
|
blst_scalar sk;
|
|
|
|
blst_p1 req_id, signature;
|
|
|
|
blst_p1_affine req_id_affine;
|
|
|
|
|
|
|
|
// get the secret key as a scalar
|
|
|
|
blst_scalar_from_bendian(&sk, targ->sk);
|
|
|
|
|
|
|
|
// Deserialize the request - it's already a serialized P1 point, we don't need to (literally) rehash it
|
|
|
|
blst_p1_uncompress(&req_id_affine, req->id_blind);
|
|
|
|
|
|
|
|
// i do not know why deserializing always gives you affine points
|
|
|
|
blst_p1_from_affine(&req_id, &req_id_affine);
|
|
|
|
|
|
|
|
// Return 1 if the point isn't in G1, this is basically a sanity check
|
|
|
|
if(!blst_p1_in_g1(&req_id)) return 1;
|
|
|
|
|
|
|
|
// sign with it
|
|
|
|
blst_sign_pk_in_g2(&signature, &req_id, &sk);
|
|
|
|
|
|
|
|
// Compress and print the signature
|
|
|
|
blst_p1_compress(sig->signature, &signature);
|
|
|
|
|
|
|
|
//debug_print_bytes("Compressed signature:", sig->signature, 48);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fstoken_gen_token(fstoken_request_descriptor* req, fstoken_signature* sig, fstoken_token* token){
|
|
|
|
blst_p1 returned_signature, unblinded_signature;
|
|
|
|
blst_p1_affine returned_signature_affine;
|
|
|
|
blst_scalar blinding_r, inverse_r;
|
|
|
|
|
|
|
|
// We now have the signature back. returned_signature is a blst_p1_affine because this is pk_in_g2.
|
|
|
|
blst_p1_uncompress(&returned_signature_affine, sig->signature);
|
|
|
|
|
|
|
|
// Convert the decompressed returned signature from an affine to a P1
|
|
|
|
blst_p1_from_affine(&returned_signature, &returned_signature_affine);
|
|
|
|
|
|
|
|
// Confirm the signature point is in the G1 group
|
|
|
|
if(!blst_p1_in_g1(&returned_signature)) return 1;
|
|
|
|
|
|
|
|
// Retreive the blinding factor and take its inverse
|
|
|
|
blst_scalar_from_bendian(&blinding_r, req->blinding_factor);
|
|
|
|
blst_sk_inverse(&inverse_r, &blinding_r);
|
|
|
|
|
|
|
|
//print_scalar("Inverse R: ", &inverse_r);
|
|
|
|
|
|
|
|
// Unblind the signature using the inverse of the blinding factor
|
|
|
|
blst_sign_pk_in_g2(&unblinded_signature, &returned_signature, &inverse_r);
|
|
|
|
|
|
|
|
memcpy(token->token_id, req->token_id, IDBYTES_MAX);
|
|
|
|
blst_p1_compress(token->signature, &unblinded_signature);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-21 20:46:42 +00:00
|
|
|
// 0 = valid token, 1 = signature check failed, 2 = signature point invalid
|
|
|
|
int fstoken_verify_token(fstoken_token* token, fstoken_target_descriptor* targ){
|
|
|
|
int targ_idbytes;
|
|
|
|
blst_p1_affine sig;
|
|
|
|
blst_p2_affine pk;
|
|
|
|
|
|
|
|
blst_p1_uncompress(&sig, token->signature);
|
|
|
|
blst_p2_uncompress(&pk, targ->pk);
|
|
|
|
|
|
|
|
BLST_ERROR returned;
|
|
|
|
|
|
|
|
if(!blst_p1_affine_in_g1(&sig)) return 2;
|
|
|
|
|
|
|
|
targ_idbytes = (targ->idbits + 7) / 8;
|
|
|
|
|
|
|
|
returned = blst_core_verify_pk_in_g2(&pk, &sig, 1, token->token_id, targ_idbytes, FSTOKEN_DST, strlen((char *) FSTOKEN_DST), targ->pk, 96);
|
|
|
|
|
|
|
|
return (returned != BLST_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2022-11-24 06:49:46 +00:00
|
|
|
void bit_truncated_copy(byte* src, byte* dest, size_t destlen, int bits){
|
|
|
|
uint8_t bitmask;
|
|
|
|
int bytes;
|
|
|
|
|
|
|
|
bytes = (bits + 7) / 8;
|
|
|
|
|
|
|
|
memset(dest, 0, destlen); //clear out the destination
|
|
|
|
memcpy(dest, src, bytes); //copy over as many bytes as are needed for the number of bits
|
|
|
|
|
|
|
|
//bitmask out the high (fsp is little-endian) bits of the destination if bits is not a round number of bytes
|
|
|
|
bitmask = 0xFF >> (bits % 8);
|
|
|
|
//printf("mask: 0x%0x\n", bitmask);
|
|
|
|
dest[bytes - 1] = dest[bytes - 1] & bitmask;
|
2023-03-21 20:46:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to get the length (in bytes) of an (unshortened) token for the target
|
|
|
|
int fstoken_target_token_length(fstoken_target_descriptor* target){
|
|
|
|
return 48 + (target->idbits + 7) / 8;
|
2022-09-09 06:47:49 +00:00
|
|
|
}
|