Expand description
OpenPGP Card (v3.4) APDU command/response layer.
Phase 4 of extending keyroost toward ykman parity. The OpenPGP applet is a
CCID/APDU smartcard applet on YubiKeys and Trussed devices (Solo 2 / Nitrokey
3, via opcard), reachable over the existing PC/SC transport — no second
transport stack. This crate is the pure-Rust command/response layer (APDU
builders + the application-related-data TLV parser); the actual card exchange
lives in keyroost-transport.
Reference: OpenPGP Card spec v3.4, and Nitrokey/opcard-rs.
§What is and isn’t here
This is the byte layer: it turns intentions into APDU byte vectors and
turns response byte slices into typed structures. It performs no I/O.
Card transmit, the 61xx / GET RESPONSE reassembly loop, PIN entry, and
the higher-level key-management operations are deliberately left for the
transport phase; see the TODO(transport) notes on Instruction and the
builders that are intentionally absent.
Unlike the OATH applet (Yubico’s SIMPLE-TLV, short-form lengths only), the
OpenPGP applet uses ISO 7816-4 BER-TLV: two-byte (“high”) tags and
long-form lengths. The parser here handles both forms; see parse_tlvs.
Structs§
- AppRelated
Data - Selected fields pulled out of the Application Related Data (
6E) object. - Public
Key - The RSA public key parsed from a GENERATE / READ PUBLIC KEY response.
- PwStatus
- PW status bytes (
C4), parsed from the 7-byte form. - RsaAttributes
- RSA key attributes parsed from an algorithm-attributes object (
C1/C2/C3). - RsaPrivate
KeyParts - RSA private key material needed to import a key into a card slot.
- Tlv
- A single parsed BER-TLV borrowed from the response buffer.
Enums§
- Instruction
- OpenPGP Card instruction bytes (OpenPGP Card spec v3.4, §7.2).
- KeyCrt
- Selects which on-card key slot a GENERATE / READ PUBLIC KEY operation refers
to. The wire form is a 2-byte Control Reference Template — the slot’s CRT tag
followed by an empty value (
B6 00,B8 00, orA4 00). - Parse
Error - Error returned by the response parsers.
- RsaImport
Format - The RSA private-key import format a card accepts, taken from byte 5 of its
algorithm-attributes object (
C1/C2/C3; OpenPGP Card spec v3.4 §4.4.3.10).
Constants§
- AID_
PREFIX - OpenPGP application AID prefix used to
SELECTthe applet by DF name: RIDD2 76 00 01 24(PGP) + application01(OpenPGP). The full 16-byte AID on the card additionally carries the spec version, manufacturer, and a serial number — butselectaddresses the applet with this 6-byte prefix. - CRT_
TAG_ AUTH - CRT tag selecting the authentication key.
- CRT_
TAG_ DECRYPT - CRT tag selecting the decryption (confidentiality) key.
- CRT_
TAG_ SIGN - CRT tag selecting the signature key (OpenPGP Card v3.4, §7.2.14, table).
- GENERATE_
KEY GENERATE ASYMMETRIC KEY PAIRP1: generate a fresh key pair.- INS_
PUT_ DATA_ ODD - Build the full PUT DATA APDU that imports an RSA private key into
crt’s slot:00 DB 3F FF <extended Lc> <4D extended header list>(odd PUT DATA; seeINS_PUT_DATA_ODD). - INS_
SELECT - ISO 7816
SELECTinstruction (used to activate the applet). - P1_
SELECT_ BY_ NAME SELECTP1: select by DF name (AID).- PSO_
COMPUTE_ SIGNATURE PSOP1-P2 selecting compute digital signature (9E 9A).- PSO_
DECIPHER PSOP1-P2 selecting decipher (80 86).- PW1_
OTHER VERIFYP2: PW1 in the “other” context (decipher, internal authenticate).- PW1_
SIGN VERIFYP2: PW1 in the signing context (valid for PSO:CDS).- PW3_
ADMIN VERIFYP2: PW3 (admin) PIN.- READ_
PUBLIC_ KEY GENERATE ASYMMETRIC KEY PAIRP1: read an existing public key.- RESET_
RC_ BY_ ADMIN RESET RETRY COUNTERP1: reset PW1 after PW3 (admin) has been verified; the body carries the new PW1 only. (The resetting-code variant, P10x00, which carriesresetting_code || new_pw1, is not modelled — the admin path is what the transport layer uses.)- SW_
MORE_ DATA - High byte of the “more data available” status (
61xx). - SW_OK
- Status word: success.
- TAG_AID
- Application Identifier (full 16-byte AID).
- TAG_
ALGO_ ATTR_ AUT - Algorithm attributes — authentication key.
- TAG_
ALGO_ ATTR_ DEC - Algorithm attributes — decryption key.
- TAG_
ALGO_ ATTR_ SIG - Algorithm attributes — signature key (inside
TAG_DISCRETIONARY). - TAG_
APPLICATION_ RELATED_ DATA - Application Related Data (constructed; the big aggregate object).
- TAG_
CARDHOLDER_ RELATED_ DATA - Cardholder Related Data (constructed: contains
5B,5F2D,5F35). - TAG_
CA_ FINGERPRINTS - CA fingerprints — 60 bytes = 3×20.
- TAG_
DISCRETIONARY - Discretionary data objects (constructed; inside
TAG_APPLICATION_RELATED_DATA). - TAG_
DS_ COUNTER - Digital signature counter (3-byte big-endian; inside
TAG_SECURITY_SUPPORT). - TAG_
EC_ PUBLIC_ POINT - EC public point (inside
TAG_PUBLIC_KEY) for ECDSA/ECDH/EdDSA keys. - TAG_
EXTENDED_ CAPABILITIES - Extended capabilities (inside
TAG_DISCRETIONARY). - TAG_
FINGERPRINTS - Fingerprints — 60 bytes = 3×20 (Sig, Dec, Aut).
- TAG_
FPR_ AUTH - Fingerprint — authentication key (
C9, 20 bytes); a standalone PUT DATA target. - TAG_
FPR_ DEC - Fingerprint — decryption key (
C8, 20 bytes); a standalone PUT DATA target. - TAG_
FPR_ SIGN - Fingerprint — signature key (
C7, 20 bytes); a standalone PUT DATA target. - TAG_
GENERATION_ TIMES - Key generation timestamps.
- TAG_
HISTORICAL_ BYTES - Historical bytes.
- TAG_
LANGUAGE - Language preference (inside
TAG_CARDHOLDER_RELATED_DATA). - TAG_
LOGIN_ DATA - Login data.
- TAG_
NAME - Cardholder Name (inside
TAG_CARDHOLDER_RELATED_DATA). - TAG_
PUBLIC_ KEY - Public-key data object (constructed) returned by GENERATE / READ PUBLIC KEY.
- TAG_
PW_ STATUS - PW status bytes (inside
TAG_DISCRETIONARY, also a standalone GET DATA). - TAG_
RSA_ EXPONENT - RSA public exponent e (inside
TAG_PUBLIC_KEY). - TAG_
RSA_ MODULUS - RSA modulus n (inside
TAG_PUBLIC_KEY). - TAG_
SECURITY_ SUPPORT - Security support template (constructed; contains
TAG_DS_COUNTER). - TAG_SEX
- Sex (inside
TAG_CARDHOLDER_RELATED_DATA). - TAG_
TIME_ AUTH - Generation timestamp — authentication key (
D0, 4-byte big-endian Unix time). - TAG_
TIME_ DEC - Generation timestamp — decryption key (
CF, 4-byte big-endian Unix time). - TAG_
TIME_ SIGN - Generation timestamp — signature key (
CE, 4-byte big-endian Unix time). - TAG_URL
- URL of the public key.
Functions§
- activate_
file ACTIVATE FILE(00 44 00 00) — the second half of the reset: re-initialize the terminated applet to factory defaults (PW1123456, PW312345678, all key slots empty). Case-1 APDU.- change_
reference_ data CHANGE REFERENCE DATA(INS24) — change a PIN fromoldtonew.- extended_
header_ list - Build just the
0x4DExtended Header List object for an RSA key import (OpenPGP Card spec v3.4, §4.4.3.12). Returned without the APDU wrapper so it can be unit-tested directly; seeimport_rsa_keyfor the full command. - find_
nested - Recursively locate the value of the first TLV with
tag, descending into constructed objects. - find_
tag - Find the value of the first TLV with
tagin a flat bag. - generate_
key GENERATE ASYMMETRIC KEY PAIRwith P1 =80(generate a fresh key pair).- get_
application_ related_ data GET DATA 006E— the Application Related Data aggregate:00 CA 00 6E 00.- get_
data GET DATAfor the data object identified by the 2-bytetag(placed in P1-P2). Case-2 APDU withLe = 0(“up to 256 bytes”); larger objects are continued withget_responsein the transport phase.- get_
pw_ status GET DATA 00C4— the standalone PW status bytes:00 CA 00 C4 00.- get_
response GET RESPONSE(case-2): retrieve the next chunk after a61xxstatus word.- import_
rsa_ key - import_
rsa_ key_ chained - Command-chaining form of
import_rsa_key: the same4DExtended Header List, but emitted as a sequence of chained0xDBPUT DATA APDUs (seeput_data_odd_chained) instead of one extended-length APDU. - parse_
application_ related_ data - Parse the Application Related Data (
6E) blob. - parse_
generated_ public_ key - Parse the public key from a GENERATE / READ PUBLIC KEY response.
- parse_
pw_ status - Parse the 7-byte PW status (
C4) value. - parse_
rsa_ algorithm_ attributes - Parse the RSA algorithm attributes (
01 | n_bits | e_bits | [format]). - parse_
signature_ counter - Parse the digital-signature counter from a Security Support Template (
7A). - parse_
tlvs - Parse a flat sequence of BER-TLVs from
buf. - pso_
compute_ signature PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE(P1-P2 =9E 9A).- pso_
decipher PERFORM SECURITY OPERATION: DECIPHER(P1-P2 =80 86).- pso_
decipher_ chained - Build PSO:DECIPHER as an ISO 7816 command-chaining sequence — the
fallback for readers/cards that won’t accept a single extended-
LcAPDU. - put_
cardholder_ name PUT DATA 005B— write the cardholder name (TAG_NAME).- put_
data PUT DATAfor the data object identified by the 2-bytetag(placed in P1-P2), carryingvalueas the body.- put_
data_ odd_ chained - Build an “odd” PUT DATA (
0xDB) as an ISO 7816 command-chaining sequence: splitdatainto chunks of at mostmax_chunkbytes, every chunk but the last carrying the chaining class bit (CLA0x10) and the final one CLA0x00. Each chunk is a case-3 APDUCLA DB <p1> <p2> <Lc> <chunk>. - put_
fingerprint PUT DATA C7/C8/C9— write the 20-byte v4 fingerprint of the key incrt’s slot (seeKeyCrt::fpr_tag).- put_
generation_ time PUT DATA CE/CF/D0— write the 4-byte big-endian Unix generation timestamp of the key incrt’s slot (seeKeyCrt::time_tag).- put_url
PUT DATA 5F50— write the URL of the public key (TAG_URL).- read_
public_ key GENERATE ASYMMETRIC KEY PAIRwith P1 =81(read the existing public key, no generation).- reset_
retry_ counter RESET RETRY COUNTER(00 2C 02 81) — unblock the user PIN (PW1) and set it tonew_pw1, after PW3 (admin) has been verified in the same session. This is how a card whose user PIN is blocked (retry counter at 0) is recovered without a factory reset. Builds a case-3 APDU00 2C 02 81 <Lc> <new_pw1>.- rsa_
v4_ fingerprint - Compute the OpenPGP v4 fingerprint (RFC 4880 §12.2) of an RSA public
key from its big-endian
modulus,exponent, and the key’screation_time(Unix seconds). - rsa_
v4_ fingerprint_ from - Convenience wrapper around
rsa_v4_fingerprinttaking a parsedPublicKey(e.g. straight fromparse_generated_public_key). - select
SELECTthe OpenPGP applet by DF name:00 A4 04 00 06 D2 76 00 01 24 01.- terminate_
df TERMINATE DF(00 E6 00 00) — the first half of an OpenPGP applet factory reset. Case-1 APDU (no body, no Le). When the applet is unblocked this needs PW3, but once PW1 and PW3 are blocked (both retry counters at 0) the card accepts it unconditionally — that’s how a forgotten-PIN card is reset. After this the applet is in the “terminated” state and onlyactivate_file(or re-SELECT) is accepted.- verify
VERIFY— presentpinagainst the password referencepw_ref(one ofPW1_SIGN,PW1_OTHER,PW3_ADMIN).
Type Aliases§
- Fingerprint
- A 20-byte (SHA-1-sized) OpenPGP key fingerprint.