import argparse
import json
import sys
from subprocess import run
tracing = False
def trace(msg):
if tracing:
sys.stderr.write(f"DEBUG: {msg}\n")
sys.stderr.flush()
def oct_raw(args):
argv = ["oct"] + args
trace(f"running {argv}")
p = run(argv, capture_output=True)
if p.returncode != 0:
raise Exception(f"oct failed:\n{p.stderr}")
o = p.stdout
trace(f"oct raw output: {o!r}")
return o
def oct_json(args):
o = json.loads(oct_raw(["--output-format=json"] + args))
trace(f"oct JSON output: {o}")
return o
def list_cards():
return oct_json(["list"])["idents"]
def pick_card(card):
cards = list_cards()
if card is None:
if not cards:
raise Exception("No cards found")
if len(cards) > 1:
raise Exception(f"Can't pick card automatically: found {len(cards)} cards")
return cards[0]
elif card in cards:
return card
else:
raise Exception(f"Can't find specified card {card}")
def factory_reset(card):
oct_raw(["factory-reset", "--card", card])
def set_card_holder(card, admin_pin, name):
trace(f"set card holder to {name!r}")
oct_raw(["admin", "--card", card, "--admin-pin", admin_pin, "name", name])
def generate_key(card, admin_pin, user_pin):
oct_raw(
[
"admin",
f"--card={card}",
f"--admin-pin={admin_pin}",
"generate",
f"--user-pin={user_pin}",
"--output=/dev/null",
"cv25519",
]
)
def status(card):
o = oct_json(["status", f"--card={card}"])
return {
"card_ident": o["ident"],
"cardholder_name": o["cardholder_name"],
"signature_key": o["signature_key"]["fingerprint"],
"decryption_key": o["signature_key"]["fingerprint"],
"authentication_key": o["signature_key"]["fingerprint"],
}
def card_is_empty(card):
o = status(card)
del o["card_ident"]
for key in o:
if o[key]:
return False
return True
def main():
p = argparse.ArgumentParser()
p.add_argument("--force", action="store_true", help="prepare a card that has data")
p.add_argument(
"--verbose", action="store_true", help="produce debugging output to stderr"
)
p.add_argument("--card", help="card identifier, default is to pick the only one")
p.add_argument("--card-holder", help="name of card holder", required=True)
p.add_argument(
"--admin-pin", action="store", help="set file with admin PIN", required=True
)
p.add_argument(
"--user-pin", action="store", help="set file with user PIN", required=True
)
args = p.parse_args()
if args.verbose:
global tracing
tracing = True
trace(f"args: {args}")
card = pick_card(args.card)
if not args.force and not card_is_empty(card):
raise Exception(f"card {card} has existing keys, not touching it")
factory_reset(card)
set_card_holder(card, args.admin_pin, args.card_holder)
key = generate_key(card, args.admin_pin, args.user_pin)
o = status(card)
print(json.dumps(o, indent=4))
if __name__ == "__main__":
try:
main()
except Exception as e:
sys.stderr.write(f"ERROR: {e}\n")
sys.exit(1)