From a4bd41099624b33334200f1ee6c9a7c1ff4ac42c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 16 Jan 2024 17:13:39 +0700 Subject: [PATCH] Verify user and subkey Signed-off-by: Nguyen Van Nguyen --- src/Key/AbstractKey.php | 150 ++++++++++++++++++++++---------------- src/Key/PrivateKey.php | 2 +- src/Type/KeyInterface.php | 2 +- 3 files changed, 89 insertions(+), 65 deletions(-) diff --git a/src/Key/AbstractKey.php b/src/Key/AbstractKey.php index b2a2d147..d1696cbb 100644 --- a/src/Key/AbstractKey.php +++ b/src/Key/AbstractKey.php @@ -256,7 +256,7 @@ public function getSigningKeyPacket( ); foreach ($subkeys as $subkey) { if (empty($keyID) || $keyID === $subkey->getKeyID()) { - if (!$subkey->isSigningKey()) { + if (!$subkey->isSigningKey() || !$subkey->verify($time)) { continue; } $signature = $subkey->getLatestBindingSignature()?->getEmbeddedSignature(); @@ -307,7 +307,7 @@ public function getEncryptionKeyPacket( ); foreach ($subkeys as $subkey) { if (empty($keyID) || $keyID === $subkey->getKeyID()) { - if (!$subkey->isEncryptionKey()) { + if (!$subkey->isEncryptionKey() || !$subkey->verify($time)) { continue; } return $subkey->getKeyPacket(); @@ -407,7 +407,7 @@ public function isPrivate(): bool public function aeadSupported(): bool { $primaryUser = $this->getPrimaryUser(); - $features = $primaryUser->getLatestSelfCertification()?->getFeatures(); + $features = $primaryUser?->getLatestSelfCertification()?->getFeatures(); if (($features instanceof Features) && $features->supportAeadEncryptedData()) { return true; } @@ -463,9 +463,13 @@ public function isCertified( ?DateTimeInterface $time = null ): bool { - return $this->getPrimaryUser()->isCertified( - $verifyKey, $certificate, $time - ); + $primaryUser = $this->getPrimaryUser(); + if ($primaryUser instanceof UserInterface) { + return $primaryUser->isCertified( + $verifyKey, $certificate, $time + ); + } + return false; } /** @@ -508,25 +512,11 @@ public function verify( /** * {@inheritdoc} */ - public function getPrimaryUser(?DateTimeInterface $time = null): UserInterface + public function getPrimaryUser(?DateTimeInterface $time = null): ?UserInterface { - $users = $this->users; - usort( - $users, - static function ($a, $b) { - $aPrimary = (int) $a->isPrimary(); - $bPrimary = (int) $b->isPrimary(); - if ($aPrimary === $bPrimary) { - $aTime = $a->getLatestSelfCertification()?->getSignatureCreationTime() - ?? new \DateTime(); - $bTime = $b->getLatestSelfCertification()?->getSignatureCreationTime() - ?? new \DateTime(); - return $aTime->getTimestamp() - $bTime->getTimestamp(); - } - else { - return $aPrimary - $bPrimary; - } - } + $users = array_filter( + $this->getSortedPrimaryUsers(), + static fn ($user) => $user->verify($time) ); return array_pop($users); } @@ -538,13 +528,17 @@ public function certifyBy( PrivateKeyInterface $signKey, ?DateTimeInterface $time = null ): self { + $users = []; + $certifedUserID = ''; $self = $this->clone(); - $certifedUser = $self->getPrimaryUser()->certifyBy($signKey, $time); - $users = [ - $certifedUser, - ]; + $primaryUser = $self->getPrimaryUser(); + if ($primaryUser instanceof UserInterface) { + $certifedUser = $primaryUser->certifyBy($signKey, $time); + $certifedUserID = $certifedUser->getUserID(); + $users[] = $certifedUser; + } foreach ($self->getUsers() as $user) { - if ($user->getUserID() !== $certifedUser->getUserID()) { + if ($user->getUserID() !== $certifedUserID) { $users[] = $user; } } @@ -573,18 +567,58 @@ public function revokeBy( return $self; } + /** + * Get key expiration from signatures. + * + * @param array $signatures + * @return DateTimeInterface + */ + public static function getKeyExpiration(array $signatures): ?DateTimeInterface + { + usort( + $signatures, + static function ($a, $b): int { + $aTime = $a->getSignatureCreationTime() ?? new \DateTime(); + $bTime = $b->getSignatureCreationTime() ?? new \DateTime(); + return $bTime->getTimestamp() - $aTime->getTimestamp(); + } + ); + foreach ($signatures as $signature) { + $keyExpirationTime = $signature->getKeyExpirationTime(); + if ($keyExpirationTime instanceof KeyExpirationTime) { + $expirationTime = $keyExpirationTime->getExpirationTime(); + $creationTime = $signature->getSignatureCreationTime() ?? new \DateTime(); + $keyExpiry = $creationTime->setTimestamp( + $creationTime->getTimestamp() + $expirationTime + ); + $signatureExpiry = $signature->getSignatureExpirationTime(); + if (empty($signatureExpiry)) { + return $keyExpiry; + } + else { + return $keyExpiry < $signatureExpiry ? $keyExpiry : $signatureExpiry; + } + } + else { + return $signature->getSignatureExpirationTime(); + } + } + return null; + } + /** * Return the key is signing or verification key * * @return bool */ - protected function isSigningKey(?DateTimeInterface $time = null): bool + protected function isSigningKey(): bool { if (!$this->keyPacket->isSigningKey()) { return false; } - $primaryUser = $this->getPrimaryUser($time); - $keyFlags = $primaryUser->getLatestSelfCertification()?->getKeyFlags(); + $users = $this->getSortedPrimaryUsers(); + $user = array_pop($users); + $keyFlags = $user?->getLatestSelfCertification()?->getKeyFlags(); if (($keyFlags instanceof KeyFlags) && !$keyFlags->isSignData()) { return false; } @@ -596,13 +630,14 @@ protected function isSigningKey(?DateTimeInterface $time = null): bool * * @return bool */ - protected function isEncryptionKey(?DateTimeInterface $time = null): bool + protected function isEncryptionKey(): bool { if (!$this->keyPacket->isEncryptionKey()) { return false; } - $primaryUser = $this->getPrimaryUser($time); - $keyFlags = $primaryUser->getLatestSelfCertification()?->getKeyFlags(); + $users = $this->getSortedPrimaryUsers(); + $user = array_pop($users); + $keyFlags = $user?->getLatestSelfCertification()?->getKeyFlags(); if (($keyFlags instanceof KeyFlags) && !($keyFlags->isEncryptCommunication() || $keyFlags->isEncryptStorage())) { @@ -612,42 +647,31 @@ protected function isEncryptionKey(?DateTimeInterface $time = null): bool } /** - * Get key expiration from signatures. + * Get sorted primary users. * - * @param array $signatures - * @return DateTimeInterface + * @return array */ - public static function getKeyExpiration(array $signatures): ?DateTimeInterface + protected function getSortedPrimaryUsers(): array { + $users = $this->users; usort( - $signatures, - static function ($a, $b): int { - $aTime = $a->getSignatureCreationTime() ?? new \DateTime(); - $bTime = $b->getSignatureCreationTime() ?? new \DateTime(); - return $bTime->getTimestamp() - $aTime->getTimestamp(); - } - ); - foreach ($signatures as $signature) { - $keyExpirationTime = $signature->getKeyExpirationTime(); - if ($keyExpirationTime instanceof KeyExpirationTime) { - $expirationTime = $keyExpirationTime->getExpirationTime(); - $creationTime = $signature->getSignatureCreationTime() ?? new \DateTime(); - $keyExpiry = $creationTime->setTimestamp( - $creationTime->getTimestamp() + $expirationTime - ); - $signatureExpiry = $signature->getSignatureExpirationTime(); - if (empty($signatureExpiry)) { - return $keyExpiry; + $users, + static function ($a, $b) { + $aPrimary = (int) $a->isPrimary(); + $bPrimary = (int) $b->isPrimary(); + if ($aPrimary === $bPrimary) { + $aTime = $a->getLatestSelfCertification()?->getSignatureCreationTime() + ?? new \DateTime(); + $bTime = $b->getLatestSelfCertification()?->getSignatureCreationTime() + ?? new \DateTime(); + return $aTime->getTimestamp() - $bTime->getTimestamp(); } else { - return $keyExpiry < $signatureExpiry ? $keyExpiry : $signatureExpiry; + return $aPrimary - $bPrimary; } } - else { - return $signature->getSignatureExpirationTime(); - } - } - return null; + ); + return $users; } /** diff --git a/src/Key/PrivateKey.php b/src/Key/PrivateKey.php index 8df268cc..9a45b3a6 100644 --- a/src/Key/PrivateKey.php +++ b/src/Key/PrivateKey.php @@ -274,7 +274,7 @@ public function getDecryptionKeyPackets( $keyPackets = []; foreach ($subkeys as $subkey) { if (empty($keyID) || $keyID === $subkey->getKeyID()) { - if (!$subkey->isEncryptionKey()) { + if (!$subkey->isEncryptionKey() || !$subkey->verify($time)) { continue; } $keyPackets[] = $subkey->getKeyPacket(); diff --git a/src/Type/KeyInterface.php b/src/Type/KeyInterface.php index 4da92410..eec2768b 100644 --- a/src/Type/KeyInterface.php +++ b/src/Type/KeyInterface.php @@ -119,7 +119,7 @@ function getEncryptionKeyPacket( * @param DateTimeInterface $time * @return UserInterface */ - function getPrimaryUser(?DateTimeInterface $time = null): UserInterface; + function getPrimaryUser(?DateTimeInterface $time = null): ?UserInterface; /** * Return key is private