Way more of the token lifecycle implemented, there's base64 now
This commit is contained in:
parent
af0ab2d19d
commit
f35923ab74
10 changed files with 851 additions and 33 deletions
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
|
BIN
blindsig2
Executable file
BIN
blindsig2
Executable file
Binary file not shown.
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
|
${CC} ${CFLAGS} -c blst/assembly.S
|
||||||
${AR} rc libblst.a server.o assembly.o
|
${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 "blst/blst.h"
|
||||||
#include "debugprint.h"
|
#include "debugprint.h"
|
||||||
#include "fstoken.h"
|
#include "fstoken.h"
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
char* token_path;
|
char* token_path;
|
||||||
|
|
||||||
|
@ -22,13 +23,18 @@ void print_help(char* name){
|
||||||
|
|
||||||
printf("%s help - display this help\n", name);
|
printf("%s help - display this help\n", name);
|
||||||
printf("%s path - display your token path\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 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 keypair [targ] using the default token format (128/256) and the system true randomness source\n", 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 keypair [targ] using token format [tfs] 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 keypair [targ] using token format specifier [tfs] and 32-byte hexadecimal seed [ikm]\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 keydump [targ] - dump Public and, if available, Secret Keys for target [targ]\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 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){
|
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);
|
||||||
strcat(*targ_path, ".target");
|
strcat(*targ_path, ".target");
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
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;
|
FILE *targ_file;
|
||||||
char *targ_path;
|
char *targ_path;
|
||||||
|
|
||||||
|
@ -58,16 +63,99 @@ int get_target(char* targ_name, target_descriptor* target){ // pk/sk/idbits/hasb
|
||||||
if(!targ_file){
|
if(!targ_file){
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
fread(target, sizeof(target_descriptor), 1, targ_file);
|
fread(target, sizeof(fstoken_target_descriptor), 1, targ_file);
|
||||||
fclose(targ_file);
|
fclose(targ_file);
|
||||||
|
|
||||||
// 0 = no keys (bad target), 1 = PK only, 2 = PK+SK
|
// 0 = no keys (bad target), 1 = PK only, 2 = PK+SK
|
||||||
return target->sk_available + 1;
|
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;
|
int targ_status;
|
||||||
target_descriptor target;
|
fstoken_target_descriptor target;
|
||||||
|
|
||||||
targ_status = get_target(targ_name, &target);
|
targ_status = get_target(targ_name, &target);
|
||||||
|
|
||||||
|
@ -77,12 +165,12 @@ int keydump(char* targ_name){
|
||||||
return 1;
|
return 1;
|
||||||
case 1:
|
case 1:
|
||||||
printf("Public Key available - can verify and request for this target\n");
|
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;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
printf("Secret Key and Public Key available - can verify, request, and sign for this target.\n");
|
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("Secret Key: ", target.sk, 32, '\0');
|
||||||
print_bytes("Public Key: ", target.pk, 96);
|
print_bytes("Public Key: ", target.pk, 96, '\0');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +179,40 @@ int keydump(char* targ_name){
|
||||||
return 0;
|
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;
|
FILE *key_file;
|
||||||
byte sk[32];
|
byte sk[32];
|
||||||
byte pk[96];
|
byte pk[96];
|
||||||
|
@ -130,7 +251,7 @@ int keydump(char* targ_name){
|
||||||
int keygen(char* target, byte* ikm, int idbits, int hashbits){
|
int keygen(char* target, byte* ikm, int idbits, int hashbits){
|
||||||
char *targ_path;
|
char *targ_path;
|
||||||
FILE *targ_file;
|
FILE *targ_file;
|
||||||
target_descriptor targ;
|
fstoken_target_descriptor targ;
|
||||||
|
|
||||||
debug_print_bytes("IKM: ", ikm, 32);
|
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");
|
printf("Could not open target file. Exiting.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
fwrite(&targ, sizeof(targ), 1, targ_file);
|
fwrite(&targ, sizeof(fstoken_target_descriptor), 1, targ_file);
|
||||||
fclose(targ_file);
|
fclose(targ_file);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -179,7 +300,7 @@ int list_targets(bool verbose){
|
||||||
int n, targname_len;
|
int n, targname_len;
|
||||||
struct dirent **files;
|
struct dirent **files;
|
||||||
char *targdir_path, *targ_path, *targ_name;
|
char *targdir_path, *targ_path, *targ_name;
|
||||||
target_descriptor targ;
|
fstoken_target_descriptor targ;
|
||||||
|
|
||||||
targdir_path = malloc(strlen(token_path) + 9);
|
targdir_path = malloc(strlen(token_path) + 9);
|
||||||
if(targdir_path == NULL) return 1;
|
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
|
// mostly boring command line parsing
|
||||||
int main(int argc, char *argv[]){
|
int main(int argc, char *argv[]){
|
||||||
token_path = getenv("FEMTOSTAR_TOKEN_PATH");
|
token_path = getenv("FEMTOSTAR_TOKEN_PATH");
|
||||||
|
@ -259,6 +388,7 @@ int main(int argc, char *argv[]){
|
||||||
|
|
||||||
if(argc > 5){
|
if(argc > 5){
|
||||||
printf("Too many arguments. Exiting.\n");
|
printf("Too many arguments. Exiting.\n");
|
||||||
|
return(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure there's a target name
|
// Make sure there's a target name
|
||||||
|
@ -292,18 +422,18 @@ int main(int argc, char *argv[]){
|
||||||
return 1;
|
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);
|
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
|
// Make sure there's a target name
|
||||||
if(argc < 3){
|
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(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return keydump(argv[2]);
|
return targinfo(argv[2]);
|
||||||
}/*else if(strcmp(argv[1], "keyrepair") == 0){
|
}/*else if(strcmp(argv[1], "keyrepair") == 0){
|
||||||
// Make sure there's a target name
|
// Make sure there's a target name
|
||||||
if(argc < 3){
|
if(argc < 3){
|
||||||
|
@ -312,5 +442,64 @@ int main(int argc, char *argv[]){
|
||||||
}
|
}
|
||||||
|
|
||||||
return keyrepair(argv[2]);
|
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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include "debugprint.h"
|
#include "debugprint.h"
|
||||||
#include "blst/blst.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);
|
printf("%s", label);
|
||||||
for(int i=0;i<length;i++){
|
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");
|
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){
|
void debug_print_bytes(__attribute__((unused)) const char* label, __attribute__((unused)) void *toprint, __attribute__((unused)) int length){
|
||||||
#ifdef INSECURE_CTM_DEBUG_PRINT
|
#ifdef INSECURE_CTM_DEBUG_PRINT
|
||||||
print_bytes(label, toprint, length);
|
print_bytes(label, toprint, length, ' ');
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,10 @@
|
||||||
// Uncomment the line below to enable debug prints (including private keys!) - use for development only, this is insecure
|
// Uncomment the line below to enable debug prints (including private keys!) - use for development only, this is insecure
|
||||||
#define INSECURE_CTM_DEBUG_PRINT
|
#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 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 print_scalar(const char* label, blst_scalar *toprint);
|
||||||
void debug_print_scalar(__attribute__((unused)) const char* label, __attribute__((unused)) 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
|
#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_p2_affine pk_affine;
|
||||||
blst_scalar sk;
|
blst_scalar sk;
|
||||||
|
|
||||||
blst_scalar_from_bendian(&sk, sk_byte);
|
blst_scalar_from_bendian(&sk, sk_byte); //convert the SK to a blst_scalar
|
||||||
blst_sk_to_pk_in_g2(&pk, &sk);
|
blst_sk_to_pk_in_g2(&pk, &sk); // get a PK from it
|
||||||
blst_p2_to_affine(&pk_affine, &pk);
|
blst_p2_to_affine(&pk_affine, &pk); // convert the PK to affine
|
||||||
blst_p2_affine_compress(pk_byte, &pk_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 "blst/blst.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.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_DEFAULT 128
|
||||||
#define IDBITS_MAX 256
|
#define IDBITS_MAX 256
|
||||||
#define HASHBITS_DEFAULT 256
|
#define HASHBITS_DEFAULT 256
|
||||||
#define HASHBITS_MAX 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.
|
// 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)
|
// 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!
|
// 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{
|
typedef struct{
|
||||||
#pragma scalar_storage_order little-endian
|
#pragma scalar_storage_order little-endian
|
||||||
|
|
||||||
|
@ -22,9 +39,42 @@ typedef struct{
|
||||||
uint16_t hashbits;
|
uint16_t hashbits;
|
||||||
byte sk[32];
|
byte sk[32];
|
||||||
byte pk[96];
|
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_keygen(byte* ikm, byte* sk_byte, byte* pk_byte);
|
||||||
void fstoken_get_pk_from_sk(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
|
#endif
|
Loading…
Reference in a new issue