Store and parse BLISS private and public keys in DER and PEM format

Additionally generate SHA-1 fingerprints of raw BLISS subjectPublicKey
and subjectPublicKeyInfo objects.

Some basic functions used by the bliss_public_key class are shared
with the bliss_private_key class.
This commit is contained in:
Andreas Steffen 2014-11-04 20:51:33 +01:00
parent 37bfe44358
commit 56009f2001
9 changed files with 441 additions and 32 deletions

View File

@ -212,7 +212,7 @@
0x03 "e-voting"
0x05 "BLISS"
0x01 "keyType"
0x01 "blissPublicKey" OID_BLISS
0x01 "blissPublicKey" OID_BLISS_PUBLICKEY
0x02 "parameters"
0x01 "BLISS-I" OID_BLISS_I
0x02 "BLISS-II" OID_BLISS_II

View File

@ -144,6 +144,10 @@ enum cred_encoding_part_t {
CRED_PART_PKCS10_ASN1_DER,
/** a PGP encoded certificate */
CRED_PART_PGP_CERT,
/** a DER encoded BLISS public key */
CRED_PART_BLISS_PUB_ASN1_DER,
/** a DER encoded BLISS private key */
CRED_PART_BLISS_PRIV_ASN1_DER,
CRED_PART_END,
};

View File

@ -18,6 +18,9 @@
#include "bliss_fft.h"
#include <crypto/mgf1/mgf1_bitspender.h>
#include <asn1/asn1.h>
#include <asn1/asn1_parser.h>
#include <asn1/oid.h>
#define _GNU_SOURCE
#include <stdlib.h>
@ -26,6 +29,18 @@ typedef struct private_bliss_private_key_t private_bliss_private_key_t;
#define SECRET_KEY_TRIALS_MAX 30
/**
* Functions shared with bliss_public_key class
*/
extern uint32_t* bliss_public_key_from_asn1(chunk_t object, int n);
extern chunk_t bliss_public_key_encode(uint32_t *pubkey, int n);
extern chunk_t bliss_public_key_info_encode(int oid, uint32_t *pubkey, int n);
extern bool bliss_public_key_fingerprint(int oid, uint32_t *pubkey, int n,
cred_encoding_type_t type, chunk_t *fp);
/**
* Private data of a bliss_private_key_t object.
*/
@ -40,6 +55,21 @@ struct private_bliss_private_key_t {
*/
bliss_param_set_t *set;
/**
* BLISS secret key S1 (coefficients of polynomial f)
*/
int8_t *s1;
/**
* BLISS secret key S2 (coefficients of polynomial 2g + 1)
*/
int8_t *s2;
/**
* BLISS public key a (coefficients of polynomial (2g + 1)/f)
*/
uint32_t *a;
/**
* reference count
*/
@ -87,7 +117,14 @@ METHOD(private_key_t, get_keysize, int,
METHOD(private_key_t, get_public_key, public_key_t*,
private_bliss_private_key_t *this)
{
public_key_t *public = NULL;
public_key_t *public;
chunk_t pubkey;
pubkey = bliss_public_key_info_encode(this->set->oid, this->a,
this->set->n);
public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_BLISS,
BUILD_BLOB_ASN1_DER, pubkey, BUILD_END);
free(pubkey.ptr);
return public;
}
@ -96,17 +133,55 @@ METHOD(private_key_t, get_encoding, bool,
private_bliss_private_key_t *this, cred_encoding_type_t type,
chunk_t *encoding)
{
bool success = TRUE;
switch (type)
{
case PRIVKEY_ASN1_DER:
case PRIVKEY_PEM:
{
chunk_t s1_chunk, s2_chunk, pubkey;
bool success = TRUE;
*encoding = chunk_empty;
pubkey = bliss_public_key_encode(this->a, this->set->n);
return success;
/* Build private key as two polynomials with 8 bit coefficients */
s1_chunk = chunk_create(this->s1, this->set->n);
s2_chunk = chunk_create(this->s2, this->set->n);
*encoding = asn1_wrap(ASN1_SEQUENCE, "mmss",
asn1_build_known_oid(this->set->oid),
pubkey,
asn1_simple_object(ASN1_OCTET_STRING, s1_chunk),
asn1_simple_object(ASN1_OCTET_STRING, s2_chunk)
);
if (type == PRIVKEY_PEM)
{
chunk_t asn1_encoding = *encoding;
success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM,
NULL, encoding, CRED_PART_BLISS_PRIV_ASN1_DER,
asn1_encoding, CRED_PART_END);
chunk_clear(&asn1_encoding);
}
return success;
}
default:
return FALSE;
}
}
METHOD(private_key_t, get_fingerprint, bool,
private_bliss_private_key_t *this, cred_encoding_type_t type, chunk_t *fp)
{
bool success = FALSE;
bool success;
if (lib->encoding->get_cache(lib->encoding, type, this, fp))
{
return TRUE;
}
success = bliss_public_key_fingerprint(this->set->oid, this->a,
this->set->n, type, fp);
lib->encoding->cache(lib->encoding, type, this, *fp);
return success;
}
@ -123,6 +198,10 @@ METHOD(private_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
lib->encoding->clear_cache(lib->encoding, this);
free(this->s1);
free(this->s2);
free(this->a);
free(this);
}
}
@ -438,9 +517,8 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
private_bliss_private_key_t *this;
u_int key_size = 1;
int i, n, trials = 0;
int8_t *s1 = NULL, *s2 = NULL;
uint32_t *A, *S1, *S2;
uint16_t q;
uint32_t *a, *A, *S1, *S2;
bool success = FALSE;
bliss_param_set_t *set;
bliss_fft_t *fft;
@ -487,8 +565,8 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
/* Some vectors needed to derive the publi key */
S1 = malloc(n * sizeof(uint32_t));
S2 = malloc(n * sizeof(uint32_t));
A = malloc(n * sizeof(uint32_t));
a = malloc(n * sizeof(uint32_t));
A = malloc(n * sizeof(uint32_t));
this->a = malloc(n * sizeof(uint32_t));
/* Instantiate a true random generator */
rng = lib->crypto->create_rng(lib->crypto, RNG_TRUE);
@ -496,7 +574,7 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
/* Loop until we have an invertible polynomial s1 */
do
{
if (!create_secret(this, rng, &s1, &s2, &trials))
if (!create_secret(this, rng, &this->s1, &this->s2, &trials))
{
break;
}
@ -504,8 +582,8 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
/* Convert signed arrays to unsigned arrays before FFT */
for (i = 0; i < n; i++)
{
S1[i] = (s1[i] < 0) ? s1[i] + q : s1[i];
S1[i] = (s2[i] < 0) ? s2[i] + q : s2[i];
S1[i] = (this->s1[i] < 0) ? this->s1[i] + q : this->s1[i];
S2[i] = (this->s2[i] > 0) ? q - this->s2[i] : -this->s2[i];
}
fft->transform(fft, S1, S1, FALSE);
fft->transform(fft, S2, S2, FALSE);
@ -516,10 +594,10 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
if (S1[i] == 0)
{
DBG1(DBG_LIB, "S1[%d] is zero - s1 is not invertible", i);
free(s1);
s1 = NULL;
free(s2);
s2 = NULL;
free(this->s1);
free(this->s2);
this->s1 = NULL;
this->s2 = NULL;
success = FALSE;
break;
}
@ -534,13 +612,13 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
if (success)
{
fft->transform(fft, A, a, TRUE);
fft->transform(fft, A, this->a, TRUE);
DBG4(DBG_LIB, " i f g a F G A");
for (i = 0; i < n; i++)
{
DBG4(DBG_LIB, "%4d %3d %3d %5u %5u %5u %5u",
i, s1[i], s2[i], a[i], S1[i], S2[i], A[i]);
i, this->s1[i], this->s2[i], this->a[i], S1[i], S2[i], A[i]);
}
}
else
@ -551,9 +629,6 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
/* Cleanup */
fft->destroy(fft);
rng->destroy(rng);
free(s1);
free(s2);
free(a);
free(A);
free(S1);
free(S2);
@ -561,25 +636,108 @@ bliss_private_key_t *bliss_private_key_gen(key_type_t type, va_list args)
return success ? &this->public : NULL;
}
/**
* ASN.1 definition of a BLISS private key
*/
static const asn1Object_t privkeyObjects[] = {
{ 0, "BLISSPrivateKey", ASN1_SEQUENCE, ASN1_NONE }, /* 0 */
{ 1, "keyType", ASN1_OID, ASN1_BODY }, /* 1 */
{ 1, "public", ASN1_OCTET_STRING, ASN1_BODY }, /* 2 */
{ 1, "secret1", ASN1_OCTET_STRING, ASN1_BODY }, /* 3 */
{ 1, "secret2", ASN1_OCTET_STRING, ASN1_BODY }, /* 4 */
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
};
#define PRIV_KEY_TYPE 1
#define PRIV_KEY_PUBLIC 2
#define PRIV_KEY_SECRET1 3
#define PRIV_KEY_SECRET2 4
/**
* See header.
*/
bliss_private_key_t *bliss_private_key_load(key_type_t type, va_list args)
{
private_bliss_private_key_t *this;
chunk_t key = chunk_empty, object;
asn1_parser_t *parser;
bool success = FALSE;
int objectID, oid;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_BLOB_ASN1_DER:
key = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
default:
return NULL;
}
break;
}
if (key.len == 0)
{
return NULL;
}
this = bliss_private_key_create_empty();
parser = asn1_parser_create(privkeyObjects, key);
parser->set_flags(parser, FALSE, TRUE);
while (parser->iterate(parser, &objectID, &object))
{
switch (objectID)
{
case PRIV_KEY_TYPE:
oid = asn1_known_oid(object);
if (oid == OID_UNKNOWN)
{
goto end;
}
this->set = bliss_param_set_get_by_oid(oid);
if (this->set == NULL)
{
goto end;
}
break;
case PRIV_KEY_PUBLIC:
if (object.len != 2*this->set->n)
{
goto end;
}
this->a = bliss_public_key_from_asn1(object, this->set->n);
break;
case PRIV_KEY_SECRET1:
if (object.len != this->set->n)
{
goto end;
}
this->s1 = malloc(this->set->n);
memcpy(this->s1, object.ptr, object.len);
break;
case PRIV_KEY_SECRET2:
if (object.len != this->set->n)
{
goto end;
}
this->s2 = malloc(this->set->n);
memcpy(this->s2, object.ptr, object.len);
break;
}
}
success = parser->success(parser);
end:
parser->destroy(parser);
if (!success)
{
destroy(this);
return NULL;
}
return &this->public;
}

View File

@ -14,6 +14,11 @@
*/
#include "bliss_public_key.h"
#include "bliss_param_set.h"
#include <asn1/asn1.h>
#include <asn1/asn1_parser.h>
#include <asn1/oid.h>
typedef struct private_bliss_public_key_t private_bliss_public_key_t;
@ -27,9 +32,14 @@ struct private_bliss_public_key_t {
bliss_public_key_t public;
/**
* BLISS type
* BLISS signature parameter set
*/
u_int key_size;
bliss_param_set_t *set;
/**
* BLISS public key a (coefficients of polynomial (2g + 1)/f)
*/
uint32_t *a;
/**
* reference counter
@ -72,7 +82,104 @@ METHOD(public_key_t, encrypt_, bool,
METHOD(public_key_t, get_keysize, int,
private_bliss_public_key_t *this)
{
return this->key_size;
return this->set->strength;
}
/**
* Parse an ASN.1 OCTET STRING into an array of public key coefficients
*/
uint32_t* bliss_public_key_from_asn1(chunk_t object, int n)
{
uint32_t *pubkey;
uint16_t coeff;
u_char *pos;
int i;
pubkey = malloc(n * sizeof(uint32_t));
pos = object.ptr;
for (i = 0; i < n; i++)
{
coeff = untoh16(pos);
pubkey[i] = (uint32_t)coeff;
pos += 2;
}
return pubkey;
}
/**
* Encode a raw BLISS subjectPublicKey in ASN.1 DER format
*/
chunk_t bliss_public_key_encode(uint32_t *pubkey, int n)
{
u_char *pos;
chunk_t encoding;
int i;
pos = asn1_build_object(&encoding, ASN1_OCTET_STRING, 2 * n);
for (i = 0; i < n; i++)
{
htoun16(pos, (uint16_t)pubkey[i]);
pos += 2;
}
return encoding;
}
/**
* Encode a BLISS subjectPublicKeyInfo record in ASN.1 DER format
*/
chunk_t bliss_public_key_info_encode(int oid, uint32_t *pubkey, int n)
{
chunk_t encoding, pubkey_encoding;
pubkey_encoding = bliss_public_key_encode(pubkey, n);
encoding = asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_wrap(ASN1_SEQUENCE, "mm",
asn1_build_known_oid(OID_BLISS_PUBLICKEY),
asn1_build_known_oid(oid)),
asn1_bitstring("m", pubkey_encoding));
return encoding;
}
/**
* Generate a BLISS public key fingerprint
*/
bool bliss_public_key_fingerprint(int oid, uint32_t *pubkey, int n,
cred_encoding_type_t type, chunk_t *fp)
{
hasher_t *hasher;
chunk_t key;
switch (type)
{
case KEYID_PUBKEY_SHA1:
key = bliss_public_key_encode(pubkey, n);
break;
case KEYID_PUBKEY_INFO_SHA1:
key = bliss_public_key_info_encode(oid, pubkey, n);
break;
default:
return FALSE;
}
hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (!hasher || !hasher->allocate_hash(hasher, key, fp))
{
DBG1(DBG_LIB, "SHA1 hash algorithm not supported, fingerprinting failed");
DESTROY_IF(hasher);
free(key.ptr);
return FALSE;
}
hasher->destroy(hasher);
free(key.ptr);
return TRUE;
}
METHOD(public_key_t, get_encoding, bool,
@ -81,15 +188,33 @@ METHOD(public_key_t, get_encoding, bool,
{
bool success = TRUE;
*encoding = chunk_empty;
*encoding = bliss_public_key_info_encode(this->set->oid, this->a,
this->set->n);
if (type != PUBKEY_SPKI_ASN1_DER)
{
chunk_t asn1_encoding = *encoding;
success = lib->encoding->encode(lib->encoding, type,
NULL, encoding, CRED_PART_BLISS_PUB_ASN1_DER,
asn1_encoding, CRED_PART_END);
chunk_clear(&asn1_encoding);
}
return success;
}
METHOD(public_key_t, get_fingerprint, bool,
private_bliss_public_key_t *this, cred_encoding_type_t type, chunk_t *fp)
{
bool success = FALSE;
bool success;
if (lib->encoding->get_cache(lib->encoding, type, this, fp))
{
return TRUE;
}
success = bliss_public_key_fingerprint(this->set->oid, this->a,
this->set->n, type, fp);
lib->encoding->cache(lib->encoding, type, this, *fp);
return success;
}
@ -106,21 +231,42 @@ METHOD(public_key_t, destroy, void,
{
if (ref_put(&this->ref))
{
lib->encoding->clear_cache(lib->encoding, this);
free(this->a);
free(this);
}
}
/**
* ASN.1 definition of a BLISS public key
*/
static const asn1Object_t pubkeyObjects[] = {
{ 0, "subjectPublicKeyInfo",ASN1_SEQUENCE, ASN1_OBJ }, /* 0 */
{ 1, "algorithm", ASN1_EOC, ASN1_RAW }, /* 1 */
{ 1, "subjectPublicKey", ASN1_BIT_STRING, ASN1_BODY }, /* 2 */
{ 0, "exit", ASN1_EOC, ASN1_EXIT }
};
#define BLISS_SUBJECT_PUBLIC_KEY_ALGORITHM 1
#define BLISS_SUBJECT_PUBLIC_KEY 2
/**
* See header.
*/
bliss_public_key_t *bliss_public_key_load(key_type_t type, va_list args)
{
private_bliss_public_key_t *this;
chunk_t blob = chunk_empty, object, param;
asn1_parser_t *parser;
bool success = FALSE;
int objectID, oid;
while (TRUE)
{
switch (va_arg(args, builder_part_t))
{
case BUILD_BLOB_ASN1_DER:
blob = va_arg(args, chunk_t);
continue;
case BUILD_END:
break;
default:
@ -129,6 +275,11 @@ bliss_public_key_t *bliss_public_key_load(key_type_t type, va_list args)
break;
}
if (blob.len == 0)
{
return NULL;
}
INIT(this,
.public = {
.key = {
@ -147,5 +298,65 @@ bliss_public_key_t *bliss_public_key_load(key_type_t type, va_list args)
.ref = 1,
);
parser = asn1_parser_create(pubkeyObjects, blob);
while (parser->iterate(parser, &objectID, &object))
{
switch (objectID)
{
case BLISS_SUBJECT_PUBLIC_KEY_ALGORITHM:
{
oid = asn1_parse_algorithmIdentifier(object,
parser->get_level(parser)+1, &param);
if (oid != OID_BLISS_PUBLICKEY)
{
goto end;
}
if (!asn1_parse_simple_object(&param, ASN1_OID,
parser->get_level(parser)+3, "blissKeyType"))
{
goto end;
}
oid = asn1_known_oid(param);
if (oid == OID_UNKNOWN)
{
goto end;
}
this->set = bliss_param_set_get_by_oid(oid);
if (this->set == NULL)
{
goto end;
}
break;
}
case BLISS_SUBJECT_PUBLIC_KEY:
if (object.len > 0 && *object.ptr == 0x00)
{
/* skip initial bit string octet defining 0 unused bits */
object = chunk_skip(object, 1);
}
if (!asn1_parse_simple_object(&object, ASN1_OCTET_STRING,
parser->get_level(parser)+1, "blissPublicKey"))
{
goto end;
}
if (object.len != 2*this->set->n)
{
goto end;
}
this->a = bliss_public_key_from_asn1(object, this->set->n);
break;
}
}
success = parser->success(parser);
end:
parser->destroy(parser);
if (!success)
{
destroy(this);
return NULL;
}
return &this->public;
}

View File

@ -53,6 +53,11 @@ bool pem_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
break;
}
}
if (cred_encoding_args(args, CRED_PART_BLISS_PUB_ASN1_DER,
&asn1, CRED_PART_END))
{
break;
}
return FALSE;
case PRIVKEY_PEM:
label ="RSA PRIVATE KEY";
@ -86,6 +91,12 @@ bool pem_encoder_encode(cred_encoding_type_t type, chunk_t *encoding,
label ="EC PRIVATE KEY";
break;
}
if (cred_encoding_args(args, CRED_PART_BLISS_PRIV_ASN1_DER,
&asn1, CRED_PART_END))
{
label ="BLISS PRIVATE KEY";
break;
}
return FALSE;
case CERT_PEM:
if (cred_encoding_args(args, CRED_PART_X509_ASN1_DER,

View File

@ -60,6 +60,9 @@ METHOD(plugin_t, get_features, int,
PLUGIN_PROVIDE(PRIVKEY, KEY_DSA),
PLUGIN_DEPENDS(PRIVKEY, KEY_DSA),
PLUGIN_SDEPEND(HASHER, HASH_MD5),
PLUGIN_REGISTER(PRIVKEY, pem_private_key_load, FALSE),
PLUGIN_PROVIDE(PRIVKEY, KEY_BLISS),
PLUGIN_DEPENDS(PRIVKEY, KEY_BLISS),
/* public key PEM decoding */
PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
@ -74,6 +77,8 @@ METHOD(plugin_t, get_features, int,
PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_DSA),
PLUGIN_DEPENDS(PUBKEY, KEY_DSA),
PLUGIN_REGISTER(PUBKEY, pem_public_key_load, FALSE),
PLUGIN_PROVIDE(PUBKEY, KEY_BLISS),
/* certificate PEM decoding */
PLUGIN_REGISTER(CERT_DECODE, pem_certificate_load, FALSE),

View File

@ -63,11 +63,18 @@ static public_key_t *parse_public_key(chunk_t blob)
}
else if (oid == OID_EC_PUBLICKEY)
{
/* we need the whole subjectPublicKeyInfo for EC public keys */
/* Need the whole subjectPublicKeyInfo for EC public keys */
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
KEY_ECDSA, BUILD_BLOB_ASN1_DER, blob, BUILD_END);
goto end;
}
else if (oid == OID_BLISS_PUBLICKEY)
{
/* Need the whole subjectPublicKeyInfo for BLISS public keys */
key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY,
KEY_BLISS, BUILD_BLOB_ASN1_DER, blob, BUILD_END);
goto end;
}
else
{
/* key type not supported */

View File

@ -32,9 +32,12 @@
static void print_pubkey(public_key_t *key)
{
chunk_t chunk;
key_type_t type;
type = key->get_type(key);
printf("pubkey: %N %d bits%s\n", key_type_names, type,
key->get_keysize(key), (type == KEY_BLISS) ? " strength" : "");
printf("pubkey: %N %d bits\n", key_type_names, key->get_type(key),
key->get_keysize(key));
if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &chunk))
{
printf("keyid: %#B\n", &chunk);
@ -596,6 +599,11 @@ static int print()
type = CRED_PRIVATE_KEY;
subtype = KEY_ECDSA;
}
else if (streq(arg, "bliss-priv"))
{
type = CRED_PRIVATE_KEY;
subtype = KEY_BLISS;
}
else
{
return command_usage( "invalid input type");
@ -668,7 +676,7 @@ static void __attribute__ ((constructor))reg()
command_register((command_t)
{ print, 'a', "print",
"print a credential in a human readable form",
{"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509|crl|ac]"},
{"[--in file] [--type rsa-priv|ecdsa-priv|bliss-priv|pub|x509|crl|ac]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "input file, default: stdin"},

View File

@ -53,6 +53,11 @@ static int pub()
type = CRED_PRIVATE_KEY;
subtype = KEY_ECDSA;
}
else if (streq(arg, "bliss"))
{
type = CRED_PRIVATE_KEY;
subtype = KEY_BLISS;
}
else if (streq(arg, "pub"))
{
type = CRED_PUBLIC_KEY;
@ -183,7 +188,7 @@ static void __attribute__ ((constructor))reg()
command_register((command_t) {
pub, 'p', "pub",
"extract the public key from a private key/certificate",
{"[--in file|--keyid hex] [--type rsa|ecdsa|pub|pkcs10|x509]",
{"[--in file|--keyid hex] [--type rsa|ecdsa|bliss|pub|pkcs10|x509]",
"[--outform der|pem|dnskey|sshkey]"},
{
{"help", 'h', 0, "show usage information"},