Skip to content

Instantly share code, notes, and snippets.

@rrottmann
Last active February 22, 2026 16:57
Show Gist options
  • Select an option

  • Save rrottmann/6db8cf92fc4501c78a7dc5d9a9d21914 to your computer and use it in GitHub Desktop.

Select an option

Save rrottmann/6db8cf92fc4501c78a7dc5d9a9d21914 to your computer and use it in GitHub Desktop.
Picokey HSM Initialization with own Domain Validation <3 librekeys team
# Based on https://github.com/Disappear9/pico-hsm-cvcgen/tree/main/research
# Tested on Debian 13.2 on aarch64 with Waveshare RP2040 zero and pico_hsm_pico-6.4.uf2
t=$(mktemp -d)
cd $t
cat >requirements.txt <<"EOF"
pycvc
pypicokey @ git+https://github.com/librekeys/pypicokey-mirror
pypicohsm @ git+https://github.com/librekeys/pypicohsm
EOF
apt update && apt install -y python3 python3-venv
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
wget https://github.com/librekeys/pico-hsm-sf/raw/refs/heads/master/tools/pico-hsm-tool.py
wget https://raw.githubusercontent.com/Disappear9/pico-hsm-cvcgen/refs/heads/main/research/convertcvc.py
wget https://raw.githubusercontent.com/Disappear9/pico-hsm-cvcgen/refs/heads/main/research/convertder.py
wget https://raw.githubusercontent.com/librekeys/pypicohsm/412aa07898a355beaec7f1b5da3895f8e85f5a09/picohsm/PicoHSM.py
cat > PicoHSM.patch <<"EOF"
--- PicoHSM.py.orig     2026-02-18 21:41:56.875706459 +0100
+++ PicoHSM.py  2026-02-19 17:08:38.328373314 +0100
@@ -29,6 +29,7 @@
 from binascii import hexlify
 import base64
 import urllib
+import json

 try:
     from picokey import PicoKey, SecureChannel, APDUResponse, Product, Platform, PhyOpt
@@ -187,14 +188,19 @@

             cert = bytearray(response)
             Y = CVC().decode(cert).pubkey().find(0x86).data()
-            #print(f'Public Point: {hexlify(Y).decode()}')
+            ##
+            print(f'Public Point: {hexlify(Y).decode()}')

             pbk = base64.urlsafe_b64encode(Y)
             params = {'pubkey': pbk}
-            if (self.__card.platform in (Platform.RP2350, Platform.ESP32, Platform.EMULATION)):
+            print(self.__card.platform)
+            if (self.__card.platform in (Platform.RP2040, Platform.RP2350, Platform.ESP32, Platform.EMULATION)):
                 params['curve'] = 'secp256k1'
+            print(params)
             data = urllib.parse.urlencode(params).encode()
-            j = get_pki_data('cvc', data=data)
+            ##j = get_pki_data('cvc', data=data)
+            input_json = input("Enter a JSON string: ")
+            j = json.loads(input_json)
             #print('Device name: '+j['devname'])
             dataef = base64.urlsafe_b64decode(
                 j['cvcert']) + base64.urlsafe_b64decode(j['dvcert']) + base64.urlsafe_b64decode(j['cacert'])
@@ -900,7 +906,7 @@

     def general_authentication(self):
         self.send(command=0x22, p1=0x41, p2=0xA4, data=ASN1().add_tag(0x80, SecureChannel.PROTO_OID).encode())
-        pkeyB = ec.generate_private_key(ec.SECP256R1())
+        pkeyB = ec.generate_private_key(ec.SECP256K1())
         pbkeyB = pkeyB.public_key()
         pbkeyBytes = PicoHSM.__pubkey_uncompressed(pbkeyB)
         dado = ASN1().add_tag(0x7C, ASN1().add_tag(0x80, pbkeyBytes).encode()).encode()
EOF
patch venv/lib64/python3.13/site-packages/picohsm/PicoHSM.py < PicoHSM.patch
cat > convertder.py.patch <<"EOF"
--- convertder.py       2026-02-19 17:00:36.676131715 +0100
+++ convertder_RP2040.py        2026-02-19 17:03:05.414877545 +0100
@@ -1,50 +1,56 @@
-import base64
-import binascii
-from cryptography.hazmat.primitives import serialization
-from cryptography.hazmat.primitives.asymmetric import ec
-
-def convert_y_to_der(y_bytearray):
-    """
-    将Y字节数组转换为DER格式的SubjectPublicKeyInfo
-    """
-    # 确保是字节格式
-    if isinstance(y_bytearray, bytearray):
-        y_bytes = bytes(y_bytearray)
-    else:
-        y_bytes = y_bytearray
-
-    # 验证格式和长度
-    if len(y_bytes) != 65 or y_bytes[0] != 0x04:
-        raise ValueError("无效的公钥格式,应为65字节以0x04开头")
-
-    # 创建secp256k1公钥对象
-    public_key = ec.EllipticCurvePublicKey.from_encoded_point(
-        ec.SECP256R1(), #RP2040 uses R1 even when K1 is explicitely requested!
-        #ec.SECP256K1(),
-        y_bytes
-    )
-
-    # 导出为DER格式
-    der_bytes = public_key.public_bytes(
-        encoding=serialization.Encoding.DER,
-        format=serialization.PublicFormat.SubjectPublicKeyInfo
-    )
-
-    return der_bytes
-
-# 你的Y值
-input_y = input("Enter Y: ")
-input_y = binascii.unhexlify(input_y)
-Y = bytearray(input_y)
-
-# 转换
-der_data = convert_y_to_der(Y)
-
-# 保存到文件
-with open('public_key.pub', 'wb') as f:
-    f.write(der_data)
-
-# 输出信息
-print(f"DER公钥长度: {len(der_data)} 字节")
-print(f"DER十六进制: {der_data.hex()}")
-print(f"文件已保存: public_key.pub")
+import binascii
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import ec
+
+def convert_pubkey_to_der(pub_bytes):
+    """
+    Convert a public key to DER-encoded SubjectPublicKeyInfo.
+    Accepts:
+      - Raw uncompressed point: 65 bytes starting with 0x04
+      - DER-encoded key: will pass through unchanged
+    """
+    # Detect raw uncompressed key
+    if len(pub_bytes) == 65 and pub_bytes[0] == 0x04:
+        # Create EC public key object (P-256)
+        public_key = ec.EllipticCurvePublicKey.from_encoded_point(
+            ec.SECP256R1(),
+            pub_bytes
+        )
+        # Export as DER
+        der_bytes = public_key.public_bytes(
+            encoding=serialization.Encoding.DER,
+            format=serialization.PublicFormat.SubjectPublicKeyInfo
+        )
+        return der_bytes
+    else:
+        # Try loading as DER
+        try:
+            public_key = serialization.load_der_public_key(pub_bytes)
+            # Re-export as DER to standardize
+            der_bytes = public_key.public_bytes(
+                encoding=serialization.Encoding.DER,
+                format=serialization.PublicFormat.SubjectPublicKeyInfo
+            )
+            return der_bytes
+        except Exception as e:
+            raise ValueError(
+                "Invalid public key format. Must be either 65-byte uncompressed point or DER."
+            ) from e
+
+# Prompt user for input
+input_str = input("Enter public key (hex): ").strip()
+
+try:
+    pub_bytes = binascii.unhexlify(input_str)
+except binascii.Error:
+    raise ValueError("Invalid hex input.")
+
+# Convert and save DER
+der_data = convert_pubkey_to_der(pub_bytes)
+
+with open('public_key.pub', 'wb') as f:
+    f.write(der_data)
+
+print(f"DER public key length: {len(der_data)} bytes")
+print(f"DER hex: {der_data.hex()}")
+
EOF
patch convertder.py < convertder.py.patch
cat > convertcvc.py.patch <<"EOF"
--- convertcvc.py       2026-02-19 18:45:49.984865489 +0100
+++ convertcvc.py.orig  2026-02-19 18:46:50.697675465 +0100
@@ -14,35 +14,40 @@
 except Exception as e:
        print(f"An error occurred: {e}")

-def read_cert(file_path):
-    try:
-        with open(file_path, 'rb') as f:
-            content = f.read()
-            return base64.urlsafe_b64encode(content).decode('ascii')
-    except FileNotFoundError:
-        print(f"File {file_path} not found.")
-        return None
-    except Exception as e:
-        print(f"An error occurred reading {file_path}: {e}")
-        return None
+try:
+       with open('ESPICOHSMCA00001.cvcert', 'rb') as cafile:
+               content = cafile.read()
+               cacert = base64.urlsafe_b64encode(content)
+except FileNotFoundError:
+       print("File not found.")
+except Exception as e:
+       print(f"An error occurred: {e}")

-# Read certs
-cacert = read_cert('ESPICOHSMCA00001.cvcert')
-dvcert = read_cert('ESPICOHSMDV00001.cvcert')
-cvcert = read_cert('ESPICOHSMTR00002.cvcert')

-# Build JSON
-data = {
-    'cvcert': cvcert,
-    'dvcert': dvcert,
-    'cacert': cacert
-}
+try:
+       with open('ESPICOHSMDV00001.cvcert', 'rb') as dvfile:
+               content = dvfile.read()
+               dvcert = base64.urlsafe_b64encode(content)
+except FileNotFoundError:
+       print("File not found.")
+except Exception as e:
+       print(f"An error occurred: {e}")

-# Serialize
-json_data = json.dumps(data, separators=(',', ':'))
+try:
+       with open('ESPICOHSMTR00002.cvcert', 'rb') as cvfile:
+               content = cvfile.read()
+               cvcert = base64.urlsafe_b64encode(content)
+except FileNotFoundError:
+       print("File not found.")
+except Exception as e:
+       print(f"An error occurred: {e}")

+data = {}
+data['cvcert'] = cvcert
+data['dvcert'] = dvcert
+data['cacert'] = cacert
+json_data = json.dumps(data, separators=(',', ':'))
 print("Json:")
 print("===================================")
 print(json_data)
 print("===================================")
-
EOF
patch convertcvc.py < convertcvc.py.patch
# Setup the CA
openssl ecparam -out ESPICOHSMCA00001.pem -name prime256v1 -genkey
openssl pkcs8 -topk8 -nocrypt -in ESPICOHSMCA00001.pem -outform DER -out ESPICOHSMCA00001.pkcs8
cvc-create --role=cvca --type=at --chr=ESPICOHSMCA00001 --days=3650 --sign-key=ESPICOHSMCA00001.pkcs8 --scheme=ECDSA_SHA_256
# Setup the DV (domain validation)
openssl ecparam -out ESPICOHSMDV00001.pem -name prime256v1 -genkey
openssl pkcs8 -topk8 -nocrypt -in ESPICOHSMDV00001.pem -outform DER -out ESPICOHSMDV00001.pkcs8
openssl ec -in ESPICOHSMDV00001.pem -out ESPICOHSMDV00001.pub -pubout -outform DER
cvc-create --role=dv_domestic --type=at --chr=ESPICOHSMDV00001 --days=1820 --sign-key=ESPICOHSMCA00001.pkcs8 --scheme=ECDSA_SHA_256 --sign-as=ESPICOHSMCA00001.cvcert --public-key=ESPICOHSMDV00001.pub

python3 pico-hsm-tool.py --pin 114514 initialize --so-pin 57621880
# ctrl + z

python3 convertder.py 

cvc-create --role=terminal --type=at --chr=ESPICOHSMTR00002 --days=365 --sign-key=ESPICOHSMDV00001.pkcs8 --scheme=ECDSA_SHA_256 --sign-as=ESPICOHSMDV00001.cvcert --public-key=public_key.pub

python3 convertcvc.py

# copy & paste JSON

fg

# enter JSON

python3 pico-hsm-tool.py phy vidpid 20a0:4230 # Nitrokey-HSM
# unplug and plug in again
cat > /etc/udev/rules.d/41-nitrokey.rules <<"EOF"
/etc/udev/rules.d/41-nitrokey.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="20a0", ATTR{idProduct}=="4230", TAG+="uaccess"
EOF
udevadm control --reload-rules && sudo udevadm trigger
 lsusb |grep Clay
#Bus 003 Device 020: ID 20a0:4230 Clay Logic Nitrokey HSM
dmesg |tail -7
#[260046.434174] usb 3-1.2: USB disconnect, device number 19
#[260049.408820] usb 3-1.2: new full-speed USB device number 20 using ehci-platform
#[260049.625840] usb 3-1.2: New USB device found, idVendor=20a0, idProduct=4230, bcdDevice= 8.05
#[260049.625872] usb 3-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
#[260049.625880] usb 3-1.2: Product: Pico Key
#[260049.625886] usb 3-1.2: Manufacturer: Pol Henarejos
#[260049.625892] usb 3-1.2: SerialNumber: E***D

pkcs15-tool -D
#Using reader with a card: Nitrokey Nitrokey HSM [Pico Key CCID OTP FIDO Interfac] (E***) 00 00
#PKCS#15 Card [Pico-HSM]:
#        Version        : 6
#        Serial number  : ESPICOHSMTR
#        Manufacturer ID: Pol Henarejos
#        Flags          : PRN generation, EID compliant
#
#
#PIN [UserPIN]
#        Object Flags   : [0x03], private, modifiable
#        Auth ID        : 02
#        ID             : 01
#        Flags          : [0x812], local, initialized, exchangeRefData
#        Length         : min_len:6, max_len:15, stored_len:0
#        Pad char       : 0x00
#        Reference      : 129 (0x81)
#        Type           : ascii-numeric
#        Path           : e82b0601040181c31f0201::
#        Tries left     : 0
#
#PIN [SOPIN]
#        Object Flags   : [0x01], private
#        ID             : 02
#        Flags          : [0x9A], local, unblock-disabled, initialized, soPin
#        Length         : min_len:16, max_len:16, stored_len:0
#        Pad char       : 0x00
#        Reference      : 136 (0x88)
#        Type           : bcd
#        Path           : e82b0601040181c31f0201::
#        Tries left     : 15

Then using scsh3gui:

  • Patch scsh3gui
  • Initialize device again with 8 key domains
  • Create your keys as normal

SCSH3 Patches

scs3/scsh/sc-hsm/SmartCardHSM.js

IMPORTANT Copy YOUR SCS3 ByteString from the convertcvc.py step! This will change everytime you roll your CA!

SmartCardHSM.rootCerts = {
	DESRCACC100001: new CVC(new ByteString("7F218201B47F4E82016C5F290100420E44455352434143433130303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A78641046D025A8026CDBA245F10DF1B72E9880FFF746DAB40A43A3D5C6BEBF27707C30F6DEA72430EE3287B0665C1EAA6EAA4FA26C46303001983F82BD1AA31E03DA0628701015F200E44455352434143433130303030317F4C10060B2B0601040181C31F0301015301C05F25060102010100095F24060302010100085F37409DBB382B1711D2BAACB0C623D40C6267D0B52BA455C01F56333DC9554810B9B2878DAF9EC3ADA19C7B065D780D6C9C3C2ECEDFD78DEB18AF40778ADF89E861CA", HEX)),
	UTSRCACC100001: new CVC(new ByteString("7F218201B47F4E82016C5F290100420E55545352434143433130303030317F4982011D060A04007F000702020202038120A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E537782207D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9832026DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B68441048BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F0469978520A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7864104A041FEB2FD116B2AD19CA6B7EACD71C9892F941BB88D67DCEEC92501F070011957E22122BA6C2CF5FF02936F482E35A6129CCBBA8E9383836D3106879C408EF08701015F200E55545352434143433130303030317F4C10060B2B0601040181C31F0301015301C05F25060102010100095F24060302010100085F3740914DD0FA00615C44048D1467435400423A4AD1BD37FD98D6DE84FD8037489582325C72956D4FDFABC6EDBA48184A754F37F1BE5142DD1C27D66569308CE19AAF", HEX)),
	//ESPICOHSMCA00001: new CVC(new ByteString("7F218201BA7F4E8201725F290100421045535049434F48534D434130303030317F4982011D060A04007F000702020202038120FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF8220FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC83205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B8441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F58520FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC6325518641046A82C0A4FEAF41D6A1336AE7E992D81AD4F827929145DD0D777E1AB63D7E3325C8F7DAC0F74B6EAE13A72F6366777EC133AC5C28F456868E5F2C315044EB54EF8701015F201045535049434F48534D434130303030317F4C12060904007F0007030102025305C0000000005F25060202000801085F24060203000801085F3740601E974F57DDE060875FE6121AEF5BC02E10FC655311C7A32CA822FD18E53A80298EDC56E0D5EBF38FB470DC12987B1600AE91A0ADB5B22C4D80080782E278AD", HEX)),
	ESPICOHSMCA00001: new CVC(new ByteString("7F218201BA7F4E8201725F290100421045535049434F48534D434130303030317F4982011D060A04007F000702020202038120FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF8220FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC83205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B8441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F58520FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551864104283C680576E42EFDF0CE25FCD3921D7E5F6C7B7D2718DB6F2B7F271048BF804E7667E53DD082977FE8119CA0FB3103E7F73876E369C7D9A3A36468C368EC4DA28701015F201045535049434F48534D434130303030317F4C12060904007F0007030102025305C0000000005F25060206000201095F24060306000201075F37409F22B2C22DD622465DB1F4655A51187003AA08E69E3FD92DF6C683B2ECF668B86268567F997F0D0EAE6E507DD7A9E27719A8498122CA4D8A1892D8EEC57CE28F", HEX))
}

scs3/keymanager/keymanager.js

	} else {
		var pk = new CVC(a.get(0));
		var devcert = new CVC(a.get(1));
		var dicacert = new CVC(a.get(2));
	}

	assert(dicacert.verifyWithCVC(this.crypto, SmartCardHSM.rootCerts[dicacert.getCAR()]));
	//assert(devcert.verifyWith(this.crypto, dicacert.getPublicKey(SmartCardHSM.rootCerts.UTSRCACC100001.getPublicKey()), dicacert.getPublicKeyOID()));
        //assert(devcert.verifyWith(this.crypto, dicacert.getPublicKey(SmartCardHSM.rootCerts.ESPICOHSMCA00001.getPublicKey()), dicacert.getPublicKeyOID()));
        assert(devcert.verifyWith(this.crypto, dicacert.getPublicKey(SmartCardHSM.rootCerts.ESPICOHSMCA00001.getPublicKey()), dicacert.getPublicKeyOID()));

	var str = (typeof(id) == "undefined" ? "Add the key issued to " : "Replace key " + id + " with ");
	var ok = Dialog.prompt(str + pk.getCHR() + " on device " + devcert.getCHR() + " (Cancel for No) ?");
	if (ok == null) {
		return;
	}

Example

# as root
# source venv/bin/activate
# openssl ecparam -out ESPICOHSMCA00001.pem -name prime256v1 -genkey
# openssl pkcs8 -topk8 -nocrypt -in ESPICOHSMCA00001.pem -outform DER -out ESPICOHSMCA00001.pkcs8
# cvc-create --role=cvca --type=at --chr=ESPICOHSMCA00001 --days=3650 --sign-key=ESPICOHSMCA00001.pkcs8 --scheme=ECDSA_SHA_256
# ls -al ESP*
-rw-r--r-- 1 root root 447 Feb 19 17:17 ESPICOHSMCA00001.cvcert
-rw------- 1 root root 302 Feb 19 17:17 ESPICOHSMCA00001.pem
-rw------- 1 root root 138 Feb 19 17:17 ESPICOHSMCA00001.pkcs8
# openssl ecparam -out ESPICOHSMDV00001.pem -name prime256v1 -genkey
# openssl pkcs8 -topk8 -nocrypt -in ESPICOHSMDV00001.pem -outform DER -out ESPICOHSMDV00001.pkcs8
# openssl ec -in ESPICOHSMDV00001.pem -out ESPICOHSMDV00001.pub -pubout -outform DER
# cvc-create --role=dv_domestic --type=at --chr=ESPICOHSMDV00001 --days=1820 --sign-key=ESPICOHSMCA00001.pkcs8 --scheme=ECDSA_SHA_256 --sign-as=ESPICOHSMCA00001.cvcert --public-key=ESPICOHSMDV00001.pub
# ls -al ESP*
-rw-r--r-- 1 root root 447 Feb 19 17:17 ESPICOHSMCA00001.cvcert
-rw------- 1 root root 302 Feb 19 17:17 ESPICOHSMCA00001.pem
-rw------- 1 root root 138 Feb 19 17:17 ESPICOHSMCA00001.pkcs8
-rw-r--r-- 1 root root 237 Feb 19 17:18 ESPICOHSMDV00001.cvcert
-rw------- 1 root root 302 Feb 19 17:18 ESPICOHSMDV00001.pem
-rw------- 1 root root 138 Feb 19 17:18 ESPICOHSMDV00001.pkcs8
-rw-r--r-- 1 root root  91 Feb 19 17:18 ESPICOHSMDV00001.pub
# python3 pico-hsm-tool.py --pin 114514 initialize --so-pin 57621880
Pico HSM Tool v2.4
Author: Pol Henarejos
Report bugs to https://github.com/polhenarejos/pico-hsm/issues


********************************
*   PLEASE READ IT CAREFULLY   *
********************************

This tool will erase and reset your device. It will delete all private and secret keys.
Are you sure?
[Press enter to confirm]
Public Point: 043034443a8c82fe8c407c26ae16bc66141323cf166aee17bcb314b0e5fd604a1904a560fedb3f60f12775502c4d255660cd9d33836ac0b18b328ff6c91fc69c03
RP2040
{'pubkey': b'BDA0RDqMgv6MQHwmrha8ZhQTI88Wau4XvLMUsOX9YEoZBKVg_ts_YPEndVAsTSVWYM2dM4NqwLGLMo_2yR_GnAM=', 'curve': 'secp256k1'}
Enter a JSON string: ^Z
[1]+  Stopped                 python3 pico-hsm-tool.py --pin 114514 initialize --so-pin 57621880
# python3 convertder.py # enter above Public Point
# ls -al public_key.pub
-rw-r--r-- 1 root root 91 Feb 19 17:20 public_key.pub

# cvc-create --role=terminal --type=at --chr=ESPICOHSMTR00002 --days=365 --sign-key=ESPICOHSMDV00001.pkcs8 --scheme=ECDSA_SHA_256 --sign-as=ESPICOHSMDV00001.cvcert --public-key=public_key.pub

# ls -al ESPICOHSMTR00002.cvcert
-rw-r--r-- 1 root root 237 Feb 19 17:22 ESPICOHSMTR00002.cvcert

# python convertcvc.py
SCS3:
===================================
b'7F218201BA7F4E8201725F290100421045535049434F48534D434130303030317F4982011D060A04007F000702020202038120FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF8220FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC83205AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B8441046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F58520FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551864104283C680576E42EFDF0CE25FCD3921D7E5F6C7B7D2718DB6F2B7F271048BF804E7667E53DD082977FE8119CA0FB3103E7F73876E369C7D9A3A36468C368EC4DA28701015F201045535049434F48534D434130303030317F4C12060904007F0007030102025305C0000000005F25060206000201095F24060306000201075F37409F22B2C22DD622465DB1F4655A51187003AA08E69E3FD92DF6C683B2ECF668B86268567F997F0D0EAE6E507DD7A9E27719A8498122CA4D8A1892D8EEC57CE28F'
===================================
Json:
===================================
{"cvcert":"fyGB6X9OgaJfKQEAQhBFU1BJQ09IU01EVjAwMDAxf0lPBgoEAH8ABwICAgIDhkEEMDREOoyC_oxAfCauFrxmFBMjzxZq7he8sxSw5f1gShkEpWD-2z9g8Sd1UCxNJVZgzZ0zg2rAsYsyj_bJH8acA18gEEVTUElDT0hTTVRSMDAwMDJ_TBIGCQQAfwAHAwECAlMFAAAAAABfJQYCBgACAQlfJAYCBwACAQlfN0DnIoRjyxrpoAjTUc_86pRNkvIRY17PrzeRye1w6C1-ZJWrfQ0BaUClhoUvl1gJSv5KXe_vbHxTi9UKpfqk6ARt","dvcert":"fyGB6X9OgaJfKQEAQhBFU1BJQ09IU01DQTAwMDAxf0lPBgoEAH8ABwICAgIDhkEE2i3Dd6u_JlGlBGJ55jEO8yxmtgBESpYlR2UAxu3malbUsSMZssPYADnklk-V8KTdrniNIkTB6eQBbDENeNd1_l8gEEVTUElDT0hTTURWMDAwMDF_TBIGCQQAfwAHAwECAlMFgAAAAABfJQYCBgACAQlfJAYDAQACAQNfN0DLjtmxLfH4U9IHmuYEBzi3JTUdVGcvCTF0USOfXkKuBZDwvbfSaHqBoTCC0voXs7t7J0mSQPhFWyCQhhGfejSD","cacert":"fyGCAbp_ToIBcl8pAQBCEEVTUElDT0hTTUNBMDAwMDF_SYIBHQYKBAB_AAcCAgICA4Eg_____wAAAAEAAAAAAAAAAAAAAAD_______________-CIP____8AAAABAAAAAAAAAAAAAAAA_______________8gyBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw-J9JgS4RBBGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfWFIP____8AAAAA__________-85vqtpxeehPO5ysL8YyVRhkEEKDxoBXbkLv3wziX805Idfl9se30nGNtvK38nEEi_gE52Z-U90IKXf-gRnKD7MQPn9zh242nH2aOjZGjDaOxNoocBAV8gEEVTUElDT0hTTUNBMDAwMDF_TBIGCQQAfwAHAwECAlMFwAAAAABfJQYCBgACAQlfJAYDBgACAQdfN0CfIrLCLdYiRl2x9GVaURhwA6oI5p4_2S32xoOy7PZouGJoVn-Zfw0Orm5Qfdep4ncZqEmBIspNihiS2O7FfOKP"}
===================================

# fg
python3 pico-hsm-tool.py --pin 114514 initialize --so-pin 57621880
{"cvcert":"fyGB6X9OgaJfKQEAQhBFU1BJQ09IU01EVjAwMDAxf0lPBgoEAH8ABwICAgIDhkEEMDREOoyC_oxAfCauFrxmFBMjzxZq7he8sxSw5f1gShkEpWD-2z9g8Sd1UCxNJVZgzZ0zg2rAsYsyj_bJH8acA18gEEVTUElDT0hTTVRSMDAwMDJ_TBIGCQQAfwAHAwECAlMFAAAAAABfJQYCBgACAQlfJAYCBwACAQlfN0CL2MmsUENHvqpZno0mAfwnJr5Cl84RQ1z-kgspswRrncbGbenWGlq-nSyx9AHah3AjSBbeFKLMSNBgNZQp0lnE","dvcert":"fyGB6X9OgaJfKQEAQhBFU1BJQ09IU01DQTAwMDAxf0lPBgoEAH8ABwICAgIDhkEE4eRcAunyi9ybpE8Ige-oj0EXkjvN87OvApCQmcslloMA26KqBThZTzxvNkDemsQJjacyWeV66qPt2vjG8fwVW18gEEVTUElDT0hTTURWMDAwMDF_TBIGCQQAfwAHAwECAlMFgAAAAABfJQYCBgACAQlfJAYDAQACAQNfN0BNSNx-kjjGhAIgsGHPFCgVX6ptxGpGecT8texnwKEIUfHMOmz1a6alxeQDma-1k0YKvlsWtR9wOGsD6zUWPtZt","cacert":"fyGCAbp_ToIBcl8pAQBCEEVTUElDT0hTTUNBMDAwMDF_SYIBHQYKBAB_AAcCAgICA4Eg_____wAAAAEAAAAAAAAAAAAAAAD_______________-CIP____8AAAABAAAAAAAAAAAAAAAA_______________8gyBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw-J9JgS4RBBGsX0fLhLEJH-Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT-NC4v4af5uO5-tKfA-eFivOM1drMV7Oy7ZAaDe_UfWFIP____8AAAAA__________-85vqtpxeehPO5ysL8YyVRhkEEYysDzWjbFJJX2Om-fRDNyrggdoz4iBaejPkmpB_TBTsD_wGq8nKu3DUg2iXzW9lJezIx5zWWt8SdxBRWHwHA8YcBAV8gEEVTUElDT0hTTUNBMDAwMDF_TBIGCQQAfwAHAwECAlMFwAAAAABfJQYCBgACAQlfJAYDBgACAQdfN0BNL42_QKW-Y2oXiZO-hKGCTxabwLDPoJgVCwaoMp4BZxnEX_3OLw898uKZZ4DhA9OBCRKQJFj5nsD2EShsW9nm"}
Certificate uploaded successfully!

Note that the device is initialized with a default PIN and configuration.
Now you can initialize the device as usual with your chosen PIN and configuration options.

# python3 pico-hsm-tool.py phy vidpid 20a0:4230
Pico HSM Tool v2.4
Author: Pol Henarejos
Report bugs to https://github.com/polhenarejos/pico-hsm/issues


Command executed successfully. Please, restart your Pico Key.

# dmesg -c
# unplug...
# dmesg -c
[328875.297176] usb 3-1.2: USB disconnect, device number 21
# ...and plugin again
[328911.303504] usb 3-1.2: new full-speed USB device number 22 using ehci-platform
[328911.520435] usb 3-1.2: New USB device found, idVendor=20a0, idProduct=4230, bcdDevice= 8.05
[328911.520468] usb 3-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[328911.520476] usb 3-1.2: Product: Pico Key
[328911.520482] usb 3-1.2: Manufacturer: Pol Henarejos
[328911.520489] usb 3-1.2: SerialNumber: E***2D

# cat > /etc/udev/rules.d/41-nitrokey.rules <<"EOF"
/etc/udev/rules.d/41-nitrokey.rules
SUBSYSTEM=="usb", ATTR{idVendor}=="20a0", ATTR{idProduct}=="4230", TAG+="uaccess"
EOF

# udevadm control --reload-rules && sudo udevadm trigger

# lsusb |grep Clay
Bus 003 Device 022: ID 20a0:4230 Clay Logic Nitrokey HSM

# killall -9 pcscd

# pkcs15-tool -D
Using reader with a card: Nitrokey Nitrokey HSM [Pico Key CCID OTP FIDO Interfac] (E***D) 00 00
PKCS#15 Card [Pico-HSM]:
        Version        : 6
        Serial number  : ESPICOHSMTR
        Manufacturer ID: Pol Henarejos
        Flags          : PRN generation, EID compliant


PIN [UserPIN]
        Object Flags   : [0x03], private, modifiable
        Auth ID        : 02
        ID             : 01
        Flags          : [0x812], local, initialized, exchangeRefData
        Length         : min_len:6, max_len:15, stored_len:0
        Pad char       : 0x00
        Reference      : 129 (0x81)
        Type           : ascii-numeric
        Path           : e82b0601040181c31f0201::
        Tries left     : 2

PIN [SOPIN]
        Object Flags   : [0x01], private
        ID             : 02
        Flags          : [0x9A], local, unblock-disabled, initialized, soPin
        Length         : min_len:16, max_len:16, stored_len:0
        Pad char       : 0x00
        Reference      : 136 (0x88)
        Type           : bcd
        Path           : e82b0601040181c31f0201::
        Tries left     : 15


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