mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Changes needed to support smartcards. Well, only _support_. There is
no real code yet.
This commit is contained in:
parent
f8c8ca26d4
commit
56341c289c
@ -1,3 +1,22 @@
|
||||
2002-02-28 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* pksign.c (agent_pksign): Detect whether a Smartcard is to be
|
||||
used and divert the operation in this case.
|
||||
* pkdecrypt.c (agent_pkdecrypt): Likewise
|
||||
* findkey.c (agent_key_from_file): Add optional arg shadow_info
|
||||
and have it return information about a shadowed key.
|
||||
* protect.c (agent_get_shadow_info): New.
|
||||
* divert-scd.c: New.
|
||||
|
||||
2002-02-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* protect.c (agent_shadow_key): New.
|
||||
|
||||
* command.c (cmd_learn): New command LEARN.
|
||||
* gpg-agent.c: New option --scdaemon-program.
|
||||
* call-scd.c (start_scd): New. Based on query.c
|
||||
* query.c: Add 2 more arguments to all uses of assuan_transact.
|
||||
|
||||
2002-02-18 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* findkey.c (unprotect): Show an error message for a bad passphrase.
|
||||
|
@ -35,7 +35,9 @@ gpg_agent_SOURCES = \
|
||||
pkdecrypt.c \
|
||||
genkey.c \
|
||||
protect.c \
|
||||
trustlist.c
|
||||
trustlist.c \
|
||||
divert-scd.c \
|
||||
call-scd.c
|
||||
|
||||
gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
|
||||
../common/libcommon.a $(LIBGCRYPT_LIBS)
|
||||
|
@ -36,6 +36,7 @@ struct {
|
||||
int batch; /* batch mode */
|
||||
const char *homedir; /* configuration directory name */
|
||||
const char *pinentry_program;
|
||||
const char *scdaemon_program;
|
||||
int no_grab; /* don't let the pinentry grab the keyboard */
|
||||
unsigned long def_cache_ttl;
|
||||
|
||||
@ -101,7 +102,8 @@ const char *trans (const char *text);
|
||||
void start_command_handler (int);
|
||||
|
||||
/*-- findkey.c --*/
|
||||
GCRY_SEXP agent_key_from_file (const unsigned char *grip);
|
||||
GCRY_SEXP agent_key_from_file (const unsigned char *grip,
|
||||
unsigned char **shadow_info);
|
||||
int agent_key_available (const unsigned char *grip);
|
||||
|
||||
/*-- query.c --*/
|
||||
@ -135,6 +137,11 @@ int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||
int agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
unsigned char **result, size_t *resultlen);
|
||||
int agent_private_key_type (const unsigned char *privatekey);
|
||||
int agent_shadow_key (const unsigned char *pubkey,
|
||||
const unsigned char *shadow_info,
|
||||
unsigned char **result);
|
||||
int agent_get_shadow_info (const unsigned char *shadowkey,
|
||||
unsigned char const **shadow_info);
|
||||
|
||||
|
||||
/*-- trustlist.c --*/
|
||||
@ -143,4 +150,14 @@ int agent_listtrusted (void *assuan_context);
|
||||
int agent_marktrusted (const char *name, const char *fpr, int flag);
|
||||
|
||||
|
||||
/*-- divert-scd.c --*/
|
||||
int divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash,
|
||||
const char *shadow_info);
|
||||
int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
|
||||
const char *shadow_info);
|
||||
|
||||
/*-- call-scd.c --*/
|
||||
int agent_learn_card (void);
|
||||
|
||||
|
||||
#endif /*AGENT_H*/
|
||||
|
@ -462,6 +462,22 @@ cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* LEARN
|
||||
|
||||
Learn something about the currently inserted smartcard
|
||||
*/
|
||||
static int
|
||||
cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = agent_learn_card ();
|
||||
if (rc)
|
||||
log_error ("agent_learn_card failed: %s\n", gnupg_strerror (rc));
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands */
|
||||
@ -485,6 +501,7 @@ register_commands (ASSUAN_CONTEXT ctx)
|
||||
{ "CLEAR_PASSPHRASE",0, cmd_clear_passphrase },
|
||||
{ "LISTTRUSTED", 0, cmd_listtrusted },
|
||||
{ "MARKTRUSTED", 0, cmd_marktrusted },
|
||||
{ "LEARN", 0, cmd_learn },
|
||||
{ "", ASSUAN_CMD_INPUT, NULL },
|
||||
{ "", ASSUAN_CMD_OUTPUT, NULL },
|
||||
{ NULL }
|
||||
|
@ -95,9 +95,12 @@ unprotect (unsigned char **keybuf, const unsigned char *grip)
|
||||
|
||||
|
||||
/* Return the secret key as an S-Exp after locating it using the grip.
|
||||
Returns NULL if key is not available. */
|
||||
Returns NULL if key is not available or the operation should be
|
||||
diverted to a token. In the latter case shadow_info will point to
|
||||
an allocated S-Expression with the shadow_info part from the
|
||||
file. */
|
||||
GCRY_SEXP
|
||||
agent_key_from_file (const unsigned char *grip)
|
||||
agent_key_from_file (const unsigned char *grip, unsigned char **shadow_info)
|
||||
{
|
||||
int i, rc;
|
||||
char *fname;
|
||||
@ -108,6 +111,9 @@ agent_key_from_file (const unsigned char *grip)
|
||||
GCRY_SEXP s_skey;
|
||||
char hexgrip[40+4+1];
|
||||
|
||||
if (shadow_info)
|
||||
*shadow_info = NULL;
|
||||
|
||||
for (i=0; i < 20; i++)
|
||||
sprintf (hexgrip+2*i, "%02X", grip[i]);
|
||||
strcpy (hexgrip+40, ".key");
|
||||
@ -173,8 +179,30 @@ agent_key_from_file (const unsigned char *grip)
|
||||
gnupg_strerror (rc));
|
||||
break;
|
||||
case PRIVATE_KEY_SHADOWED:
|
||||
log_error ("shadowed private keys are not yet supported\n");
|
||||
rc = GNUPG_Not_Implemented;
|
||||
if (shadow_info)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
|
||||
rc = agent_get_shadow_info (buf, &s);
|
||||
if (!rc)
|
||||
{
|
||||
n = gcry_sexp_canon_len (s, 0, NULL,NULL);
|
||||
assert (n);
|
||||
*shadow_info = xtrymalloc (n);
|
||||
if (!*shadow_info)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
{
|
||||
memcpy (*shadow_info, s, n);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
if (rc)
|
||||
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
|
||||
}
|
||||
rc = -1; /* ugly interface: we return an error but keep a value
|
||||
in shadow_info. */
|
||||
break;
|
||||
default:
|
||||
log_error ("invalid private key format\n");
|
||||
|
@ -69,6 +69,7 @@ enum cmd_and_opt_values
|
||||
oBatch,
|
||||
|
||||
oPinentryProgram,
|
||||
oScdaemonProgram,
|
||||
oDefCacheTTL,
|
||||
|
||||
aTest };
|
||||
@ -92,7 +93,8 @@ static ARGPARSE_OPTS opts[] = {
|
||||
{ oNoGrab, "no-grab" ,0, N_("do not grab keyboard and mouse")},
|
||||
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
|
||||
|
||||
{ oPinentryProgram, "pinentry-program", 2 , "path of PIN Entry program" },
|
||||
{ oPinentryProgram, "pinentry-program", 2 , "path to PIN Entry program" },
|
||||
{ oScdaemonProgram, "scdaemon-program", 2 , "path to SCdaemon program" },
|
||||
{ oDefCacheTTL, "default-cache-ttl", 4,
|
||||
"|N|expire cached PINs after N seconds"},
|
||||
|
||||
@ -372,6 +374,7 @@ main (int argc, char **argv )
|
||||
case oServer: pipe_server = 1; break;
|
||||
|
||||
case oPinentryProgram: opt.pinentry_program = pargs.r.ret_str; break;
|
||||
case oScdaemonProgram: opt.scdaemon_program = pargs.r.ret_str; break;
|
||||
case oDefCacheTTL: opt.def_cache_ttl = pargs.r.ret_ulong; break;
|
||||
|
||||
default : pargs.err = configfp? 1:2; break;
|
||||
|
@ -4,7 +4,8 @@ keyformat.txt (wk 2001-12-18)
|
||||
|
||||
Some notes on the format of the secret keys used with gpg-agent.
|
||||
|
||||
|
||||
Location of keys
|
||||
================
|
||||
The secret keys[1] are stored on a per file basis in a directory below
|
||||
the ~/.gnupg home directory. This directory is named
|
||||
|
||||
@ -13,9 +14,12 @@ the ~/.gnupg home directory. This directory is named
|
||||
and should have permissions 700.
|
||||
|
||||
The secret keys are stored in files with a name matching the
|
||||
hexadecimal representation of the keygrip[2]. The content of the file
|
||||
is an S-Expression like the ones used with Libgcrypt. Here is an
|
||||
example of an unprotected file:
|
||||
hexadecimal representation of the keygrip[2].
|
||||
|
||||
Unprotected Private Key Format
|
||||
==============================
|
||||
The content of the file is an S-Expression like the ones used with
|
||||
Libgcrypt. Here is an example of an unprotected file:
|
||||
|
||||
(private-key
|
||||
(rsa
|
||||
@ -47,8 +51,9 @@ keys is in canonical representation[3]:
|
||||
)
|
||||
|
||||
|
||||
|
||||
This describes an unprotected key; a protected key is like this:
|
||||
Protected Private Key Format
|
||||
==============================
|
||||
A protected key is like this:
|
||||
|
||||
(protected-private-key
|
||||
(rsa
|
||||
@ -116,12 +121,35 @@ the stored one - If they don't match the integrity of the key is not
|
||||
given.
|
||||
|
||||
|
||||
Shadowed Private Key Format
|
||||
============================
|
||||
To keep track of keys stored on IC cards we use a third format for
|
||||
private kyes which are called shadow keys as they are only a reference
|
||||
to keys stored on a token:
|
||||
|
||||
(shadowed-private-key
|
||||
(rsa
|
||||
(n #00e0ce9..[some bytes not shown]..51#)
|
||||
(e #010001#)
|
||||
(shadowed protocol (info))
|
||||
)
|
||||
(uri http://foo.bar x-foo:whatever_you_want)
|
||||
)
|
||||
|
||||
The currently used protocol is "ti-v1" (token info version 1). The
|
||||
second list with the information has this layout:
|
||||
|
||||
(card_serial_number id_string_of_key)
|
||||
|
||||
More items may be added to the list.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
======
|
||||
[1] I usually use the terms private and secret key exchangeable but prefer the
|
||||
term secret key because it can be visually be better distinguished
|
||||
from the term public key.
|
||||
|
@ -39,6 +39,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
|
||||
FILE *outfp)
|
||||
{
|
||||
GCRY_SEXP s_skey = NULL, s_cipher = NULL, s_plain = NULL;
|
||||
unsigned char *shadow_info = NULL;
|
||||
int rc;
|
||||
char *buf = NULL;
|
||||
size_t len;
|
||||
@ -63,27 +64,38 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
|
||||
log_printhex ("keygrip:", ctrl->keygrip, 20);
|
||||
log_printhex ("cipher: ", ciphertext, ciphertextlen);
|
||||
}
|
||||
s_skey = agent_key_from_file (ctrl->keygrip);
|
||||
if (!s_skey)
|
||||
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info);
|
||||
if (!s_skey && !shadow_info)
|
||||
{
|
||||
log_error ("failed to read the secret key\n");
|
||||
rc = seterr (No_Secret_Key);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
if (!s_skey)
|
||||
{ /* divert operation to the smartcard */
|
||||
rc = divert_pkdecrypt (&s_plain, s_cipher, shadow_info);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* no smartcard, but a private key */
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
}
|
||||
|
||||
rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("decryption failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
rc = gcry_pk_decrypt (&s_plain, s_cipher, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("decryption failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
@ -106,6 +118,7 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
|
||||
gcry_sexp_release (s_plain);
|
||||
gcry_sexp_release (s_cipher);
|
||||
xfree (buf);
|
||||
xfree (shadow_info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -90,6 +90,7 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
||||
{
|
||||
GCRY_SEXP s_skey = NULL, s_hash = NULL, s_sig = NULL;
|
||||
GCRY_MPI frame = NULL;
|
||||
unsigned char *shadow_info = NULL;
|
||||
int rc;
|
||||
char *buf = NULL;
|
||||
size_t len;
|
||||
@ -97,39 +98,50 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
||||
if (!ctrl->have_keygrip)
|
||||
return seterr (No_Secret_Key);
|
||||
|
||||
s_skey = agent_key_from_file (ctrl->keygrip);
|
||||
if (!s_skey)
|
||||
s_skey = agent_key_from_file (ctrl->keygrip, &shadow_info);
|
||||
if (!s_skey && !shadow_info)
|
||||
{
|
||||
log_error ("failed to read the secret key\n");
|
||||
rc = seterr (No_Secret_Key);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* put the hash into a sexp */
|
||||
/* put the hash into a sexp FIXME: this belongs into libgcrypt/divert-scd.c*/
|
||||
rc = do_encode_md (ctrl->digest.value,
|
||||
ctrl->digest.valuelen,
|
||||
ctrl->digest.algo,
|
||||
gcry_pk_get_nbits (s_skey),
|
||||
&frame);
|
||||
if (rc)
|
||||
goto leave;
|
||||
goto leave;
|
||||
if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) )
|
||||
BUG ();
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
if (!s_skey)
|
||||
{ /* divert operation to the smartcard */
|
||||
rc = divert_pksign (&s_sig, s_hash, shadow_info);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc));
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* no smartcard, but a private key */
|
||||
if (DBG_CRYPTO)
|
||||
{
|
||||
log_debug ("skey: ");
|
||||
gcry_sexp_dump (s_skey);
|
||||
}
|
||||
|
||||
|
||||
/* sign */
|
||||
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("signing failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
/* sign */
|
||||
rc = gcry_pk_sign (&s_sig, s_hash, s_skey);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("signing failed: %s\n", gcry_strerror (rc));
|
||||
rc = map_gcry_err (rc);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
if (DBG_CRYPTO)
|
||||
@ -138,7 +150,6 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
||||
gcry_sexp_dump (s_sig);
|
||||
}
|
||||
|
||||
|
||||
len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
assert (len);
|
||||
buf = xmalloc (len);
|
||||
@ -156,6 +167,7 @@ agent_pksign (CTRL ctrl, FILE *outfp)
|
||||
gcry_sexp_release (s_sig);
|
||||
gcry_mpi_release (frame);
|
||||
xfree (buf);
|
||||
xfree (shadow_info);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,8 @@ enum cmd_and_opt_values
|
||||
oUnprotect = 'u',
|
||||
|
||||
oNoVerbose = 500,
|
||||
oShadow,
|
||||
oShowShadowInfo,
|
||||
|
||||
aTest };
|
||||
|
||||
@ -65,6 +67,8 @@ static ARGPARSE_OPTS opts[] = {
|
||||
{ oPassphrase, "passphrase", 2, "|STRING| Use passphrase STRING" },
|
||||
{ oProtect, "protect", 256, "protect a private key"},
|
||||
{ oUnprotect, "unprotect", 256, "unprotect a private key"},
|
||||
{ oShadow, "shadow", 256, "create a shadow entry for a priblic key"},
|
||||
{ oShowShadowInfo, "show-shadow-info", 256, "return the shadow info"},
|
||||
|
||||
{0}
|
||||
};
|
||||
@ -125,8 +129,7 @@ my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
||||
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
|
||||
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
|
||||
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
|
||||
default: level = JNLIB_LOG_ERROR; break;
|
||||
}
|
||||
default: level = JNLIB_LOG_ERROR; break; }
|
||||
log_logv (level, fmt, arg_ptr);
|
||||
}
|
||||
|
||||
@ -291,6 +294,79 @@ read_and_unprotect (const char *fname)
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
read_and_shadow (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
unsigned char *result;
|
||||
size_t resultlen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_shadow_key (key, "(8:313233342:43)", &result);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
resultlen = gcry_sexp_canon_len (result, 0, NULL,NULL);
|
||||
assert (resultlen);
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (result, resultlen);
|
||||
xfree (result);
|
||||
if (!p)
|
||||
return;
|
||||
result = p;
|
||||
resultlen = strlen (p);
|
||||
}
|
||||
|
||||
fwrite (result, resultlen, 1, stdout);
|
||||
xfree (result);
|
||||
}
|
||||
|
||||
static void
|
||||
show_shadow_info (const char *fname)
|
||||
{
|
||||
int rc;
|
||||
unsigned char *key;
|
||||
const unsigned char *info;
|
||||
size_t infolen;
|
||||
|
||||
key = read_key (fname);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
rc = agent_get_shadow_info (key, &info);
|
||||
xfree (key);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("get_shadow_info failed: %s\n", gnupg_strerror (rc));
|
||||
return;
|
||||
}
|
||||
infolen = gcry_sexp_canon_len (info, 0, NULL,NULL);
|
||||
assert (infolen);
|
||||
|
||||
if (opt_armor)
|
||||
{
|
||||
char *p = make_advanced (info, infolen);
|
||||
if (!p)
|
||||
return;
|
||||
fwrite (p, strlen (p), 1, stdout);
|
||||
xfree (p);
|
||||
}
|
||||
else
|
||||
fwrite (info, infolen, 1, stdout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv )
|
||||
@ -325,6 +401,8 @@ main (int argc, char **argv )
|
||||
|
||||
case oProtect: cmd = oProtect; break;
|
||||
case oUnprotect: cmd = oUnprotect; break;
|
||||
case oShadow: cmd = oShadow; break;
|
||||
case oShowShadowInfo: cmd = oShowShadowInfo; break;
|
||||
|
||||
case oPassphrase: passphrase = pargs.r.ret_str; break;
|
||||
|
||||
@ -341,6 +419,10 @@ main (int argc, char **argv )
|
||||
read_and_protect (*argv);
|
||||
else if (cmd == oUnprotect)
|
||||
read_and_unprotect (*argv);
|
||||
else if (cmd == oShadow)
|
||||
read_and_shadow (*argv);
|
||||
else if (cmd == oShowShadowInfo)
|
||||
show_shadow_info (*argv);
|
||||
else
|
||||
log_info ("no action requested\n");
|
||||
|
||||
|
153
agent/protect.c
153
agent/protect.c
@ -780,7 +780,7 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
|
||||
PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the
|
||||
value 0), PRIVATE_KEY_CLEAR for an unprotected private key.
|
||||
PRIVATE_KEY_PROTECTED for an protected private key or
|
||||
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are store
|
||||
PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored
|
||||
elsewhere. */
|
||||
int
|
||||
agent_private_key_type (const unsigned char *privatekey)
|
||||
@ -886,3 +886,154 @@ hash_passphrase (const char *passphrase, int hashalgo,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create a shadow key from a public key. We use the shadow protocol
|
||||
"ti-v1" and insert the S-expressionn SHADOW_INFO. The resulting
|
||||
S-expression is returned in an allocated buffer RESULT will point
|
||||
to. The input parameters are expected to be valid canonilized
|
||||
S-expressions */
|
||||
int
|
||||
agent_shadow_key (const unsigned char *pubkey,
|
||||
const unsigned char *shadow_info,
|
||||
unsigned char **result)
|
||||
{
|
||||
const unsigned char *s;
|
||||
const unsigned char *point;
|
||||
size_t n;
|
||||
int depth = 0;
|
||||
unsigned char *p;
|
||||
size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
|
||||
size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
|
||||
|
||||
if (!pubkey_len || !shadow_info_len)
|
||||
return GNUPG_Invalid_Value;
|
||||
s = pubkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "public-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n; /* skip over the algorithm name */
|
||||
|
||||
while (*s != ')')
|
||||
{
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s +=n; /* skip value */
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
s++;
|
||||
}
|
||||
point = s; /* insert right before the point */
|
||||
depth--;
|
||||
s++;
|
||||
assert (depth == 1);
|
||||
|
||||
/* calculate required length by taking in account: the "shadowed-"
|
||||
prefix, the "shadowed", "t1-v1" as well as some parenthesis */
|
||||
n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
|
||||
*result = p = xtrymalloc (n);
|
||||
if (!p)
|
||||
return GNUPG_Out_Of_Core;
|
||||
p = stpcpy (p, "(20:shadowed-private-key");
|
||||
/* (10:public-key ...)*/
|
||||
memcpy (p, pubkey+14, point - (pubkey+14));
|
||||
p += point - (pubkey+14);
|
||||
p = stpcpy (p, "(8:shadowed5:t1-v1");
|
||||
memcpy (p, shadow_info, shadow_info_len);
|
||||
p += shadow_info_len;
|
||||
*p++ = ')';
|
||||
memcpy (p, point, pubkey_len - (point - pubkey));
|
||||
p += pubkey_len - (point - pubkey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a canonical encoded shadowed key and return a pointer to the
|
||||
inner list with the shadow_info */
|
||||
int
|
||||
agent_get_shadow_info (const unsigned char *shadowkey,
|
||||
unsigned char const **shadow_info)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
int depth = 0;
|
||||
|
||||
s = shadowkey;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (!smatch (&s, n, "shadowed-private-key"))
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s += n; /* skip over the algorithm name */
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*s == ')')
|
||||
return GNUPG_Unknown_Sexp;
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth++;
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (smatch (&s, n, "shadowed"))
|
||||
break;
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
s +=n; /* skip value */
|
||||
if (*s != ')')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
depth--;
|
||||
s++;
|
||||
}
|
||||
/* found the shadowed list, s points to the protocol */
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return GNUPG_Invalid_Sexp;
|
||||
if (smatch (&s, n, "t1-v1"))
|
||||
{
|
||||
if (*s != '(')
|
||||
return GNUPG_Invalid_Sexp;
|
||||
*shadow_info = s;
|
||||
}
|
||||
else
|
||||
return GNUPG_Unsupported_Protocol;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ start_pinentry (void)
|
||||
|
||||
rc = assuan_transact (entry_ctx,
|
||||
opt.no_grab? "OPTION no-grab":"OPTION grab",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -160,14 +160,14 @@ agent_askpin (const char *desc_text, const char *start_err_text,
|
||||
|
||||
snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
rc = assuan_transact (entry_ctx,
|
||||
pininfo->min_digits? "SETPROMPT PIN:"
|
||||
: "SETPROMPT Passphrase:",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -189,13 +189,13 @@ agent_askpin (const char *desc_text, const char *start_err_text,
|
||||
snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)",
|
||||
errtext, pininfo->failed_tries+1, pininfo->max_tries);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
errtext = NULL;
|
||||
}
|
||||
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
|
||||
if (rc == ASSUAN_Too_Much_Data)
|
||||
errtext = pininfo->min_digits? trans ("PIN too long")
|
||||
: trans ("Passphrase too long");
|
||||
@ -248,13 +248,13 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
|
||||
else
|
||||
snprintf (line, DIM(line)-1, "RESET");
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase");
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -262,7 +262,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
|
||||
{
|
||||
snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
@ -274,7 +274,7 @@ agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
|
||||
return seterr (Out_Of_Core);
|
||||
|
||||
assuan_begin_confidential (entry_ctx);
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (parm.buffer);
|
||||
@ -317,7 +317,7 @@ agent_get_confirmation (const char *desc, const char *prompt)
|
||||
else
|
||||
snprintf (line, DIM(line)-1, "RESET");
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -325,12 +325,12 @@ agent_get_confirmation (const char *desc, const char *prompt)
|
||||
{
|
||||
snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
|
@ -94,6 +94,11 @@ enum {
|
||||
GNUPG_Not_Confirmed = 65,
|
||||
GNUPG_Configuration_Error = 66,
|
||||
GNUPG_No_Policy_Match = 67,
|
||||
GNUPG_Invalid_Index = 68,
|
||||
GNUPG_Invalid_Id = 69,
|
||||
GNUPG_No_Scdaemon = 70,
|
||||
GNUPG_Scdaemon_Error = 71,
|
||||
GNUPG_Unsupported_Protocol = 72,
|
||||
};
|
||||
|
||||
/* Status codes - fixme: should go into another file */
|
||||
|
@ -44,6 +44,7 @@ map_ksba_err (int err)
|
||||
break;
|
||||
|
||||
case KSBA_Unsupported_Algorithm: err = GNUPG_Unsupported_Algorithm; break;
|
||||
case KSBA_Invalid_Index: err = GNUPG_Invalid_Index; break;
|
||||
|
||||
default:
|
||||
err = seterr (General_Error);
|
||||
@ -136,6 +137,7 @@ map_assuan_err (int err)
|
||||
break;
|
||||
|
||||
case ASSUAN_Canceled: err = GNUPG_Canceled; break;
|
||||
case ASSUAN_Invalid_Index: err = GNUPG_Invalid_Index; break;
|
||||
|
||||
case ASSUAN_Not_Implemented: err = GNUPG_Not_Implemented; break;
|
||||
case ASSUAN_Server_Fault: err = GNUPG_Assuan_Server_Fault; break;
|
||||
@ -153,6 +155,7 @@ map_assuan_err (int err)
|
||||
case ASSUAN_No_PKCS15_App: err = GNUPG_No_PKCS15_App; break;
|
||||
case ASSUAN_Card_Not_Present: err= GNUPG_Card_Not_Present; break;
|
||||
case ASSUAN_Not_Confirmed: err = GNUPG_Not_Confirmed; break;
|
||||
case ASSUAN_Invalid_Id: err = GNUPG_Invalid_Id; break;
|
||||
|
||||
default:
|
||||
err = err < 100? GNUPG_Assuan_Server_Fault : GNUPG_Assuan_Error;
|
||||
@ -182,6 +185,7 @@ map_to_assuan_status (int rc)
|
||||
case GNUPG_Invalid_Name: rc = ASSUAN_Invalid_Name; break;
|
||||
case GNUPG_Not_Trusted: rc = ASSUAN_Not_Trusted; break;
|
||||
case GNUPG_Canceled: rc = ASSUAN_Canceled; break;
|
||||
case GNUPG_Invalid_Index: rc = ASSUAN_Invalid_Index; break;
|
||||
|
||||
case GNUPG_Card_Error:
|
||||
case GNUPG_Card_Reset:
|
||||
@ -194,7 +198,7 @@ map_to_assuan_status (int rc)
|
||||
case GNUPG_Invalid_Card: rc = ASSUAN_Invalid_Card; break;
|
||||
case GNUPG_No_PKCS15_App: rc = ASSUAN_No_PKCS15_App; break;
|
||||
case GNUPG_Not_Confirmed: rc = ASSUAN_Not_Confirmed; break;
|
||||
|
||||
case GNUPG_Invalid_Id: rc = ASSUAN_Invalid_Id; break;
|
||||
|
||||
case GNUPG_Bad_PIN:
|
||||
case GNUPG_Bad_Passphrase:
|
||||
|
@ -20,7 +20,8 @@
|
||||
|
||||
bin_PROGRAMS = scdaemon
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS)
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBOPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
|
||||
$(LIBKSBA_CFLAGS)
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
scdaemon_SOURCES = \
|
||||
@ -29,7 +30,8 @@ scdaemon_SOURCES = \
|
||||
|
||||
|
||||
scdaemon_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a \
|
||||
../common/libcommon.a $(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS)
|
||||
../common/libcommon.a \
|
||||
$(LIBOPENSC_LIBS) $(LIBGCRYPT_LIBS) $(LIBKSBA_LIBS)
|
||||
|
||||
|
||||
|
||||
|
208
scd/card.c
208
scd/card.c
@ -26,6 +26,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include <opensc-pkcs15.h>
|
||||
#include <ksba.h>
|
||||
|
||||
#include "scdaemon.h"
|
||||
|
||||
@ -114,13 +115,13 @@ card_open (CARD *rcard)
|
||||
}
|
||||
card->ctx->error_file = log_get_stream ();
|
||||
card->ctx->debug_file = log_get_stream ();
|
||||
if (sc_detect_card (card->ctx, card->reader) != 1)
|
||||
if (sc_detect_card_presence (card->ctx->reader[card->reader], 0) != 1)
|
||||
{
|
||||
rc = GNUPG_Card_Not_Present;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = sc_connect_card (card->ctx, card->reader, &card->scard);
|
||||
rc = sc_connect_card (card->ctx->reader[card->reader], 0, &card->scard);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to connect card in reader %d: %s\n",
|
||||
@ -175,7 +176,7 @@ card_close (CARD card)
|
||||
if (card->scard)
|
||||
{
|
||||
sc_unlock (card->scard);
|
||||
sc_disconnect_card (card->scard);
|
||||
sc_disconnect_card (card->scard, 0);
|
||||
card->scard = NULL;
|
||||
}
|
||||
if (card->ctx)
|
||||
@ -219,3 +220,204 @@ card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp)
|
||||
return GNUPG_Out_Of_Core;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Get the keygrip from CERT, return 0 on success */
|
||||
static int
|
||||
get_keygrip (KsbaCert cert, unsigned char *array)
|
||||
{
|
||||
GCRY_SEXP s_pkey;
|
||||
int rc;
|
||||
KsbaSexp p;
|
||||
size_t n;
|
||||
|
||||
p = ksba_cert_get_public_key (cert);
|
||||
if (!p)
|
||||
return -1; /* oops */
|
||||
n = gcry_sexp_canon_len (p, 0, NULL, NULL);
|
||||
if (!n)
|
||||
return -1; /* libksba did not return a proper S-expression */
|
||||
rc = gcry_sexp_sscan ( &s_pkey, NULL, p, n);
|
||||
xfree (p);
|
||||
if (rc)
|
||||
return -1; /* can't parse that S-expression */
|
||||
array = gcry_pk_get_keygrip (s_pkey, array);
|
||||
gcry_sexp_release (s_pkey);
|
||||
if (!array)
|
||||
return -1; /* failed to calculate the keygrip */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Enumerate all keypairs on the card and return the Keygrip as well
|
||||
as the internal identification of the key. KEYGRIP must be a
|
||||
caller provided buffer with a size of 20 bytes which will receive
|
||||
the KEYGRIP of the keypair. If KEYID is not NULL, it returns the
|
||||
ID field of the key in allocated memory, NKEYID will then receive
|
||||
the length of it. The function returns -1 when all keys have been
|
||||
enumerated. Note that the error GNUPG_Missing_Certificate may be
|
||||
returned if there is just the private key but no public key (ie.e a
|
||||
certificate) available. Applications might want to continue
|
||||
enumerating after this error.*/
|
||||
int
|
||||
card_enum_keypairs (CARD card, int idx,
|
||||
unsigned char *keygrip,
|
||||
unsigned char **keyid, size_t *nkeyid)
|
||||
{
|
||||
int rc;
|
||||
KsbaError krc;
|
||||
struct sc_pkcs15_prkey_info *pinfo;
|
||||
struct sc_pkcs15_cert_info *certinfo;
|
||||
struct sc_pkcs15_cert *certder;
|
||||
KsbaCert cert;
|
||||
|
||||
if (keyid)
|
||||
*keyid = NULL;
|
||||
if (nkeyid)
|
||||
*nkeyid = 0;
|
||||
|
||||
if (!card || !keygrip || !card->p15card)
|
||||
return GNUPG_Invalid_Value;
|
||||
if (idx < 0)
|
||||
return GNUPG_Invalid_Index;
|
||||
|
||||
rc = sc_pkcs15_enum_private_keys (card->p15card);
|
||||
if (rc < 0)
|
||||
{
|
||||
log_error ("sc_pkcs15_enum_private_keys failed: %s\n", sc_strerror (rc));
|
||||
return GNUPG_Card_Error;
|
||||
}
|
||||
if ( idx >= card->p15card->prkey_count)
|
||||
return -1;
|
||||
pinfo = card->p15card->prkey_info + idx;
|
||||
|
||||
/* now we need to read the certificate so that we can calculate the
|
||||
keygrip */
|
||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &certinfo);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("certificate for private key %d not found: %s\n",
|
||||
idx, sc_strerror (rc));
|
||||
/* but we should return the ID anyway */
|
||||
if (keyid)
|
||||
{
|
||||
*keyid = xtrymalloc (pinfo->id.len);
|
||||
if (!*keyid)
|
||||
return GNUPG_Out_Of_Core;
|
||||
memcpy (*keyid, pinfo->id.value, pinfo->id.len);
|
||||
}
|
||||
if (nkeyid)
|
||||
*nkeyid = pinfo->id.len;
|
||||
return GNUPG_Missing_Certificate;
|
||||
}
|
||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("failed to read certificate for private key %d: %s\n",
|
||||
idx, sc_strerror (rc));
|
||||
return GNUPG_Card_Error;
|
||||
}
|
||||
|
||||
cert = ksba_cert_new ();
|
||||
if (!cert)
|
||||
{
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return GNUPG_Out_Of_Core;
|
||||
}
|
||||
krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
if (krc)
|
||||
{
|
||||
log_error ("failed to parse the certificate for private key %d: %s\n",
|
||||
idx, ksba_strerror (krc));
|
||||
ksba_cert_release (cert);
|
||||
return GNUPG_Card_Error;
|
||||
}
|
||||
if (get_keygrip (cert, keygrip))
|
||||
{
|
||||
log_error ("failed to calculate the keygrip of private key %d\n", idx);
|
||||
ksba_cert_release (cert);
|
||||
return GNUPG_Card_Error;
|
||||
}
|
||||
ksba_cert_release (cert);
|
||||
|
||||
/* return the iD */
|
||||
if (keyid)
|
||||
{
|
||||
*keyid = xtrymalloc (pinfo->id.len);
|
||||
if (!*keyid)
|
||||
return GNUPG_Out_Of_Core;
|
||||
memcpy (*keyid, pinfo->id.value, pinfo->id.len);
|
||||
}
|
||||
if (nkeyid)
|
||||
*nkeyid = pinfo->id.len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read the certificate identified by CERTIDSTR which is the
|
||||
hexadecimal encoded ID of the certificate, prefixed with the string
|
||||
"3F005015.". The certificate is return in DER encoded form in CERT
|
||||
and NCERT. */
|
||||
int
|
||||
card_read_cert (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert)
|
||||
{
|
||||
struct sc_pkcs15_id certid;
|
||||
struct sc_pkcs15_cert_info *certinfo;
|
||||
struct sc_pkcs15_cert *certder;
|
||||
const char *s;
|
||||
int rc, n;
|
||||
|
||||
if (!card || !certidstr || !card->p15card || !cert || !ncert)
|
||||
return GNUPG_Invalid_Value;
|
||||
|
||||
/* For now we only support the standard DF */
|
||||
if (strncmp (certidstr, "3F005015.", 9) )
|
||||
return GNUPG_Invalid_Id;
|
||||
for (s=certidstr+9, n=0; hexdigitp (s); s++, n++)
|
||||
;
|
||||
if (*s || (n&1))
|
||||
return GNUPG_Invalid_Id; /* invalid or odd number of digits */
|
||||
n /= 2;
|
||||
if (!n || n > SC_PKCS15_MAX_ID_SIZE)
|
||||
return GNUPG_Invalid_Id; /* empty or too large */
|
||||
for (s=certidstr+9, n=0; *s; s += 2, n++)
|
||||
certid.value[n] = xtoi_2 (s);
|
||||
certid.len = n;
|
||||
|
||||
rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &certinfo);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("certificate '%s' not found: %s\n",
|
||||
certidstr, sc_strerror (rc));
|
||||
return -1;
|
||||
}
|
||||
rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("failed to read certificate '%s': %s\n",
|
||||
certidstr, sc_strerror (rc));
|
||||
return GNUPG_Card_Error;
|
||||
}
|
||||
|
||||
*cert = xtrymalloc (certder->data_len);
|
||||
if (!*cert)
|
||||
{
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return GNUPG_Out_Of_Core;
|
||||
}
|
||||
memcpy (*cert, certder->data, certder->data_len);
|
||||
*ncert = certder->data_len;
|
||||
sc_pkcs15_free_certificate (certder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
154
scd/command.c
154
scd/command.c
@ -75,14 +75,20 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
|
||||
/* LEARN [--force]
|
||||
|
||||
Learn all useful information of the currently inserted card. When
|
||||
used without the force options, the command might to an INQUIRE
|
||||
used without the force options, the command might do an INQUIRE
|
||||
like this:
|
||||
|
||||
INQUIRE KNOWNCARDP <hexstring_with_serialNumber> <timestamp>
|
||||
|
||||
The client should just send an "END" if the processing should go on
|
||||
or a "CANCEL" to force the function to terminate with a Cancel
|
||||
error message.
|
||||
error message. The response of this command is a list of status
|
||||
lines formatted as this:
|
||||
|
||||
S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
|
||||
|
||||
If there is no certificate yet stored on the card a single "X" is
|
||||
returned as the keygrip.
|
||||
|
||||
*/
|
||||
static int
|
||||
@ -90,6 +96,7 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
int rc = 0;
|
||||
int idx;
|
||||
|
||||
/* if this is the first command issued for a new card, open the card and
|
||||
and create a context */
|
||||
@ -104,40 +111,138 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
|
||||
the card using a serial number and inquiring the client with
|
||||
that. The client may choose to cancel the operation if he already
|
||||
knows about this card */
|
||||
if (!has_option (line, "--force"))
|
||||
{
|
||||
char *serial_and_stamp;
|
||||
char *serial;
|
||||
time_t stamp;
|
||||
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
|
||||
xfree (serial);
|
||||
if (rc < 0)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
rc = 0;
|
||||
assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
|
||||
|
||||
if (!has_option (line, "--force"))
|
||||
{
|
||||
char *command;
|
||||
|
||||
rc = asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
|
||||
if (rc < 0)
|
||||
{
|
||||
free (serial_and_stamp);
|
||||
return ASSUAN_Out_Of_Core;
|
||||
}
|
||||
rc = 0;
|
||||
rc = assuan_inquire (ctx, command, NULL, NULL, 0);
|
||||
free (command); /* (must use standard free here) */
|
||||
if (rc)
|
||||
{
|
||||
if (rc != ASSUAN_Canceled)
|
||||
log_error ("inquire KNOWNCARDP failed: %s\n",
|
||||
assuan_strerror (rc));
|
||||
free (serial_and_stamp);
|
||||
return rc;
|
||||
}
|
||||
/* not canceled, so we have to proceeed */
|
||||
}
|
||||
free (serial_and_stamp);
|
||||
}
|
||||
|
||||
for (idx=0; !rc; idx++)
|
||||
{
|
||||
char *serial;
|
||||
time_t stamp;
|
||||
char *command;
|
||||
unsigned char keygrip[20];
|
||||
unsigned char *keyid;
|
||||
size_t nkeyid;
|
||||
int no_cert = 0;
|
||||
|
||||
rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
|
||||
rc = asprintf (&command, "KNOWNCARDP %s %lu",
|
||||
serial, (unsigned long)stamp);
|
||||
xfree (serial);
|
||||
if (rc < 0)
|
||||
return ASSUAN_Out_Of_Core;
|
||||
rc = 0;
|
||||
rc = assuan_inquire (ctx, command, NULL, NULL, 0);
|
||||
free (command); /* (must use standard free here) */
|
||||
if (rc)
|
||||
rc = card_enum_keypairs (ctrl->card_ctx, idx,
|
||||
keygrip, &keyid, &nkeyid);
|
||||
if (rc == GNUPG_Missing_Certificate && keyid)
|
||||
{
|
||||
if (rc != ASSUAN_Canceled)
|
||||
log_error ("inquire KNOWNCARDP failed: %s\n",
|
||||
assuan_strerror (rc));
|
||||
return rc;
|
||||
/* this does happen with an incomplete personalized
|
||||
card; i.e. during the time we have stored the key on the
|
||||
card but not stored the certificate; probably becuase it
|
||||
has not yet been received back from the CA. Note that we
|
||||
must release KEYID in this case. */
|
||||
rc = 0;
|
||||
no_cert = 1;
|
||||
}
|
||||
/* not canceled, so we have to proceeed */
|
||||
if (!rc)
|
||||
{
|
||||
char *buf, *p;
|
||||
|
||||
buf = p = xtrymalloc (40+1+9+2*nkeyid+1);
|
||||
if (!buf)
|
||||
rc = GNUPG_Out_Of_Core;
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
if (no_cert)
|
||||
*p++ = 'X';
|
||||
else
|
||||
{
|
||||
for (i=0; i < 20; i++, p += 2)
|
||||
sprintf (p, "%02X", keygrip[i]);
|
||||
}
|
||||
*p++ = ' ';
|
||||
/* fixme: we need to get the pkcs-15 DF from the card function */
|
||||
p = stpcpy (p, "3F005015.");
|
||||
for (i=0; i < nkeyid; i++, p += 2)
|
||||
sprintf (p, "%02X", keyid[i]);
|
||||
*p = 0;
|
||||
assuan_write_status (ctx, "KEYPAIRINFO", buf);
|
||||
xfree (buf);
|
||||
}
|
||||
}
|
||||
xfree (keyid);
|
||||
}
|
||||
if (rc == -1)
|
||||
rc = 0;
|
||||
|
||||
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* READCERT <hexified_certid>
|
||||
|
||||
*/
|
||||
static int
|
||||
cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
|
||||
{
|
||||
CTRL ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
unsigned char *cert;
|
||||
size_t ncert;
|
||||
|
||||
if (!ctrl->card_ctx)
|
||||
{
|
||||
rc = card_open (&ctrl->card_ctx);
|
||||
if (rc)
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("card_read_cert failed: %s\n", gnupg_strerror (rc));
|
||||
}
|
||||
if (!rc)
|
||||
{
|
||||
rc = assuan_send_data (ctx, cert, ncert);
|
||||
xfree (cert);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return map_to_assuan_status (rc);
|
||||
}
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands */
|
||||
@ -150,6 +255,7 @@ register_commands (ASSUAN_CONTEXT ctx)
|
||||
int (*handler)(ASSUAN_CONTEXT, char *line);
|
||||
} table[] = {
|
||||
{ "LEARN", 0, cmd_learn },
|
||||
{ "READCERT", 0, cmd_readcert },
|
||||
{ "", ASSUAN_CMD_INPUT, NULL },
|
||||
{ "", ASSUAN_CMD_OUTPUT, NULL },
|
||||
{ NULL }
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <ksba.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#define JNLIB_NEED_LOG_LOGV
|
||||
@ -242,6 +243,7 @@ main (int argc, char **argv )
|
||||
"1.1.5", gcry_check_version (NULL) );
|
||||
}
|
||||
|
||||
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
|
||||
gcry_set_log_handler (my_gcry_logger, NULL);
|
||||
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
||||
|
@ -77,6 +77,11 @@ void scd_command_handler (int);
|
||||
int card_open (CARD *rcard);
|
||||
void card_close (CARD card);
|
||||
int card_get_serial_and_stamp (CARD card, char **serial, time_t *stamp);
|
||||
int card_enum_keypairs (CARD card, int idx,
|
||||
unsigned char *keygrip,
|
||||
unsigned char **keyid, size_t *nkeyid);
|
||||
int card_read_cert (CARD card, const char *certidstr,
|
||||
unsigned char **cert, size_t *ncert);
|
||||
|
||||
|
||||
|
||||
|
@ -1,3 +1,8 @@
|
||||
2002-02-27 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* call-dirmngr.c, call-agent.c: Add 2 more arguments to all uses
|
||||
of assuan_transact.
|
||||
|
||||
2002-02-25 Werner Koch <wk@gnupg.org>
|
||||
|
||||
* server.c (option_handler): Allow to use -2 for "send all certs
|
||||
|
@ -245,13 +245,13 @@ gpgsm_agent_pksign (const char *keygrip,
|
||||
if (digestlen*2 + 50 > DIM(line))
|
||||
return seterr (General_Error);
|
||||
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -259,13 +259,13 @@ gpgsm_agent_pksign (const char *keygrip,
|
||||
p = line + strlen (line);
|
||||
for (i=0; i < digestlen ; i++, p += 2 )
|
||||
sprintf (p, "%02X", digest[i]);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
init_membuf (&data, 1024);
|
||||
rc = assuan_transact (agent_ctx, "PKSIGN",
|
||||
membuf_data_cb, &data, NULL, NULL);
|
||||
membuf_data_cb, &data, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
@ -327,14 +327,14 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
assert ( DIM(line) >= 50 );
|
||||
snprintf (line, DIM(line)-1, "SETKEY %s", keygrip);
|
||||
line[DIM(line)-1] = 0;
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -344,7 +344,7 @@ gpgsm_agent_pkdecrypt (const char *keygrip,
|
||||
cipher_parm.ciphertextlen = ciphertextlen;
|
||||
rc = assuan_transact (agent_ctx, "PKDECRYPT",
|
||||
membuf_data_cb, &data,
|
||||
inq_ciphertext_cb, &cipher_parm);
|
||||
inq_ciphertext_cb, &cipher_parm, NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
@ -403,7 +403,7 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
if (rc)
|
||||
return map_assuan_err (rc);
|
||||
|
||||
@ -415,7 +415,7 @@ gpgsm_agent_genkey (KsbaConstSexp keyparms, KsbaSexp *r_pubkey)
|
||||
return GNUPG_Invalid_Value;
|
||||
rc = assuan_transact (agent_ctx, "GENKEY",
|
||||
membuf_data_cb, &data,
|
||||
inq_genkey_parms, &gk_parm);
|
||||
inq_genkey_parms, &gk_parm, NULL, NULL);
|
||||
if (rc)
|
||||
{
|
||||
xfree (get_membuf (&data, &len));
|
||||
@ -458,7 +458,7 @@ gpgsm_agent_istrusted (KsbaCert cert)
|
||||
line[DIM(line)-1] = 0;
|
||||
xfree (fpr);
|
||||
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
@ -492,7 +492,7 @@ gpgsm_agent_marktrusted (KsbaCert cert)
|
||||
ksba_free (dn);
|
||||
xfree (fpr);
|
||||
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
@ -516,7 +516,7 @@ gpgsm_agent_havekey (const char *hexkeygrip)
|
||||
snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
|
||||
line[DIM(line)-1] = 0;
|
||||
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL);
|
||||
rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,8 @@ gpgsm_dirmngr_isvalid (KsbaCert cert)
|
||||
line[DIM(line)-1] = 0;
|
||||
xfree (certid);
|
||||
|
||||
rc = assuan_transact (dirmngr_ctx, line, NULL, NULL, inq_certificate, &parm);
|
||||
rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
|
||||
inq_certificate, &parm, NULL, NULL);
|
||||
return map_assuan_err (rc);
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ cmd_message (ASSUAN_CONTEXT ctx, char *line)
|
||||
|
||||
|
||||
/* Note that the line contains a space separated list of pappern where
|
||||
each pappern is percent escaped and spacesmay be replaced by
|
||||
each pappern is percent escaped and spaces may be replaced by
|
||||
'+'. */
|
||||
static int
|
||||
cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
|
||||
|
Loading…
x
Reference in New Issue
Block a user