Implement Assuan-based communication with card

Draft but functional code to connect to a running scdaemon and
issue a GET CHALLENGE command, and retrieve the generated random
data.
pull/1/head
Damien Goutte-Gattat 8 years ago
parent 6b920141ba
commit fba56566c9
  1. 10
      configure.ac
  2. 173
      src/scdrand.c

@ -18,6 +18,16 @@ AC_PROG_INSTALL
dnl Check for some non-ubiquitous
ICP_CHECK_NOTCH_FUNCS
dnl Check for Assuan and libgpg-error
AM_PATH_LIBASSUAN([2.1.0],
[CFLAGS="$CFLAGS $LIBASSUAN_CFLAGS"
LIBS="$LIBS $LIBASSUAN_LIBS"],
[AC_MSG_ERROR([libassuan not found])])
AM_PATH_GPG_ERROR([1.11],
[CFLAGS="$CFLAGS $GPG_ERROR_CFLAGS"
LIBS="$LIBS $GPG_ERROR_LIBS"],
[AC_MSG_ERROR([libgpg-error not found])])
dnl Output files
AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile])
AC_OUTPUT

@ -22,10 +22,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <err.h>
#include <xmem.h>
#include <assuan.h>
static void
usage(int status)
{
@ -49,10 +54,159 @@ This program is released under the GNU General Public License.\n\
exit(EXIT_SUCCESS);
}
/*
* Obtain the pathname of a running GPG Agent's listening socket
* from the environment. Terminate the program if the socket
* cannnot be found.
*
* @return
* A newly allocated string containing the socket's pathname,
* to be freed by the caller.
*/
static char *
get_gpg_agent_socket_name(void)
{
char *agent_info, *socket_name, *colon_index;
if ( ! (agent_info = getenv("GPG_AGENT_INFO")) )
errx(EXIT_FAILURE, "No running GPG Agent found");
socket_name = xstrdup(agent_info);
if ( (colon_index = strchr(socket_name, ':')) )
*colon_index = '\0';
return socket_name;
}
/*
* Data callback for the below function.
*/
gpg_error_t
scd_running_data_cb(void *arg, const void *line, size_t len)
{
*((char **)arg) = xstrdup(line);
return 0;
}
/*
* Obtain the pathname of a running scdaemon's listening socket.
* Terminate the program if the socket cannot be found.
*
* @return
* A newly allocated string containing the socket's pathname,
* to be freed by the caller.
*/
static char *
get_scd_socket_name(void)
{
char *gpg_agent_socket_name, *scd_socket_name;
assuan_context_t ctx;
gpg_error_t ge;
gpg_agent_socket_name = get_gpg_agent_socket_name();
if ( (ge = assuan_new(&ctx)) )
errx(EXIT_FAILURE, "Cannot create Assuan context: %s",
gpg_strerror(ge));
if ( (ge = assuan_socket_connect(ctx, gpg_agent_socket_name,
ASSUAN_INVALID_PID, 0)) )
errx(EXIT_FAILURE, "Cannot connect to GPG Agent: %s",
gpg_strerror(ge));
if ( (ge = assuan_transact(ctx, "GETINFO scd_running",
NULL, NULL, NULL, NULL, NULL, NULL)) )
errx(EXIT_FAILURE, "No running scdaemon found");
if ( (ge = assuan_transact(ctx, "SCD GETINFO socket_name",
scd_running_data_cb, &scd_socket_name,
NULL, NULL, NULL, NULL)) )
errx(EXIT_FAILURE, "Cannot obtain scdaemon's socket name: %s",
gpg_strerror(ge));
assuan_release(ctx);
free(gpg_agent_socket_name);
return scd_socket_name;
}
/*
* Represents a response to a GET CHALLENGE command.
*/
struct challenge
{
size_t len;
unsigned char *data;
};
/*
* Data callback for the below function.
*/
gpg_error_t
get_challenge_data_cb(void *arg, const void *line, size_t len)
{
struct challenge *c;
int i;
c = (struct challenge *)arg;
for ( i = 0; i < len && i < c->len; i++ )
c->data[i] = ((const unsigned char *)line)[i];
c->len = len;
return 0;
}
/*
* Get random data ("challenge") from the smartcard.
*
* @param scd_socket_name The pathname of the scdaemon socket.
* @param buffer The buffer to store the returned data into.
* @param len Number of bytes of random data to retrieve. The buffer
* must be big enough to store the requested amount.
*
* @return
* The number of bytes of random data actually retrieved and stored
* in the provided buffer.
*/
static int
get_challenge(const char *scd_socket_name, unsigned char *buffer, size_t len)
{
char command[12];
assuan_context_t ctx;
gpg_error_t ge;
struct challenge c;
snprintf(command, sizeof(command), "RANDOM %d", len);
if ( (ge = assuan_new(&ctx)) )
errx(EXIT_FAILURE, "Cannot create Assuan context: %s",
gpg_strerror(ge));
if ( (ge = assuan_socket_connect(ctx, scd_socket_name,
ASSUAN_INVALID_PID, 0)) )
errx(EXIT_FAILURE, "Cannot connect to scdaemon: %s", gpg_strerror(ge));
c.len = len;
c.data = buffer;
if ( (ge = assuan_transact(ctx, command, get_challenge_data_cb, &c,
NULL, NULL, NULL, NULL)) )
errx(EXIT_FAILURE, "Cannot get challenge from card: %s",
gpg_strerror(ge));
assuan_release(ctx);
return c.len;
}
int
main(int argc, char **argv)
{
int c;
char *scd_socket_name;
unsigned char random_buffer[64];
struct option options[] = {
{ "help", 0, NULL, 'h' },
@ -78,5 +232,24 @@ main(int argc, char **argv)
}
}
#ifndef GPG_ERR_INITIALIZED
gpg_err_init();
#endif
assuan_set_gpg_err_source(GPG_ERR_SOURCE_USER_1);
scd_socket_name = get_scd_socket_name();
if ( scd_socket_name ) {
int n, i;
n = get_challenge(scd_socket_name, random_buffer, 12);
fprintf(stderr, "Get %d bytes\n", n);
for ( i = 0; i < n; i++ ) {
fprintf(stderr, "%02X:", random_buffer[i]);
}
fprintf(stderr, "\n");
free(scd_socket_name);
}
return EXIT_SUCCESS;
}

Loading…
Cancel
Save