Programming with OpenPACE

Using OpenPACE in C/C++

See also

The OpenPACE API documentation has all details of the native C/C++ interface.

Here we have a small example in C:

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

const unsigned char EF_CARDACCESS[] = { 0x31, 0x81, 0x82, 0x30, 0x0D, 0x06, 0x08, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x02, 0x02, 0x01, 0x02, 0x30, 0x12, 0x06, 0x0A, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x41, 0x30, 0x12, 0x06, 0x0A, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x0D, 0x30, 0x1C, 0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x03, 0x02, 0x30, 0x0C, 0x06, 0x07, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x01, 0x02, 0x02, 0x01, 0x0D, 0x02, 0x01, 0x41, 0x30, 0x2B, 0x06, 0x08, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x06, 0x16, 0x1F, 0x65, 0x50, 0x41, 0x20, 0x2D, 0x20, 0x42, 0x44, 0x72, 0x20, 0x47, 0x6D, 0x62, 0x48, 0x20, 0x2D, 0x20, 0x54, 0x65, 0x73, 0x74, 0x6B, 0x61, 0x72, 0x74, 0x65, 0x20, 0x76, 0x32, 0x2E, 0x30, 0x04, 0x49, 0x17, 0x15, 0x41, 0x19, 0x28, 0x80, 0x0A, 0x01, 0xB4, 0x21, 0xFA, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x10, 0x10, 0x29, 0x10, 0x10, };
const char PIN[] = "123456";

#include <eac/eac.h>
#include <eac/pace.h>
#include <openssl/bio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    int r;
    BIO *bio = NULL;
    PACE_SEC *secret = NULL;
    EAC_CTX *picc_ctx = NULL, *pcd_ctx = NULL;
    BUF_MEM *enc_nonce = NULL, *pcd_mapping_data = NULL,
            *picc_mapping_data = NULL, *pcd_ephemeral_pubkey = NULL,
            *picc_ephemeral_pubkey = NULL, *pcd_token = NULL,
            *picc_token = NULL;

    EAC_init();

    puts("EF.CardAccess:");
    bio = BIO_new_fp(stdout, BIO_NOCLOSE|BIO_FP_TEXT);
    BIO_dump_indent(bio, (char *) EF_CARDACCESS, sizeof EF_CARDACCESS, 4);

    secret = PACE_SEC_new(PIN, strlen(PIN), PACE_PIN);

    puts("Secret:");
    PACE_SEC_print_private(bio, secret, 4);

    picc_ctx = EAC_CTX_new();
    pcd_ctx = EAC_CTX_new();
    EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, sizeof EF_CARDACCESS, pcd_ctx);
    EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, sizeof EF_CARDACCESS, picc_ctx);

    puts("PACE step 1");
    enc_nonce = PACE_STEP1_enc_nonce(picc_ctx, secret);

    puts("PACE step 2");
    PACE_STEP2_dec_nonce(pcd_ctx, secret, enc_nonce);

    puts("PACE step 3A");
    pcd_mapping_data = PACE_STEP3A_generate_mapping_data(pcd_ctx);
    picc_mapping_data = PACE_STEP3A_generate_mapping_data(picc_ctx);

    PACE_STEP3A_map_generator(pcd_ctx, picc_mapping_data);
    PACE_STEP3A_map_generator(picc_ctx, pcd_mapping_data);

    puts("PACE step 3B");
    pcd_ephemeral_pubkey = PACE_STEP3B_generate_ephemeral_key(pcd_ctx);
    picc_ephemeral_pubkey = PACE_STEP3B_generate_ephemeral_key(picc_ctx);

    PACE_STEP3B_compute_shared_secret(pcd_ctx, picc_ephemeral_pubkey);
    PACE_STEP3B_compute_shared_secret(picc_ctx, pcd_ephemeral_pubkey);

    puts("PACE step 3C");
    PACE_STEP3C_derive_keys(pcd_ctx);
    PACE_STEP3C_derive_keys(picc_ctx);

    puts("PACE step 3D");
    pcd_token = PACE_STEP3D_compute_authentication_token(pcd_ctx, picc_ephemeral_pubkey);
    picc_token = PACE_STEP3D_compute_authentication_token(picc_ctx, pcd_ephemeral_pubkey);

    r = PACE_STEP3D_verify_authentication_token(pcd_ctx, picc_token);
    if (r == 1)
        r = PACE_STEP3D_verify_authentication_token(picc_ctx, pcd_token);

    puts("PICC's EAC_CTX:");
    EAC_CTX_print_private(bio, picc_ctx, 4);
    puts("PCD's EAC_CTX:");
    EAC_CTX_print_private(bio, pcd_ctx, 4);

    EAC_CTX_clear_free(pcd_ctx);
    EAC_CTX_clear_free(picc_ctx);
    PACE_SEC_clear_free(secret);

    EAC_cleanup();

    if (bio)
        BIO_free_all(bio);
    if (enc_nonce)
        BUF_MEM_free(enc_nonce);
    if (pcd_mapping_data)
        BUF_MEM_free(pcd_mapping_data);
    if (picc_mapping_data)
        BUF_MEM_free(picc_mapping_data);
    if (pcd_ephemeral_pubkey)
        BUF_MEM_free(pcd_ephemeral_pubkey);
    if (picc_ephemeral_pubkey)
        BUF_MEM_free(picc_ephemeral_pubkey);
    if (pcd_token)
        BUF_MEM_free(pcd_token);
    if (picc_token)
        BUF_MEM_free(picc_token);

    if (r != 1)
        return 1;

    return 0;
}

See also

Have a look at the OpenSC Project 2 for a more complex project that uses the C Interface from OpenPACE.

Using OpenPACE in Python

Python bindings must be configured with --enable-python. They depend on SWIG and Python.

In case of a non-standard installation of OpenPACE you might – in addition to LD_LIBRARY_PATH – also need to setup the PYTHONPATH environment variable.

Here is a sample script that shows how OpenPACE is accessed from Python:

EF_CARDACCESS = b"\x31\x81\x82\x30\x0D\x06\x08\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x01\x02\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x04\x02\x02\x02\x01\x02\x02\x01\x0D\x30\x1C\x06\x09\x04\x00\x7F\x00\x07\x02\x02\x03\x02\x30\x0C\x06\x07\x04\x00\x7F\x00\x07\x01\x02\x02\x01\x0D\x02\x01\x41\x30\x2B\x06\x08\x04\x00\x7F\x00\x07\x02\x02\x06\x16\x1F\x65\x50\x41\x20\x2D\x20\x42\x44\x72\x20\x47\x6D\x62\x48\x20\x2D\x20\x54\x65\x73\x74\x6B\x61\x72\x74\x65\x20\x76\x32\x2E\x30\x04\x49\x17\x15\x41\x19\x28\x80\x0A\x01\xB4\x21\xFA\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x10\x10\x29\x10\x10"
PIN = b"123456"

import eac
eac.EAC_init()

secret = eac.PACE_SEC_new(PIN, eac.PACE_PIN)

buf = eac.get_buf(EF_CARDACCESS)
eac.hexdump(b"EF.CardAccess", buf)

print("Secret:")
print(eac.PACE_SEC_print_private(secret, 4))

picc_ctx = eac.EAC_CTX_new()
pcd_ctx = eac.EAC_CTX_new()
eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, pcd_ctx)
eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, picc_ctx)

print("PACE step 1")
enc_nonce = eac.PACE_STEP1_enc_nonce(picc_ctx, secret)

print("PACE step 2")
eac.PACE_STEP2_dec_nonce(pcd_ctx, secret, enc_nonce)

print("PACE step 3A")
pcd_mapping_data = eac.PACE_STEP3A_generate_mapping_data(pcd_ctx)
picc_mapping_data = eac.PACE_STEP3A_generate_mapping_data(picc_ctx)

eac.PACE_STEP3A_map_generator(pcd_ctx, picc_mapping_data)
eac.PACE_STEP3A_map_generator(picc_ctx, pcd_mapping_data)

print("PACE step 3B")
pcd_ephemeral_pubkey = eac.PACE_STEP3B_generate_ephemeral_key(pcd_ctx)
picc_ephemeral_pubkey = eac.PACE_STEP3B_generate_ephemeral_key(picc_ctx)

eac.PACE_STEP3B_compute_shared_secret(pcd_ctx, picc_ephemeral_pubkey)
eac.PACE_STEP3B_compute_shared_secret(picc_ctx, pcd_ephemeral_pubkey)

print("PACE step 3C")
eac.PACE_STEP3C_derive_keys(pcd_ctx)
eac.PACE_STEP3C_derive_keys(picc_ctx)

print("PACE step 3D")
pcd_token = eac.PACE_STEP3D_compute_authentication_token(pcd_ctx, picc_ephemeral_pubkey)
picc_token = eac.PACE_STEP3D_compute_authentication_token(picc_ctx, pcd_ephemeral_pubkey)

eac.PACE_STEP3D_verify_authentication_token(pcd_ctx, picc_token)
r = eac.PACE_STEP3D_verify_authentication_token(picc_ctx, pcd_token)

print("PICC's EAC_CTX:")
print(eac.EAC_CTX_print_private(picc_ctx, 4))
print("PCD's EAC_CTX:")
print(eac.EAC_CTX_print_private(pcd_ctx, 4))

eac.EAC_CTX_clear_free(pcd_ctx)
eac.EAC_CTX_clear_free(picc_ctx)
eac.PACE_SEC_clear_free(secret)

eac.EAC_cleanup()

if r != 1:
    sys.exit(1)

See also

Have a look at the Emulator for the German Identity Card 3 for a more complex project that uses the Python Interface from OpenPACE.

Unfortunately, OpenPACE’s Python bindings are currently poorly documented.

New in version 0.8: The SWIG bindings from pyPACE 1 have been integrated into OpenPACE.

Using OpenPACE in Ruby

Ruby bindings must be configured with --enable-ruby. They depend on SWIG and Ruby.

Here is a sample script that shows how OpenPACE is accessed from Ruby:

EF_CARDACCESS = ["318182300D060804007F00070202020201023012060A04007F000702020302020201020201413012060A04007F0007020204020202010202010D301C060904007F000702020302300C060704007F0007010202010D020141302B060804007F0007020206161F655041202D2042447220476D6248202D20546573746B617274652076322E3004491715411928800A01B421FA07000000000000000000000000000000000000201010291010"].pack('H*')
PIN = "123456"

require 'eac'
Eac.EAC_init()

secret = Eac.PACE_SEC_new(PIN, Eac::PACE_PIN)

buf = Eac.get_buf(EF_CARDACCESS)
Eac.hexdump("EF.CardAccess", buf)

puts "Secret:"
puts Eac.PACE_SEC_print_private(secret, 4)

picc_ctx = Eac.EAC_CTX_new()
pcd_ctx = Eac.EAC_CTX_new()
Eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, pcd_ctx)
Eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, picc_ctx)

puts "PACE step 1"
enc_nonce = Eac.PACE_STEP1_enc_nonce(picc_ctx, secret)

puts "PACE step 2"
Eac.PACE_STEP2_dec_nonce(pcd_ctx, secret, enc_nonce)

puts "PACE step 3A"
pcd_mapping_data = Eac.PACE_STEP3A_generate_mapping_data(pcd_ctx)
picc_mapping_data = Eac.PACE_STEP3A_generate_mapping_data(picc_ctx)

Eac.PACE_STEP3A_map_generator(pcd_ctx, picc_mapping_data)
Eac.PACE_STEP3A_map_generator(picc_ctx, pcd_mapping_data)

puts "PACE step 3B"
pcd_ephemeral_pubkey = Eac.PACE_STEP3B_generate_ephemeral_key(pcd_ctx)
picc_ephemeral_pubkey = Eac.PACE_STEP3B_generate_ephemeral_key(picc_ctx)

Eac.PACE_STEP3B_compute_shared_secret(pcd_ctx, picc_ephemeral_pubkey)
Eac.PACE_STEP3B_compute_shared_secret(picc_ctx, pcd_ephemeral_pubkey)

puts "PACE step 3C"
Eac.PACE_STEP3C_derive_keys(pcd_ctx)
Eac.PACE_STEP3C_derive_keys(picc_ctx)

puts "PACE step 3D"
pcd_token = Eac.PACE_STEP3D_compute_authentication_token(pcd_ctx, picc_ephemeral_pubkey)
picc_token = Eac.PACE_STEP3D_compute_authentication_token(picc_ctx, pcd_ephemeral_pubkey)

Eac.PACE_STEP3D_verify_authentication_token(pcd_ctx, picc_token)
r = Eac.PACE_STEP3D_verify_authentication_token(picc_ctx, pcd_token)

puts "PICC's EAC_CTX:"
puts Eac.EAC_CTX_print_private(picc_ctx, 4)
puts "PCD's EAC_CTX:"
puts Eac.EAC_CTX_print_private(pcd_ctx, 4)

Eac.EAC_CTX_clear_free(pcd_ctx)
Eac.EAC_CTX_clear_free(picc_ctx)
Eac.PACE_SEC_clear_free(secret)

Eac.EAC_cleanup()

if r != 1
    exit 1
end

New in version 0.9: Added Ruby bindings.

Using OpenPACE in Go

Go bindings must be configured with --enable-go. They depend on SWIG and gccgo.

Here is a sample program that shows how OpenPACE is accessed from Go:

package main

import (
	"fmt"
    "os"
    "eac"
)

func main() {

    EF_CARDACCESS := "\x31\x81\x82\x30\x0D\x06\x08\x04\x00\x7F\x00\x07\x02\x02\x02\x02\x01\x02\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x03\x02\x02\x02\x01\x02\x02\x01\x41\x30\x12\x06\x0A\x04\x00\x7F\x00\x07\x02\x02\x04\x02\x02\x02\x01\x02\x02\x01\x0D\x30\x1C\x06\x09\x04\x00\x7F\x00\x07\x02\x02\x03\x02\x30\x0C\x06\x07\x04\x00\x7F\x00\x07\x01\x02\x02\x01\x0D\x02\x01\x41\x30\x2B\x06\x08\x04\x00\x7F\x00\x07\x02\x02\x06\x16\x1F\x65\x50\x41\x20\x2D\x20\x42\x44\x72\x20\x47\x6D\x62\x48\x20\x2D\x20\x54\x65\x73\x74\x6B\x61\x72\x74\x65\x20\x76\x32\x2E\x30\x04\x49\x17\x15\x41\x19\x28\x80\x0A\x01\xB4\x21\xFA\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x10\x10\x29\x10\x10"
    PIN := "123456"

    eac.EAC_init()

    secret := eac.PACE_SEC_new(PIN, eac.PACE_PIN)

    buf := eac.Get_buf(EF_CARDACCESS)
    eac.Hexdump("EF.CardAccess", buf)

    /*fmt.Println("Secret:")*/
    /*eac.PACE_SEC_print_private(secret, 4)*/

    picc_ctx := eac.EAC_CTX_new()
    pcd_ctx := eac.EAC_CTX_new()
    eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, pcd_ctx)
    eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, picc_ctx)

    fmt.Println("PACE step 1")
    enc_nonce := eac.PACE_STEP1_enc_nonce(picc_ctx, secret)

    fmt.Println("PACE step 2")
    eac.PACE_STEP2_dec_nonce(pcd_ctx, secret, enc_nonce)

    fmt.Println("PACE step 3A")
    pcd_mapping_data := eac.PACE_STEP3A_generate_mapping_data(pcd_ctx)
    picc_mapping_data := eac.PACE_STEP3A_generate_mapping_data(picc_ctx)

    eac.PACE_STEP3A_map_generator(pcd_ctx, picc_mapping_data)
    eac.PACE_STEP3A_map_generator(picc_ctx, pcd_mapping_data)

    fmt.Println("PACE step 3B")
    pcd_ephemeral_pubkey := eac.PACE_STEP3B_generate_ephemeral_key(pcd_ctx)
    picc_ephemeral_pubkey := eac.PACE_STEP3B_generate_ephemeral_key(picc_ctx)

    eac.PACE_STEP3B_compute_shared_secret(pcd_ctx, picc_ephemeral_pubkey)
    eac.PACE_STEP3B_compute_shared_secret(picc_ctx, pcd_ephemeral_pubkey)

    fmt.Println("PACE step 3C")
    eac.PACE_STEP3C_derive_keys(pcd_ctx)
    eac.PACE_STEP3C_derive_keys(picc_ctx)

    fmt.Println("PACE step 3D")
    pcd_token := eac.PACE_STEP3D_compute_authentication_token(pcd_ctx, picc_ephemeral_pubkey)
    picc_token := eac.PACE_STEP3D_compute_authentication_token(picc_ctx, pcd_ephemeral_pubkey)

    eac.PACE_STEP3D_verify_authentication_token(pcd_ctx, picc_token)
    r := eac.PACE_STEP3D_verify_authentication_token(picc_ctx, pcd_token)

    /*fmt.Println("PICC's EAC_CTX:")*/
    /*eac.EAC_CTX_print_private(picc_ctx, 4)*/
    /*fmt.Println("PCD's EAC_CTX:")*/
    /*eac.EAC_CTX_print_private(pcd_ctx, 4)*/

    eac.EAC_CTX_clear_free(pcd_ctx)
    eac.EAC_CTX_clear_free(picc_ctx)
    eac.PACE_SEC_clear_free(secret)

    eac.EAC_cleanup()

    if r != 1 {
        os.Exit(1)
    }
}

New in version 0.9: Added Go bindings.

Using OpenPACE in Java

Ruby bindings must be configured with --enable-java. They depend on SWIG, a java compiler and the JNI developement headers. You may set the JAVAC environment variable to your preferred Java compiler.

Here is a sample program that shows how OpenPACE is accessed from Java:

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;

public class Example {
	static {
		System.loadLibrary("jeac");
	}

    public static void main(String argv[]) {
        final byte[] EF_CARDACCESS = new BigInteger("318182300D060804007F00070202020201023012060A04007F000702020302020201020201413012060A04007F0007020204020202010202010D301C060904007F000702020302300C060704007F0007010202010D020141302B060804007F0007020206161F655041202D2042447220476D6248202D20546573746B617274652076322E3004491715411928800A01B421FA07000000000000000000000000000000000000201010291010", 16).toByteArray();
        final String pin = "123456";
        byte[] PIN = null;
        try {
            PIN = pin.getBytes("ISO-8859-1");
        } catch (UnsupportedEncodingException ex) {
        }

        eac.EAC_init();

        SWIGTYPE_p_PACE_SEC secret = eac.PACE_SEC_new(PIN, s_type.PACE_PIN);

        SWIGTYPE_p_BUF_MEM buf = eac.get_buf(EF_CARDACCESS);
        eac.hexdump("EF.CardAccess", buf);

        //System.out.println("Secret:");
        //System.out.println(eac.PACE_SEC_print_private(secret, 4));

        SWIGTYPE_p_EAC_CTX picc_ctx = eac.EAC_CTX_new();
        SWIGTYPE_p_EAC_CTX pcd_ctx = eac.EAC_CTX_new();
        eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, pcd_ctx);
        eac.EAC_CTX_init_ef_cardaccess(EF_CARDACCESS, picc_ctx);

        System.out.println("PACE step 1");
        SWIGTYPE_p_BUF_MEM enc_nonce = eac.PACE_STEP1_enc_nonce(picc_ctx, secret);

        System.out.println("PACE step 2");
        eac.PACE_STEP2_dec_nonce(pcd_ctx, secret, enc_nonce);

        System.out.println("PACE step 3A");
        SWIGTYPE_p_BUF_MEM pcd_mapping_data = eac.PACE_STEP3A_generate_mapping_data(pcd_ctx);
        SWIGTYPE_p_BUF_MEM picc_mapping_data = eac.PACE_STEP3A_generate_mapping_data(picc_ctx);

        eac.PACE_STEP3A_map_generator(pcd_ctx, picc_mapping_data);
        eac.PACE_STEP3A_map_generator(picc_ctx, pcd_mapping_data);

        System.out.println("PACE step 3B");
        SWIGTYPE_p_BUF_MEM pcd_ephemeral_pubkey = eac.PACE_STEP3B_generate_ephemeral_key(pcd_ctx);
        SWIGTYPE_p_BUF_MEM picc_ephemeral_pubkey = eac.PACE_STEP3B_generate_ephemeral_key(picc_ctx);

        eac.PACE_STEP3B_compute_shared_secret(pcd_ctx, picc_ephemeral_pubkey);
        eac.PACE_STEP3B_compute_shared_secret(picc_ctx, pcd_ephemeral_pubkey);

        System.out.println("PACE step 3C");
        eac.PACE_STEP3C_derive_keys(pcd_ctx);
        eac.PACE_STEP3C_derive_keys(picc_ctx);

        System.out.println("PACE step 3D");
        SWIGTYPE_p_BUF_MEM pcd_token = eac.PACE_STEP3D_compute_authentication_token(pcd_ctx, picc_ephemeral_pubkey);
        SWIGTYPE_p_BUF_MEM picc_token = eac.PACE_STEP3D_compute_authentication_token(picc_ctx, pcd_ephemeral_pubkey);

        eac.PACE_STEP3D_verify_authentication_token(pcd_ctx, picc_token);
        int r = eac.PACE_STEP3D_verify_authentication_token(picc_ctx, pcd_token);

        //System.out.println("PICC's EAC_CTX:");
        //System.out.println(eac.EAC_CTX_print_private(picc_ctx, 4));
        //System.out.println("PCD's EAC_CTX:");
        //System.out.println(eac.EAC_CTX_print_private(pcd_ctx, 4));

        eac.EAC_CTX_clear_free(pcd_ctx);
        eac.EAC_CTX_clear_free(picc_ctx);
        eac.PACE_SEC_clear_free(secret);

        eac.EAC_cleanup();

        if (r != 1)
            System.out.println("Result was: " + r);
    }
}
1

http://pypace.sourceforge.net

2

https://github.com/OpenSC/OpenSC/blob/master/src/sm/sm-eac.c

3

https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html