Skip to content
Draft
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
27 changes: 26 additions & 1 deletion server/parsec/components/memory/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
from parsec.components.account import ValidationCodeInfo
from parsec.components.invite import InvitationCreatedBy
from parsec.components.organization import TermsOfService
from parsec.components.pki import PkiCertificate
from parsec.components.pki import MAX_INTERMEDIATE_CERTIFICATES_DEPTH, PkiCertificate
from parsec.components.sequester import SequesterServiceType
from parsec.locks import AdvisoryLock

Expand Down Expand Up @@ -476,6 +476,31 @@ async def save_trustchain(self, trustchain: list[PkiCertificate]):
cert.fingerprint_sha256, cert.content, cert.signed_by
)

async def get_trustchain(self, leaf_fingerprint: bytes) -> list[MemoryPkiCertificate] | None:
try:
current = self.pki_certificates[leaf_fingerprint]
except KeyError:
return None
trustchain = {leaf_fingerprint: current}
for _ in range(MAX_INTERMEDIATE_CERTIFICATES_DEPTH):
# check circular trustchain (allows to stop iteration)
if current.signed_by in trustchain.keys():
return list(trustchain.values())
match current.signed_by:
# Last cert signed by no one
case None:
return list(trustchain.values())
case signed_by_fingerprint:
pass
try:
cert = self.pki_certificates[signed_by_fingerprint]
# the last signer is not in DB
except ValueError:
return list(trustchain.values())
# add signer to trust chain
trustchain[cert.sha256_fingerprint] = cert
current = cert


@dataclass(slots=True)
class MemorySequesterService:
Expand Down
41 changes: 27 additions & 14 deletions server/parsec/components/memory/pki.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
PkiEnrollmentSubmitBadOutcome,
PkiEnrollmentSubmitX509CertificateAlreadySubmitted,
PkiTrustchainError,
parse_pki_cert,
pki_enrollment_accept_validate,
)
from parsec.events import EventCommonCertificate, EventPkiEnrollment
Expand Down Expand Up @@ -268,21 +269,33 @@ async def list(
if author_user.current_profile != UserProfile.ADMIN:
return PkiEnrollmentListBadOutcome.AUTHOR_NOT_ALLOWED

return sorted(
[
PkiEnrollmentListItem(
enrollment_id=enrollment.enrollment_id,
payload=enrollment.submit_payload,
payload_signature=enrollment.submit_payload_signature,
payload_signature_algorithm=enrollment.submit_payload_signature_algorithm,
submitted_on=enrollment.submitted_on,
der_x509_certificate=enrollment.submitter_der_x509_certificate,
# TODO: https://github.com/Scille/parsec-cloud/issues/11558
intermediate_der_x509_certificates=[],
ret = []
for enrollment in org.pki_enrollments.values():
if enrollment.enrollment_state == MemoryPkiEnrollmentState.SUBMITTED:
try:
cert = parse_pki_cert(enrollment.submitter_der_x509_certificate)
except ValueError:
return PkiEnrollmentListBadOutcome.INVALID_CERTIFICATE
match await org.get_trustchain(cert.fingerprint_sha256):
case None:
intermediate_der_x509_certificates = []
case trustchain:
intermediate_der_x509_certificates = [c.der_content for c in trustchain]
Comment on lines +279 to +283
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Given how None is used, get_trustchain could always return a list


ret.append(
PkiEnrollmentListItem(
enrollment_id=enrollment.enrollment_id,
payload=enrollment.submit_payload,
payload_signature=enrollment.submit_payload_signature,
payload_signature_algorithm=enrollment.submit_payload_signature_algorithm,
submitted_on=enrollment.submitted_on,
der_x509_certificate=enrollment.submitter_der_x509_certificate,
intermediate_der_x509_certificates=intermediate_der_x509_certificates,
)
)
for enrollment in org.pki_enrollments.values()
if enrollment.enrollment_state == MemoryPkiEnrollmentState.SUBMITTED
],

return sorted(
ret,
key=lambda x: x.submitted_on,
)

Expand Down
9 changes: 6 additions & 3 deletions server/parsec/components/pki.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ class PkiEnrollmentListBadOutcome(BadOutcomeEnum):
AUTHOR_NOT_FOUND = auto()
AUTHOR_REVOKED = auto()
AUTHOR_NOT_ALLOWED = auto()
INVALID_CERTIFICATE = auto()


class PkiEnrollmentRejectBadOutcome(BadOutcomeEnum):
Expand Down Expand Up @@ -354,9 +355,6 @@ async def build_trustchain(

return PkiTrustchainError.TrustchainTooLong

async def retrieve_trust_chain(self, fingerprint: bytes) -> list[PkiCertificate] | None:
raise NotImplementedError

async def get_cert(self, fingerprint: bytes) -> PkiCertificate | None:
raise NotImplementedError

Expand Down Expand Up @@ -493,6 +491,11 @@ async def api_pki_enrollment_list(
client_ctx.author_not_found_abort()
case PkiEnrollmentListBadOutcome.AUTHOR_REVOKED:
client_ctx.author_revoked_abort()
case PkiEnrollmentListBadOutcome.INVALID_CERTIFICATE:
# TODO add parse error to protocol
return authenticated_cmds.latest.pki_enrollment_list.RepUnknownStatus(
"unexpected parse error", None
)

@api
async def api_pki_enrollment_reject(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ async def test_authenticated_pki_enrollment_list_ok(
enrollment_id=enrollment_id,
submitted_on=submitted_on,
der_x509_certificate=certs[i],
intermediate_der_x509_certificates=[],
intermediate_der_x509_certificates=[test_pki.root["black_mesa"].der_certificate],
payload_signature=f"<user{i} submit payload signature>".encode(),
payload_signature_algorithm=PkiSignatureAlgorithm.RSASSA_PSS_SHA256,
payload=submit_payload,
Expand All @@ -70,7 +70,7 @@ async def test_authenticated_pki_enrollment_list_ok(
force=False,
submitter_human_handle=human_handle,
submitter_der_x509_certificate=expected_enrollment_item.der_x509_certificate,
intermediate_certificates=[],
intermediate_certificates=[test_pki.root["black_mesa"].der_certificate],
submit_payload_signature=expected_enrollment_item.payload_signature,
submit_payload_signature_algorithm=expected_enrollment_item.payload_signature_algorithm,
submit_payload=expected_enrollment_item.payload,
Expand Down
Loading