Way more of the token lifecycle implemented, there's base64 now
This commit is contained in:
parent
af0ab2d19d
commit
f35923ab74
214
base64.c
Normal file
214
base64.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
This is derived from the libb64 project, which has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
int base64_decode_value(char value_in)
|
||||
{
|
||||
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static const char decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in >= decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
const char* codechar = code_in;
|
||||
char* plainchar = plaintext_out;
|
||||
char fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
// fall through
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
// fall through
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
// fall through
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return encoding[(int)value_in];
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_A:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
// fall through
|
||||
case step_B:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
// fall through
|
||||
case step_C:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
|
||||
#ifdef BASE64_USE_NEWLINES
|
||||
if (state_in->stepcount == BASE64_CHARS_PER_LINE/4)
|
||||
{
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
#ifdef BASE64_USE_NEWLINES
|
||||
*codechar++ = '\n';
|
||||
#endif
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
// These "one-shot" functions were not in the original project, I have added them and still need to be sure they work
|
||||
|
||||
// note - output MUST be large enough to accomodate the base64'd input (i.e. must be 1/3 bigger than input_len plus the null terminator - 65 bytes for 48-byte input)
|
||||
void base64_encode_oneshot(char* input, int input_len, char* output){
|
||||
base64_encodestate s;
|
||||
char* c = output;
|
||||
|
||||
base64_init_encodestate(&s);
|
||||
c += base64_encode_block(input, input_len, output, &s);
|
||||
c += base64_encode_blockend(c, &s);
|
||||
*c = '\0';
|
||||
}
|
||||
|
||||
// note - output MUST be large enough to accomodate the decoded input (i.e. must be at least 3/4 the size of the input - 48 bytes for 64-byte input)
|
||||
void base64_decode_oneshot(char* input, int input_len, char* output){
|
||||
base64_decodestate s;
|
||||
|
||||
base64_init_decodestate(&s);
|
||||
base64_decode_block(input, input_len, output, &s);
|
||||
}
|
52
base64.h
Normal file
52
base64.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This is derived from the libb64 project, which has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#define BASE64_CHARS_PER_LINE 4
|
||||
//#define BASE64_USE_NEWLINES
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
void base64_encode_oneshot(char* input, int input_len, char* output);
|
||||
void base64_decode_oneshot(char* input, int input_len, char* output);
|
||||
|
||||
#endif
|
188
blindsig2.c
Normal file
188
blindsig2.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
// This is a (very rough) test of BLST blind signatures based on run.me from BLST's Python example code
|
||||
// Do not trust this to be secure, also this doesn't do a lot of the sanity checking yet
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include "blst/blst.h"
|
||||
|
||||
const byte dst[] = "MY-DST";
|
||||
double time_taken;
|
||||
clock_t t;
|
||||
|
||||
byte signer_private_key[32];
|
||||
byte signer_public_key[96];
|
||||
|
||||
void printbytes(byte *toprint, int length){
|
||||
for(int i=0;i<length;i++){
|
||||
printf("%.2x ", toprint[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void signer_key_setup(){
|
||||
blst_scalar sk;
|
||||
blst_p2 pk;
|
||||
blst_p2_affine pk_affine;
|
||||
|
||||
byte myikm[32] = {'*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*'};
|
||||
|
||||
// On signer's side:
|
||||
printf("IKM: ");
|
||||
printbytes(myikm, 32);
|
||||
|
||||
blst_keygen(&sk, myikm, 32, 0, 0);
|
||||
|
||||
blst_bendian_from_scalar(signer_private_key, &sk);
|
||||
printf("Secret Key: ");
|
||||
printbytes(signer_private_key, 32);
|
||||
|
||||
blst_sk_to_pk_in_g2(&pk, &sk);
|
||||
|
||||
blst_p2_to_affine(&pk_affine, &pk);
|
||||
|
||||
blst_p2_affine_compress(signer_public_key, &pk_affine);
|
||||
printf("Compressed Public Key (affine): ");
|
||||
printbytes(signer_public_key, 96);
|
||||
}
|
||||
|
||||
void signer(byte *compressed_signature, byte *msg_for_wire){
|
||||
blst_scalar sk;
|
||||
blst_p1 msg, signature;
|
||||
blst_p1_affine msg_affine;
|
||||
byte debug_print_buf[256];
|
||||
|
||||
// get the secret key as a scalar
|
||||
blst_scalar_from_bendian(&sk, signer_private_key);
|
||||
|
||||
// Deserialize the message - it's already a serialized P1 point, we don't need to (literally) rehash it
|
||||
blst_p1_uncompress(&msg_affine, msg_for_wire);
|
||||
|
||||
// i do not know why deserializing always gives you affine points
|
||||
blst_p1_from_affine(&msg, &msg_affine);
|
||||
|
||||
// Confirm the message point is in the G1 group
|
||||
assert(blst_p1_in_g1(&msg));
|
||||
|
||||
// sign with it
|
||||
blst_sign_pk_in_g2(&signature, &msg, &sk);
|
||||
|
||||
// Serialize and print the signature
|
||||
blst_p1_serialize(debug_print_buf, &signature);
|
||||
printf("Signature: ");
|
||||
printbytes(debug_print_buf, 96);
|
||||
|
||||
// Compress and print the signature
|
||||
blst_p1_compress(compressed_signature, &signature);
|
||||
printf("Compressed Signature: ");
|
||||
printbytes(compressed_signature, 48);
|
||||
}
|
||||
|
||||
void verifier(byte *compressed_signature, byte *msg){
|
||||
blst_p1_affine sig;
|
||||
blst_p2_affine pk;
|
||||
|
||||
blst_p1_uncompress(&sig, compressed_signature);
|
||||
blst_p2_uncompress(&pk, signer_public_key);
|
||||
|
||||
BLST_ERROR returned;
|
||||
|
||||
// TODO: check if in g2 group
|
||||
|
||||
returned = blst_core_verify_pk_in_g2(&pk, &sig, 1, msg, 16, dst, strlen((char *) dst), signer_public_key, 96);
|
||||
|
||||
if(returned == BLST_SUCCESS){
|
||||
printf("Verified!\n");
|
||||
}else{
|
||||
printf("Not verified!\n");
|
||||
}
|
||||
}
|
||||
|
||||
// main is the "user" in this test
|
||||
int main(){
|
||||
byte debug_print_buf[256];
|
||||
byte compressed_blinded_signature[48];
|
||||
byte compressed_signature[48];
|
||||
byte msg[16] = {'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A'};
|
||||
byte blinding_r_bytes[32] = {'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R'};
|
||||
blst_scalar blinding_r, inverse_blinding_r;
|
||||
blst_p1 hash, msg_for_wire;
|
||||
byte msg_for_wire_bytes[96];
|
||||
blst_p1_affine returned_signature_affine;
|
||||
blst_p1 returned_signature, unblinded_signature;
|
||||
|
||||
printf("msg is now ", msg);
|
||||
printbytes(msg, 16);
|
||||
printf("\n");
|
||||
|
||||
// Set up the signer's keys first so that we can know its public key
|
||||
signer_key_setup();
|
||||
|
||||
// Get a hash of the message - we put the signer's public key in aug here, I don't know why
|
||||
blst_hash_to_g1(&hash, msg, 16, dst, strlen((char *) dst), signer_public_key, 96);
|
||||
|
||||
printf("HASH: ");
|
||||
blst_p1_serialize(debug_print_buf, &hash);
|
||||
printbytes(debug_print_buf, 96);
|
||||
|
||||
// Get a BLST scalar of your "random" (LOL) blinding factor r
|
||||
blst_scalar_from_bendian(&blinding_r, blinding_r_bytes);
|
||||
|
||||
printf("R BYTES: ");
|
||||
printbytes(blinding_r_bytes, 32);
|
||||
|
||||
// Blind the message by signing it with the blinding factor R as if it was a secret key
|
||||
blst_sign_pk_in_g2(&msg_for_wire, &hash, &blinding_r);
|
||||
|
||||
// Serialize the blinded message to send it over the wire
|
||||
blst_p1_compress(msg_for_wire_bytes, &msg_for_wire);
|
||||
|
||||
printf("Blinded and compressed for wire: ");
|
||||
printbytes(msg_for_wire_bytes, 48);
|
||||
|
||||
// Send the message off to be signed and get the results back
|
||||
signer(compressed_blinded_signature, msg_for_wire_bytes);
|
||||
|
||||
printf("COMPRESSED BLINDED SIG: ");
|
||||
printbytes(compressed_blinded_signature, 48);
|
||||
|
||||
// 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, compressed_blinded_signature);
|
||||
|
||||
// Convert the uncompressed 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
|
||||
assert(blst_p1_in_g1(&returned_signature));
|
||||
|
||||
printf("RETURNED SIGNATURE: ");
|
||||
blst_p1_serialize(debug_print_buf, &returned_signature);
|
||||
printbytes(debug_print_buf, 96);
|
||||
|
||||
// Get the inverse of R. We'll need this to unblind the signature.
|
||||
blst_sk_inverse(&inverse_blinding_r, &blinding_r);
|
||||
|
||||
// Print the inverse of R
|
||||
printf("INVERSE R: ");
|
||||
blst_bendian_from_scalar(debug_print_buf, &inverse_blinding_r);
|
||||
printbytes(debug_print_buf, 32);
|
||||
|
||||
// Sign the blinded signature we get back from the signer with the inverse of the blinding factor
|
||||
blst_sign_pk_in_g2(&unblinded_signature, &returned_signature, &inverse_blinding_r);
|
||||
|
||||
blst_p1_compress(compressed_signature, &unblinded_signature);
|
||||
|
||||
printf("UNBLINDED SIGNATURE: ");
|
||||
printbytes(compressed_signature, 48);
|
||||
|
||||
// uncomment to change the message and have the signature fail the check
|
||||
//msg[8] = ' ';
|
||||
|
||||
printf("msg is now ", msg);
|
||||
printbytes(msg, 16);
|
||||
printf("\n");
|
||||
|
||||
// Now on verifier's side (after compressed_signature, serialized_public_key, and msg are passed over the network)
|
||||
verifier(compressed_signature, msg);
|
||||
}
|
2
build.sh
2
build.sh
|
@ -8,4 +8,4 @@ ${CC} ${CFLAGS} -c blst/server.c
|
|||
${CC} ${CFLAGS} -c blst/assembly.S
|
||||
${AR} rc libblst.a server.o assembly.o
|
||||
|
||||
${CC} ${CFLAGS} -o ctm ctm.c fstoken.c debugprint.c libblst.a
|
||||
${CC} ${CFLAGS} -o ctm ctm.c fstoken.c debugprint.c base64.c libblst.a
|
235
ctm.c
235
ctm.c
|
@ -9,6 +9,7 @@
|
|||
#include "blst/blst.h"
|
||||
#include "debugprint.h"
|
||||
#include "fstoken.h"
|
||||
#include "base64.h"
|
||||
|
||||
char* token_path;
|
||||
|
||||
|
@ -22,13 +23,18 @@ void print_help(char* name){
|
|||
|
||||
printf("%s help - display this help\n", name);
|
||||
printf("%s path - display your token path\n", name);
|
||||
printf("%s list - list targets you have keys for - use \"%s list verbose\" to also display paths to the keys\n", name, name);
|
||||
printf("%s keygen [targ] - create a new target keypair [targ] using the default token format (128/256) and the system true randomness source\n", name);
|
||||
printf("%s keygen [targ] [tfs] - create a new target keypair [targ] using token format [tfs] and the system true randomness source\n", name);
|
||||
printf("%s keygen [targ] [tfs] [ikm] - create a new target keypair [targ] using token format specifier [tfs] and 32-byte hexadecimal seed [ikm]\n", name);
|
||||
printf("%s keydump [targ] - dump Public and, if available, Secret Keys for target [targ]\n", name);
|
||||
printf("%s list - list available targets - use \"%s list verbose\" to also display format specifiers and paths to target files\n", name, name);
|
||||
printf("%s keygen [targ] - create a new target [targ] using the default token format (128/256) and the system true randomness source\n", name);
|
||||
printf("%s keygen [targ] [tfs] - create a new target[targ] using token format [tfs] and the system true randomness source\n", name);
|
||||
printf("%s keygen [targ] [tfs] [ikm] - create a new target [targ] using token format specifier [tfs] and 32-byte hexadecimal seed [ikm]\n", name);
|
||||
printf("%s info [targ] - dump all available target information for target [targ], including keys\n", name);
|
||||
//printf("%s keyrepair [targ] - regenerate a missing Public Key for a target [targ] for which a Secret Key is available\n", name);
|
||||
printf("%s req [targ] - generate a token request for target [targ]\n", name);
|
||||
printf("%s strip [targin] [targout] - duplicate target [targin] as [targout], removing Secret Key (for distribution as a Public-Key-only target)\n", name);
|
||||
printf("%s ireq [targ] - interactively generate a Token Request Code for target [targ] using a random Token ID and Blinding Factor\n", name);
|
||||
printf("%s ireq [targ] [id] - interactively generate a Token Request Code for target [targ] using a set Token ID [id] and a random Blinding Factor\n", name);
|
||||
printf("%s ireq [targ] [id] [factor] - interactively generate a Token Request Code for target [targ] using a set Token ID [id] and Blinding Factor [factor]\n", name);
|
||||
printf("%s sign [targ] [req] - generate a signature for Token Request Code [req], on target [targ]\n", name);
|
||||
printf("%s bsign [targ] - bulk-sign token requests, accepting Token Request Codes on stdin and outputting signatures to stdout\n", name);
|
||||
}
|
||||
|
||||
int get_targ_path(char* target, char** targ_path){
|
||||
|
@ -44,11 +50,10 @@ int get_targ_path(char* target, char** targ_path){
|
|||
strcat(*targ_path, target);
|
||||
strcat(*targ_path, ".target");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_target(char* targ_name, target_descriptor* target){ // pk/sk/idbits/hasbits pointers can be NULL if you don't want to read those
|
||||
int get_target(char* targ_name, fstoken_target_descriptor* target){ // pk/sk/idbits/hasbits pointers can be NULL if you don't want to read those
|
||||
FILE *targ_file;
|
||||
char *targ_path;
|
||||
|
||||
|
@ -58,16 +63,99 @@ int get_target(char* targ_name, target_descriptor* target){ // pk/sk/idbits/hasb
|
|||
if(!targ_file){
|
||||
return 0;
|
||||
}
|
||||
fread(target, sizeof(target_descriptor), 1, targ_file);
|
||||
fread(target, sizeof(fstoken_target_descriptor), 1, targ_file);
|
||||
fclose(targ_file);
|
||||
|
||||
// 0 = no keys (bad target), 1 = PK only, 2 = PK+SK
|
||||
return target->sk_available + 1;
|
||||
}
|
||||
|
||||
int keydump(char* targ_name){
|
||||
// Fill a buffer dest of size dest_size with exactly (dest_size - 2) characters and a null terminator, returning 1 if the user supplies any other number
|
||||
int get_fixed_length(char* dest, size_t dest_size){
|
||||
dest[dest_size - 2] = '\0'; // Initialize this place, we're about to need to check it even if fgets never gets here
|
||||
fgets(dest, dest_size, stdin);
|
||||
if(dest[dest_size - 2] != '\n') return 1; //If there isn't a newline in the second-last character (i.e. the user specified too many) return 1
|
||||
|
||||
dest[dest_size - 2] = '\0'; // Cut it down to length and get rid of the newline we're now sure is in the right place
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sign_single(char* targ_name, char* req_b64){
|
||||
fstoken_request req;
|
||||
fstoken_target_descriptor targ;
|
||||
fstoken_signature sig;
|
||||
|
||||
base64_decode_oneshot((char*) req_b64, 64, (char*) req.id_blind);
|
||||
|
||||
//debug_print_bytes("Signature hex: ", req.id_blind, 48);
|
||||
|
||||
if(get_target(targ_name, &targ) != 2){
|
||||
printf("Target or private key unavailable. Are you sure you have a private key for this target?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(fstoken_sign_request(&req, &sig, &targ)){
|
||||
printf("Signing failed. Is the token request valid?\n");
|
||||
return 1;
|
||||
};
|
||||
|
||||
print_base64(sig.signature, 48);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int interactive_request(char* targ_name, byte* token_id, byte* blinding_factor){
|
||||
fstoken_request req;
|
||||
fstoken_request_descriptor reqdesc;
|
||||
fstoken_target_descriptor target;
|
||||
fstoken_signature sig;
|
||||
fstoken_token token;
|
||||
char req_base64_encoded[65];
|
||||
char sig_base64[66];
|
||||
int token_length;
|
||||
|
||||
if(!get_target(targ_name, &target)){
|
||||
printf("Invalid target %s\n", targ_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fstoken_gen_request(&req, &reqdesc, &target, token_id, blinding_factor);
|
||||
|
||||
base64_encode_oneshot((char*) req.id_blind, 48, req_base64_encoded);
|
||||
|
||||
printf("A Token Request Code has been generated. Provide this code to the signer. Once you have received a signature, enter it below to generate your token.\n");
|
||||
printf("Token Request Code: %s\n", req_base64_encoded);
|
||||
printf("Enter your signature: ");
|
||||
|
||||
if(get_fixed_length(sig_base64, sizeof(sig_base64))){
|
||||
printf("Invalid signature length\n");
|
||||
return 1;
|
||||
};
|
||||
|
||||
base64_decode_oneshot(sig_base64, 64, (char*) sig.signature);
|
||||
|
||||
//debug_print_bytes("Signature as hex: ", sig.signature, 48);
|
||||
|
||||
if(fstoken_gen_token(&reqdesc, &sig, &token)){
|
||||
printf("Failed to generate token.\n");
|
||||
return 1;
|
||||
};
|
||||
|
||||
token_length = 48 + (target.idbits + 7) / 8;
|
||||
|
||||
//debug_print_bytes("Token Unblinded Signature: ", token.signature, 48);
|
||||
//debug_print_bytes("TOKEN: ", &token, token_length);
|
||||
|
||||
printf("\nToken: ");
|
||||
print_base64(&token, token_length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int targinfo(char* targ_name){
|
||||
int targ_status;
|
||||
target_descriptor target;
|
||||
fstoken_target_descriptor target;
|
||||
|
||||
targ_status = get_target(targ_name, &target);
|
||||
|
||||
|
@ -77,12 +165,12 @@ int keydump(char* targ_name){
|
|||
return 1;
|
||||
case 1:
|
||||
printf("Public Key available - can verify and request for this target\n");
|
||||
print_bytes("Public Key: ", target.pk, 96);
|
||||
print_bytes("Public Key: ", target.pk, 96, '\0');
|
||||
break;
|
||||
case 2:
|
||||
printf("Secret Key and Public Key available - can verify, request, and sign for this target.\n");
|
||||
print_bytes("Secret Key: ", target.sk, 32);
|
||||
print_bytes("Public Key: ", target.pk, 96);
|
||||
print_bytes("Secret Key: ", target.sk, 32, '\0');
|
||||
print_bytes("Public Key: ", target.pk, 96, '\0');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -91,7 +179,40 @@ int keydump(char* targ_name){
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*int keyrepair(char* target){
|
||||
int striptarg(char* targ_name_in, char* targ_name_out){
|
||||
FILE *targ_out_file;
|
||||
fstoken_target_descriptor targ_out;
|
||||
char* targ_out_path;
|
||||
|
||||
if(!get_target(targ_name_in, &targ_out)){
|
||||
printf("Unknown input target. Exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(targ_out.sk_available == false){
|
||||
printf("Target %s already lacks a Secret Key - only targets with Secret Keys can be stripped.\n", targ_name_in);
|
||||
return 2;
|
||||
}
|
||||
|
||||
memset(targ_out.sk, 0x00, 32);
|
||||
targ_out.sk_available = false;
|
||||
|
||||
get_targ_path(targ_name_out, &targ_out_path);
|
||||
|
||||
targ_out_file = fopen(targ_out_path, "w");
|
||||
if(!targ_out_file){
|
||||
printf("Could not open output target file. Exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
fwrite(&targ_out, sizeof(fstoken_target_descriptor), 1, targ_out_file);
|
||||
fclose(targ_out_file);
|
||||
|
||||
printf("Public-Key-only target %s succesfully created from original target %s.\n", targ_name_in, targ_name_out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*int keyrepair(char* target){ // this is probably no longer necessary with single-file targets
|
||||
FILE *key_file;
|
||||
byte sk[32];
|
||||
byte pk[96];
|
||||
|
@ -130,7 +251,7 @@ int keydump(char* targ_name){
|
|||
int keygen(char* target, byte* ikm, int idbits, int hashbits){
|
||||
char *targ_path;
|
||||
FILE *targ_file;
|
||||
target_descriptor targ;
|
||||
fstoken_target_descriptor targ;
|
||||
|
||||
debug_print_bytes("IKM: ", ikm, 32);
|
||||
|
||||
|
@ -153,7 +274,7 @@ int keygen(char* target, byte* ikm, int idbits, int hashbits){
|
|||
printf("Could not open target file. Exiting.\n");
|
||||
return 1;
|
||||
}
|
||||
fwrite(&targ, sizeof(targ), 1, targ_file);
|
||||
fwrite(&targ, sizeof(fstoken_target_descriptor), 1, targ_file);
|
||||
fclose(targ_file);
|
||||
|
||||
return 0;
|
||||
|
@ -179,7 +300,7 @@ int list_targets(bool verbose){
|
|||
int n, targname_len;
|
||||
struct dirent **files;
|
||||
char *targdir_path, *targ_path, *targ_name;
|
||||
target_descriptor targ;
|
||||
fstoken_target_descriptor targ;
|
||||
|
||||
targdir_path = malloc(strlen(token_path) + 9);
|
||||
if(targdir_path == NULL) return 1;
|
||||
|
@ -233,6 +354,14 @@ void bendian_from_hex_string(byte* bendian, char* string, int length){
|
|||
}
|
||||
}
|
||||
|
||||
void lendian_from_hex_string(byte* lendian, char* string, int length){
|
||||
char byte[2];
|
||||
for(int i = (length-1); i >= 0; i--){
|
||||
memcpy(byte, &string[i*2], 2);
|
||||
lendian[i] = strtol(byte, 0, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// mostly boring command line parsing
|
||||
int main(int argc, char *argv[]){
|
||||
token_path = getenv("FEMTOSTAR_TOKEN_PATH");
|
||||
|
@ -259,6 +388,7 @@ int main(int argc, char *argv[]){
|
|||
|
||||
if(argc > 5){
|
||||
printf("Too many arguments. Exiting.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
// Make sure there's a target name
|
||||
|
@ -292,18 +422,18 @@ int main(int argc, char *argv[]){
|
|||
return 1;
|
||||
}
|
||||
|
||||
bendian_from_hex_string(ikm, argv[4], 64);
|
||||
lendian_from_hex_string(ikm, argv[4], 32);
|
||||
}
|
||||
|
||||
return keygen(argv[2], ikm, idbits, hashbits);
|
||||
}else if(strcmp(argv[1], "keydump") == 0){
|
||||
}else if(strcmp(argv[1], "info") == 0){
|
||||
// Make sure there's a target name
|
||||
if(argc < 3){
|
||||
fprintf(stderr, "A target name must be provided, e.g. %s keydump [targ]\n", argv[0]);
|
||||
fprintf(stderr, "A target name must be provided, e.g. %s info [targ]\n", argv[0]);
|
||||
return(1);
|
||||
}
|
||||
|
||||
return keydump(argv[2]);
|
||||
return targinfo(argv[2]);
|
||||
}/*else if(strcmp(argv[1], "keyrepair") == 0){
|
||||
// Make sure there's a target name
|
||||
if(argc < 3){
|
||||
|
@ -312,5 +442,64 @@ int main(int argc, char *argv[]){
|
|||
}
|
||||
|
||||
return keyrepair(argv[2]);
|
||||
}*/
|
||||
}*/else if(strcmp(argv[1], "strip") == 0){
|
||||
// Make sure there's a target name
|
||||
if(argc < 4){
|
||||
fprintf(stderr, "Two target names must be provided, e.g. %s strip [targin] [targout]\n", argv[0]);
|
||||
return(1);
|
||||
}
|
||||
|
||||
return striptarg(argv[2], argv[3]);
|
||||
}else if(strcmp(argv[1], "ireq") == 0){
|
||||
byte blinding_factor[32];
|
||||
byte token_id[IDBYTES_MAX];
|
||||
|
||||
// Make sure there's a target name
|
||||
if(argc < 3){
|
||||
fprintf(stderr, "A target name must be provided, e.g. %s req [targ]\n", argv[0]);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if(argc > 5){
|
||||
printf("Too many arguments. Exiting.\n");
|
||||
return(1);
|
||||
}
|
||||
|
||||
if(argc < 4){
|
||||
getrandom(token_id, IDBYTES_MAX, GRND_NONBLOCK);
|
||||
}else{
|
||||
if(strlen(argv[3]) > (IDBYTES_MAX * 2)){
|
||||
fprintf(stderr, "If providing a Token ID, it must be %i bits (%i hexadecimal digits) or less \n", IDBITS_MAX, IDBYTES_MAX * 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lendian_from_hex_string(token_id, argv[3], (strlen(argv[3]) + 1) / 2);
|
||||
}
|
||||
|
||||
// If no IKM is provided, use the system true randomness source
|
||||
if(argc < 5){
|
||||
getrandom(blinding_factor, 32, GRND_NONBLOCK);
|
||||
}else{
|
||||
if(strlen(argv[4]) != 64){
|
||||
fprintf(stderr, "If providing a blinding factor, it must be 32 bytes (64 hexadecimal digits)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
lendian_from_hex_string(blinding_factor, argv[4], 32);
|
||||
}
|
||||
|
||||
return interactive_request(argv[2], token_id, blinding_factor);
|
||||
}else if(strcmp(argv[1], "sign") == 0){
|
||||
if(argc != 4){
|
||||
fprintf(stderr, "Please provide a target [targ] and a Token Request Code [req]. See help.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(strlen(argv[3]) != 64){
|
||||
fprintf(stderr, "Token request code must be 48 bytes (64 base64 characters).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
sign_single(argv[2], argv[3]);
|
||||
}
|
||||
}
|
22
debugprint.c
22
debugprint.c
|
@ -1,18 +1,34 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "debugprint.h"
|
||||
#include "blst/blst.h"
|
||||
#include "base64.h"
|
||||
|
||||
void print_bytes(const char* label, void *toprint, int length){
|
||||
void print_bytes(const char* label, void *toprint, int length, char separator){
|
||||
printf("%s", label);
|
||||
for(int i=0;i<length;i++){
|
||||
printf("%.2x ", ((byte *) toprint)[i]);
|
||||
printf("%.2x", ((byte *) toprint)[i]);
|
||||
if(separator != '\0') printf("%c", separator);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void print_base64(void *toprint, int input_len){
|
||||
int buffer_size;
|
||||
char* output;
|
||||
|
||||
buffer_size = (((input_len + 2) / 3) * 4) + 1;
|
||||
|
||||
output = malloc(buffer_size); //create a buffer large enough for the base64'd input, including with padding if need be
|
||||
|
||||
base64_encode_oneshot(toprint, input_len, output);
|
||||
printf("%s\n", output);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void debug_print_bytes(__attribute__((unused)) const char* label, __attribute__((unused)) void *toprint, __attribute__((unused)) int length){
|
||||
#ifdef INSECURE_CTM_DEBUG_PRINT
|
||||
print_bytes(label, toprint, length);
|
||||
print_bytes(label, toprint, length, ' ');
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
// Uncomment the line below to enable debug prints (including private keys!) - use for development only, this is insecure
|
||||
#define INSECURE_CTM_DEBUG_PRINT
|
||||
|
||||
void print_bytes(const char* label, void *toprint, int length);
|
||||
void print_bytes(const char* label, void *toprint, int length, char separator);
|
||||
void debug_print_bytes(__attribute__((unused)) const char* label, __attribute__((unused)) void *toprint, __attribute__((unused)) int length);
|
||||
void print_scalar(const char* label, blst_scalar *toprint);
|
||||
void debug_print_scalar(__attribute__((unused)) const char* label, __attribute__((unused)) blst_scalar *toprint);
|
||||
void print_base64(void *toprint, int input_len);
|
||||
|
||||
#endif
|
116
fstoken.c
116
fstoken.c
|
@ -18,8 +18,116 @@ void fstoken_get_pk_from_sk(byte* sk_byte, byte* pk_byte){
|
|||
blst_p2_affine pk_affine;
|
||||
blst_scalar sk;
|
||||
|
||||
blst_scalar_from_bendian(&sk, sk_byte);
|
||||
blst_sk_to_pk_in_g2(&pk, &sk);
|
||||
blst_p2_to_affine(&pk_affine, &pk);
|
||||
blst_p2_affine_compress(pk_byte, &pk_affine);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
52
fstoken.h
52
fstoken.h
|
@ -3,16 +3,33 @@
|
|||
#include "blst/blst.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// only used for debugging
|
||||
#include <stdio.h>
|
||||
#include "debugprint.h"
|
||||
|
||||
// Note: In constrained environments where the IDBITS and HASHBITS values of the tokens in use are known, such as in fsp, these can be set exactly.
|
||||
// However, that may render some structs (such as the request descriptor) incompatible if moved to an environment where this is nonstandard.
|
||||
#define IDBITS_DEFAULT 128
|
||||
#define IDBITS_MAX 256
|
||||
#define HASHBITS_DEFAULT 256
|
||||
#define HASHBITS_MAX 256
|
||||
|
||||
#define IDBYTES_MAX (IDBITS_MAX + 7) / 8
|
||||
#define HASHBYTES_MAX (HASHBITS_MAX + 7) / 8
|
||||
|
||||
#define FSTOKEN_DST ((byte*) "MY-DST")
|
||||
|
||||
// For efficiency's sake, structs intended for use with the FemtoStar Protocol must be strictly-aligned without padding, with little-endian byte ordering.
|
||||
// This avoids wasting network capacity on padding, and avoids the performance impact of reordering on most modern platforms (most importantly x86 and RISC-V)
|
||||
// while ensuring compatibility with strict-alignment architectures. Take note: This is NOT typical network-order!
|
||||
|
||||
// Additionally, note that most structs do not explicitly include what target they are for (even the target descriptor!). Keeping what is for what target is
|
||||
// not a core part of fstoken, and is the responsibility of the software using fstoken (e.g. ctm or an implementation of fsp). This keeps tokens light in
|
||||
// situations where which target is relevant is unambiguous (e.g. in the session setup with a satellite, where the satellite is always the same target).
|
||||
|
||||
// A target_descriptor is a stored description of a target, which can be used to request, verify, and sometimes sign tokens for it.
|
||||
typedef struct{
|
||||
#pragma scalar_storage_order little-endian
|
||||
|
||||
|
@ -22,9 +39,42 @@ typedef struct{
|
|||
uint16_t hashbits;
|
||||
byte sk[32];
|
||||
byte pk[96];
|
||||
} target_descriptor;
|
||||
} fstoken_target_descriptor;
|
||||
|
||||
// A request descriptor is a structure held privately by the requester until they receive a signature for their request - it is then used to generate a token
|
||||
typedef struct{
|
||||
#pragma scalar_storage_order little-endian
|
||||
|
||||
byte blinding_factor[32];
|
||||
byte token_id[IDBYTES_MAX];
|
||||
} fstoken_request_descriptor;
|
||||
|
||||
// A request is a structure sent by the requester to the signer, to be signed and a signature returned. It need not be kept private.
|
||||
typedef struct{
|
||||
#pragma scalar_storage_order little-endian
|
||||
|
||||
byte id_blind[48];
|
||||
} fstoken_request;
|
||||
|
||||
// I'm unsure if these "single-item structs" make any sense - signatures in blst are independent of system or scalar_storage_order endianness anyway
|
||||
typedef struct{
|
||||
#pragma scalar_storage_order little-endian
|
||||
|
||||
byte signature[48];
|
||||
} fstoken_signature;
|
||||
|
||||
typedef struct{
|
||||
#pragma scalar_storage_order little-endian
|
||||
|
||||
byte signature[48];
|
||||
byte token_id[IDBYTES_MAX];
|
||||
} fstoken_token;
|
||||
|
||||
void fstoken_keygen(byte* ikm, byte* sk_byte, byte* pk_byte);
|
||||
void fstoken_get_pk_from_sk(byte* sk_byte, byte* pk_byte);
|
||||
void bit_truncated_copy(byte* src, byte* dest, size_t destlen, int bits);
|
||||
void fstoken_gen_request(fstoken_request* req, fstoken_request_descriptor* reqdesc, fstoken_target_descriptor* target, byte* token_id, byte* blinding_factor);
|
||||
int fstoken_sign_request(fstoken_request* req, fstoken_signature* sig, fstoken_target_descriptor* targ);
|
||||
int fstoken_gen_token(fstoken_request_descriptor* req, fstoken_signature* sig, fstoken_token* token);
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue