Library libpskc: PSKC Library (libpskc) Manual

## Library libpskc

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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #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;
      size_t len;
      pskc_t *container;
      pskc_key_t *keypackage;
      int rc;

      if (argc != 2)
        {
          printf ("Usage: %s <PSKCFILE>\n", argv[0]);
          return 1;
        }
      fh = fopen (argv[1], "r");
      if (!fh)
        {
          perror ("fopen");
          return 1;
        }
      len = fread (buffer, 1, sizeof (buffer), fh);
      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 != (size_t) 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:~$