Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup yubi for release #210

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libpkpass/commands/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,12 @@
"help": "SC backend to use: opensc or yubi",
},
},
"PKCS11_module_path": {
"args": ["--PKCS11-module-path"],
"kwargs": {
"type": str,
"default": "/usr/local/lib/libykcs11.dylib",
"help": "Path to yubi PKCS11 module",
},
},
}
1 change: 1 addition & 0 deletions libpkpass/commands/clip.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def _run_command_execution(self):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
if not self.args["noverify"]:
result = password.verify_entry(
Expand Down
2 changes: 2 additions & 0 deletions libpkpass/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def update_pass(self, pass_value):
escrow_users=self.args["escrow_users"],
minimum=self.args["min_escrow"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
pass_entry["recipients"][self.args["identity"]] = swap_pass["recipients"][
self.args["identity"]
Expand Down Expand Up @@ -199,6 +200,7 @@ def create_pass(self, password1, description, authorizer, recipient_list=None):
escrow_users=self.args["escrow_users"],
minimum=self.args["min_escrow"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)

password.write_password_data(
Expand Down
2 changes: 2 additions & 0 deletions libpkpass/commands/distribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def _run_command_execution(self):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
password.add_recipients(
secret=plaintext_pw,
Expand All @@ -62,6 +63,7 @@ def _run_command_execution(self):
escrow_users=self.args["escrow_users"],
minimum=self.args["min_escrow"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)

password.write_password_data(dist_pass)
Expand Down
1 change: 1 addition & 0 deletions libpkpass/commands/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def _iterate_pdb(self, passworddb, crypt_pass=False):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
password.recipients[uid]["encrypted_secret"] = plaintext_pw.encode("UTF-8")
password.write_password_data(
Expand Down
1 change: 1 addition & 0 deletions libpkpass/commands/populate.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def _decrypt_password_entry(self, password):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
distributor = password.recipients[self.iddb.id["name"]]["distributor"]
if not self.args["noverify"]:
Expand Down
1 change: 1 addition & 0 deletions libpkpass/commands/rename.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def _run_command_execution(self):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
self._confirmation(plaintext_pw)
else:
Expand Down
2 changes: 2 additions & 0 deletions libpkpass/commands/show.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def _behalf_prep(self, password):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
with open(temp_key, "w", encoding="ASCII") as fname:
fname.write(
Expand Down Expand Up @@ -156,6 +157,7 @@ def _decrypt_password_entry(self, password, distributor):
passphrase=self.passphrase,
card_slot=self.args["card_slot"],
SCBackend=self.args["SCBackend"],
PKCS11_module_path=self.args["PKCS11_module_path"],
)
dist_obj = (
self.iddb.session.query(Recipient)
Expand Down
23 changes: 10 additions & 13 deletions libpkpass/crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
SignatureCreationError,
X509CertificateError,
BadBackendError,
PKPassError,
)


Expand Down Expand Up @@ -96,9 +97,8 @@ def print_card_info(card_slot, identity, verbosity, color, theme_map, SCBackend)
for out in out_list:
stripped = out.decode("UTF-8").strip()
if "Yubico" not in stripped:
print("unsupported SC type")
# todo: better handling
exit(1)
# exit(1)
raise PKPassError("Unsupported SC type for backend yubi.\nYubico not in:\n" + stripped)
if int(stripped.split('CCID')[1] or 0) == int(card_slot):
verbosity = verbosity + 1 if verbosity < 2 else 2
stripped = "Using Slot" + ("\n").join(stripped.split("\n")[:verbosity])
Expand All @@ -120,7 +120,7 @@ def print_all_slots(slot_info, color, theme_map):


def pk_decrypt_string(
ciphertext_string, ciphertext_derived_key, identity, passphrase, SCBackend="opensc", card_slot=None
ciphertext_string, ciphertext_derived_key, identity, passphrase, SCBackend="opensc", card_slot=None, PKCS11_module_path="/usr/local/lib/libykcs11.dylib"
):
####################################################################
"""Decrypt a base64 encoded string for the provided identity"""
Expand Down Expand Up @@ -161,7 +161,7 @@ def pk_decrypt_string(
raise DecryptionError(stdout) from err
returncode = proc.returncode
elif SCBackend == "yubi":
# todo: fix this
# todo: this can be improved to not use temp files
# https://developers.yubico.com/yubico-piv-tool/YKCS11/Supported_applications/openssl_engine.html
with NamedTemporaryFile(delete=False) as fname:
fname.write(urlsafe_b64decode(ciphertext_derived_key))
Expand All @@ -175,8 +175,7 @@ def pk_decrypt_string(
"-pkeyopt", "rsa_padding_mode:pkcs1",
"-in", fname.name
]
# todo: make path an option
with Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT, env=dict(environ, PKCS11_MODULE_PATH="/usr/local/lib/libykcs11.dylib")) as proc:
with Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT, env=dict(environ, PKCS11_MODULE_PATH=PKCS11_module_path)) as proc:
stdout, _ = proc.communicate(
input=urlsafe_b64decode(ciphertext_derived_key)
)
Expand All @@ -198,7 +197,7 @@ def pk_decrypt_string(
)


def pk_sign_string(string, identity, passphrase, SCBackend="opensc", card_slot=None):
def pk_sign_string(string, identity, passphrase, SCBackend="opensc", card_slot=None, PKCS11_module_path="/usr/local/lib/libykcs11.dylib"):
####################################################################
"""Compute the hash of string and create a digital signature"""
####################################################################
Expand Down Expand Up @@ -237,7 +236,7 @@ def pk_sign_string(string, identity, passphrase, SCBackend="opensc", card_slot=N
with open(out.name, "rb") as sigfile:
signature = urlsafe_b64encode(handle_python_strings(sigfile.read()))
elif SCBackend == "yubi":
# todo: fix this
# todo: this can be improved to not use temp files
# https://developers.yubico.com/yubico-piv-tool/YKCS11/Supported_applications/openssl_engine.html
with NamedTemporaryFile(delete=False) as fname:
fname.write(stringhash.encode("UTF-8"))
Expand All @@ -253,8 +252,7 @@ def pk_sign_string(string, identity, passphrase, SCBackend="opensc", card_slot=N
"-in", fname.name,
"-out", out.name
]
# todo: make this an option
with Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT, env=dict(environ, PKCS11_MODULE_PATH="/usr/local/lib/libykcs11.dylib")) as proc:
with Popen(command, stdout=PIPE, stdin=PIPE, stderr=STDOUT, env=dict(environ, PKCS11_MODULE_PATH=PKCS11_module_path)) as proc:
stdout, _ = proc.communicate(
input=stringhash.encode("UTF-8")
)
Expand Down Expand Up @@ -442,7 +440,7 @@ def get_card_subjecthash(SCBackend, card_slot=None):

def get_card_serial(card_slot=None):
####################################################################
"""Return the serial element of a card"""
"""Return the serial element of a yubico card"""
####################################################################
command = ["yubico-piv-tool", "-a", "status"]
if card_slot is not None and card_slot != 0:
Expand All @@ -454,7 +452,6 @@ def get_card_serial(card_slot=None):
for line in stdout.decode("utf-8").strip().lower().splitlines():
if "serial number:" in line:
return line.split(":")[1].replace(" ", "").replace("\t", "")
# todo: fix this
raise X509CertificateError("Smartcard not detected")
return None

Expand Down
25 changes: 19 additions & 6 deletions libpkpass/password.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def process_escrow_map(
escrow_users=None,
minimum=None,
SCBackend="opensc",
PKCS11_module_path="/usr/local/lib/libykcs11.dylib",
):
####################################################################
"""Process the escrow user map into escrow users"""
Expand All @@ -104,6 +105,7 @@ def process_escrow_map(
passphrase,
card_slot,
SCBackend,
PKCS11_module_path,
)
i += 1

Expand All @@ -125,6 +127,7 @@ def add_recipients(
escrow_users=None,
minimum=None,
SCBackend="opensc",
PKCS11_module_path="/usr/local/lib/libykcs11.dylib",
):
####################################################################
"""Add recipients to the recipient list of this password object"""
Expand All @@ -142,6 +145,7 @@ def add_recipients(
passphrase=passphrase,
card_slot=card_slot,
SCBackend=SCBackend,
PKCS11_module_path=PKCS11_module_path,
)
for r in tqdm(recipients, leave=False)
}
Expand Down Expand Up @@ -171,6 +175,7 @@ def add_recipients(
escrow_users=escrow_users,
minimum=minimum,
SCBackend=SCBackend,
PKCS11_module_path=PKCS11_module_path,
)
except ValueError as err:
print(f"Warning cannot create escrow shares, reason: {err}")
Expand Down Expand Up @@ -220,6 +225,7 @@ def _add_recipient(
passphrase=None,
card_slot=None,
SCBackend="opensc",
PKCS11_module_path="/usr/local/lib/libykcs11.dylib",
):
####################################################################
"""Add recipient or sharer to list"""
Expand All @@ -242,6 +248,7 @@ def _add_recipient(
passphrase,
SCBackend,
card_slot,
PKCS11_module_path,
)

return recipient_entry
Expand All @@ -250,7 +257,7 @@ def _add_recipient(
f"Identity '{recipient}' is not on the recipient list for password '{self.metadata['name']}'"
) from err

def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBackend=None):
def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBackend=None, PKCS11_module_path="/usr/local/lib/libykcs11.dylib"):
####################################################################
"""Decrypt this password entry for a particular identity
(usually the user)"""
Expand All @@ -271,6 +278,7 @@ def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBacken
passphrase,
SCBackend,
card_slot,
PKCS11_module_path,
)
except KeyError:
try:
Expand All @@ -283,6 +291,8 @@ def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBacken
identity,
passphrase,
SCBackend,
card_slot,
PKCS11_module_path,
)
else:
cert_key = get_card_fingerprint(SCBackend, card_slot=card_slot)
Expand All @@ -295,9 +305,10 @@ def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBacken
passphrase,
SCBackend,
card_slot,
PKCS11_module_path,
)
except DecryptionError as err:
msg = create_error_message(recipient_entry["timestamp"], card_slot, SCBackend)
msg = create_error_message(recipient_entry["timestamp"], card_slot, SCBackend, err)
raise DecryptionError(
f"Error decrypting password named '{self.metadata['name']}'. {msg}"
) from err
Expand All @@ -306,7 +317,7 @@ def decrypt_entry(self, identity=None, passphrase=None, card_slot=None, SCBacken
f"Error decrypting password named '{self.metadata['name']}'. Appropriate private key not found"
) from err
except DecryptionError as err:
msg = create_error_message(recipient_entry["timestamp"], card_slot, SCBackend)
msg = create_error_message(recipient_entry["timestamp"], card_slot, SCBackend, err)
raise DecryptionError(
f"Error decrypting password named '{self.metadata['name']}'. {msg}"
) from err
Expand Down Expand Up @@ -402,16 +413,18 @@ def write_password_data(
raise PasswordIOError(f"Error creating '{filename}'") from error


def create_error_message(recipient_timestamp, card_slot, SCBackend="opensc"):
def create_error_message(recipient_timestamp, card_slot, SCBackend="opensc", err=None):
card_start = get_card_startdate(SCBackend)
card_start = datetime.timestamp(parser.parse(card_start))
distribute_time = float(recipient_timestamp)
# Slots are indexed at 0 so when enumerating you add 1
# There is also an additional information line so add 1 again
if int(card_slot) + 2 > len(get_card_info(SCBackend)[0]):
# For opensc there is also an additional information line so add 1 again
if (int(card_slot) + 2 > len(get_card_info(SCBackend)[0]) and SCBackend == "opensc") or (int(card_slot) + 1 > len(get_card_info(SCBackend)[0]) and SCBackend == "yubi"):
msg = "Attempting to use card slot that is not connected"
elif distribute_time < card_start:
msg = "Password distributed before this certificate was created"
elif err is not None and "dlopen" in str(err.msg):
msg = "Perhaps a bad path in PKCS11_module_path"
else:
msg = "Perhaps a bad pin/passphrase?"
return msg
1 change: 1 addition & 0 deletions libpkpass/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def collect_args(parsedargs):
"color": True,
"verbosity": 0,
"SCBackend": "opensc",
"PKCS11_module_path": "/usr/local/lib/libykcs11.dylib",
}
cli_args = parsedargs if isinstance(parsedargs, dict) else vars(parsedargs)
config_args = get_config_args(cli_args["config"], cli_args)
Expand Down