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.
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);
}
}