From 357d81f60c448c0beba152fa025bc6c7f17c5cde Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Mon, 7 Nov 2022 09:53:24 +0100 Subject: [PATCH 1/5] Adds get_card_version function This gives us the chance to identify the card we are using. --- docs/smartcard.rst | 3 +++ johnnycanencrypt/johnnycanencrypt.pyi | 1 + src/lib.rs | 12 +++++++++ src/scard.rs | 35 +++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/docs/smartcard.rst b/docs/smartcard.rst index c996ad1..0f1b4bb 100644 --- a/docs/smartcard.rst +++ b/docs/smartcard.rst @@ -18,6 +18,9 @@ The part of the code is written in Rust, so you will have to import the internal Smartcard API -------------- +.. function:: get_card_version() -> tuple[int, int, int]: + + Returns a tuple containing the Yubikey firmware version. Example: (5,2,7) or (4,3,1). .. function:: reset_yubikey() -> bool: diff --git a/johnnycanencrypt/johnnycanencrypt.pyi b/johnnycanencrypt/johnnycanencrypt.pyi index f070234..b6c4bb0 100644 --- a/johnnycanencrypt/johnnycanencrypt.pyi +++ b/johnnycanencrypt/johnnycanencrypt.pyi @@ -81,6 +81,7 @@ def encrypt_file_internal( publickeys: List[List[bytes]], filepath: bytes, output: bytes, armor: Optional[bool] ) -> bytes: ... def is_smartcard_connected() -> bool: ... +def get_card_version() -> tuple[int, int, int]: ... class Johnny: def __init__(self, certdata: bytes) -> Johnny: ... diff --git a/src/lib.rs b/src/lib.rs index 4f417f4..269142d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3029,6 +3029,17 @@ pub fn is_smartcard_connected() -> PyResult { } } +/// Returns a tuple with the firmware version. +#[pyfunction] +pub fn get_card_version(py: Python) -> Result { + let data = match scard::internal_get_version() { + Ok(value) => value, + Err(_) => return Err(JceError::new("Can not get Yubikey version".to_string())), + }; + let result = PyTuple::new(py, data.iter()); + Ok(result.into()) +} + /// A Python module implemented in Rust. #[pymodule] fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { @@ -3065,6 +3076,7 @@ fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(certify_key))?; m.add_wrapped(wrap_pyfunction!(get_ssh_pubkey))?; m.add_wrapped(wrap_pyfunction!(get_signing_pubkey))?; + m.add_wrapped(wrap_pyfunction!(get_card_version))?; m.add("CryptoError", _py.get_type::())?; m.add("SameKeyError", _py.get_type::())?; m.add_class::()?; diff --git a/src/scard.rs b/src/scard.rs index d11791f..4495b29 100644 --- a/src/scard.rs +++ b/src/scard.rs @@ -36,6 +36,41 @@ pub fn chagne_admin_pin(pw3change: apdus::APDU) -> Result Result, errors::TalktoSCError> { + let card = talktosc::create_connection(); + let card = match card { + Ok(card) => card, + Err(value) => return Err(value), + }; + + let select_openpgp = apdus::create_apdu_select_openpgp(); + let resp = talktosc::send_and_parse(&card, select_openpgp); + let resp = match resp { + Ok(_) => resp.unwrap(), + Err(value) => return Err(value), + }; + // Just make sure we can talk + if !resp.is_okay() { + return Err(errors::TalktoSCError::PinError); + } + // Now let us ask about version + // + let select_version = apdus::APDU::new(0x00, 0xF1, 0x00, 0x00, None); + let resp = talktosc::send_and_parse(&card, select_version); + let resp = match resp { + Ok(_) => resp.unwrap(), + Err(value) => { + talktosc::disconnect(card); + return Err(value); + } + }; + + talktosc::disconnect(card); + Ok(resp.get_data()) +} + #[allow(unused)] pub fn is_smartcard_connected() -> Result { let card = talktosc::create_connection(); From 5fd31ac52917f0b0cc6144ab6771aca4a1caac49 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Mon, 7 Nov 2022 19:30:47 +0100 Subject: [PATCH 2/5] Adds TouchMode and reformats --- changelog.md | 3 ++ docs/api.rst | 6 +++ docs/smartcard.rst | 8 +++ johnnycanencrypt/__init__.py | 74 +++++++++++++++++++++++---- johnnycanencrypt/johnnycanencrypt.pyi | 9 ++++ src/lib.rs | 10 ++++ 6 files changed, 100 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 159614c..02ad677 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,9 @@ - Type annotation for the rust part of the codebase. - `can_primary_expire` new argument to `create_key` function call. - Updated `pyo3` dependency to `0.17.2`. +- Adds `get_card_version` in rjce. +- Adds `TouchMode` enum in rjce. +- Adds `get_card_touch_policies` function to find available options. ## [0.10.0] - 2022-09-20 diff --git a/docs/api.rst b/docs/api.rst index b01cd61..478a362 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -279,3 +279,9 @@ For the rest of the documentation we assume that you imported the module as foll Enum class to mark the kind of certification one can do on another key. Possible values are **SignatureType.GenericCertification**, **SignatureType.PersonaCertification**, **SignatureType.CasualCertification**, **SignatureType.PositiveCertification**. + + +.. function:: get_card_touch_policies() -> List[TouchMode] + + Returns a list of Enum values from TouchMode. To be used to determine the touch capabilities of the smartcard. + diff --git a/docs/smartcard.rst b/docs/smartcard.rst index 0f1b4bb..310d1a8 100644 --- a/docs/smartcard.rst +++ b/docs/smartcard.rst @@ -18,6 +18,14 @@ The part of the code is written in Rust, so you will have to import the internal Smartcard API -------------- +.. class:: TouchMode + + .. py:attribute:: Off + .. py:attribute:: On + .. py:attribute:: Fixed + .. py:attribute:: Cached + .. py:attribute:: CachedFixed + .. function:: get_card_version() -> tuple[int, int, int]: Returns a tuple containing the Yubikey firmware version. Example: (5,2,7) or (4,3,1). diff --git a/johnnycanencrypt/__init__.py b/johnnycanencrypt/__init__.py index a61025f..50b57d0 100644 --- a/johnnycanencrypt/__init__.py +++ b/johnnycanencrypt/__init__.py @@ -26,6 +26,7 @@ merge_keys, parse_cert_bytes, parse_cert_file, + TouchMode, ) import johnnycanencrypt.johnnycanencrypt as rjce @@ -276,7 +277,12 @@ def certify_key( other_k = otherkey cert = rjce.certify_key( - k.keyvalue, other_k.keyvalue, sig_type.value, uids, password.encode("utf-8"), oncard + k.keyvalue, + other_k.keyvalue, + sig_type.value, + uids, + password.encode("utf-8"), + oncard, ) # Now if the otherkey is secret, then merge this new public key into the secret key if other_k.keytype == KeyType.SECRET: @@ -1020,7 +1026,13 @@ def _find_keys(self, keys: List[Union[str, Key]]): final_keys.append(k.keyvalue) return final_keys - def encrypt(self, keys: Union[List[Union[str, Key]], Union[str, Key]], data: Union[str, bytes], outputfile: Union[str, bytes]="", armor=True): + def encrypt( + self, + keys: Union[List[Union[str, Key]], Union[str, Key]], + data: Union[str, bytes], + outputfile: Union[str, bytes] = "", + armor=True, + ): """Encrypts the given data with the list of keys and returns the output. :param keys: List of fingerprints or Key objects @@ -1120,7 +1132,9 @@ def encrypt_file(self, keys, inputfilepath, outputfilepath, armor=True): encrypt_filehandler_to_file(final_key_paths, fh, encrypted_file, armor) return True - def decrypt_file(self, key: Union[str, Key], encrypted_path, outputfile, password=""): + def decrypt_file( + self, key: Union[str, Key], encrypted_path, outputfile, password="" + ): """Decryptes the given file to the output path. :param key: Fingerprint or secret Key object @@ -1189,7 +1203,9 @@ def sign_detached(self, key: Union[str, Key], data: Union[str, bytes], password) jp = Johnny(k.keyvalue) return jp.sign_bytes_detached(data, password) - def verify(self, key: Union[str, Key], data: Union[str, bytes], signature: Optional[str]) -> bool: + def verify( + self, key: Union[str, Key], data: Union[str, bytes], signature: Optional[str] + ) -> bool: """Verifies the given data and the signature :param key: Fingerprint or public Key object @@ -1212,7 +1228,14 @@ def verify(self, key: Union[str, Key], data: Union[str, bytes], signature: Optio else: return jp.verify_bytes(data) - def sign_file(self, key: Union[str, Key], filepath: Union[str, bytes], outputpath: Union[str, bytes], password, cleartext=False) -> bool: + def sign_file( + self, + key: Union[str, Key], + filepath: Union[str, bytes], + outputpath: Union[str, bytes], + password, + cleartext=False, + ) -> bool: """Signs the given input file with key and saves in the outputpath. :param key: Fingerprint or secret Key object, public key in case card based operation. @@ -1256,7 +1279,13 @@ def sign_file(self, key: Union[str, Key], filepath: Union[str, bytes], outputpat return result - def sign_file_detached(self, key: Union[str, Key], filepath: Union[str, bytes], password: str, write=False): + def sign_file_detached( + self, + key: Union[str, Key], + filepath: Union[str, bytes], + password: str, + write=False, + ): """Signs the given data with the key. It also writes filename.asc in the same directory of the file as the signature if write value is True. :param key: Fingerprint or secret Key object @@ -1294,7 +1323,9 @@ def sign_file_detached(self, key: Union[str, Key], filepath: Union[str, bytes], return signature - def verify_file_detached(self, key: Union[str, Key], filepath: Union[str, bytes], signature_path): + def verify_file_detached( + self, key: Union[str, Key], filepath: Union[str, bytes], signature_path + ): """Verifies the given filepath based on the signature file. :param key: Fingerprint or public Key object @@ -1348,7 +1379,9 @@ def verify_file(self, key: Union[str, Key], filepath): jp = Johnny(k.keyvalue) return jp.verify_file(input_filepath) - def verify_and_extract_bytes(self, key: Union[str, Key], data: Union[str, bytes]) -> bytes: + def verify_and_extract_bytes( + self, key: Union[str, Key], data: Union[str, bytes] + ) -> bytes: """Verifies the given data and returns the acutal data. :param key: Fingerprint or public Key object. @@ -1367,7 +1400,9 @@ def verify_and_extract_bytes(self, key: Union[str, Key], data: Union[str, bytes] return jp.verify_and_extract_bytes(data) - def verify_and_extract_file(self, key: Union[str, Key], filepath: Union[str, bytes], output: bytes) -> bool: + def verify_and_extract_file( + self, key: Union[str, Key], filepath: Union[str, bytes], output: bytes + ) -> bool: """Verifies the given signed file and saves the actual data in output. :param key: Fingerprint or public Key object. @@ -1397,7 +1432,6 @@ def verify_and_extract_file(self, key: Union[str, Key], filepath: Union[str, byt return jp.verify_and_extract_file(input_filepath, outputpath) - def fetch_key_by_fingerprint(self, fingerprint: str): """Fetches key from keys.openpgp.org based on the fingerprint. @@ -1502,3 +1536,23 @@ def sync_smartcard(self): fingerprint = result["fingerprint"] return fingerprint + + +def get_card_touch_policies() -> Union[List[TouchMode], None]: + "Get the supported touch policies of the smartcard" + result: List[TouchMode] = [] + version = rjce.get_card_version() + if version < (4, 2, 0): + result = [] + elif version < (5, 2, 1): + result = [TouchMode.On, TouchMode.Off, TouchMode.Fixed] + elif version >= (5, 2, 1): + result = [ + TouchMode.On, + TouchMode.Off, + TouchMode.Fixed, + TouchMode.Cached, + TouchMode.CachedFixed, + ] + # Now return the result + return result diff --git a/johnnycanencrypt/johnnycanencrypt.pyi b/johnnycanencrypt/johnnycanencrypt.pyi index b6c4bb0..5edcb7a 100644 --- a/johnnycanencrypt/johnnycanencrypt.pyi +++ b/johnnycanencrypt/johnnycanencrypt.pyi @@ -1,9 +1,18 @@ +import io from typing import List, Dict, Optional, Tuple, Any, BinaryIO from datetime import datetime +from enum import IntEnum class CryptoError(BaseException): ... class SameKeyError(BaseException): ... +class TouchMode(IntEnum): + Off: int + On: int + Fixed: int + Cached: int + CachedFixed: int + def update_subkeys_expiry_in_cert( certdata: bytes, fingerprints: List[str], expirytime: int, password: str ) -> bytes: ... diff --git a/src/lib.rs b/src/lib.rs index 269142d..38afaa1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3040,6 +3040,15 @@ pub fn get_card_version(py: Python) -> Result { Ok(result.into()) } +#[pyclass] +enum TouchMode { + Off = 0x00, + On = 0x01, + Fixed = 0x02, + Cached = 0x03, + CachedFixed = 0x04, +} + /// A Python module implemented in Rust. #[pymodule] fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { @@ -3080,5 +3089,6 @@ fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { m.add("CryptoError", _py.get_type::())?; m.add("SameKeyError", _py.get_type::())?; m.add_class::()?; + m.add_class::()?; Ok(()) } From 2c973697b088e6261bc658d4a50f3716a5d0b710 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Tue, 8 Nov 2022 19:52:08 +0100 Subject: [PATCH 3/5] Adds touch functions --- Cargo.toml | 2 +- changelog.md | 4 ++ docs/api.rst | 1 + docs/smartcard.rst | 25 ++++++++++ johnnycanencrypt/johnnycanencrypt.pyi | 8 ++++ src/lib.rs | 68 ++++++++++++++++++++++++++- src/scard.rs | 37 +++++++++++++++ 7 files changed, 143 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ac576d7..668a1d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ talktosc = "0.1.3" sshkeys = "0.3.2" [dependencies.pyo3] -version = "0.17.2" +version = "0.17.3" [package.metadata.maturin] diff --git a/changelog.md b/changelog.md index 02ad677..0ac2537 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,10 @@ - Adds `get_card_version` in rjce. - Adds `TouchMode` enum in rjce. - Adds `get_card_touch_policies` function to find available options. +- Adds `KeySlot` enum in rjce +- Adds `get_keyslot_touch_policy` function to set touch policy. +- Adds `set_keyslot_touch_policy` function to set touch policy. +- Updates pyo3 to `0.17.3` ## [0.10.0] - 2022-09-20 diff --git a/docs/api.rst b/docs/api.rst index 478a362..276b04b 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -284,4 +284,5 @@ For the rest of the documentation we assume that you imported the module as foll .. function:: get_card_touch_policies() -> List[TouchMode] Returns a list of Enum values from TouchMode. To be used to determine the touch capabilities of the smartcard. + Remember to verify this list before calling :func:`set_keyslot_touch_policy`. diff --git a/docs/smartcard.rst b/docs/smartcard.rst index 310d1a8..5e61ea4 100644 --- a/docs/smartcard.rst +++ b/docs/smartcard.rst @@ -18,14 +18,39 @@ The part of the code is written in Rust, so you will have to import the internal Smartcard API -------------- +.. class:: KeySlot + + These are the available KeySlots in a card. + + .. py:attribute:: Signature + .. py:attribute:: Encryption + .. py:attribute:: Authentication + .. py:attribute:: Attestation + + + .. class:: TouchMode + The different touch mode for a key. + .. py:attribute:: Off .. py:attribute:: On .. py:attribute:: Fixed .. py:attribute:: Cached .. py:attribute:: CachedFixed + + +.. function:: 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 :func:`get_card_touch_policies` first. + +.. function:: get_keyslot_touch_policy(slot: KeySlot) -> TouchMode: + + Returns the available `TouchMode` of the given slot in the smartcard. + .. function:: get_card_version() -> tuple[int, int, int]: Returns a tuple containing the Yubikey firmware version. Example: (5,2,7) or (4,3,1). diff --git a/johnnycanencrypt/johnnycanencrypt.pyi b/johnnycanencrypt/johnnycanencrypt.pyi index 5edcb7a..b110f57 100644 --- a/johnnycanencrypt/johnnycanencrypt.pyi +++ b/johnnycanencrypt/johnnycanencrypt.pyi @@ -13,6 +13,14 @@ class TouchMode(IntEnum): Cached: int CachedFixed: int +class KeySlot(IntEnum): + Signature: int + Encryption: int + Authentication: int + Attestation: int + +def set_keyslot_touch_policy(adminpin: bytes, slot: KeySlot, mode: TouchMode) -> bool: ... +def get_keyslot_touch_policy(slot: KeySlot) -> TouchMode: ... def update_subkeys_expiry_in_cert( certdata: bytes, fingerprints: List[str], expirytime: int, password: str ) -> bytes: ... diff --git a/src/lib.rs b/src/lib.rs index 38afaa1..42c5af4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3040,8 +3040,10 @@ pub fn get_card_version(py: Python) -> Result { Ok(result.into()) } +/// TouchMode for Yubikeys #[pyclass] -enum TouchMode { +#[derive(Clone, Debug)] +pub enum TouchMode { Off = 0x00, On = 0x01, Fixed = 0x02, @@ -3049,6 +3051,67 @@ enum TouchMode { CachedFixed = 0x04, } +#[pyclass] +#[derive(Clone, Debug)] +pub enum KeySlot { + Signature = 0xD6, + Encryption = 0xD7, + Authentication = 0xD8, + Attestation = 0xD9, +} + +#[pyfunction] +pub fn get_keyslot_touch_policy(py: Python, slot: Py) -> Result> { + let actual_slot = slot.extract(py)?; + let data = match scard::get_touch_policy(actual_slot) { + Ok(value) => value, + Err(e) => return Err(JceError::new(e.to_string())), + }; + match data[0] { + 0 => { + return Ok(Py::new(py, TouchMode::Off)?); + } + 1 => { + return Ok(Py::new(py, TouchMode::On)?); + } + 2 => { + return Ok(Py::new(py, TouchMode::Fixed)?); + } + 3 => { + return Ok(Py::new(py, TouchMode::Cached)?); + } + 4 => { + return Ok(Py::new(py, TouchMode::CachedFixed)?); + } + _ => { + return Err(JceError::new("Can not parse touch policy.".to_string())); + } + } +} + +#[pyfunction] +pub fn set_keyslot_touch_policy( + py: Python, + adminpin: Vec, + slot: Py, + mode: Py, +) -> Result { + let actual_slot: KeySlot = slot.extract(py).unwrap(); + let slot_value = actual_slot as u8; + + let actual_mode: TouchMode = mode.extract(py).unwrap(); + let mode_value = actual_mode as u8; + + let pw3_apdu = talktosc::apdus::create_apdu_verify_pw3(adminpin); + // 0x20 is for the touch mode button + let touch_apdu = apdus::APDU::new(0x00, 0xDA, 0x00, slot_value, Some(vec![mode_value, 0x20])); + + match scard::set_data(pw3_apdu, touch_apdu) { + Ok(value) => Ok(value), + Err(value) => Err(CardError::new_err(format!("Error {}", value)).into()), + } +} + /// A Python module implemented in Rust. #[pymodule] fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { @@ -3086,9 +3149,12 @@ fn johnnycanencrypt(_py: Python, m: &PyModule) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(get_ssh_pubkey))?; m.add_wrapped(wrap_pyfunction!(get_signing_pubkey))?; m.add_wrapped(wrap_pyfunction!(get_card_version))?; + m.add_wrapped(wrap_pyfunction!(get_keyslot_touch_policy))?; + m.add_wrapped(wrap_pyfunction!(set_keyslot_touch_policy))?; m.add("CryptoError", _py.get_type::())?; m.add("SameKeyError", _py.get_type::())?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; Ok(()) } diff --git a/src/scard.rs b/src/scard.rs index 4495b29..6260f1a 100644 --- a/src/scard.rs +++ b/src/scard.rs @@ -3,6 +3,7 @@ use crate::openpgp::packet::key; use crate::openpgp::types::SymmetricAlgorithm; +use crate::KeySlot; use openpgp::crypto; use openpgp::packet::prelude::*; use sequoia_openpgp as openpgp; @@ -36,6 +37,42 @@ pub fn chagne_admin_pin(pw3change: apdus::APDU) -> Result Result, errors::TalktoSCError> { + let card = talktosc::create_connection(); + let card = match card { + Ok(card) => card, + Err(value) => return Err(value), + }; + + let select_openpgp = apdus::create_apdu_select_openpgp(); + let resp = talktosc::send_and_parse(&card, select_openpgp); + let resp = match resp { + Ok(_) => resp.unwrap(), + Err(value) => return Err(value), + }; + // Just make sure we can talk + if !resp.is_okay() { + return Err(errors::TalktoSCError::PinError); + } + // Now let us ask about the touch policy + // + let slot_value = slot as u8; + let select_touchpolicy = apdus::APDU::new(0x00, 0xCA, 0x00, slot_value, None); + let resp = talktosc::send_and_parse(&card, select_touchpolicy); + let resp = match resp { + Ok(_) => resp.unwrap(), + Err(value) => { + talktosc::disconnect(card); + return Err(value); + } + }; + + talktosc::disconnect(card); + Ok(resp.get_data()) +} + // To get the Yubikey card firmware version #[allow(unused)] pub fn internal_get_version() -> Result, errors::TalktoSCError> { From 521fe6fb17057a2843763e9aef97ca2cdefa0e5a Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Tue, 8 Nov 2022 20:04:09 +0100 Subject: [PATCH 4/5] Adds better card disconnection on error --- src/scard.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/scard.rs b/src/scard.rs index 6260f1a..0c76201 100644 --- a/src/scard.rs +++ b/src/scard.rs @@ -27,13 +27,18 @@ pub fn chagne_admin_pin(pw3change: apdus::APDU) -> Result resp.unwrap(), - Err(value) => return Err(value), + Err(value) => { + talktosc::disconnect(card); + return Err(value); + } }; // Verify if the admin pin worked or not. if !resp.is_okay() { + talktosc::disconnect(card); return Err(errors::TalktoSCError::PinError); } + talktosc::disconnect(card); Ok(true) } @@ -86,10 +91,14 @@ pub fn internal_get_version() -> Result, errors::TalktoSCError> { let resp = talktosc::send_and_parse(&card, select_openpgp); let resp = match resp { Ok(_) => resp.unwrap(), - Err(value) => return Err(value), + Err(value) => { + talktosc::disconnect(card); + return Err(value); + } }; // Just make sure we can talk if !resp.is_okay() { + talktosc::disconnect(card); return Err(errors::TalktoSCError::PinError); } // Now let us ask about version @@ -120,10 +129,14 @@ pub fn is_smartcard_connected() -> Result { let resp = talktosc::send_and_parse(&card, select_openpgp); let resp = match resp { Ok(_) => resp.unwrap(), - Err(value) => return Err(value), + Err(value) => { + talktosc::disconnect(card); + return Err(value); + } }; // Verify if the admin pin worked or not. if !resp.is_okay() { + talktosc::disconnect(card); return Err(errors::TalktoSCError::PinError); } From 80bcebf8db34e8a21f390d515adc8821a9c874bb Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Tue, 8 Nov 2022 20:05:13 +0100 Subject: [PATCH 5/5] Formats the test --- smartcardtests/smartcards_for_primary.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/smartcardtests/smartcards_for_primary.py b/smartcardtests/smartcards_for_primary.py index 62a73e1..917a50b 100644 --- a/smartcardtests/smartcards_for_primary.py +++ b/smartcardtests/smartcards_for_primary.py @@ -83,7 +83,15 @@ ks.sync_smartcard() other = ks.import_key("tests/files/store/kushal_updated_key.asc") -newother = ks.certify_key(k, other, ["Kushal Das ",], jce.SignatureType.PersonaCertification, password="123456".encode("utf-8"), oncard=True) +newother = ks.certify_key( + k, + other, + [ + "Kushal Das ", + ], + jce.SignatureType.PersonaCertification, + password="123456", + oncard=True, +) with open("hello.public", "wb") as f: f.write(newother.keyvalue) -