blue-app-openpgp-card/src/gpg_gen.c
Cédric Mesnil b0d13ad6d5 1.0 RC2
App:
  Serial value depends on active slot: last nible encode active slot
  Fix severals PIN management bugs
  Make PW1 distinction (81/82 tag) for commands PSO:CDS/DEC
  Invalid PW1 'verified' status on PSO:CDS command according to PWstatus
  Remove access control on INS_CHANGE_REFERENCE_DATA
  Remove unused variables
  New icon

 Build
   Add glyphs GIF sources
   Use sdk 1.3.1-4
2017-03-28 19:24:02 +02:00

280 lines
9.3 KiB
C

/* Copyright 2017 Cedric Mesnil <cslashm@gmail.com>, Ledger SAS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "os.h"
#include "cx.h"
#include "gpg_types.h"
#include "gpg_api.h"
#include "gpg_vars.h"
/* @in slot slot num [0 ; GPG_KEYS_SLOTS[
* @out seed 32 bytes master seed for given slot
*/
static void gpg_pso_derive_slot_seed(int slot, unsigned char *seed) {
unsigned int path[2];
unsigned char chain[32];
os_memset(chain, 0, 32);
path[0] = 0x80475047;
path[1] = slot+1;
os_perso_derive_node_bip32(CX_CURVE_SECP256K1, path, 2 , seed, chain);
}
/* @in Sn master seed slot number
* @in key_name key name: 'sig ', 'auth ', 'dec '
* @in idx sub-seed index
* @out Ski generated sub_seed
* @in Ski_len sub-seed length
*/
static void gpg_pso_derive_key_seed(unsigned char *Sn, unsigned char* key_name, unsigned int idx,
unsigned char *Ski, unsigned int Ski_len) {
unsigned char h[32];
h[0] = idx >>8;
h[1] = idx;
cx_sha256_init(&G_gpg_vstate.work.md.sha256);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, Sn, 32, NULL);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, 0, (unsigned char *)key_name, 4, NULL);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha256, CX_LAST, h , 2, h);
cx_sha3_xof_init(&G_gpg_vstate.work.md.sha3, 256, Ski_len);
cx_hash((cx_hash_t*)&G_gpg_vstate.work.md.sha3, CX_LAST, h, 32, Ski);
}
/* assume command is fully received */
int gpg_apdu_gen() {
unsigned int t,l,ksz,reset_cnt;
gpg_key_t *keygpg;
unsigned char seed[32];
unsigned char* name;
switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) {
case 0x8000:
case 0x8001:
case 0x8100:
break;
default:
THROW(SW_INCORRECT_P1P2);
}
if (G_gpg_vstate.io_lc != 2){
THROW(SW_WRONG_LENGTH);
}
gpg_io_fetch_tl(&t,&l);
gpg_io_discard(1);
reset_cnt = 0;
switch(t) {
case 0xB6:
keygpg = &G_gpg_vstate.kslot->sig;
name = (unsigned char*)PIC("sig ");
reset_cnt = 0;
break;
case 0xA4:
keygpg = &G_gpg_vstate.kslot->aut;
name = (unsigned char*)PIC("aut ");
break;
case 0xB8:
keygpg = &G_gpg_vstate.kslot->dec;
name = (unsigned char*)PIC("dec ");
break;
default:
THROW(SW_WRONG_DATA);
}
switch ((G_gpg_vstate.io_p1<<8)|G_gpg_vstate.io_p2) {
// -- generate keypair ---
case 0x8000:
case 0x8001:
// RSA
if (keygpg->attributes.value[0] == 0x01) {
#define GPG_RSA_DEFAULT_PUB 0x010001U
unsigned char *pq;
cx_rsa_public_key_t *rsa_pub;
cx_rsa_private_key_t *rsa_priv, *pkey;
unsigned int pkey_size;
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
ksz = ksz >> 3;
switch(ksz) {
case 1024/8:
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa1024.public;
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa1024.private;
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa1024;
pkey_size = sizeof(cx_rsa_1024_private_key_t);
break;
case 2048/8:
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa2048.public;
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa2048.private;
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa2048;
pkey_size = sizeof(cx_rsa_2048_private_key_t);
break;
case 3072/8:
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa3072.public;
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa3072.private;
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa3072;
pkey_size = sizeof(cx_rsa_3072_private_key_t);
break;
case 4096/8:
rsa_pub = (cx_rsa_public_key_t*)&G_gpg_vstate.work.rsa4096.public;
rsa_priv = (cx_rsa_private_key_t*)&G_gpg_vstate.work.rsa4096.private;
pkey = (cx_rsa_private_key_t*)&keygpg->key.rsa4096;
pkey_size = sizeof(cx_rsa_4096_private_key_t);
break;
}
pq = NULL;
if ((G_gpg_vstate.io_p2 == 0x01) || (G_gpg_vstate.seed_mode)){
pq = &rsa_pub->n[0];
unsigned int size;
size = ksz>>1;
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
gpg_pso_derive_key_seed(seed, name, 1, pq, size);
gpg_pso_derive_key_seed(seed, name, 2, pq+size, size);
*pq |= 0x80;
*(pq+size) |= 0x80;
cx_math_next_prime(pq,size);
cx_math_next_prime(pq+size,size);
}
cx_rsa_generate_pair(ksz, rsa_pub, rsa_priv, GPG_RSA_DEFAULT_PUB, pq);
nvm_write(pkey, rsa_priv, pkey_size);
nvm_write(&keygpg->pub_key.rsa[0], rsa_pub->e, 4);
if (reset_cnt) {
reset_cnt = 0;
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
}
gpg_io_clear();
goto send_rsa_pub;
#undef GPG_RSA_DEFAULT_PUB
}
// ECC
if ((keygpg->attributes.value[0] == 18) ||
(keygpg->attributes.value[0] == 19) ||
(keygpg->attributes.value[0] == 22) ){
unsigned int curve,keepprivate;
keepprivate = 0;
curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1);
if ((G_gpg_vstate.io_p2 == 0x01) | (G_gpg_vstate.seed_mode)) {
gpg_pso_derive_slot_seed(G_gpg_vstate.slot, seed);
gpg_pso_derive_key_seed(seed, name, 1, seed, 32);
cx_ecfp_init_private_key(curve,seed, 32, &G_gpg_vstate.work.ecfp256.private);
keepprivate = 1;
}
cx_ecfp_generate_pair(curve,
&G_gpg_vstate.work.ecfp256.public,
&G_gpg_vstate.work.ecfp256.private,
keepprivate);
nvm_write(&keygpg->key.ecfp256, &G_gpg_vstate.work.ecfp256.private, sizeof(cx_ecfp_private_key_t));
nvm_write(&keygpg->pub_key.ecfp256, &G_gpg_vstate.work.ecfp256.public, sizeof(cx_ecfp_public_key_t));
if (reset_cnt) {
reset_cnt = 0;
nvm_write(&G_gpg_vstate.kslot->sig_count,&reset_cnt,sizeof(unsigned int));
}
gpg_io_clear();
goto send_ecc_pub;
}
break;
// --- read pubkey ---
case 0x8100:
if (keygpg->attributes.value[0] == 0x01) {
/// read RSA
send_rsa_pub:
gpg_io_discard(1);
//check length
ksz = (keygpg->attributes.value[1]<<8)|keygpg->attributes.value[2];
ksz = ksz>>3;
gpg_io_mark();
switch(ksz) {
case 1024/8:
if (keygpg->key.rsa1024.size == 0) {
THROW (SW_REFERENCED_DATA_NOT_FOUND);
}
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa1024.n);
break;
case 2048/8:
if (keygpg->key.rsa2048.size == 0) {
THROW (SW_REFERENCED_DATA_NOT_FOUND);
}
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa2048.n);
break;
case 3072/8:
if (keygpg->key.rsa3072.size == 0) {
THROW (SW_REFERENCED_DATA_NOT_FOUND);
}
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa3072.n);
break;
case 4096/8:
if (keygpg->key.rsa4096.size == 0) {
THROW (SW_REFERENCED_DATA_NOT_FOUND);
}
gpg_io_insert_tlv(0x81,ksz,(unsigned char*)&keygpg->key.rsa4096.n);
break;
}
gpg_io_insert_tlv(0x82, 4, keygpg->pub_key.rsa);
l = G_gpg_vstate.io_length;
gpg_io_set_offset(IO_OFFSET_MARK);
gpg_io_insert_tl(0x7f49,l);
gpg_io_set_offset(IO_OFFSET_END);
return SW_OK;
}
if ((keygpg->attributes.value[0] == 18) ||
(keygpg->attributes.value[0] == 19) ||
(keygpg->attributes.value[0] == 22) ){
unsigned int curve;
/// read ECC
send_ecc_pub:
if (keygpg->pub_key.ecfp256.W_len == 0) {
THROW (SW_REFERENCED_DATA_NOT_FOUND);
}
gpg_io_discard(1);
gpg_io_mark();
curve = gpg_oid2curve(keygpg->attributes.value+1, keygpg->attributes.length-1);
if (curve == CX_CURVE_Ed25519) {
os_memmove(G_gpg_vstate.work.io_buffer+128, keygpg->pub_key.ecfp256.W,keygpg->pub_key.ecfp256.W_len);
cx_edward_compress_point(CX_CURVE_Ed25519, G_gpg_vstate.work.io_buffer+128);
gpg_io_insert_tlv(0x86, 32, G_gpg_vstate.work.io_buffer+129); //129: discard 02
} else if (curve == CX_CURVE_Curve25519) {
unsigned int i,len;
len = keygpg->pub_key.ecfp256.W_len-1;
for (i = 0; i <=len; i++) {
G_gpg_vstate.work.io_buffer[128+i] = keygpg->pub_key.ecfp256.W[len-i];
}
gpg_io_insert_tlv(0x86, 32, G_gpg_vstate.work.io_buffer+128);
} else {
gpg_io_insert_tlv(0x86, keygpg->pub_key.ecfp256.W_len, (unsigned char*)&keygpg->pub_key.ecfp256.W );
}
l = G_gpg_vstate.io_length;
gpg_io_set_offset(IO_OFFSET_MARK);
gpg_io_insert_tl(0x7f49,l);
gpg_io_set_offset(IO_OFFSET_END);
return SW_OK;
}
break;
}
THROW(SW_WRONG_DATA);
}