Skip to content

Instantly share code, notes, and snippets.

@darconeous
Created March 17, 2020 21:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darconeous/adb1b2c4b15d3d8fbc72a5097270cdaf to your computer and use it in GitHub Desktop.
Save darconeous/adb1b2c4b15d3d8fbc72a5097270cdaf to your computer and use it in GitHub Desktop.
Installer script for ledger-u2f-javacard
#!/bin/sh
GP=${GP-java -jar gp.jar}
set -v
set -e
# This is for the ACR122U to put it into
# a nice state where it won't time out on us.
$GP -d -v -a ff0041ff00 -a ff00520000 || true
$GP -d -v \
--uninstall U2FApplet.cap
$GP -d -v \
--install U2FApplet.cap \
--create A0000006472F0001 \
--params "000140f3fccc0d00d8031954f90864d43c247f4bf5f0665c6b50cc17749a27d1cf7664"
$GP -d -v \
-a "00 A4 04 00 08 A0 00 00 06 47 2F 00 01" \
-a "80 01 00 00 80 30 82 01 3c 30 81 e4 a0 03 02 01 02 02 0a 47 90 12 80 00 11 55 95 73 52 30 0a 06 08 2a 86 48 ce 3d 04 03 02 30 17 31 15 30 13 06 03 55 04 03 13 0c 47 6e 75 62 62 79 20 50 69 6c 6f 74 30 1e 17 0d 31 32 30 38 31 34 31 38 32 39 33 32 5a 17 0d 31 33 30 38 31 34 31 38 32 39 33 32 5a 30 31 31 2f 30 2d 06 03 55 04 03 13 26 50 69 6c 6f 74 47 6e 75 62 62 79 2d 30 2e 34 2e 31 2d 34 37 39 30" \
-a "80 01 00 80 80 31 32 38 30 30 30 31 31 35 35 39 35 37 33 35 32 30 59 30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a 86 48 ce 3d 03 01 07 03 42 00 04 8d 61 7e 65 c9 50 8e 64 bc c5 67 3a c8 2a 67 99 da 3c 14 46 68 2c 25 8c 46 3f ff df 58 df d2 fa 3e 6c 37 8b 53 d7 95 c4 a4 df fb 41 99 ed d7 86 2f 23 ab af 02 03 b4 b8 91 1b a0 56 99 94 e1 01 30 0a 06 08 2a 86 48 ce 3d 04 03 02 03 47 00 30 44 02 20 60 cd" \
-a "80 01 01 00 40 b6 06 1e 9c 22 26 2d 1a ac 1d 96 d8 c7 08 29 b2 36 65 31 dd a2 68 83 2c b8 36 bc d3 0d fa 02 20 63 1b 14 59 f0 9e 63 30 05 57 22 c8 d8 9b 7f 48 88 3b 90 89 b8 8d 60 d1 d9 79 59 02 b3 04 10 df"
@jaredvacanti
Copy link

I believe this installer script reflects the change of CLA from F0 -> 80

Before using the applet, the attestation certificate shall be loaded using a proprietary APDU

CLA INS P1 P2 Data
F0 80 01 offset (high) offset (low) Certificate data chunk

Do you know any details of the attestation certificate? Is this the private key of a generic U2F cert providers know about? Is it available publicly?

@darconeous
Copy link
Author

Hi!

I believe this installer script reflects the change of CLA from F0 -> 80

I have not updated the readme in my fork yet. Yes, this is using CLA 0x80 instead of CLA 0xF0. CCU2F also uses 0x80. I'm not sure why the original uses 0xF0, it causes all sorts of problems. But my fork will accept a CLA of 0xF0, too.

Do you know any details of the attestation certificate? Is this the private key of a generic U2F cert providers know about? Is it available publicly?

If I remember correctly, it is the certificate and private key from the example section of the FIDO U2F packet format documentation. It's about as generic as it gets.

@darconeous
Copy link
Author

For what it's worth, I've gone ahead and updated the README.

@jaredvacanti
Copy link

Thanks to your clarifications, I was able to track down the example attestation certificate in the Fido Alliance's spec page FIDO U2F Raw Message Formats:

Assume we have a U2F token with the following private attestation key:

f3fccc0d00d8031954f90864d43c247f4bf5f0665c6b50cc17749a27d1cf7664

the corresponding public key:

048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101

and the following attestation cert:

[
[
Version: V3
Subject: CN=PilotGnubby-0.4.1-47901280001155957352 Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2
Key: EC Public Key
X:
8d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa Y:
3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101

Validity: [From: Tue Aug 14 11:29:32 PDT 2012, To: Wed Aug 14 11:29:32 PDT 2013]

Issuer: CN=Gnubby Pilot
SerialNumber: [ 47901280 00115595 7352] ]

Algorithm: [SHA256withECDSA]
Signature:
0000: 30 44 02 20 60 CD B6 06 1E 9C 22 26 2D 1A AC 1D 0D. `....."&-...
0010: 96 D8 C7 08 29 B2 36 65 31 DD A2 68 83 2C B8 36 ....).6e1..h.,.6
0020: BC D3 0D FA 02 20 63 1B 14 59 F0 9E 63 30 05 57 ..... c..Y..c0.W
0030: 22 C8 D8 9B 7F 48 88 3B 90 89 B8 8D 60 D1 D9 79 "....H.;....`..y
0040: 59 02 B3 04 10 DF Y.....
]

...
...

Posting here in case anyone else has the same confusion. Thanks again!

@makkarpov
Copy link

makkarpov commented Aug 29, 2021

I made an extended version of script that will generate really private private keys and corresponding attestation cert:

#!/bin/bash

function usage {
  echo "usage: $0 <cap file> <gp jar> <certificate subject> [extra gp args...]" >&2
  echo "" >&2
  echo "Generates fresh key and certificate, installs applet on a card and" >&2
  echo "uploads a certificate, giving you ready-to-use card" >&2
  echo "" >&2
  echo "If you have a locked card, specify extra arguments like '--key XXX'" >&2
  exit 2
}

if [ -z "$1" -o -z "$2" -o -z "$3" ]; then
  usage
fi

CAP_FILE="$1"
GP_JAR="$2"
CERT_SUBJECT="$3"
shift 3

echo "++ Generating keys and certificates..."

set -e # make shell exit on errors

[ -d "certs" ] || mkdir certs
[ -f "certs/private.pem" ] || openssl ecparam -genkey -name P-256 -out "certs/private.pem"

## Extract OpenSSL config: config is needed to avoid space-consuming extensions like "Subject key info"
if [ ! -f "certs/config.conf" ]; then
  base64 -d <<EOF | gzip -d > "certs/config.conf"
H4sIAJrPK2ECA22MQQqAMAwE73lF/+DZl5QSxC4axFSbKvb3VjzqdWZnvcvYXaAoVkSnQ2xGZB1W
uP5R/BX0YFwFapLU2u7suDGCjjVvhRfUBjUR+f+HQP5NAt0Vsx1wgAAAAA==
EOF
fi

# Generate self-signed cert
[ -f "certs/cert.crt" ] || openssl req -x509 -config "certs/config.conf" -key "certs/private.pem" \
  -sha256 -days 36500 -nodes -out "certs/cert.crt" -subj "/CN=$CERT_SUBJECT"
  
# Convert from PEM to DER
openssl x509 -in "certs/cert.crt" -outform DER -out "certs/cert.cer"

# Convert private key to just 32 raw bytes
PRIVATE_KEY=$(openssl ec -in "certs/private.pem" -text 2>/dev/null | grep -A 3 priv: | tail -n +2 | tr -d ':[:space:]')
CERT_LENGTH=$(stat --printf "%s" "certs/cert.cer")

echo "++ Installing everything on card..."

function gp {
  echo "+ java -jar \"$GP_JAR\" $@"
  java -jar "$GP_JAR" $@
}

gp "$@" \
  --install "$CAP_FILE" \
  --create A0000006472F0001 \
  --params "00$(printf "%04x" "$CERT_LENGTH")$PRIVATE_KEY"

OFFSET=0
FRAGMENT=64
APDUS=(--apdu "00A4040008A0000006472F0001")

while [ "$OFFSET" -lt "$CERT_LENGTH" ]; do
  CERT_CHUNK=$(dd if="certs/cert.cer" bs=1 skip="$OFFSET" count="$FRAGMENT" status=none | xxd -p -c 9999)
  CHUNK_LENGTH=$(($(echo "$CERT_CHUNK" | wc -c) / 2))
  APDUS=(${APDUS[@]} --apdu "8001$(printf "%04x%02x" "$OFFSET" "$CHUNK_LENGTH")$CERT_CHUNK")  
  OFFSET=$(($OFFSET+$FRAGMENT))
done

# Install the certificate
gp ${APDUS[@]}

@darconeous
Copy link
Author

I made an extended version of script that will generate really private private keys and corresponding attestation cert:

That's really cool! Thanks for writing that up!

One of the reasons why I avoid doing this for my own tokens is that it makes the token uniquely identifiable across services, which is something I was trying to avoid.

@makkarpov
Copy link

Well, homebrew U2F token is sufficiently unique on it's own :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment