»Home
»News
»Download
»Documentation
     »oathtool(1)
     »pskctool(1)
     »Liboath API
     »Libpskc Quickstart
     »Libpskc API
     »pam_oath
»Contribute
     »Code
     »Bug tracker
OATH Toolkit
One-time password components

Quickstart

The Portable Symmetric Key Container (PSKC) format is used to transport and provision symmetric keys to cryptographic devices or software. The PSKC Library allows you to parse, validate and generate PSKC data. The PSKC Library is written in C, uses LibXML, and is licensed under LGPLv2+. A companion to the library is a command line tool (pskctool) to interactively manipulate PSKC data.

To get a feeling of the PSKC data format we show the shortest possible valid PSKC content.

1
2
3
4
<?xml version="1.0"?>
<KeyContainer xmlns="urn:ietf:params:xml:ns:keyprov:pskc" Version="1.0">
  <KeyPackage/>
</KeyContainer>

Of course, since the intent with PSKC is to transport cryptographic keys, the example above is of little use since it does not carry any keys. The next example is more realistic, and show PSKC data used to transport a key used for a OATH HOTP implementation. The interesting values are the DeviceInfo values to identify the intended hardware, the Key Id "12345678", and the base64-encoded shared secret "MTIzNA==".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<KeyContainer Version="1.0"
	      xmlns="urn:ietf:params:xml:ns:keyprov:pskc">
  <KeyPackage>
    <DeviceInfo>
      <Manufacturer>Manufacturer</Manufacturer>
      <SerialNo>987654321</SerialNo>
    </DeviceInfo>
    <Key Id="12345678"
         Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:hotp">
      <AlgorithmParameters>
        <ResponseFormat Length="8" Encoding="DECIMAL"/>
      </AlgorithmParameters>
      <Data>
        <Secret>
          <PlainValue>MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
          </PlainValue>
        </Secret>
        <Counter>
          <PlainValue>0</PlainValue>
        </Counter>
      </Data>
    </Key>
  </KeyPackage>
</KeyContainer>

To illustrate how the library works, let's give an example on how to parse the data above and print the device serial number (SerialNo field). The code below is complete and working but performs minimal error checking.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <pskc/pskc.h>

/*
 * $ cc -o serialno serialno.c $(pkg-config --cflags --libs libpskc)
 * $ ./serialno pskc-hotp.xml
 * SerialNo: 987654321
 * $
 */

#define PSKC_CHECK_RC					   \
  if (rc != PSKC_OK) {					   \
    printf ("%s (%d): %s\n", pskc_strerror_name (rc),	   \
	    rc, pskc_strerror (rc));			   \
    return 1;						   \
  }

int
main (int argc, const char *argv[])
{
  char buffer[4096];
  FILE *fh = fopen (argv[1], "r");
  size_t len = fread (buffer, 1, sizeof (buffer), fh);
  pskc_t *container;
  pskc_key_t *keypackage;
  int rc;

  fclose (fh);

  rc = pskc_global_init ();
  PSKC_CHECK_RC;

  rc = pskc_init (&container);
  PSKC_CHECK_RC;
  rc = pskc_parse_from_memory (container, len, buffer);
  PSKC_CHECK_RC;

  keypackage = pskc_get_keypackage (container, 0);

  if (keypackage)
    printf ("SerialNo: %s\n", pskc_get_device_serialno (keypackage));

  pskc_done (container);
  pskc_global_done ();
}

Compiling and linking code with the PSKC Library requires that you specify correct compilation flags so that the header include file and the shared library is found. There is only one include file and it should be used like #include <pskc/pskc.h>. The library is called libpskc.so on GNU systems and libpskc.dll on Windows systems. To build the previous file, assuming the code is stored in a file called "serialno.c", invoke the following command.

1
cc -o serialno serialno.c -I/path/to/pskc/include/path -L/path/to/pskc/lib/path -Wl,-rpath/path/to/pskc/lib/path -lpskc

A pkg-config file is provided, so that you may use pkg-config to select proper compilation flags if you want.

1
cc -o serialno serialno.c $(pkg-config --cflags --libs libpskc)

After building the tool you would invoke it passing the name of the file with the PSKC input above, and it will print the serial number.

1
2
3
jas@latte:~$ ./serialno pskc.xml
SerialNo: 987654321
jas@latte:~$

Converting PSKC data to CSV format

We conclude with a larger example illustrating how to read a PSKC file, parse it and print a human readable summary of the PSKC data to stderr, validate it against the PSKC XML Schema (this is normally not needed) and print the validation outcome to stderr, and iterate through all keys in the file and print to stdout a comma-separated list with three fields: the key id, the device serialno, and the hex encoded cryptographic key. This code example check error codes and releases resources.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pskc/pskc.h>

/*
 * $ cc -o pskc2csv pskc2csv.c $(pkg-config --cflags --libs libpskc)
 * $ ./pskc2csv pskc.xml 2> /dev/null
 * 12345678,12345678,MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
 * $
 */

int
main (int argc, const char *argv[])
{
  struct stat st;
  FILE *fh = NULL;
  char *buffer = NULL, *out;
  size_t i;
  pskc_t *container = NULL;
  pskc_key_t *keypackage;
  int exit_code = EXIT_FAILURE, rc, isvalid;

  rc = pskc_global_init ();
  if (rc != PSKC_OK)
    {
      fprintf (stderr, "pskc_global_init: %s\n", pskc_strerror (rc));
      goto done;
    }

  if (argc != 2)
    {
      fprintf (stderr, "Usage: %s PSKCFILE\n", argv[0]);
      goto done;
    }

  /* Part 1: Read file. */

  fh = fopen (argv[1], "r");
  if (fh == NULL)
    {
      perror ("fopen");
      goto done;
    }

  if (fstat (fileno (fh), &st) != 0)
    {
      perror ("fstat");
      goto done;
    }

  buffer = malloc (st.st_size);
  if (buffer == NULL)
    {
      perror ("malloc");
      goto done;
    }

  i = fread (buffer, 1, st.st_size, fh);
  if (i != st.st_size)
    {
      fprintf (stderr, "short read\n");
      goto done;
    }

  /* Part 2: Parse PSKC data. */

  rc = pskc_init (&container);
  if (rc != PSKC_OK)
    {
      fprintf (stderr, "pskc_init: %s\n", pskc_strerror (rc));
      goto done;
    }

  rc = pskc_parse_from_memory (container, i, buffer);
  if (rc != PSKC_OK)
    {
      fprintf (stderr, "pskc_parse_from_memory: %s\n", pskc_strerror (rc));
      goto done;
    }

  /* Part 3: Output human readable variant of PSKC data to stderr. */

  rc = pskc_output (container, PSKC_OUTPUT_HUMAN_COMPLETE, &out, &i);
  if (rc != PSKC_OK)
    {
      fprintf (stderr, "pskc_output: %s\n", pskc_strerror (rc));
      goto done;
    }

  fprintf (stderr, "%.*s\n", (int) i, out);

  pskc_free (out);

  /* Part 4: Validate PSKC data. */

  rc = pskc_validate (container, &isvalid);
  if (rc != PSKC_OK)
    {
      fprintf (stderr, "pskc_validate: %s\n", pskc_strerror (rc));
      goto done;
    }

  fprintf (stderr, "PSKC data is Schema valid: %s\n", isvalid ? "YES" : "NO");

  /* Part 5: Iterate through keypackages and print key id, device
     serial number and base64 encoded secret. */

  for (i = 0; (keypackage = pskc_get_keypackage (container, i)); i++)
    {
      const char *key_id = pskc_get_key_id (keypackage);
      const char *device_serialno = pskc_get_key_id (keypackage);
      const char *b64secret = pskc_get_key_data_b64secret (keypackage);

      printf ("%s,%s,%s\n", key_id ? key_id : "",
	      device_serialno ? device_serialno : "",
	      b64secret ? b64secret : "");
    }

  exit_code = EXIT_SUCCESS;

done:
  pskc_done (container);
  if (fh && fclose (fh) != 0)
    perror ("fclose");
  free (buffer);
  pskc_global_done ();
  exit (exit_code);
}

Below we'll illustrate how to build the tool and run it on the same PSKC data as above. The tool prints different things to stdout and stderr, which you can see below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
jas@latte:~$ cc -o pskc2csv pskc2csv.c $(pkg-config --cflags --libs libpskc)
jas@latte:~$ ./pskc2csv pskc.xml 2> /dev/null
12345678,12345678,MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
jas@latte:~$ ./pskc2csv pskc.xml > /dev/null
Portable Symmetric Key Container (PSKC):
	Version: 1.0
	KeyPackage 0:
		DeviceInfo:
			Manufacturer: Manufacturer
			SerialNo: 987654321
		Key:
			Id: 12345678
			Algorithm: urn:ietf:params:xml:ns:keyprov:pskc:hotp
			Key Secret (base64): MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=
			Key Counter: 0
			Response Format Length: 8
			Response Format Encoding: DECIMAL

PSKC data is Schema valid: YES
jas@latte:~$