Commit 33355c4c authored by filatov's avatar filatov
Browse files

Certificate generator V3

parent 1dc414c4
######################################################################
##
## Created by: Denis Filatov
##
## Copyleft (c) 2015
## This code is provided under the CeCill-C license agreement.
######################################################################
PROJECTROOT = ..
BUILDROOT = ../build
PROJECT = asn1certgen
DEBUG = yes
bins = asn1certgen
sources := asn1certgen.c ecc_openssl.c
sources-WIN32 := applink.c
packages += cshared openssl
includes += ../../../bin/asn1
libs += ../../../bin/asn1/libItsAsn.so
include ../common.mk
#define _CRT_SECURE_NO_WARNINGS
#define APPLINK_STDIN 1
#define APPLINK_STDOUT 2
#define APPLINK_STDERR 3
#define APPLINK_FPRINTF 4
#define APPLINK_FGETS 5
#define APPLINK_FREAD 6
#define APPLINK_FWRITE 7
#define APPLINK_FSETMOD 8
#define APPLINK_FEOF 9
#define APPLINK_FCLOSE 10 /* should not be used */
#define APPLINK_FOPEN 11 /* solely for completeness */
#define APPLINK_FSEEK 12
#define APPLINK_FTELL 13
#define APPLINK_FFLUSH 14
#define APPLINK_FERROR 15
#define APPLINK_CLEARERR 16
#define APPLINK_FILENO 17 /* to be used with below */
#define APPLINK_OPEN 18 /* formally can't be used, as flags can vary */
#define APPLINK_READ 19
#define APPLINK_WRITE 20
#define APPLINK_LSEEK 21
#define APPLINK_CLOSE 22
#define APPLINK_MAX 22 /* always same as last macro */
#ifndef APPMACROS_ONLY
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
static void *app_stdin(void) { return stdin; }
static void *app_stdout(void) { return stdout; }
static void *app_stderr(void) { return stderr; }
static int app_feof(FILE *fp) { return feof(fp); }
static int app_ferror(FILE *fp) { return ferror(fp); }
static void app_clearerr(FILE *fp) { clearerr(fp); }
static int app_fileno(FILE *fp) { return _fileno(fp); }
static int app_fsetmod(FILE *fp,char mod)
{ return _setmode (_fileno(fp),mod=='b'?_O_BINARY:_O_TEXT); }
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport)
void **
#if defined(__BORLANDC__)
__stdcall /* __stdcall appears to be the only way to get the name
* decoration right with Borland C. Otherwise it works
* purely incidentally, as we pass no parameters. */
#else
__cdecl
#endif
OPENSSL_Applink(void)
{ static int once=1;
static void *OPENSSL_ApplinkTable[APPLINK_MAX+1]={(void *)APPLINK_MAX};
if (once)
{ OPENSSL_ApplinkTable[APPLINK_STDIN] = app_stdin;
OPENSSL_ApplinkTable[APPLINK_STDOUT] = app_stdout;
OPENSSL_ApplinkTable[APPLINK_STDERR] = app_stderr;
OPENSSL_ApplinkTable[APPLINK_FPRINTF] = fprintf;
OPENSSL_ApplinkTable[APPLINK_FGETS] = fgets;
OPENSSL_ApplinkTable[APPLINK_FREAD] = fread;
OPENSSL_ApplinkTable[APPLINK_FWRITE] = fwrite;
OPENSSL_ApplinkTable[APPLINK_FSETMOD] = app_fsetmod;
OPENSSL_ApplinkTable[APPLINK_FEOF] = app_feof;
OPENSSL_ApplinkTable[APPLINK_FCLOSE] = fclose;
OPENSSL_ApplinkTable[APPLINK_FOPEN] = fopen;
OPENSSL_ApplinkTable[APPLINK_FSEEK] = fseek;
OPENSSL_ApplinkTable[APPLINK_FTELL] = ftell;
OPENSSL_ApplinkTable[APPLINK_FFLUSH] = fflush;
OPENSSL_ApplinkTable[APPLINK_FERROR] = app_ferror;
OPENSSL_ApplinkTable[APPLINK_CLEARERR] = app_clearerr;
OPENSSL_ApplinkTable[APPLINK_FILENO] = app_fileno;
OPENSSL_ApplinkTable[APPLINK_OPEN] = _open;
OPENSSL_ApplinkTable[APPLINK_READ] = _read;
OPENSSL_ApplinkTable[APPLINK_WRITE] = _write;
OPENSSL_ApplinkTable[APPLINK_LSEEK] = _lseek;
OPENSSL_ApplinkTable[APPLINK_CLOSE] = _close;
once = 0;
}
return OPENSSL_ApplinkTable;
}
#ifdef __cplusplus
}
#endif
#endif
#include <stdio.h>
#include <math.h>
#include "../cshared/copts.h"
#include "../cshared/cstr.h"
#include "ecc_api.h"
#include <ctype.h>
#include "asn_application.h"
#include "Etsits103097Certificate.h"
#include "xer_support.h"
#define CERT_MAX_SIZE 0x10000
static const char * _outPath = ".";
static const char * _searchPath = NULL;
static const char * _certName = NULL;
static char * _profileName = NULL;
static char * _signerName = NULL;
static ecc_format _outKeyFormat = ecc_bin;
static const char * _verificationKey = NULL;
static const char * _decriptionKey = NULL;
static const char * _keyPath = NULL;
static int _force = 0;
static const char * _cfgFile = NULL;
EtsiTs103097Certificate_t * _cert = NULL;
EtsiTs103097Certificate_t * _issuer = NULL;
char _tbsHash[256]; // has space for issuer hash
int _tbsHashLength = 0;
char _signerHash[256]; // has space for issuer hash
int _signerHashLength = 0;
static const char * const _key_formats[] = {
"bin", "hex", "pem", NULL
};
static copt_t _options [] = {
{ "h?", "help", COPT_HELP, NULL, "Print this help page" },
{ "C", "config", COPT_CFGFILE, (void*)&_cfgFile, "Config file path [no cfg file]" },
{ "o", "out", COPT_STR, (void*)&_outPath, "Output path [current dir by default]" },
{ "k", "key-format", COPT_STRENUM, (void*)_key_formats, "Keys output format (bin|hex|pem)[binary by default]" },
{ "S", "certs", COPT_STR, (void*)&_searchPath, "Certificates search path [Output path by default]" },
{ "K", "keys", COPT_STR, (void*)&_keyPath, "Private key storage path [Output path by default]" },
{ "f", "force", COPT_BOOL, (void*)&_force, "Force regenerate existing certificate and keys" },
{ "n", "name", COPT_STR, (void*)&_certName, "Certificate name (take from profile by default)" },
{ "v", "vkey", COPT_STR, (void*)&_verificationKey, "Verification public key (generate key pair by default)" },
{ "e", "ekey", COPT_STR, (void*)&_decriptionKey, "Encription public key (generate key pair if neccessary)" },
{ "s", "signer", COPT_STR, (void*)&_signerName, "Signer certificate name [take from profile by default]" },
{ NULL, NULL, COPT_END, NULL, NULL }
};
static int is_P384CurvePoint_empty(EccP384CurvePoint_t* point);
static int is_P256CurvePoint_empty(EccP256CurvePoint_t* point);
static void fill_curve_point_eccP256(EccP256CurvePoint_t* point, ecc_curve_id curveType, const char * keyPath);
static void fill_curve_point_eccP384(EccP384CurvePoint_t* point, ecc_curve_id curveType, const char * keyPath);
static int _issuer_parser_cb(pxml_chunk_type_e _type,
const void *_chunk_data, size_t _chunk_size, void *_key)
{
char * f = cstrnstr((const char *)_chunk_data, _chunk_size, "name=\"");
if (f){
_signerName = f + 6;
f = cstrchr(_signerName, '"');
_signerName = cstrndup(_signerName, f - _signerName);
}
return -1;
}
static asn_dec_rval_t IssuerIdentifier_xer_decoder(const asn_codec_ctx_t *opt_codec_ctx,
const asn_TYPE_descriptor_t *td, void **struct_ptr,
const char *opt_mname, const void *buf_ptr, size_t size)
{
if (_signerName == NULL){
int stateContext = 0;
pxml_parse(&stateContext, buf_ptr, size, _issuer_parser_cb, NULL);
}
return CHOICE_decode_xer(opt_codec_ctx, td, struct_ptr, opt_mname, buf_ptr, size);
}
typedef struct enc_to_buf_arg {
void *buffer;
size_t left;
} enc_to_buf_arg;
static asn_enc_rval_t ToBeSignedCertificate_oer_encoder(const asn_TYPE_descriptor_t *td,
const asn_oer_constraints_t *constraints, const void *sptr,
asn_app_consume_bytes_f *cb, void *app_key)
{
asn_enc_rval_t rc;
ToBeSignedCertificate_t * tbs = (ToBeSignedCertificate_t*)sptr;
enc_to_buf_arg *a = (enc_to_buf_arg*)app_key;
rc = SEQUENCE_encode_oer(td, constraints, sptr, cb, app_key);
if (rc.encoded > 0){
// calculate hash
if (tbs->verifyKeyIndicator.present == VerificationKeyIndicator_PR_verificationKey &&
tbs->verifyKeyIndicator.choice.verificationKey.present == PublicVerificationKey_PR_ecdsaBrainpoolP384r1) {
_tbsHashLength = 48;
sha384_calculate(_tbsHash, (const char*)a->buffer, rc.encoded);
}
else{
_tbsHashLength = 32;
sha256_calculate(_tbsHash, (const char*)a->buffer, rc.encoded);
}
}
return rc;
}
static ecc_curve_id _pk_type_to_curveid[] = {
0,
ecies_nistp256, //Signature_PR_ecdsaNistP256Signature,
ecies_brainpoolp256r, //Signature_PR_ecdsaBrainpoolP256r1Signature,
ecies_brainpoolp384r //Signature_PR_ecdsaBrainpoolP384r1Signature
};
static ecc_curve_id _pk_type_to_hashid[] = {
0,
sha_256, //Signature_PR_ecdsaNistP256Signature,
sha_256, //Signature_PR_ecdsaBrainpoolP256r1Signature,
sha_384 //Signature_PR_ecdsaBrainpoolP384r1Signature
};
static asn_enc_rval_t Signature_oer_encoder(const asn_TYPE_descriptor_t *td,
const asn_oer_constraints_t *constraints, const void *sptr,
asn_app_consume_bytes_f *cb, void *app_key)
{
Signature_t * s = (Signature_t *)sptr;
if (is_P256CurvePoint_empty(&s->choice.ecdsaNistP256Signature.rSig)){
// look for signer private key
ecc_curve_id alg = _pk_type_to_curveid[s->present];
ecc_hash_id hashId = _pk_type_to_hashid[s->present];
const char * sName = _signerName;
if (sName == NULL && _cert->issuer.present == IssuerIdentifier_PR_self)
sName = _profileName;
char* pk = cvstrdup(_keyPath, "/", sName, ".vkey", NULL);
void *k = ecc_key_private_load(pk, alg);
if (k){
char h[48];
int hl = 0;
// calculate joint hash
memcpy(_tbsHash, _signerHash, _signerHashLength);
switch (hashId)
{
case sha_256:
sha256_calculate(h, _tbsHash, _tbsHashLength + _signerHashLength);
hl = sha256_hash_size;
break;
case sha_384:
sha384_calculate(h, _tbsHash, _tbsHashLength + _signerHashLength);
hl = sha384_hash_size;
break;
}
OCTET_STRING_fromBuf(&s->choice.ecdsaNistP256Signature.rSig.choice.x_only, h, hl);
OCTET_STRING_fromBuf(&s->choice.ecdsaNistP256Signature.sSig, h, hl);
ecc_sign(k, h, hl, s->choice.ecdsaNistP256Signature.rSig.choice.x_only.buf, s->choice.ecdsaNistP256Signature.sSig.buf);
}
}
return asn_OP_CHOICE.oer_encoder(td, constraints, sptr, cb, app_key);
}
int main(int argc, char ** argv)
{
// set default time to the begining of this year
//_setup_default_time();
//parse options
argc = coptions(argc, argv, COPT_HELP_NOVALUES , _options);
if(argc < 2){
if(argc<0 && (0-argc)<((sizeof(_options)/sizeof(_options[0]))-1)){
printf("Unknown option %s\n", argv[0-argc]);
}
const char * a = strrchr(argv[0], '/');
if (a == NULL) a = argv[0];
coptions_help(stdout, a, COPT_HELP_NOVALUES, _options, "<profile> [signer]");
return -1;
}
if (_searchPath == NULL) _searchPath = _outPath;
if (_keyPath == NULL) _keyPath = _outPath;
_outKeyFormat = copts_enum_value(_options, 3, _key_formats);
if(argc > 2){
// set signer certificate file name
_signerName = argv[2];
}
if(ecc_api_init()){
return -1;
}
_profileName = cstrdup(cstrlastpathelement(argv[1]));
if(_profileName){
char * p = strrchr(_profileName, '.');
if(p) *p = 0;
}
//load XER file
char * buf = malloc(CERT_MAX_SIZE);
char * ebuf;
EtsiTs103097Certificate_t * cert = NULL;
ebuf = cstrnload(buf, CERT_MAX_SIZE, argv[1]);
if(ebuf == NULL){
fprintf(stderr, "%s: Certificate profile not found\n", argv[1]);
return -1;
}
FILE * f;
asn_dec_rval_t rc_d;
asn_enc_rval_t rc_e;
asn_TYPE_operation_t issuerOps = *asn_DEF_IssuerIdentifier.op;
asn_DEF_IssuerIdentifier.op = &issuerOps;
asn_TYPE_operation_t tbsOps = *asn_DEF_ToBeSignedCertificate.op;
asn_DEF_ToBeSignedCertificate.op = &tbsOps;
asn_TYPE_operation_t signatureOps = *asn_DEF_Signature.op;
asn_DEF_Signature.op = &signatureOps;
issuerOps.xer_decoder = IssuerIdentifier_xer_decoder;
tbsOps.oer_encoder = ToBeSignedCertificate_oer_encoder;
signatureOps.oer_encoder = Signature_oer_encoder;
rc_d = asn_decode(NULL, ATS_BASIC_XER, &asn_DEF_EtsiTs103097Certificate, (void**)&cert, buf, ebuf - buf);
if (rc_d.code != RC_OK){
fprintf(stderr, "%s: failed to load at position %d\n %.30s\n", _profileName, rc_d.consumed, buf + rc_d.consumed);
return -1;
}
//check signer
if (!_signerName && cert->issuer.present != IssuerIdentifier_PR_self){
fprintf(stderr, "%s: unknown signer\n", argv[1]);
return -1;
}
if (_signerName){
PublicVerificationKey_PR hashType = PublicVerificationKey_PR_NOTHING;
cvstrncpy(buf, CERT_MAX_SIZE, _searchPath, "/", _signerName, ".oer", NULL);
ebuf = cstrnload(buf, CERT_MAX_SIZE, buf);
if (ebuf == NULL){
fprintf(stderr, "%s: signer certificate not found", _signerName);
return -1;
}
// decode it to detect hash algorythm
asn_dec_rval_t rc_d;
EtsiTs103097Certificate_t * signer = NULL;
rc_d = asn_decode(NULL, ATS_BASIC_OER, &asn_DEF_EtsiTs103097Certificate, (void**)&signer, buf, ebuf - buf);
if (rc_d.code != RC_OK){
fprintf(stderr, "%s: failed to load signer certificate at position %d\n %.30s\n", _signerName, rc_d.consumed, buf + rc_d.consumed);
return -1;
}
switch (signer->toBeSigned.verifyKeyIndicator.present){
case VerificationKeyIndicator_PR_verificationKey:
hashType = signer->toBeSigned.verifyKeyIndicator.choice.verificationKey.present;
break;
case VerificationKeyIndicator_PR_reconstructionValue:
hashType = PublicVerificationKey_PR_ecdsaNistP256;
break;
}
switch (hashType){
case PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
case PublicVerificationKey_PR_ecdsaNistP256:
if (cert->issuer.present == IssuerIdentifier_PR_NOTHING)
cert->issuer.present = IssuerIdentifier_PR_sha256AndDigest;
sha256_calculate(_signerHash, buf, ebuf - buf);
_signerHashLength = sha256_hash_size;
OCTET_STRING_fromBuf(&cert->issuer.choice.sha256AndDigest, &_signerHash[sha256_hash_size-8], 8);
break;
case PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
if (cert->issuer.present == IssuerIdentifier_PR_NOTHING)
cert->issuer.present = IssuerIdentifier_PR_sha384AndDigest;
sha384_calculate(_signerHash, buf, ebuf - buf);
_signerHashLength = sha384_hash_size;
OCTET_STRING_fromBuf(&cert->issuer.choice.sha384AndDigest, &_signerHash[sha384_hash_size - 8], 8);
break;
}
if (signer){
ASN_STRUCT_FREE(asn_DEF_EtsiTs103097Certificate, signer);
}
}
// generate keys if necessary
// buf = name of private key file
cvstrncpy(buf, CERT_MAX_SIZE, _keyPath, "/", _profileName, ".vkey", NULL);
if (_force){
remove(buf);
}
switch (cert->toBeSigned.verifyKeyIndicator.present){
case VerificationKeyIndicator_PR_verificationKey:
switch (cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.present){
case PublicVerificationKey_PR_ecdsaNistP256:
fill_curve_point_eccP256(&cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaNistP256, ecies_nistp256, buf);
break;
case PublicVerificationKey_PR_ecdsaBrainpoolP256r1:
fill_curve_point_eccP256(&cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaBrainpoolP256r1, ecies_brainpoolp256r, buf);
break;
case PublicVerificationKey_PR_ecdsaBrainpoolP384r1:
fill_curve_point_eccP384(&cert->toBeSigned.verifyKeyIndicator.choice.verificationKey.choice.ecdsaBrainpoolP384r1, ecies_brainpoolp384r, buf);
break;
default:
fprintf(stderr, "Unknown verification key curve type\n");
return -1;
}
break;
case VerificationKeyIndicator_PR_reconstructionValue:
fprintf(stderr, "TODO: reconstruction value generation is unsupported\n");
case VerificationKeyIndicator_PR_NOTHING:
default:
break;
}
if (cert->toBeSigned.encryptionKey){
cvstrncpy(buf, CERT_MAX_SIZE, _keyPath, "/", _profileName, ".ekey", NULL);
if (_force){
remove(buf);
}
switch (cert->toBeSigned.encryptionKey->publicKey.present){
case BasePublicEncryptionKey_PR_NOTHING:
cert->toBeSigned.encryptionKey->publicKey.present = BasePublicEncryptionKey_PR_eciesNistP256;
case BasePublicEncryptionKey_PR_eciesNistP256:
fill_curve_point_eccP256(&cert->toBeSigned.encryptionKey->publicKey.choice.eciesNistP256, ecies_nistp256, buf);
break;
case BasePublicEncryptionKey_PR_eciesBrainpoolP256r1:
fill_curve_point_eccP256(&cert->toBeSigned.encryptionKey->publicKey.choice.eciesBrainpoolP256r1, ecies_brainpoolp256r, buf);
break;
default:
break;
}
}
cvstrncpy(buf, CERT_MAX_SIZE, _outPath, "/", _profileName, ".oer", NULL);
f = fopen(buf, "wb");
if (f == NULL){
perror(buf);
return -1;
}
_cert = cert;
// Encode as OER
rc_e = asn_encode_to_buffer(NULL, ATS_CANONICAL_OER, &asn_DEF_EtsiTs103097Certificate, cert, buf, CERT_MAX_SIZE);
if (rc_e.encoded <0){
fprintf(stderr, "%s: OER encoding failed for %s\n", _profileName, rc_e.failed_type->name);
return -1;
}
fwrite(buf, 1, rc_e.encoded, f);
fclose(f);
if(cert){
ASN_STRUCT_FREE(asn_DEF_EtsiTs103097Certificate, cert);
}
return 0;
}
static int is_P256CurvePoint_empty(EccP256CurvePoint_t* point)
{
switch (point->present){
case EccP256CurvePoint_PR_x_only:
case EccP256CurvePoint_PR_compressed_y_0:
case EccP256CurvePoint_PR_compressed_y_1:
return point->choice.x_only.size < 32;
case EccP256CurvePoint_PR_uncompressedP256:
return point->choice.uncompressedP256.x.size < 32 || point->choice.uncompressedP256.y.size < 32;
default:
break;
}
return 1;
}
static void fill_curve_point_eccP256(EccP256CurvePoint_t* point, ecc_curve_id curveType, const char * keyPath)
{
void * key;
char x[32], y[32];
int compressed_y;
key = ecc_key_private_load(keyPath, curveType);
if (key == NULL){
key = ecc_key_gen(curveType);
ecc_key_private_save(key, keyPath, _outKeyFormat);
}
ecc_key_public(key, x, y, &compressed_y);
OCTET_STRING_fromBuf(&point->choice.x_only, x, 32);
if (point->present == EccP256CurvePoint_PR_uncompressedP256){
OCTET_STRING_fromBuf(&point->choice.uncompressedP256.y, y, 32);
}
else if (point->present == EccP256CurvePoint_PR_compressed_y_0 || point->present == EccP256CurvePoint_PR_compressed_y_1){
point->present = compressed_y ? EccP256CurvePoint_PR_compressed_y_1 : EccP256CurvePoint_PR_compressed_y_0;
}
ecc_key_free(key);
}
static int is_P384CurvePoint_empty(EccP384CurvePoint_t* point){
switch (point->present){
case EccP256CurvePoint_PR_x_only:
case EccP256CurvePoint_PR_compressed_y_0:
case EccP256CurvePoint_PR_compressed_y_1:
return point->choice.x_only.size < 48;
case EccP256CurvePoint_PR_uncompressedP256:
return point->choice.uncompressedP384.x.size < 48 || point->choice.uncompressedP384.y.size < 48;
default:
break;
}
return 1;
}
static void fill_curve_point_eccP384(EccP384CurvePoint_t* point, ecc_curve_id curveType, const char * keyPath)
{
void * key;
char x[48], y[48];
int compressed_y;
key = ecc_key_private_load(keyPath, curveType);
if (key == NULL){
key = ecc_key_gen(curveType);
ecc_key_private_save(key, keyPath, _outKeyFormat);
}
ecc_key_public(key, x, y, &compressed_y);
OCTET_STRING_fromBuf(&point->choice.x_only, x, 48);
if (point->present == EccP384CurvePoint_PR_uncompressedP384){
OCTET_STRING_fromBuf(&point->choice.uncompressedP384.y, y, 48);
}
else if (point->present == EccP256CurvePoint_PR_compressed_y_0 || point->present == EccP256CurvePoint_PR_compressed_y_1){
point->present = compressed_y ? EccP256CurvePoint_PR_compressed_y_1 : EccP256CurvePoint_PR_compressed_y_0;
}
ecc_key_free(key);
}
This diff is collapsed.
/*********************************************************************
######################################################################
##
## Created by: Denis Filatov
##
## Copyleft (c) 2015
## This code is provided under the CeCill-C license agreement.
######################################################################
*********************************************************************/
#ifndef ecc_api_h
#define ecc_api_h
#ifdef __cplusplus
extern "C" {
#endif
int ecc_api_init();
int ecc_api_done();
typedef enum {
ecies_nistp256,
ecies_brainpoolp256r,
ecies_brainpoolp384r,
}ecc_curve_id;
typedef enum {
sha_256,
sha_384
}ecc_hash_id;
typedef enum {
aes_128_ccm
}ecc_sym_algorithm;
typedef enum {
ecc_bin,
ecc_hex,
ecc_pem
}ecc_format;
typedef enum {
ecc_x_only = 0,
ecc_compressed_y0 = 2,