diff --git a/Cargo.toml b/Cargo.toml index 8ec20da..ac576d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ talktosc = "0.1.3" sshkeys = "0.3.2" [dependencies.pyo3] -version = "0.16.4" +version = "0.17.2" [package.metadata.maturin] diff --git a/README.md b/README.md index 2f90c84..27375cc 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ sudo dnf install nettle clang clang-devel nettle-devel python3-devel pcsc-lite-d ### Build dependencies in Debian Bullseye ``` -sudo apt install -y python3-dev libnettle8 nettle-dev libhogweed6 python3-pip python3-venv clang libpcsclite-dev libpcsclite1 libclang-9-dev +sudo apt install -y python3-dev libnettle8 nettle-dev libhogweed6 python3-pip python3-venv clang libpcsclite-dev libpcsclite1 libclang-9-dev pkg-config ``` diff --git a/changelog.md b/changelog.md index 9c354e1..159614c 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,11 @@ ## [unreleased] +### Added + +- 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`. ## [0.10.0] - 2022-09-20 diff --git a/docs/api.rst b/docs/api.rst index da87602..b01cd61 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -42,7 +42,7 @@ For the rest of the documentation we assume that you imported the module as foll This method signs the given list of userid(s) in `otherkey` using the primary key of the `key`, by default it signs as *SignatureType.GenericCertification*, but you can do other types too. If the primary key is on a smartcard, then pass `oncard=True`, default value is `False`. - .. method:: create_key(password: str, uids: Optional[Union[List[str], str]] = [], ciphersuite: Cipher = Cipher.RSA4k, creation: Optional[datetime.datetime] = None, expiration: Optional[datetime.datetime] = None, subkeys_expiration= False, whichkeys = 7, can_primary_sign: bool = True) -> Key: + .. method:: create_key(password: str, uids: Optional[Union[List[str], str]] = [], ciphersuite: Cipher = Cipher.RSA4k, creation: Optional[datetime.datetime] = None, expiration: Optional[datetime.datetime] = None, subkeys_expiration= False, whichkeys = 7, can_primary_sign: bool = False, can_primary_expire=False) -> Key: Returns the public part of the newly created `Key` in the store directory. You can mention ciphersuite :class:`Cipher` as diff --git a/johnnycanencrypt/__init__.py b/johnnycanencrypt/__init__.py index 607ee79..a61025f 100644 --- a/johnnycanencrypt/__init__.py +++ b/johnnycanencrypt/__init__.py @@ -937,6 +937,7 @@ def create_key( subkeys_expiration=False, whichkeys=7, can_primary_sign=False, + can_primary_expire=False, ) -> Key: """Returns a public `Key` object after creating a new key in the store @@ -945,9 +946,10 @@ def create_key( :param ciphersuite: Default Cipher.RSA4k, other values are Cipher.RSA2k, Cipher.Cv25519 :param creation: datetime.datetime, default datetime.now() (via rust) :param expiration: datetime.datetime, default 0 (Never) - :param subkeys_expiration: Bool (default False), pass True if you want to set the expiry date to the subkeys instead of master key. + :param subkeys_expiration: Bool (default False), pass True if you want to set the expiry date to the subkeys. :param whichkeys: Decides which all subkeys to generate, 1 (for encryption), 2 for signing, 4 for authentication. Add the numbers for mixed result. :param can_primary_sign: Boolean to indicate if the primary key can do signing + :param can_primary_expire: Boolean to indicate if the primary key can expire, default False. """ if creation: ctime = creation.timestamp() @@ -974,6 +976,7 @@ def create_key( subkeys_expiration, whichkeys, can_primary_sign, + can_primary_expire, ) # Now save the secret key key_filename = os.path.join(self.path, f"{fingerprint}.sec") diff --git a/johnnycanencrypt/johnnycanencrypt.pyi b/johnnycanencrypt/johnnycanencrypt.pyi index 2ef9c7c..f070234 100644 --- a/johnnycanencrypt/johnnycanencrypt.pyi +++ b/johnnycanencrypt/johnnycanencrypt.pyi @@ -66,6 +66,7 @@ def create_key( subkeys_expiration: bool, whichkeys: int, can_primary_sign: bool, + can_primary_expire: bool, ) -> Tuple[str, str, str]: ... def encrypt_filehandler_to_file( publickeys: List[bytes], fh: io.TextIOWrapper, output: bytes, armor: Optional[bool] diff --git a/src/lib.rs b/src/lib.rs index 27b0ebe..4f417f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2319,7 +2319,7 @@ fn internal_parse_cert( /// key and the fingerprint in hex. Remember to save the keys for future use. #[pyfunction] #[pyo3( - text_signature = "(password, userid, cipher, creation, expiration, subkeys_expiration, whichkeys, can_primary_sign)" + text_signature = "(password, userid, cipher, creation, expiration, subkeys_expiration, whichkeys, can_primary_sign, can_primary_expire)" )] fn create_key( password: String, @@ -2330,6 +2330,7 @@ fn create_key( subkeys_expiration: bool, whichkeys: u8, can_primary_sign: bool, + can_primary_expire: bool, ) -> Result<(String, String, String)> { let mut cdt: Option> = None; // Default we create RSA4k keys @@ -2407,9 +2408,12 @@ fn create_key( 0, ), }; - if !subkeys_expiration { + let crtbuilder = if can_primary_expire { crtbuilder.set_validity_period(validity) } else { + crtbuilder + }; + if subkeys_expiration { let crtbuilder = if (whichkeys & 0x01) == 0x01 { crtbuilder.add_subkey( KeyFlags::empty() @@ -2433,6 +2437,8 @@ fn create_key( crtbuilder }; crtbuilder + } else { + crtbuilder } } }; diff --git a/tests/test_keystore.py b/tests/test_keystore.py index bd2d564..288f21d 100644 --- a/tests/test_keystore.py +++ b/tests/test_keystore.py @@ -375,10 +375,10 @@ def test_ks_creation_expiration_time(tmp_path): assert ctime.date() == newk.creationtime.date() assert not newk.expirationtime - # Now both creation and expirationtime + # Now both creation and expirationtime for primary key ctime = datetime.datetime(2008, 10, 10, 20, 53, 47) etime = datetime.datetime(2025, 12, 15, 20, 53, 47) - newk = ks.create_key("redhat", "Another test key", creation=ctime, expiration=etime) + newk = ks.create_key("redhat", "Another test key", creation=ctime, expiration=etime, can_primary_expire=True) assert ctime.date() == newk.creationtime.date() assert etime.date() == newk.expirationtime.date() @@ -405,9 +405,23 @@ def test_ks_creation_expiration_time(tmp_path): subkeys_expiration=True, ) assert datetime.datetime.now().date() == newk.creationtime.date() + assert not newk.expirationtime for skeyid, subkey in newk.othervalues["subkeys"].items(): assert subkey[1].date() == etime.date() + # Now verify both subkeys and primary can expire + etime = datetime.datetime(2030, 6, 5, 20, 53, 47) + newk = ks.create_key( + "redhat", + "Test key with subkey expiration", + expiration=etime, + subkeys_expiration=True, + can_primary_expire=True, + ) + assert datetime.datetime.now().date() == newk.creationtime.date() + assert etime.date() == newk.expirationtime.date() + for skeyid, subkey in newk.othervalues["subkeys"].items(): + assert subkey[1].date() == etime.date() def test_get_all_keys(): ks = jce.KeyStore(BASE_TESTSDIR / "files/store")