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

## Using OpenPACE in Javascript

Technically the process for getting OpenPACE into Javascript is similar to cross compiling. It is covered in Compiling for Javascript

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