blue-app-openpgp-card/src/gpg_pso.c
Cédric Mesnil a1c8e7766c On the road to 1.1.0
Fix PIN management

Add 3.3.1 spec addons
  - VERIFY with get status
  - MSE
  - ECC public key import
  - AES PSO:ENC
  - AES multi blcok
2017-08-31 17:03:27 +02:00

321 lines
9.8 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"
const unsigned char gpg_oid_sha256[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
};
const unsigned char gpg_oid_sha512[] = {
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
};
static void gpg_pso_reset_PW1() {
if (N_gpg_pstate->PW_status[0] ==0) {
gpg_pin_set_verified(PIN_ID_PW1,0);
}
}
static int gpg_sign(gpg_key_t *sigkey) {
// --- RSA
if (sigkey->attributes.value[0] == 1) {
cx_rsa_private_key_t *key;
unsigned int ksz,l;
ksz = (sigkey->attributes.value[1]<<8) | sigkey->attributes.value[2];
ksz = ksz>>3;
switch(ksz) {
case 1024/8:
key = (cx_rsa_private_key_t *)&sigkey->key.rsa1024;
break;
case 2048/8:
key = (cx_rsa_private_key_t *)&sigkey->key.rsa2048;
break;
case 3072/8:
key = (cx_rsa_private_key_t *)&sigkey->key.rsa3072;
break;
case 4096/8:
key = (cx_rsa_private_key_t *)&sigkey->key.rsa4096;
break;
}
if (key->size != ksz) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
//sign
l = ksz - G_gpg_vstate.io_length;
os_memmove(G_gpg_vstate.work.io_buffer+l,
G_gpg_vstate.work.io_buffer,
G_gpg_vstate.io_length);
os_memset(G_gpg_vstate.work.io_buffer, 0xFF, l);
G_gpg_vstate.work.io_buffer[0] = 0;
G_gpg_vstate.work.io_buffer[1] = 1;
G_gpg_vstate.work.io_buffer[l-1] = 0;
ksz = cx_rsa_decrypt(key,
CX_PAD_NONE,
CX_NONE,
G_gpg_vstate.work.io_buffer, ksz,
G_gpg_vstate.work.io_buffer, ksz);
//send
gpg_io_discard(0);
gpg_io_inserted(ksz);
gpg_pso_reset_PW1();
return SW_OK;
}
// --- ECDSA/EdDSA
if ((sigkey->attributes.value[0] == 19) ||
(sigkey->attributes.value[0] == 22)) {
cx_ecfp_private_key_t *key;
unsigned int sz,i,rs_len;
unsigned char *rs;
key = &sigkey->key.ecfp256;
if (key->d_len != 32) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
//sign
if (sigkey->attributes.value[0] == 19) {
sz = cx_ecdsa_sign(key,
CX_RND_TRNG,
CX_NONE,
G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length,
G_gpg_vstate.work.io_buffer);
//reencode r,s in MPI format
gpg_io_discard(0);
rs_len = G_gpg_vstate.work.io_buffer[3];
rs = &G_gpg_vstate.work.io_buffer[4];
for (i = 0; i<2; i++) {
if (*rs == 0) {
rs++;
rs_len--;
}
gpg_io_insert_u8(0);
gpg_io_insert(rs,rs_len);
rs = rs+rs_len;
rs_len = rs[1];
rs += 2;
}
} else{
sz = cx_eddsa_sign(key, NULL,
CX_NONE,
CX_SHA512,
G_gpg_vstate.work.io_buffer, G_gpg_vstate.io_length,
G_gpg_vstate.work.io_buffer+128);
gpg_io_discard(0);
gpg_io_insert(G_gpg_vstate.work.io_buffer+128, sz);
}
//send
gpg_pso_reset_PW1();
return SW_OK;
}
// --- PSO:CDS NOT SUPPORTED
THROW(SW_REFERENCED_DATA_NOT_FOUND);
return SW_REFERENCED_DATA_NOT_FOUND;
}
int gpg_apdu_pso(unsigned int pso) {
unsigned int t,l,ksz;
switch(pso) {
// --- PSO:CDS ---
case 0x9e9a: {
unsigned int cnt;
int sw;
sw = gpg_sign(&G_gpg_vstate.kslot->sig);
cnt = G_gpg_vstate.kslot->sig_count+1;
nvm_write(&G_gpg_vstate.kslot->sig_count,&cnt,sizeof(unsigned int));
return sw;
}
// --- PSO:ENC ---
case 0x8680: {
unsigned int msg_len;
cx_aes_key_t *key;
unsigned int sz;
key = &G_gpg_vstate.kslot->AES_dec;
if (!(key->size != 16)) {
THROW(SW_CONDITIONS_NOT_SATISFIED+5);
return SW_CONDITIONS_NOT_SATISFIED;
}
msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset;
sz = cx_aes(key,
CX_ENCRYPT|CX_CHAIN_CBC|CX_LAST,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len,
G_gpg_vstate.work.io_buffer+1);
//send
gpg_io_discard(0);
G_gpg_vstate.work.io_buffer[0] = 0x02;
gpg_io_inserted(1+sz);
return SW_OK;
}
// --- PSO:DEC ---
case 0x8086: {
unsigned int msg_len;
unsigned int pad_byte;
unsigned int sz;
pad_byte = gpg_io_fetch_u8();
switch(pad_byte) {
// --- PSO:DEC:RSA
case 0x00: {
cx_rsa_private_key_t *key;
if (G_gpg_vstate.mse_dec->attributes.value[0] != 0x01) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
ksz = (G_gpg_vstate.mse_dec->attributes.value[1]<<8) | G_gpg_vstate.mse_dec->attributes.value[2];
ksz = ksz>>3;
key = NULL;
switch(ksz) {
case 1024/8:
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->key.rsa1024;
break;
case 2048/8:
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->key.rsa2048;
break;
case 3072/8:
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->key.rsa3072;
break;
case 4096/8:
key = (cx_rsa_private_key_t *)&G_gpg_vstate.mse_dec->key.rsa4096;
break;
}
if ((key == NULL) || (key->size != ksz)) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset;
sz = cx_rsa_decrypt(key,
CX_PAD_PKCS1_1o5,
CX_NONE,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len,
G_gpg_vstate.work.io_buffer,ksz);
//send
gpg_io_discard(0);
gpg_io_inserted(sz);
return SW_OK;
}
// --- PSO:DEC:AES
case 0x02: {
cx_aes_key_t *key;
unsigned int sz;
key = &G_gpg_vstate.kslot->AES_dec;
if (!(key->size != 16)) {
THROW(SW_CONDITIONS_NOT_SATISFIED+5);
return SW_CONDITIONS_NOT_SATISFIED;
}
msg_len = G_gpg_vstate.io_length - G_gpg_vstate.io_offset;
sz = cx_aes(key,
CX_DECRYPT|CX_CHAIN_CBC|CX_LAST,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset, msg_len,
G_gpg_vstate.work.io_buffer);
//send
gpg_io_discard(0);
gpg_io_inserted(sz);
return SW_OK;
}
// --- PSO:DEC:ECDH
case 0xA6: {
cx_ecfp_private_key_t *key;
unsigned int sz;
unsigned int curve;
if (G_gpg_vstate.mse_dec->attributes.value[0] != 0x12) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
key = &G_gpg_vstate.mse_dec->key.ecfp256;
if (key->d_len != 32) {
THROW(SW_CONDITIONS_NOT_SATISFIED);
return SW_CONDITIONS_NOT_SATISFIED;
}
gpg_io_fetch_l(&l);
gpg_io_fetch_tl(&t, &l);
if (t != 0x7f49) {
THROW(SW_WRONG_DATA);
return SW_WRONG_DATA;
}
gpg_io_fetch_tl(&t, &l);
if (t != 0x86) {
THROW(SW_WRONG_DATA);
return SW_WRONG_DATA;
}
curve = gpg_oid2curve(G_gpg_vstate.mse_dec->attributes.value+1, G_gpg_vstate.mse_dec->attributes.length-1);
if (curve == CX_CURVE_Curve25519) {
unsigned int i;
for (i = 0; i <=31; i++) {
G_gpg_vstate.work.io_buffer[512+i] = (G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset)[31-i];
}
G_gpg_vstate.work.io_buffer[511] = 0x02;
sz = cx_ecdh(key,
CX_ECDH_X,
G_gpg_vstate.work.io_buffer+511,
G_gpg_vstate.work.io_buffer+256);
for (i = 0; i <=31; i++) {
G_gpg_vstate.work.io_buffer[128+i] = G_gpg_vstate.work.io_buffer[287-i];
}
sz = 32;
} else {
sz = cx_ecdh(key,
CX_ECDH_X,
G_gpg_vstate.work.io_buffer+G_gpg_vstate.io_offset,
G_gpg_vstate.work.io_buffer+128);
}
//send
gpg_io_discard(0);
gpg_io_insert( G_gpg_vstate.work.io_buffer+128,sz);
return SW_OK;
}
// --- PSO:DEC:xx NOT SUPPORTDED
default:
THROW(SW_REFERENCED_DATA_NOT_FOUND);
return SW_REFERENCED_DATA_NOT_FOUND;
}
}
//--- PSO:yy NOT SUPPPORTED ---
default:
THROW(SW_REFERENCED_DATA_NOT_FOUND);
return SW_REFERENCED_DATA_NOT_FOUND;
}
THROW(SW_REFERENCED_DATA_NOT_FOUND);
return SW_REFERENCED_DATA_NOT_FOUND;
}
int gpg_apdu_internal_authenticate() {
if (G_gpg_vstate.mse_aut->attributes.value[0] == 1) {
if ( G_gpg_vstate.io_length > ((G_gpg_vstate.mse_aut->attributes.value[1]<<8)|G_gpg_vstate.mse_aut->attributes.value[2])*40/100) {
THROW(SW_WRONG_LENGTH);
return SW_WRONG_LENGTH;
}
}
return gpg_sign(G_gpg_vstate.mse_aut);
}