mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-21 14:47:03 +01:00
scd: Implement decryption for PIV cards.
* scd/app-piv.c (do_decipher): New. -- Note that ECDH decryption has not been tested due to the lack of ECC support in gpgsm. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
b283869440
commit
43b14b4cc2
147
scd/app-piv.c
147
scd/app-piv.c
@ -2259,6 +2259,151 @@ do_auth (app_t app, const char *keyidstr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Decrypt the data in (INDATA,INDATALEN) and on success store the
|
||||||
|
* mallocated result at (R_OUTDATA,R_OUTDATALEN). */
|
||||||
|
static gpg_error_t
|
||||||
|
do_decipher (app_t app, const char *keyidstr,
|
||||||
|
gpg_error_t (*pincb)(void*, const char *, char **),
|
||||||
|
void *pincb_arg,
|
||||||
|
const void *indata_arg, size_t indatalen,
|
||||||
|
unsigned char **r_outdata, size_t *r_outdatalen,
|
||||||
|
unsigned int *r_info)
|
||||||
|
{
|
||||||
|
const unsigned char *indata = indata_arg;
|
||||||
|
gpg_error_t err;
|
||||||
|
data_object_t dobj;
|
||||||
|
unsigned char *outdata = NULL;
|
||||||
|
size_t outdatalen;
|
||||||
|
const unsigned char *s;
|
||||||
|
size_t n;
|
||||||
|
int keyref, mechanism;
|
||||||
|
unsigned int framelen;
|
||||||
|
unsigned char *indata_buffer = NULL; /* Malloced helper. */
|
||||||
|
unsigned char *apdudata = NULL;
|
||||||
|
size_t apdudatalen;
|
||||||
|
|
||||||
|
if (!keyidstr || !*keyidstr)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
dobj = find_dobj_by_keyref (app, keyidstr);
|
||||||
|
if ((keyref = keyref_from_dobj (dobj)) == -1)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_INV_ID);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (keyref == 0x9A || keyref == 0x9C || keyref == 0x9E)
|
||||||
|
{
|
||||||
|
/* Signing only reference. We only allow '9D' and the retired
|
||||||
|
* cert key management DOs. */
|
||||||
|
err = gpg_error (GPG_ERR_INV_ID);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_key_algorithm_by_dobj (app, dobj, &mechanism);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
switch (mechanism)
|
||||||
|
{
|
||||||
|
case PIV_ALGORITHM_ECC_P256:
|
||||||
|
framelen = 1+32+32;
|
||||||
|
break;
|
||||||
|
case PIV_ALGORITHM_ECC_P384:
|
||||||
|
framelen = 1+48+48;
|
||||||
|
break;
|
||||||
|
case PIV_ALGORITHM_RSA:
|
||||||
|
framelen = 2048 / 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = gpg_error (GPG_ERR_INTERNAL);
|
||||||
|
log_debug ("piv: unknown PIV mechanism %d while decrypting\n", mechanism);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the ciphertext has the right length; due to internal
|
||||||
|
* convey mechanism using MPIs leading zero bytes might have been
|
||||||
|
* lost. Adjust for this. Note that for ECC this actually
|
||||||
|
* superfluous because the first octet is always '04' to indicate an
|
||||||
|
* uncompressed point. */
|
||||||
|
if (indatalen > framelen)
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||||
|
log_error ("piv: input of %zu octets too large for mechanism %d\n",
|
||||||
|
indatalen, mechanism);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
if (indatalen < framelen)
|
||||||
|
{
|
||||||
|
indata_buffer = xtrycalloc (1, framelen);
|
||||||
|
if (!indata_buffer)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
memcpy (indata_buffer+(framelen-indatalen), indata, indatalen);
|
||||||
|
indata = indata_buffer;
|
||||||
|
indatalen = framelen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now verify the Application PIN. */
|
||||||
|
err = verify_chv (app, 0x80, pincb, pincb_arg);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Build the Dynamic Authentication Template. */
|
||||||
|
err = concat_tlv_list (&apdudata, &apdudatalen,
|
||||||
|
(int)0x7c, (size_t)0, NULL, /* Constructed. */
|
||||||
|
(int)0x82, (size_t)0, "",
|
||||||
|
mechanism == PIV_ALGORITHM_RSA?
|
||||||
|
(int)0x81 : (int)0x85, (size_t)indatalen, indata,
|
||||||
|
(int)0, (size_t)0, NULL);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
/* Note: the -1 requests command chaining. */
|
||||||
|
err = iso7816_general_authenticate (app->slot, -1,
|
||||||
|
mechanism, keyref,
|
||||||
|
apdudata, (int)apdudatalen, 0,
|
||||||
|
&outdata, &outdatalen);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
/* Parse the response. */
|
||||||
|
if (outdatalen && *outdata == 0x7c
|
||||||
|
&& (s = find_tlv (outdata, outdatalen, 0x82, &n)))
|
||||||
|
{
|
||||||
|
memmove (outdata, outdata + (s - outdata), n);
|
||||||
|
outdatalen = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err = gpg_error (GPG_ERR_CARD);
|
||||||
|
log_error ("piv: response does not contain a proper result\n");
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
leave:
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
xfree (outdata);
|
||||||
|
*r_outdata = NULL;
|
||||||
|
*r_outdatalen = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*r_outdata = outdata;
|
||||||
|
*r_outdatalen = outdatalen;
|
||||||
|
}
|
||||||
|
*r_info = 0;
|
||||||
|
xfree (apdudata);
|
||||||
|
xfree (indata_buffer);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Check whether a key for DOBJ already exists. We detect this by
|
/* Check whether a key for DOBJ already exists. We detect this by
|
||||||
* reading the certificate described by DOBJ. If FORCE is TRUE a
|
* reading the certificate described by DOBJ. If FORCE is TRUE a
|
||||||
* diagnositic will be printed but no error returned if the key
|
* diagnositic will be printed but no error returned if the key
|
||||||
@ -2679,7 +2824,7 @@ app_select_piv (app_t app)
|
|||||||
app->fnc.genkey = do_genkey;
|
app->fnc.genkey = do_genkey;
|
||||||
app->fnc.sign = do_sign;
|
app->fnc.sign = do_sign;
|
||||||
app->fnc.auth = do_auth;
|
app->fnc.auth = do_auth;
|
||||||
/* app->fnc.decipher = do_decipher; */
|
app->fnc.decipher = do_decipher;
|
||||||
app->fnc.change_pin = do_change_chv;
|
app->fnc.change_pin = do_change_chv;
|
||||||
app->fnc.check_pin = do_check_chv;
|
app->fnc.check_pin = do_check_chv;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user