Smartcard access¶
Johnnycanencrypt provides limilted smardcard access for OpenPGP operations. This is built on top of the 3.4.1 spec.
We only tested the code against Yubikey 5 and Yubikey 4 series.
Note
Remember the Cv25519 keys will only work on Yubikey 5 series.
The part of the code is written in Rust, so you will have to import the internal johnnycanencrypt module.
import johnnycanencrypt.johnnycanencrypt as rjce
Smartcard API¶
-
class
KeySlot
¶ - These are the available KeySlots in a card.
-
Signature
¶
-
Encryption
¶
-
Authentication
¶
-
Attestation
¶
-
-
set_keyslot_touch_policy
(adminpin: bytes, slot: KeySlot, mode: TouchMode) → bool:¶ Sets the given TouchMode to the slot. Returns False if it is already set as Fixed.
Important
Remember to verify the available touch modes via get_card_touch_policies()
first.
-
get_keyslot_touch_policy
(slot: KeySlot) → TouchMode:¶ Returns the available TouchMode of the given slot in the smartcard.
-
get_card_version
() → tuple[int, int, int]:¶ Returns a tuple containing the Yubikey firmware version. Example: (5,2,7) or (4,3,1).
-
reset_yubikey
() → bool:¶ Returns True after successfully resetting a Yubikey.
Warning
This is a dangerous function as it will destroy all the data in the card. Use it carefully.
-
get_card_details
() → Dict[str, Any]:¶ Returns a dictionary containing various card information in a dictionary.
- Available keys:
- serial_number, the serial number of the card
- url, for the public key url.
- name, the card holder’s name, surname<<<firstname
- PW1, number of user pin retries left
- RC, number of reset pin retries left
- PW2, number of admin pin retries left
- signatures, total number signatures made by the card
- sig_f Signature key fingerprint
- enc_f encryption key fingerprint
- auth_f authentication key fingerprint
-
change_user_pin
(adminpin: bytes, newpin: bytes) → bool:¶ Changes the user pin to the given pin. The pin must be 6 chars or more. Requires current admin pin of the card.
-
change_admin_pin
(adminpin: bytes, newadminpin: bytes) → bool:¶ Changes the admin pin to the given pin. The pin must be 8 chars or more. Requires current admin pin of the card.
-
decrypt_bytes_on_card(certdata: bytes, data: bytes, pin:bytes): -> bytes
Decryptes the given encrypted bytes using the smartcard. You will have to pass the public key as the certdata argument.
-
decrypt_file_on_card(certdata: bytes, filepath: bytes, output: bytes, pin:bytes): -> None
Decryptes the given filepath and writes the output to the given output path using the smartcard. You will have to pass the public key as the certdata argument.
-
decrypt_filehandler_on_card(certdata: bytes, fh: typing.IO, output: bytes, pin:bytes): -> None
Decryptes the given opened fh and writes the output to the given output path using the smartcard. You will have to pass the public key as the certdata argument.
Note
This function first reads the whole file and then decrypts it. So, try to use the decrypt_file_on_card function instead.
-
is_smartcard_connected
() → bool:¶ Returns True if it can find a Yubikey attached to the system, or else returns False.
-
set_name
(name: bytes, pin: bytes) → bool:¶ Sets the name of the card holder (in bytes) in surname<<firstname format. The length must be less than 39 in size. Requires admin pin in bytes.
-
set_url
(url: bytes, pin: bytes) → bool:¶ Sets the public key URL on the card. Requires the admin pin in bytes.
-
sign_bytes_detached_on_card
(certdata: bytes, data: bytes, pin: bytes) → str:¶ Signs the given bytes on the card, and returns the detached signature as base64 encoded string. Also requires the public key in certdata argument.
-
sign_bytes_on_card
(certdata: bytes, data: bytes, pin: bytes) → bytes:¶ Signs the given bytes on the card, and returns the signed bytes. Also requires the public key in certdata argument.
-
sign_file_detached_on_card
(certdata: bytes, filepath: bytes, pin: bytes) → str:¶ Signs the given filepath and returns the detached signature as base64 encoded string. Also requires the the public in certdata argument.
-
sign_file_on_card
(certdata: bytes, filepath: bytes, output: bytes, pin: bytes, cleartext: bool) → bool:¶ Signs the given filepath and writes to output. Also requires the the public in certdata argument. For things like email, you would want to sign them in clear text.
-
upload_to_smartcard
(certdata: bytes, pin: bytes, password: str, whichkeys: int) → bool:¶ Uploads the marked (via whichkeys argument) subkeys to the smartcard. Takes the whole certdata (from Key.keyvalue) in bytes, and the admin pin of the card, the password (as string) of the key. You can choose which subkeys to be uploaded via the following values of whichkeys argument:
- 1 for encryption
- 2 for signing
- 4 for authentication
And then you can add them up for the required combination. For example 7 means you want to upload all 3 kinds of subkeys, but 3 means only encryption and signing subkeys will be loaded into the smartcard.
- 3 for both encryption and signing
- 5 for encryption and authentication
- 6 for signing and authentication
- 7 for all 3 different subkeys
import johnnycanencrypt as jce import johnnycanencrypt.johnnycanencrypt as rjce ks = jce.KeyStore("/tmp/demo") # By default it creates all 3 subkeys key = ks.create_key("redhat", ["First Last <fl@example.com>",], jce.Cipher.Cv25519) print(key.fingerprint) # We want to upload only the encryption and signing subkeys to the smartcard result = rjce.upload_to_smartcard(key.keyvalue, b"12345678", "redhat", 3) print(result)
-
upload_primary_to_smartcard
(certdata: bytes, pin: bytes, password: str, whichslot: int) → bool:¶ Uploads the primary key to the smartcard in the given slot. Takes the whole certdata (from Key.keyvalue) in bytes, and the admin pin of the card, the password (as string) of the key. You can choose which subkeys to be uploaded via the following values of whichslot argument:
- 2 for signing slot
- 4 for authentication slot
import johnnycanencrypt as jce import johnnycanencrypt.johnnycanencrypt as rjce ks = jce.KeyStore("/tmp/demo") # Create a primary key with signing capability & an encryption subkey key = ks.create_key("redhat", ["First Last <fl@example.com>",], jce.Cipher.Cv25519, whichkeys=1, can_primary_sign=True) print(key.fingerprint) # We want to upload first the primary key to the signing slot of the card result = rjce.upload_primary_to_smartcard(key.keyvalue, b"12345678", "redhat", whichslot=2) # We want to upload only the encryption subkey to the smartcard result = rjce.upload_to_smartcard(key.keyvalue, b"12345678", "redhat", 1) print(result)