Skip to content

Commit

Permalink
Merge pull request #121 from kushaldas/both_can_expire
Browse files Browse the repository at this point in the history
Primary key and the subkeys can expire
  • Loading branch information
kushaldas authored Nov 1, 2022
2 parents 60760fb + 7c95843 commit ee9d1aa
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion johnnycanencrypt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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")
Expand Down
1 change: 1 addition & 0 deletions johnnycanencrypt/johnnycanencrypt.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
10 changes: 8 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<DateTime<Utc>> = None;
// Default we create RSA4k keys
Expand Down Expand Up @@ -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()
Expand All @@ -2433,6 +2437,8 @@ fn create_key(
crtbuilder
};
crtbuilder
} else {
crtbuilder
}
}
};
Expand Down
18 changes: 16 additions & 2 deletions tests/test_keystore.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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")
Expand Down

0 comments on commit ee9d1aa

Please sign in to comment.