diff --git a/README.md b/README.md index e087b0d..6248f25 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,11 @@ SmartPGP is a free and open source implementation of the [OpenPGP card 3.4 specification](https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf) in JavaCard. +The main improvement introduced in OpenPGP card 3.x specification from +previous version is the support of elliptic curve cryptography with +several existing curves (NIST P-256, NIST P-384, NIST P-521, brainpool +p256r1, brainpool p384r1 and brainpool p512r1). + ## Features @@ -26,6 +31,8 @@ of them depend on underlying hardware support and available - AES 128/256 bits deciphering primitive; +- Secure messaging (see below). + ## Default values @@ -39,18 +46,47 @@ The SmartPGP applet is configured with the following default values: - RSA 2048 bits for PGP keys; +- NIST P-256 for the secure messaging key. + These values can be changed by modifying default values in the code (see the [Constants](src/fr/anssi/smartpgp/Constants.java) class). When the applet is installed, one can use the `smartpgp-cli` utility given in the `bin` directory to change these values. Keep in mind that -when you change the algorithm attributes of a PGP key, the key and the -corresponding certificate are +when you change the algorithm attributes of a PGP key or of the secure +messaging key, the key and the corresponding certificate are erased. Also note that hard coded default values will be restored upon a factory reset. +## Compliance with OpenPGP card 3.4 specification + +The SmartPGP applet implements the complete OpenPGP card 3.4 +specification, except the secure messaging related features: + +- Commands and responses protection is not implemented as described in + the specification. Motivation and implementation details are + explained in the + [secure messaging document](secure_messaging/smartpgp_sm.pdf); + +- A command protected by secure messaging is not granted admin + rights. Secure messaging can thus be used to protect communications + only, especially when the token is used contactless; + +- If and only if secure messaging static key and certificate have been + provisioned, all commands containing sensitive data (e.g. PIN code, + decrypted data, private key, ...) emitted through a contactless + interface must be protected by secure messaging or they will be + refused; + +- The `ACTIVATE FILE` with P1 = P2 = 0, as described in the + specification, resets everything except the secure messaging static + key and certificate. Complete reset, including these elements, can + be performed with `ACTIVATE FILE` with P1 = 0 and P2 = 1. + + + # Application support Tokens following the OpenPGP card 3.4 specification are not yet fully @@ -61,18 +97,28 @@ supported by most PGP applications. OpenPGP card 3.x is supported by [GnuPG](https://www.gnupg.org/) starting from version 2.1.16. +The specific secure messaging of the SmartPGP applet is **not** +supported at is not part of the OpenPGP card specification. + ## OpenKeychain OpenPGP card 3.x is supported by [OpenKeychain](https://www.openkeychain.org/) starting from version 4.2. +The secure messaging of the SmartPGP applet is fully supported in +OpenKeychain. See the section below for more information on the setup process. + # Content of the repository The repository contains several directories: - `bin` contains a Python library and command line tool called - `smartpgp-cli` to interact with an OpenPGP card 3.x; + `smartpgp-cli` to interact with an OpenPGP card 3.x but also to deal + with the specific secure messaging feature of the SmartPGP applet; + +- `secure_messaging` contains documentation and example scripts to + play with the secure messaging feature of SmartPGP; - `src` contains the JavaCard source code of the SmartPGP applet; @@ -131,7 +177,8 @@ resource consumption by tweaking the following variables: - `Constants.EXTENDED_CAPABILITIES`, bytes 5 and 6: the maximal size in bytes of a certificate associated to a key. Following the OpenPGP card specification, a certificate can be stored for each of the - three keys. + three keys. In SmartPGP, a fourth certificate is stored for secure + messaging. ## Building the CAP file @@ -158,3 +205,93 @@ Be careful to use a valid AID according to the OpenPGP card specification (see section 4.2.1) for each card (`-create ` with GlobalPlatformPro) + + +# Setting up secure messaging with OpenKeychain + +## Secure messaging without token authentication + +Without token authentication, you are not protected against +man-in-the-middle attack as your device cannot ensure it is +communicating directly with a trusted token. Nevertheless, the +communications with the token are still protected in confidentiality +against passive attacks (i.e. trafic capture). + +If you want to test secure messaging without token authentication, you +can use the following command to order the token to generate its +secure messaging key on-board. + +`./smartpgp-cli -r X -I generate-sm-key -o pubkey.raw` + +In this case, you have to deactivate the certificate verification in +OpenKeychain: go to "Parameters" > "Experimental features" and +deactivate the option called "SmartPGP verify certificate". + + +## Secure messaging with token authentication + +The `secure_messaging` directory contains a subdirectory called `pki` +which contains two sample scripts to generate a certificate +authority and token certificates. + +The sample scripts are given **only** for test purposes of the secure +messaging feature with certificate verification. They require +`openssl` to be installed on your system. + +If you want to use your own PKI, you have to generate a specific +intermediate certificate authority to sign the certificates of your +token(s). Then, you have to provision the complete certificate chain +from this new intermediate CA to your root CA in OpenKeychain because +the certificate verification implemented in the given patch does not +rely on the system keystore. + +### Generate a sample CA key and certificate + +Change your current directory to the `pki` directory and execute the +script `./generate_ca.sh`. It will produce a sample CA key in +`PKI/private/ca.key.pem` and the corresponding certificate in +`PKI/certs/ca.cert.pem`. + +### Generate a sample token key and certificate + +Change your current directory to the `pki` directory and execute the +script + +`./generate_token.sh mycard1` + +where `mycard1` is some unique identifier for the token. It will +produce a sample token key in `PKI/private/mycard1.key.pem` and the +corresponding certificate in `PKI/certs/mycard1.cert.pem`. + +### Provision the token with its sample key and certificate + +Change your current directory to the `bin` directory and execute the +following commands after replacing the reader number `X` by the number +of the reader that contains your token, and the path to the `pki` +directory used in previous sections. + +The following command imports the token key in the token. + +`./smartpgp-cli -r X -I -i path_to_the_pki_dir/PKI/private/mycard1.key.der put-sm-key` + +The following command imports the token certificate in the token. + +`./smartpgp-cli -r X -I -i path_to_the_pki_dir/PKI/certs/mycard1.cert.der put-sm-certificate` + +These commands have to be executed in this order because the key +import clears any previously stored certificate. + +Once the token key is imported, you should remove the token private +key from you system as there is no need to keep it outside of your +token. + +### Install the CA in OpenKeychain + +- Upload the CA certificate `PKI/certs/ca.cert.pem` to your phone; + +- Go to "Parameters" > "Experimental features" and activate the option called "SmartPGP verify certificate`; + +- Click on "SmartPGP trusted authorities", and then on "+" at the top left; + +- Set a name for this authority and select the file you uploaded. + diff --git a/secure_messaging/pki/generate_ca.sh b/secure_messaging/pki/generate_ca.sh new file mode 100755 index 0000000..3bd41c8 --- /dev/null +++ b/secure_messaging/pki/generate_ca.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +CURVE=secp521r1 +DAYS=1825 + +###### + +DIR=PKI + +###### + +set -e -u + +if [[ -e "$DIR/private/ca.key.pem" ]] ; then + echo "CA already exists, please remove it manually if you want to generate a new one" 1>&2 + exit 2 +fi + +mkdir -p "$DIR/private" "$DIR/certs" + +openssl ecparam -name "$CURVE" -genkey -check -noout -outform pem -out "$DIR/private/ca.key.pem" + +openssl req -config openssl.cnf -extensions v3_ca -days $DAYS -new -x509 -sha256 -keyform pem -key "$DIR/private/ca.key.pem" -outform pem -out "$DIR/certs/ca.cert.pem" + +touch $DIR/index.txt + +echo 1000 > $DIR/serial + +echo 1000 > $DIR/crlnumber diff --git a/secure_messaging/pki/generate_token.sh b/secure_messaging/pki/generate_token.sh new file mode 100755 index 0000000..b434dad --- /dev/null +++ b/secure_messaging/pki/generate_token.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +CURVE=secp256r1 +DAYS=730 + +###### + +DIR=PKI + +###### + +set -e -u + +if [[ $# -lt 1 ]] ; then + echo "Missing card certificate identifier" 1>&2 + exit 1 +fi +if [[ $# -gt 1 ]] ; then + echo "Too many parameters" 1>&2 + exit 2 +fi + +if [[ ! -e "$DIR/private/ca.key.pem" ]] ; then + echo "Missing CA (please execute generate_ca.sh)" 1>&2 + exit 2 +fi + + +NAME="$1" + +mkdir -p "$DIR/csr" + +openssl ecparam -name "$CURVE" -genkey -check -noout -outform der -out "$DIR/private/$NAME.key.der" + +openssl req -config openssl.cnf -new -sha256 -keyform der -key "$DIR/private/$NAME.key.der" -outform pem -out "$DIR/csr/$NAME.csr.pem" + +openssl ca -config openssl.cnf -extensions card_cert -days $DAYS -md sha256 -in "$DIR/csr/$NAME.csr.pem" -out "$DIR/certs/$NAME.cert.pem" + +openssl x509 -inform pem -in "$DIR/certs/$NAME.cert.pem" -outform der -out "$DIR/certs/$NAME.cert.der" + +rm "$DIR/certs/$NAME.cert.pem" + diff --git a/secure_messaging/pki/openssl.cnf b/secure_messaging/pki/openssl.cnf new file mode 100644 index 0000000..bda6751 --- /dev/null +++ b/secure_messaging/pki/openssl.cnf @@ -0,0 +1,91 @@ + +[ ca ] +default_ca = CA_default + +[ CA_default ] +# Directory and file locations. +dir = ./PKI/ +certs = $dir/certs +crl_dir = $dir/crl +new_certs_dir = $dir/certs +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $dir/private/.rand + +# The root key and root certificate. +private_key = $dir/private/ca.key.pem +certificate = $dir/certs/ca.cert.pem + +# For certificate revocation lists. +crlnumber = $dir/crlnumber +crl = $dir/crl/ca.crl.pem +crl_extensions = crl_ext +default_crl_days = 30 + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +name_opt = ca_default +cert_opt = ca_default +default_days = 730 +preserve = no +policy = policy_loose + +[ policy_loose ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +# Options for the `req` tool (`man req`). +default_bits = 2048 +distinguished_name = req_distinguished_name +string_mask = utf8only + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +# Extension to add when the -x509 option is used. +x509_extensions = v3_ca + +[ req_distinguished_name ] +# See . +countryName = Country Name (2 letter code) +stateOrProvinceName = State or Province Name +localityName = Locality Name +0.organizationName = Organization Name +organizationalUnitName = Organizational Unit Name +commonName = Common Name +emailAddress = Email Address + +# Optionally, specify some defaults. +countryName_default = +stateOrProvinceName_default = +localityName_default = +0.organizationName_default = +organizationalUnitName_default = +emailAddress_default = + +[ v3_ca ] +# Extensions for a typical CA (`man x509v3_config`). +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true, pathlen:1 +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ card_cert ] +# Extensions for client certificates (`man x509v3_config`). +basicConstraints = CA:FALSE +nsCertType = client +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation + +[ crl_ext ] +# Extension for CRLs (`man x509v3_config`). +authorityKeyIdentifier=keyid:always +