diff --git a/capiscio_sdk/_rpc/client.py b/capiscio_sdk/_rpc/client.py index bd7994c..6933e95 100644 --- a/capiscio_sdk/_rpc/client.py +++ b/capiscio_sdk/_rpc/client.py @@ -1346,6 +1346,125 @@ def init( "registered": response.registered, }, None + def create_envelope( + self, + key_id: str, + subject_did: str, + capability_class: str, + delegation_depth_remaining: int, + issuer_badge_jti: str, + txn_id: str = "", + expires_in_seconds: int = 3600, + constraints_json: str = "", + subject_badge_jti: str = "", + enforcement_mode_min: str = "", + ) -> tuple[str, str, str, Optional[str]]: + """Create a root Authority Envelope (RFC-008 §6.1). + + Args: + key_id: Key to sign with (from key store) + subject_did: DID of the agent receiving authority + capability_class: e.g., "tools.database.read" + delegation_depth_remaining: How many further delegations allowed + issuer_badge_jti: JTI of the issuer's badge + txn_id: Transaction ID (auto-generated if empty) + expires_in_seconds: TTL from now (default: 3600) + constraints_json: Optional JSON constraints object + subject_badge_jti: JTI of the subject's badge (optional) + enforcement_mode_min: Optional minimum enforcement mode + + Returns: + Tuple of (envelope_jws, envelope_id, issuer_did, error_message) + """ + request = simpleguard_pb2.CreateEnvelopeRequest( + key_id=key_id, + subject_did=subject_did, + capability_class=capability_class, + delegation_depth_remaining=delegation_depth_remaining, + txn_id=txn_id, + expires_in_seconds=expires_in_seconds, + constraints_json=constraints_json, + issuer_badge_jti=issuer_badge_jti, + subject_badge_jti=subject_badge_jti, + enforcement_mode_min=enforcement_mode_min, + ) + response = self._stub.CreateEnvelope(request) + error = response.error_message if response.error_message else None + return response.envelope_jws, response.envelope_id, response.issuer_did, error + + def derive_envelope( + self, + parent_envelope_jws: str, + key_id: str, + subject_did: str, + capability_class: str, + delegation_depth_remaining: int, + issuer_badge_jti: str, + expires_in_seconds: int = 1800, + constraints_json: str = "", + subject_badge_jti: str = "", + enforcement_mode_min: str = "", + ) -> tuple[str, str, str, Optional[str]]: + """Derive a child Authority Envelope from a parent (RFC-008 §6.3). + + Args: + parent_envelope_jws: Parent envelope JWS + key_id: Key to sign child with + subject_did: DID of the next delegate + capability_class: Must be equal or narrower than parent + delegation_depth_remaining: Must be < parent's depth + issuer_badge_jti: JTI of the child issuer's own badge + expires_in_seconds: TTL from now (default: 1800) + constraints_json: Must be equal or more restrictive + subject_badge_jti: JTI of the subject's badge + enforcement_mode_min: Optional minimum enforcement mode + + Returns: + Tuple of (envelope_jws, envelope_id, parent_authority_hash, error_message) + """ + request = simpleguard_pb2.DeriveEnvelopeRequest( + parent_envelope_jws=parent_envelope_jws, + key_id=key_id, + subject_did=subject_did, + capability_class=capability_class, + delegation_depth_remaining=delegation_depth_remaining, + expires_in_seconds=expires_in_seconds, + constraints_json=constraints_json, + subject_badge_jti=subject_badge_jti, + issuer_badge_jti=issuer_badge_jti, + enforcement_mode_min=enforcement_mode_min, + ) + response = self._stub.DeriveEnvelope(request) + error = response.error_message if response.error_message else None + return response.envelope_jws, response.envelope_id, response.parent_authority_hash, error + + def build_transport_headers( + self, + chain: list[str], + badge_map: Optional[dict[str, str]] = None, + ) -> tuple[str, str, str, Optional[str]]: + """Build RFC-008 §15 transport headers for a delegation chain. + + Args: + chain: Ordered JWS array [root, ..., leaf] + badge_map: DID → badge JWS for intermediate agents + + Returns: + Tuple of (authority_header, authority_chain_header, badge_map_header, error_message) + """ + request = simpleguard_pb2.BuildTransportHeadersRequest( + chain=chain, + badge_map=badge_map or {}, + ) + response = self._stub.BuildTransportHeaders(request) + error = response.error_message if response.error_message else None + return ( + response.authority_header, + response.authority_chain_header, + response.badge_map_header, + error, + ) + class MCPClient: """Client wrapper for MCPService (RFC-006 Tool Authority + RFC-007 Server Identity). diff --git a/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2.py b/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2.py index 3636eec..a7c65d7 100644 --- a/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2.py +++ b/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2.py @@ -26,14 +26,14 @@ from capiscio_sdk._rpc.gen.capiscio.v1 import trust_pb2 as capiscio_dot_v1_dot_trust__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63\x61piscio/v1/simpleguard.proto\x12\x0b\x63\x61piscio.v1\x1a\x18\x63\x61piscio/v1/common.proto\x1a\x17\x63\x61piscio/v1/trust.proto\"\xf1\x01\n\x0bSignRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12\x34\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1c.capiscio.v1.SignatureFormatR\x06\x66ormat\x12?\n\x07headers\x18\x04 \x03(\x0b\x32%.capiscio.v1.SignRequest.HeadersEntryR\x07headers\x1a:\n\x0cHeadersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"|\n\x0cSignResponse\x12\x1c\n\tsignature\x18\x01 \x01(\x0cR\tsignature\x12)\n\x10signature_string\x18\x02 \x01(\tR\x0fsignatureString\x12#\n\rerror_message\x18\x03 \x01(\tR\x0c\x65rrorMessage\"\x9b\x01\n\rVerifyRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x1c\n\tsignature\x18\x02 \x01(\x0cR\tsignature\x12)\n\x10signature_string\x18\x03 \x01(\tR\x0fsignatureString\x12\'\n\x0f\x65xpected_signer\x18\x04 \x01(\tR\x0e\x65xpectedSigner\"\xc0\x01\n\x0eVerifyResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12\x1d\n\nsigner_did\x18\x02 \x01(\tR\tsignerDid\x12\x15\n\x06key_id\x18\x03 \x01(\tR\x05keyId\x12=\n\nvalidation\x18\x04 \x01(\x0b\x32\x1d.capiscio.v1.ValidationResultR\nvalidation\x12#\n\rerror_message\x18\x05 \x01(\tR\x0c\x65rrorMessage\"\xa8\x02\n\x13SignAttachedRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12\x34\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1c.capiscio.v1.SignatureFormatR\x06\x66ormat\x12G\n\x07headers\x18\x04 \x03(\x0b\x32-.capiscio.v1.SignAttachedRequest.HeadersEntryR\x07headers\x12%\n\x0e\x64\x65tach_payload\x18\x05 \x01(\x08R\rdetachPayload\x1a:\n\x0cHeadersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"M\n\x14SignAttachedResponse\x12\x10\n\x03jws\x18\x01 \x01(\tR\x03jws\x12#\n\rerror_message\x18\x02 \x01(\tR\x0c\x65rrorMessage\"}\n\x15VerifyAttachedRequest\x12\x10\n\x03jws\x18\x01 \x01(\tR\x03jws\x12)\n\x10\x64\x65tached_payload\x18\x02 \x01(\x0cR\x0f\x64\x65tachedPayload\x12\'\n\x0f\x65xpected_signer\x18\x03 \x01(\tR\x0e\x65xpectedSigner\"\xe2\x01\n\x16VerifyAttachedResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12\x18\n\x07payload\x18\x02 \x01(\x0cR\x07payload\x12\x1d\n\nsigner_did\x18\x03 \x01(\tR\tsignerDid\x12\x15\n\x06key_id\x18\x04 \x01(\tR\x05keyId\x12=\n\nvalidation\x18\x05 \x01(\x0b\x32\x1d.capiscio.v1.ValidationResultR\nvalidation\x12#\n\rerror_message\x18\x06 \x01(\tR\x0c\x65rrorMessage\"\xf4\x01\n\x16GenerateKeyPairRequest\x12\x37\n\talgorithm\x18\x01 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12M\n\x08metadata\x18\x03 \x03(\x0b\x32\x31.capiscio.v1.GenerateKeyPairRequest.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xb5\x02\n\x17GenerateKeyPairResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x1d\n\npublic_key\x18\x02 \x01(\x0cR\tpublicKey\x12\x1f\n\x0bprivate_key\x18\x03 \x01(\x0cR\nprivateKey\x12$\n\x0epublic_key_pem\x18\x04 \x01(\tR\x0cpublicKeyPem\x12&\n\x0fprivate_key_pem\x18\x05 \x01(\tR\rprivateKeyPem\x12\x37\n\talgorithm\x18\x06 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12#\n\rerror_message\x18\x07 \x01(\tR\x0c\x65rrorMessage\x12\x17\n\x07\x64id_key\x18\x08 \x01(\tR\x06\x64idKey\"}\n\x0eLoadKeyRequest\x12\x1b\n\tfile_path\x18\x01 \x01(\tR\x08\x66ilePath\x12.\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x16.capiscio.v1.KeyFormatR\x06\x66ormat\x12\x1e\n\npassphrase\x18\x03 \x01(\tR\npassphrase\"\xae\x01\n\x0fLoadKeyResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x37\n\talgorithm\x18\x02 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12&\n\x0fhas_private_key\x18\x03 \x01(\x08R\rhasPrivateKey\x12#\n\rerror_message\x18\x04 \x01(\tR\x0c\x65rrorMessage\"\xbf\x01\n\x10\x45xportKeyRequest\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x1b\n\tfile_path\x18\x02 \x01(\tR\x08\x66ilePath\x12.\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x16.capiscio.v1.KeyFormatR\x06\x66ormat\x12\'\n\x0finclude_private\x18\x04 \x01(\x08R\x0eincludePrivate\x12\x1e\n\npassphrase\x18\x05 \x01(\tR\npassphrase\"U\n\x11\x45xportKeyResponse\x12\x1b\n\tfile_path\x18\x01 \x01(\tR\x08\x66ilePath\x12#\n\rerror_message\x18\x02 \x01(\tR\x0c\x65rrorMessage\"*\n\x11GetKeyInfoRequest\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\"\xb5\x03\n\x12GetKeyInfoResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x37\n\talgorithm\x18\x02 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12&\n\x0fhas_private_key\x18\x03 \x01(\x08R\rhasPrivateKey\x12\x1d\n\npublic_key\x18\x04 \x01(\x0cR\tpublicKey\x12$\n\x0epublic_key_pem\x18\x05 \x01(\tR\x0cpublicKeyPem\x12\x35\n\ncreated_at\x18\x06 \x01(\x0b\x32\x16.capiscio.v1.TimestampR\tcreatedAt\x12I\n\x08metadata\x18\x07 \x03(\x0b\x32-.capiscio.v1.GetKeyInfoResponse.MetadataEntryR\x08metadata\x12#\n\rerror_message\x18\x08 \x01(\tR\x0c\x65rrorMessage\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xcf\x02\n\x0bInitRequest\x12\x17\n\x07\x61pi_key\x18\x01 \x01(\tR\x06\x61piKey\x12\x19\n\x08\x61gent_id\x18\x02 \x01(\tR\x07\x61gentId\x12\x1d\n\nserver_url\x18\x03 \x01(\tR\tserverUrl\x12\x1d\n\noutput_dir\x18\x04 \x01(\tR\toutputDir\x12\x14\n\x05\x66orce\x18\x05 \x01(\x08R\x05\x66orce\x12\x37\n\talgorithm\x18\x06 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12\x42\n\x08metadata\x18\x07 \x03(\x0b\x32&.capiscio.v1.InitRequest.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xa2\x02\n\x0cInitResponse\x12\x10\n\x03\x64id\x18\x01 \x01(\tR\x03\x64id\x12\x19\n\x08\x61gent_id\x18\x02 \x01(\tR\x07\x61gentId\x12(\n\x10private_key_path\x18\x03 \x01(\tR\x0eprivateKeyPath\x12&\n\x0fpublic_key_path\x18\x04 \x01(\tR\rpublicKeyPath\x12&\n\x0f\x61gent_card_path\x18\x05 \x01(\tR\ragentCardPath\x12&\n\x0f\x61gent_card_json\x18\x06 \x01(\tR\ragentCardJson\x12\x1e\n\nregistered\x18\x07 \x01(\x08R\nregistered\x12#\n\rerror_message\x18\x08 \x01(\tR\x0c\x65rrorMessage*\x8e\x01\n\x0fSignatureFormat\x12 \n\x1cSIGNATURE_FORMAT_UNSPECIFIED\x10\x00\x12 \n\x1cSIGNATURE_FORMAT_JWS_COMPACT\x10\x01\x12\x1d\n\x19SIGNATURE_FORMAT_JWS_JSON\x10\x02\x12\x18\n\x14SIGNATURE_FORMAT_RAW\x10\x03\x32\xc0\x05\n\x12SimpleGuardService\x12;\n\x04Sign\x12\x18.capiscio.v1.SignRequest\x1a\x19.capiscio.v1.SignResponse\x12\x41\n\x06Verify\x12\x1a.capiscio.v1.VerifyRequest\x1a\x1b.capiscio.v1.VerifyResponse\x12S\n\x0cSignAttached\x12 .capiscio.v1.SignAttachedRequest\x1a!.capiscio.v1.SignAttachedResponse\x12Y\n\x0eVerifyAttached\x12\".capiscio.v1.VerifyAttachedRequest\x1a#.capiscio.v1.VerifyAttachedResponse\x12\\\n\x0fGenerateKeyPair\x12#.capiscio.v1.GenerateKeyPairRequest\x1a$.capiscio.v1.GenerateKeyPairResponse\x12\x44\n\x07LoadKey\x12\x1b.capiscio.v1.LoadKeyRequest\x1a\x1c.capiscio.v1.LoadKeyResponse\x12J\n\tExportKey\x12\x1d.capiscio.v1.ExportKeyRequest\x1a\x1e.capiscio.v1.ExportKeyResponse\x12M\n\nGetKeyInfo\x12\x1e.capiscio.v1.GetKeyInfoRequest\x1a\x1f.capiscio.v1.GetKeyInfoResponse\x12;\n\x04Init\x12\x18.capiscio.v1.InitRequest\x1a\x19.capiscio.v1.InitResponseB\xb6\x01\n\x0f\x63om.capiscio.v1B\x10SimpleguardProtoP\x01ZDgithub.com/capiscio/capiscio-core/pkg/rpc/gen/capiscio/v1;capisciov1\xa2\x02\x03\x43XX\xaa\x02\x0b\x43\x61piscio.V1\xca\x02\x0b\x43\x61piscio\\V1\xe2\x02\x17\x43\x61piscio\\V1\\GPBMetadata\xea\x02\x0c\x43\x61piscio::V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63\x61piscio/v1/simpleguard.proto\x12\x0b\x63\x61piscio.v1\x1a\x18\x63\x61piscio/v1/common.proto\x1a\x17\x63\x61piscio/v1/trust.proto\"\xf1\x01\n\x0bSignRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12\x34\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1c.capiscio.v1.SignatureFormatR\x06\x66ormat\x12?\n\x07headers\x18\x04 \x03(\x0b\x32%.capiscio.v1.SignRequest.HeadersEntryR\x07headers\x1a:\n\x0cHeadersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"|\n\x0cSignResponse\x12\x1c\n\tsignature\x18\x01 \x01(\x0cR\tsignature\x12)\n\x10signature_string\x18\x02 \x01(\tR\x0fsignatureString\x12#\n\rerror_message\x18\x03 \x01(\tR\x0c\x65rrorMessage\"\x9b\x01\n\rVerifyRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x1c\n\tsignature\x18\x02 \x01(\x0cR\tsignature\x12)\n\x10signature_string\x18\x03 \x01(\tR\x0fsignatureString\x12\'\n\x0f\x65xpected_signer\x18\x04 \x01(\tR\x0e\x65xpectedSigner\"\xc0\x01\n\x0eVerifyResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12\x1d\n\nsigner_did\x18\x02 \x01(\tR\tsignerDid\x12\x15\n\x06key_id\x18\x03 \x01(\tR\x05keyId\x12=\n\nvalidation\x18\x04 \x01(\x0b\x32\x1d.capiscio.v1.ValidationResultR\nvalidation\x12#\n\rerror_message\x18\x05 \x01(\tR\x0c\x65rrorMessage\"\xa8\x02\n\x13SignAttachedRequest\x12\x18\n\x07payload\x18\x01 \x01(\x0cR\x07payload\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12\x34\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1c.capiscio.v1.SignatureFormatR\x06\x66ormat\x12G\n\x07headers\x18\x04 \x03(\x0b\x32-.capiscio.v1.SignAttachedRequest.HeadersEntryR\x07headers\x12%\n\x0e\x64\x65tach_payload\x18\x05 \x01(\x08R\rdetachPayload\x1a:\n\x0cHeadersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"M\n\x14SignAttachedResponse\x12\x10\n\x03jws\x18\x01 \x01(\tR\x03jws\x12#\n\rerror_message\x18\x02 \x01(\tR\x0c\x65rrorMessage\"}\n\x15VerifyAttachedRequest\x12\x10\n\x03jws\x18\x01 \x01(\tR\x03jws\x12)\n\x10\x64\x65tached_payload\x18\x02 \x01(\x0cR\x0f\x64\x65tachedPayload\x12\'\n\x0f\x65xpected_signer\x18\x03 \x01(\tR\x0e\x65xpectedSigner\"\xe2\x01\n\x16VerifyAttachedResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12\x18\n\x07payload\x18\x02 \x01(\x0cR\x07payload\x12\x1d\n\nsigner_did\x18\x03 \x01(\tR\tsignerDid\x12\x15\n\x06key_id\x18\x04 \x01(\tR\x05keyId\x12=\n\nvalidation\x18\x05 \x01(\x0b\x32\x1d.capiscio.v1.ValidationResultR\nvalidation\x12#\n\rerror_message\x18\x06 \x01(\tR\x0c\x65rrorMessage\"\xf4\x01\n\x16GenerateKeyPairRequest\x12\x37\n\talgorithm\x18\x01 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12M\n\x08metadata\x18\x03 \x03(\x0b\x32\x31.capiscio.v1.GenerateKeyPairRequest.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xb5\x02\n\x17GenerateKeyPairResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x1d\n\npublic_key\x18\x02 \x01(\x0cR\tpublicKey\x12\x1f\n\x0bprivate_key\x18\x03 \x01(\x0cR\nprivateKey\x12$\n\x0epublic_key_pem\x18\x04 \x01(\tR\x0cpublicKeyPem\x12&\n\x0fprivate_key_pem\x18\x05 \x01(\tR\rprivateKeyPem\x12\x37\n\talgorithm\x18\x06 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12#\n\rerror_message\x18\x07 \x01(\tR\x0c\x65rrorMessage\x12\x17\n\x07\x64id_key\x18\x08 \x01(\tR\x06\x64idKey\"}\n\x0eLoadKeyRequest\x12\x1b\n\tfile_path\x18\x01 \x01(\tR\x08\x66ilePath\x12.\n\x06\x66ormat\x18\x02 \x01(\x0e\x32\x16.capiscio.v1.KeyFormatR\x06\x66ormat\x12\x1e\n\npassphrase\x18\x03 \x01(\tR\npassphrase\"\xae\x01\n\x0fLoadKeyResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x37\n\talgorithm\x18\x02 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12&\n\x0fhas_private_key\x18\x03 \x01(\x08R\rhasPrivateKey\x12#\n\rerror_message\x18\x04 \x01(\tR\x0c\x65rrorMessage\"\xbf\x01\n\x10\x45xportKeyRequest\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x1b\n\tfile_path\x18\x02 \x01(\tR\x08\x66ilePath\x12.\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x16.capiscio.v1.KeyFormatR\x06\x66ormat\x12\'\n\x0finclude_private\x18\x04 \x01(\x08R\x0eincludePrivate\x12\x1e\n\npassphrase\x18\x05 \x01(\tR\npassphrase\"U\n\x11\x45xportKeyResponse\x12\x1b\n\tfile_path\x18\x01 \x01(\tR\x08\x66ilePath\x12#\n\rerror_message\x18\x02 \x01(\tR\x0c\x65rrorMessage\"*\n\x11GetKeyInfoRequest\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\"\xb5\x03\n\x12GetKeyInfoResponse\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x37\n\talgorithm\x18\x02 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12&\n\x0fhas_private_key\x18\x03 \x01(\x08R\rhasPrivateKey\x12\x1d\n\npublic_key\x18\x04 \x01(\x0cR\tpublicKey\x12$\n\x0epublic_key_pem\x18\x05 \x01(\tR\x0cpublicKeyPem\x12\x35\n\ncreated_at\x18\x06 \x01(\x0b\x32\x16.capiscio.v1.TimestampR\tcreatedAt\x12I\n\x08metadata\x18\x07 \x03(\x0b\x32-.capiscio.v1.GetKeyInfoResponse.MetadataEntryR\x08metadata\x12#\n\rerror_message\x18\x08 \x01(\tR\x0c\x65rrorMessage\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xcf\x02\n\x0bInitRequest\x12\x17\n\x07\x61pi_key\x18\x01 \x01(\tR\x06\x61piKey\x12\x19\n\x08\x61gent_id\x18\x02 \x01(\tR\x07\x61gentId\x12\x1d\n\nserver_url\x18\x03 \x01(\tR\tserverUrl\x12\x1d\n\noutput_dir\x18\x04 \x01(\tR\toutputDir\x12\x14\n\x05\x66orce\x18\x05 \x01(\x08R\x05\x66orce\x12\x37\n\talgorithm\x18\x06 \x01(\x0e\x32\x19.capiscio.v1.KeyAlgorithmR\talgorithm\x12\x42\n\x08metadata\x18\x07 \x03(\x0b\x32&.capiscio.v1.InitRequest.MetadataEntryR\x08metadata\x1a;\n\rMetadataEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xa2\x02\n\x0cInitResponse\x12\x10\n\x03\x64id\x18\x01 \x01(\tR\x03\x64id\x12\x19\n\x08\x61gent_id\x18\x02 \x01(\tR\x07\x61gentId\x12(\n\x10private_key_path\x18\x03 \x01(\tR\x0eprivateKeyPath\x12&\n\x0fpublic_key_path\x18\x04 \x01(\tR\rpublicKeyPath\x12&\n\x0f\x61gent_card_path\x18\x05 \x01(\tR\ragentCardPath\x12&\n\x0f\x61gent_card_json\x18\x06 \x01(\tR\ragentCardJson\x12\x1e\n\nregistered\x18\x07 \x01(\x08R\nregistered\x12#\n\rerror_message\x18\x08 \x01(\tR\x0c\x65rrorMessage\"\xb0\x03\n\x15\x43reateEnvelopeRequest\x12\x15\n\x06key_id\x18\x01 \x01(\tR\x05keyId\x12\x1f\n\x0bsubject_did\x18\x02 \x01(\tR\nsubjectDid\x12)\n\x10\x63\x61pability_class\x18\x03 \x01(\tR\x0f\x63\x61pabilityClass\x12<\n\x1a\x64\x65legation_depth_remaining\x18\x04 \x01(\x05R\x18\x64\x65legationDepthRemaining\x12\x15\n\x06txn_id\x18\x05 \x01(\tR\x05txnId\x12,\n\x12\x65xpires_in_seconds\x18\x06 \x01(\x03R\x10\x65xpiresInSeconds\x12)\n\x10\x63onstraints_json\x18\x07 \x01(\tR\x0f\x63onstraintsJson\x12(\n\x10issuer_badge_jti\x18\x08 \x01(\tR\x0eissuerBadgeJti\x12*\n\x11subject_badge_jti\x18\t \x01(\tR\x0fsubjectBadgeJti\x12\x30\n\x14\x65nforcement_mode_min\x18\n \x01(\tR\x12\x65nforcementModeMin\"\xa0\x01\n\x16\x43reateEnvelopeResponse\x12!\n\x0c\x65nvelope_jws\x18\x01 \x01(\tR\x0b\x65nvelopeJws\x12\x1f\n\x0b\x65nvelope_id\x18\x02 \x01(\tR\nenvelopeId\x12\x1d\n\nissuer_did\x18\x03 \x01(\tR\tissuerDid\x12#\n\rerror_message\x18\x04 \x01(\tR\x0c\x65rrorMessage\"\xc9\x03\n\x15\x44\x65riveEnvelopeRequest\x12.\n\x13parent_envelope_jws\x18\x01 \x01(\tR\x11parentEnvelopeJws\x12\x15\n\x06key_id\x18\x02 \x01(\tR\x05keyId\x12\x1f\n\x0bsubject_did\x18\x03 \x01(\tR\nsubjectDid\x12)\n\x10\x63\x61pability_class\x18\x04 \x01(\tR\x0f\x63\x61pabilityClass\x12<\n\x1a\x64\x65legation_depth_remaining\x18\x05 \x01(\x05R\x18\x64\x65legationDepthRemaining\x12,\n\x12\x65xpires_in_seconds\x18\x06 \x01(\x03R\x10\x65xpiresInSeconds\x12)\n\x10\x63onstraints_json\x18\x07 \x01(\tR\x0f\x63onstraintsJson\x12*\n\x11subject_badge_jti\x18\x08 \x01(\tR\x0fsubjectBadgeJti\x12\x30\n\x14\x65nforcement_mode_min\x18\t \x01(\tR\x12\x65nforcementModeMin\x12(\n\x10issuer_badge_jti\x18\n \x01(\tR\x0eissuerBadgeJti\"\xb5\x01\n\x16\x44\x65riveEnvelopeResponse\x12!\n\x0c\x65nvelope_jws\x18\x01 \x01(\tR\x0b\x65nvelopeJws\x12\x1f\n\x0b\x65nvelope_id\x18\x02 \x01(\tR\nenvelopeId\x12\x32\n\x15parent_authority_hash\x18\x03 \x01(\tR\x13parentAuthorityHash\x12#\n\rerror_message\x18\x04 \x01(\tR\x0c\x65rrorMessage\"\xc7\x01\n\x1c\x42uildTransportHeadersRequest\x12\x14\n\x05\x63hain\x18\x01 \x03(\tR\x05\x63hain\x12T\n\tbadge_map\x18\x02 \x03(\x0b\x32\x37.capiscio.v1.BuildTransportHeadersRequest.BadgeMapEntryR\x08\x62\x61\x64geMap\x1a;\n\rBadgeMapEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xcf\x01\n\x1d\x42uildTransportHeadersResponse\x12)\n\x10\x61uthority_header\x18\x01 \x01(\tR\x0f\x61uthorityHeader\x12\x34\n\x16\x61uthority_chain_header\x18\x02 \x01(\tR\x14\x61uthorityChainHeader\x12(\n\x10\x62\x61\x64ge_map_header\x18\x03 \x01(\tR\x0e\x62\x61\x64geMapHeader\x12#\n\rerror_message\x18\x04 \x01(\tR\x0c\x65rrorMessage\"\x97\x02\n\x1aVerifyEnvelopeChainRequest\x12\x14\n\x05\x63hain\x18\x01 \x03(\tR\x05\x63hain\x12R\n\tbadge_map\x18\x02 \x03(\x0b\x32\x35.capiscio.v1.VerifyEnvelopeChainRequest.BadgeMapEntryR\x08\x62\x61\x64geMap\x12\'\n\x0ftrusted_issuers\x18\x03 \x03(\tR\x0etrustedIssuers\x12)\n\x10\x65nforcement_mode\x18\x04 \x01(\tR\x0f\x65nforcementMode\x1a;\n\rBadgeMapEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x02\x38\x01\"\xbc\x02\n\x1bVerifyEnvelopeChainResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12\'\n\x0froot_capability\x18\x02 \x01(\tR\x0erootCapability\x12\'\n\x0fleaf_capability\x18\x03 \x01(\tR\x0eleafCapability\x12\x1f\n\x0btotal_depth\x18\x04 \x01(\x05R\ntotalDepth\x12&\n\x0fleaf_issuer_did\x18\x05 \x01(\tR\rleafIssuerDid\x12(\n\x10leaf_subject_did\x18\x06 \x01(\tR\x0eleafSubjectDid\x12\x1d\n\nerror_code\x18\x07 \x01(\tR\terrorCode\x12#\n\rerror_message\x18\x08 \x01(\tR\x0c\x65rrorMessage*\x8e\x01\n\x0fSignatureFormat\x12 \n\x1cSIGNATURE_FORMAT_UNSPECIFIED\x10\x00\x12 \n\x1cSIGNATURE_FORMAT_JWS_COMPACT\x10\x01\x12\x1d\n\x19SIGNATURE_FORMAT_JWS_JSON\x10\x02\x12\x18\n\x14SIGNATURE_FORMAT_RAW\x10\x03\x32\xd0\x08\n\x12SimpleGuardService\x12;\n\x04Sign\x12\x18.capiscio.v1.SignRequest\x1a\x19.capiscio.v1.SignResponse\x12\x41\n\x06Verify\x12\x1a.capiscio.v1.VerifyRequest\x1a\x1b.capiscio.v1.VerifyResponse\x12S\n\x0cSignAttached\x12 .capiscio.v1.SignAttachedRequest\x1a!.capiscio.v1.SignAttachedResponse\x12Y\n\x0eVerifyAttached\x12\".capiscio.v1.VerifyAttachedRequest\x1a#.capiscio.v1.VerifyAttachedResponse\x12\\\n\x0fGenerateKeyPair\x12#.capiscio.v1.GenerateKeyPairRequest\x1a$.capiscio.v1.GenerateKeyPairResponse\x12\x44\n\x07LoadKey\x12\x1b.capiscio.v1.LoadKeyRequest\x1a\x1c.capiscio.v1.LoadKeyResponse\x12J\n\tExportKey\x12\x1d.capiscio.v1.ExportKeyRequest\x1a\x1e.capiscio.v1.ExportKeyResponse\x12M\n\nGetKeyInfo\x12\x1e.capiscio.v1.GetKeyInfoRequest\x1a\x1f.capiscio.v1.GetKeyInfoResponse\x12;\n\x04Init\x12\x18.capiscio.v1.InitRequest\x1a\x19.capiscio.v1.InitResponse\x12Y\n\x0e\x43reateEnvelope\x12\".capiscio.v1.CreateEnvelopeRequest\x1a#.capiscio.v1.CreateEnvelopeResponse\x12Y\n\x0e\x44\x65riveEnvelope\x12\".capiscio.v1.DeriveEnvelopeRequest\x1a#.capiscio.v1.DeriveEnvelopeResponse\x12n\n\x15\x42uildTransportHeaders\x12).capiscio.v1.BuildTransportHeadersRequest\x1a*.capiscio.v1.BuildTransportHeadersResponse\x12h\n\x13VerifyEnvelopeChain\x12\'.capiscio.v1.VerifyEnvelopeChainRequest\x1a(.capiscio.v1.VerifyEnvelopeChainResponseB;Z9github.com/capiscio/capiscio-core/pkg/rpc/gen/capiscio/v1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'capiscio.v1.simpleguard_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\n\017com.capiscio.v1B\020SimpleguardProtoP\001ZDgithub.com/capiscio/capiscio-core/pkg/rpc/gen/capiscio/v1;capisciov1\242\002\003CXX\252\002\013Capiscio.V1\312\002\013Capiscio\\V1\342\002\027Capiscio\\V1\\GPBMetadata\352\002\014Capiscio::V1' + _globals['DESCRIPTOR']._serialized_options = b'Z9github.com/capiscio/capiscio-core/pkg/rpc/gen/capiscio/v1' _globals['_SIGNREQUEST_HEADERSENTRY']._loaded_options = None _globals['_SIGNREQUEST_HEADERSENTRY']._serialized_options = b'8\001' _globals['_SIGNATTACHEDREQUEST_HEADERSENTRY']._loaded_options = None @@ -44,8 +44,12 @@ _globals['_GETKEYINFORESPONSE_METADATAENTRY']._serialized_options = b'8\001' _globals['_INITREQUEST_METADATAENTRY']._loaded_options = None _globals['_INITREQUEST_METADATAENTRY']._serialized_options = b'8\001' - _globals['_SIGNATUREFORMAT']._serialized_start=3814 - _globals['_SIGNATUREFORMAT']._serialized_end=3956 + _globals['_BUILDTRANSPORTHEADERSREQUEST_BADGEMAPENTRY']._loaded_options = None + _globals['_BUILDTRANSPORTHEADERSREQUEST_BADGEMAPENTRY']._serialized_options = b'8\001' + _globals['_VERIFYENVELOPECHAINREQUEST_BADGEMAPENTRY']._loaded_options = None + _globals['_VERIFYENVELOPECHAINREQUEST_BADGEMAPENTRY']._serialized_options = b'8\001' + _globals['_SIGNATUREFORMAT']._serialized_start=6069 + _globals['_SIGNATUREFORMAT']._serialized_end=6211 _globals['_SIGNREQUEST']._serialized_start=98 _globals['_SIGNREQUEST']._serialized_end=339 _globals['_SIGNREQUEST_HEADERSENTRY']._serialized_start=281 @@ -92,6 +96,26 @@ _globals['_INITREQUEST_METADATAENTRY']._serialized_end=1799 _globals['_INITRESPONSE']._serialized_start=3521 _globals['_INITRESPONSE']._serialized_end=3811 - _globals['_SIMPLEGUARDSERVICE']._serialized_start=3959 - _globals['_SIMPLEGUARDSERVICE']._serialized_end=4663 + _globals['_CREATEENVELOPEREQUEST']._serialized_start=3814 + _globals['_CREATEENVELOPEREQUEST']._serialized_end=4246 + _globals['_CREATEENVELOPERESPONSE']._serialized_start=4249 + _globals['_CREATEENVELOPERESPONSE']._serialized_end=4409 + _globals['_DERIVEENVELOPEREQUEST']._serialized_start=4412 + _globals['_DERIVEENVELOPEREQUEST']._serialized_end=4869 + _globals['_DERIVEENVELOPERESPONSE']._serialized_start=4872 + _globals['_DERIVEENVELOPERESPONSE']._serialized_end=5053 + _globals['_BUILDTRANSPORTHEADERSREQUEST']._serialized_start=5056 + _globals['_BUILDTRANSPORTHEADERSREQUEST']._serialized_end=5255 + _globals['_BUILDTRANSPORTHEADERSREQUEST_BADGEMAPENTRY']._serialized_start=5196 + _globals['_BUILDTRANSPORTHEADERSREQUEST_BADGEMAPENTRY']._serialized_end=5255 + _globals['_BUILDTRANSPORTHEADERSRESPONSE']._serialized_start=5258 + _globals['_BUILDTRANSPORTHEADERSRESPONSE']._serialized_end=5465 + _globals['_VERIFYENVELOPECHAINREQUEST']._serialized_start=5468 + _globals['_VERIFYENVELOPECHAINREQUEST']._serialized_end=5747 + _globals['_VERIFYENVELOPECHAINREQUEST_BADGEMAPENTRY']._serialized_start=5196 + _globals['_VERIFYENVELOPECHAINREQUEST_BADGEMAPENTRY']._serialized_end=5255 + _globals['_VERIFYENVELOPECHAINRESPONSE']._serialized_start=5750 + _globals['_VERIFYENVELOPECHAINRESPONSE']._serialized_end=6066 + _globals['_SIMPLEGUARDSERVICE']._serialized_start=6214 + _globals['_SIMPLEGUARDSERVICE']._serialized_end=7318 # @@protoc_insertion_point(module_scope) diff --git a/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2_grpc.py b/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2_grpc.py index 80c82ed..69b55a5 100644 --- a/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2_grpc.py +++ b/capiscio_sdk/_rpc/gen/capiscio/v1/simpleguard_pb2_grpc.py @@ -60,6 +60,26 @@ def __init__(self, channel): request_serializer=capiscio_dot_v1_dot_simpleguard__pb2.InitRequest.SerializeToString, response_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.InitResponse.FromString, _registered_method=True) + self.CreateEnvelope = channel.unary_unary( + '/capiscio.v1.SimpleGuardService/CreateEnvelope', + request_serializer=capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeRequest.SerializeToString, + response_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeResponse.FromString, + _registered_method=True) + self.DeriveEnvelope = channel.unary_unary( + '/capiscio.v1.SimpleGuardService/DeriveEnvelope', + request_serializer=capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeRequest.SerializeToString, + response_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeResponse.FromString, + _registered_method=True) + self.BuildTransportHeaders = channel.unary_unary( + '/capiscio.v1.SimpleGuardService/BuildTransportHeaders', + request_serializer=capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersRequest.SerializeToString, + response_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersResponse.FromString, + _registered_method=True) + self.VerifyEnvelopeChain = channel.unary_unary( + '/capiscio.v1.SimpleGuardService/VerifyEnvelopeChain', + request_serializer=capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainRequest.SerializeToString, + response_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainResponse.FromString, + _registered_method=True) class SimpleGuardServiceServicer(object): @@ -130,6 +150,34 @@ def Init(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def CreateEnvelope(self, request, context): + """RFC-008: Create a root Authority Envelope (§6.1) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DeriveEnvelope(self, request, context): + """RFC-008: Derive a child Authority Envelope from a parent (§6.3) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def BuildTransportHeaders(self, request, context): + """RFC-008: Build transport headers for a delegation chain (§15.1–§15.3) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def VerifyEnvelopeChain(self, request, context): + """RFC-008: Verify an Authority Envelope chain (§9.2) + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_SimpleGuardServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -178,6 +226,26 @@ def add_SimpleGuardServiceServicer_to_server(servicer, server): request_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.InitRequest.FromString, response_serializer=capiscio_dot_v1_dot_simpleguard__pb2.InitResponse.SerializeToString, ), + 'CreateEnvelope': grpc.unary_unary_rpc_method_handler( + servicer.CreateEnvelope, + request_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeRequest.FromString, + response_serializer=capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeResponse.SerializeToString, + ), + 'DeriveEnvelope': grpc.unary_unary_rpc_method_handler( + servicer.DeriveEnvelope, + request_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeRequest.FromString, + response_serializer=capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeResponse.SerializeToString, + ), + 'BuildTransportHeaders': grpc.unary_unary_rpc_method_handler( + servicer.BuildTransportHeaders, + request_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersRequest.FromString, + response_serializer=capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersResponse.SerializeToString, + ), + 'VerifyEnvelopeChain': grpc.unary_unary_rpc_method_handler( + servicer.VerifyEnvelopeChain, + request_deserializer=capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainRequest.FromString, + response_serializer=capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'capiscio.v1.SimpleGuardService', rpc_method_handlers) @@ -432,3 +500,111 @@ def Init(request, timeout, metadata, _registered_method=True) + + @staticmethod + def CreateEnvelope(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/capiscio.v1.SimpleGuardService/CreateEnvelope', + capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeRequest.SerializeToString, + capiscio_dot_v1_dot_simpleguard__pb2.CreateEnvelopeResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def DeriveEnvelope(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/capiscio.v1.SimpleGuardService/DeriveEnvelope', + capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeRequest.SerializeToString, + capiscio_dot_v1_dot_simpleguard__pb2.DeriveEnvelopeResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def BuildTransportHeaders(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/capiscio.v1.SimpleGuardService/BuildTransportHeaders', + capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersRequest.SerializeToString, + capiscio_dot_v1_dot_simpleguard__pb2.BuildTransportHeadersResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) + + @staticmethod + def VerifyEnvelopeChain(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary( + request, + target, + '/capiscio.v1.SimpleGuardService/VerifyEnvelopeChain', + capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainRequest.SerializeToString, + capiscio_dot_v1_dot_simpleguard__pb2.VerifyEnvelopeChainResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/capiscio_sdk/simple_guard.py b/capiscio_sdk/simple_guard.py index 386faab..d23908b 100644 --- a/capiscio_sdk/simple_guard.py +++ b/capiscio_sdk/simple_guard.py @@ -364,3 +364,155 @@ def _setup_trust_store(self) -> None: # (signing key is loaded separately during guard initialization) if self_trust_path.exists(): self._client.simpleguard.load_key(str(self_trust_path)) + + # ========================================================================= + # RFC-008: Authority Envelope Operations + # ========================================================================= + + def create_envelope( + self, + subject_did: str, + capability_class: str, + delegation_depth_remaining: int = 1, + issuer_badge_jti: str = "", + txn_id: str = "", + expires_in_seconds: int = 3600, + constraints: Optional[Dict[str, Any]] = None, + subject_badge_jti: str = "", + enforcement_mode_min: str = "", + ) -> str: + """ + Create a root Authority Envelope delegating authority to another agent (RFC-008 §6.1). + + The envelope is signed with this agent's key and authorizes `subject_did` + to act within the specified capability class. + + Args: + subject_did: DID of the agent receiving delegated authority. + capability_class: Dot-notation capability (e.g., "tools.database.read"). + delegation_depth_remaining: How many further delegations allowed (default: 1). + issuer_badge_jti: JTI of the issuer's badge (optional). + txn_id: Transaction ID (auto-generated if empty). + expires_in_seconds: TTL from now (default: 3600). + constraints: Optional constraints dict (JSON-serializable). + subject_badge_jti: JTI of the subject's badge (optional). + enforcement_mode_min: Minimum enforcement mode (optional). + + Returns: + JWS Compact Serialization string of the signed envelope. + + Raises: + ConfigurationError: If signing fails or key is not available. + """ + constraints_json = json.dumps(constraints) if constraints else "" + + jws, env_id, issuer_did, err = self._client.simpleguard.create_envelope( + key_id=self.signing_kid, + subject_did=subject_did, + capability_class=capability_class, + delegation_depth_remaining=delegation_depth_remaining, + txn_id=txn_id, + expires_in_seconds=expires_in_seconds, + constraints_json=constraints_json, + issuer_badge_jti=issuer_badge_jti, + subject_badge_jti=subject_badge_jti, + enforcement_mode_min=enforcement_mode_min, + ) + if err: + raise ConfigurationError(f"Failed to create envelope: {err}") + logger.info( + "Created root envelope %s (issuer=%s, subject=%s, capability=%s)", + env_id, issuer_did, subject_did, capability_class, + ) + return jws + + def derive_envelope( + self, + parent_envelope_jws: str, + subject_did: str, + capability_class: str, + delegation_depth_remaining: int = 0, + issuer_badge_jti: str = "", + expires_in_seconds: int = 1800, + constraints: Optional[Dict[str, Any]] = None, + subject_badge_jti: str = "", + enforcement_mode_min: str = "", + ) -> str: + """ + Derive a child Authority Envelope from a parent (RFC-008 §6.3). + + Creates a hash-linked child with monotonic narrowing validation. + + Args: + parent_envelope_jws: The parent envelope JWS to derive from. + subject_did: DID of the next delegate. + capability_class: Must be equal or narrower than parent's capability. + delegation_depth_remaining: Must be < parent's depth (default: 0 = no further delegation). + issuer_badge_jti: JTI of the child issuer's own badge (optional). + expires_in_seconds: TTL from now (default: 1800). + constraints: Must be equal or more restrictive than parent's. + subject_badge_jti: JTI of the subject's badge (optional). + enforcement_mode_min: Minimum enforcement mode (optional, inherited from parent if not set). + + Returns: + JWS Compact Serialization string of the signed child envelope. + + Raises: + ConfigurationError: On narrowing violation, depth exceeded, or signing failure. + """ + constraints_json = json.dumps(constraints) if constraints else "" + + jws, env_id, parent_hash, err = self._client.simpleguard.derive_envelope( + parent_envelope_jws=parent_envelope_jws, + key_id=self.signing_kid, + subject_did=subject_did, + capability_class=capability_class, + delegation_depth_remaining=delegation_depth_remaining, + expires_in_seconds=expires_in_seconds, + constraints_json=constraints_json, + issuer_badge_jti=issuer_badge_jti, + subject_badge_jti=subject_badge_jti, + enforcement_mode_min=enforcement_mode_min, + ) + if err: + raise ConfigurationError(f"Failed to derive envelope: {err}") + logger.info("Derived child envelope %s (parent_hash=%s)", env_id, parent_hash) + return jws + + def make_delegation_headers( + self, + chain: list[str], + badge_map: Optional[Dict[str, str]] = None, + payload: Optional[Dict[str, Any]] = None, + body: Optional[bytes] = None, + ) -> Dict[str, str]: + """ + Generate HTTP headers for a delegated request (RFC-008 §15.1–§15.3). + + Combines the delegation chain headers with the standard badge header. + + Args: + chain: Ordered list of envelope JWS strings [root, ..., leaf]. + badge_map: DID → badge JWS for intermediate agents in the chain. + payload: Optional payload for badge signing (if badge_token not set). + body: Optional HTTP body bytes to bind to badge signature. + + Returns: + Dict with X-Capiscio-Badge, X-Capiscio-Authority, + X-Capiscio-Authority-Chain, and optionally X-Capiscio-Badge-Map headers. + """ + # Get badge header + badge_headers = self.make_headers(payload or {}, body=body) + + # Get chain transport headers via gRPC + authority, chain_header, badge_map_header, err = \ + self._client.simpleguard.build_transport_headers(chain, badge_map or {}) + if err: + raise ConfigurationError(f"Failed to build transport headers: {err}") + + headers = {**badge_headers} + headers["X-Capiscio-Authority"] = authority + headers["X-Capiscio-Authority-Chain"] = chain_header + if badge_map_header: + headers["X-Capiscio-Badge-Map"] = badge_map_header + return headers diff --git a/tests/unit/test_envelope.py b/tests/unit/test_envelope.py new file mode 100644 index 0000000..0ea6995 --- /dev/null +++ b/tests/unit/test_envelope.py @@ -0,0 +1,264 @@ +"""Tests for SimpleGuard envelope operations (RFC-008). + +These tests verify the envelope creation, derivation, and transport header +generation methods that delegate to the Go core via gRPC. +""" + +import json +import os +import pytest +from unittest.mock import patch, MagicMock + +from capiscio_sdk.simple_guard import SimpleGuard +from capiscio_sdk.errors import ConfigurationError + + +@pytest.fixture +def temp_workspace(tmp_path): + """Create a temporary workspace for SimpleGuard.""" + cwd = os.getcwd() + os.chdir(tmp_path) + yield tmp_path + os.chdir(cwd) + + +@pytest.fixture +def mock_rpc_client(): + """Create a mock RPC client for testing without requiring Go core.""" + with patch("capiscio_sdk.simple_guard.CapiscioRPCClient") as MockClient: + mock_instance = MagicMock() + MockClient.return_value = mock_instance + + # Setup simpleguard service mock + mock_instance.simpleguard = MagicMock() + mock_instance.simpleguard.load_key.return_value = ({"key_id": "test-key"}, None) + mock_instance.simpleguard.generate_key_pair.return_value = ({ + "key_id": "test-key", + "private_key_pem": "-----BEGIN PRIVATE KEY-----\ntest\n-----END PRIVATE KEY-----", + "public_key_pem": "-----BEGIN PUBLIC KEY-----\ntest\n-----END PUBLIC KEY-----", + }, None) + mock_instance.simpleguard.sign_attached.return_value = ("mock.jws.token", None) + mock_instance.simpleguard.verify_attached.return_value = ( + True, + b'{"sub": "test", "iss": "local-dev-agent"}', + "test-key", + None, + ) + + # Envelope operation mocks + mock_instance.simpleguard.create_envelope.return_value = ( + "eyJhbGciOiJFZERTQSJ9.root-payload.root-sig", + "env-001", + "did:key:z6Mk_issuer", + None, + ) + mock_instance.simpleguard.derive_envelope.return_value = ( + "eyJhbGciOiJFZERTQSJ9.child-payload.child-sig", + "env-002", + "sha256:abc123", + None, + ) + mock_instance.simpleguard.build_transport_headers.return_value = ( + "eyJhbGciOiJFZERTQSJ9.child-payload.child-sig", + "base64url-encoded-chain", + "base64url-encoded-badge-map", + None, + ) + + yield mock_instance + + +@pytest.fixture +def guard(temp_workspace, mock_rpc_client): + """Create a SimpleGuard instance in dev_mode for testing.""" + g = SimpleGuard(dev_mode=True) + yield g + g.close() + + +class TestCreateEnvelope: + """Tests for SimpleGuard.create_envelope().""" + + def test_basic_creation(self, guard, mock_rpc_client): + """Test basic envelope creation with required params.""" + jws = guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.database.read", + delegation_depth_remaining=2, + issuer_badge_jti="badge-jti-001", + ) + + assert jws == "eyJhbGciOiJFZERTQSJ9.root-payload.root-sig" + mock_rpc_client.simpleguard.create_envelope.assert_called_once() + call_kwargs = mock_rpc_client.simpleguard.create_envelope.call_args[1] + assert call_kwargs["subject_did"] == "did:key:z6Mk_subject" + assert call_kwargs["capability_class"] == "tools.database.read" + assert call_kwargs["delegation_depth_remaining"] == 2 + assert call_kwargs["issuer_badge_jti"] == "badge-jti-001" + assert call_kwargs["key_id"] == "local-dev-key" + + def test_with_constraints(self, guard, mock_rpc_client): + """Test envelope creation with constraints JSON.""" + constraints = {"max_tokens": 1000, "allowed_models": ["gpt-4"]} + jws = guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.llm.invoke", + constraints=constraints, + ) + + assert jws == "eyJhbGciOiJFZERTQSJ9.root-payload.root-sig" + call_kwargs = mock_rpc_client.simpleguard.create_envelope.call_args[1] + assert json.loads(call_kwargs["constraints_json"]) == constraints + + def test_with_enforcement_mode_min(self, guard, mock_rpc_client): + """Test envelope creation with enforcement_mode_min field.""" + jws = guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.database.write", + enforcement_mode_min="EM-AUDIT", + ) + + assert jws is not None + call_kwargs = mock_rpc_client.simpleguard.create_envelope.call_args[1] + assert call_kwargs["enforcement_mode_min"] == "EM-AUDIT" + + def test_without_constraints_sends_empty_string(self, guard, mock_rpc_client): + """Test that no constraints results in empty string (not 'null').""" + guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.database.read", + ) + + call_kwargs = mock_rpc_client.simpleguard.create_envelope.call_args[1] + assert call_kwargs["constraints_json"] == "" + + def test_error_raises_configuration_error(self, guard, mock_rpc_client): + """Test that gRPC errors are raised as ConfigurationError.""" + mock_rpc_client.simpleguard.create_envelope.return_value = ( + "", "", "", "key not found: test-key" + ) + + with pytest.raises(ConfigurationError, match="key not found"): + guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.database.read", + ) + + def test_custom_expiry(self, guard, mock_rpc_client): + """Test custom expiry time.""" + guard.create_envelope( + subject_did="did:key:z6Mk_subject", + capability_class="tools.database.read", + expires_in_seconds=7200, + ) + + call_kwargs = mock_rpc_client.simpleguard.create_envelope.call_args[1] + assert call_kwargs["expires_in_seconds"] == 7200 + + +class TestDeriveEnvelope: + """Tests for SimpleGuard.derive_envelope().""" + + def test_basic_derivation(self, guard, mock_rpc_client): + """Test basic child envelope derivation.""" + parent_jws = "eyJhbGciOiJFZERTQSJ9.parent-payload.parent-sig" + jws = guard.derive_envelope( + parent_envelope_jws=parent_jws, + subject_did="did:key:z6Mk_grandchild", + capability_class="tools.database.read", + delegation_depth_remaining=0, + issuer_badge_jti="child-badge-jti", + ) + + assert jws == "eyJhbGciOiJFZERTQSJ9.child-payload.child-sig" + call_kwargs = mock_rpc_client.simpleguard.derive_envelope.call_args[1] + assert call_kwargs["parent_envelope_jws"] == parent_jws + assert call_kwargs["subject_did"] == "did:key:z6Mk_grandchild" + assert call_kwargs["delegation_depth_remaining"] == 0 + assert call_kwargs["key_id"] == "local-dev-key" + + def test_narrowing_violation_error(self, guard, mock_rpc_client): + """Test that narrowing violations from gRPC are propagated.""" + mock_rpc_client.simpleguard.derive_envelope.return_value = ( + "", "", "", "ENVELOPE_NARROWING_VIOLATION: capability tools.admin exceeds parent" + ) + + with pytest.raises(ConfigurationError, match="NARROWING_VIOLATION"): + guard.derive_envelope( + parent_envelope_jws="eyJ.parent.sig", + subject_did="did:key:z6Mk_sub", + capability_class="tools.admin", + ) + + def test_with_constraints(self, guard, mock_rpc_client): + """Test derivation with constraints (must be narrower than parent).""" + constraints = {"region": "eu-west-1"} + guard.derive_envelope( + parent_envelope_jws="eyJ.parent.sig", + subject_did="did:key:z6Mk_sub", + capability_class="tools.database.read", + constraints=constraints, + ) + + call_kwargs = mock_rpc_client.simpleguard.derive_envelope.call_args[1] + assert json.loads(call_kwargs["constraints_json"]) == constraints + + def test_enforcement_mode_inheritance(self, guard, mock_rpc_client): + """Test enforcement_mode_min passes through for inheritance check.""" + guard.derive_envelope( + parent_envelope_jws="eyJ.parent.sig", + subject_did="did:key:z6Mk_sub", + capability_class="tools.database.read", + enforcement_mode_min="EM-ENFORCE", + ) + + call_kwargs = mock_rpc_client.simpleguard.derive_envelope.call_args[1] + assert call_kwargs["enforcement_mode_min"] == "EM-ENFORCE" + + +class TestMakeDelegationHeaders: + """Tests for SimpleGuard.make_delegation_headers().""" + + def test_basic_headers(self, guard, mock_rpc_client): + """Test delegation header generation with single envelope chain.""" + chain = ["eyJ.root.sig"] + headers = guard.make_delegation_headers(chain=chain) + + assert "X-Capiscio-Authority" in headers + assert "X-Capiscio-Authority-Chain" in headers + assert "X-Capiscio-Badge" in headers # from make_headers() + mock_rpc_client.simpleguard.build_transport_headers.assert_called_once() + + def test_with_badge_map(self, guard, mock_rpc_client): + """Test delegation headers with badge map for intermediaries.""" + chain = ["eyJ.root.sig", "eyJ.child.sig"] + badge_map = {"did:key:z6Mk_intermediary": "eyJ.badge.sig"} + headers = guard.make_delegation_headers(chain=chain, badge_map=badge_map) + + assert "X-Capiscio-Badge-Map" in headers + call_args = mock_rpc_client.simpleguard.build_transport_headers.call_args + assert call_args[0][0] == chain # positional arg 0 + assert call_args[0][1] == badge_map # positional arg 1 + + def test_error_raises_configuration_error(self, guard, mock_rpc_client): + """Test that transport header errors are propagated.""" + mock_rpc_client.simpleguard.build_transport_headers.return_value = ( + "", "", "", "chain is empty" + ) + + with pytest.raises(ConfigurationError, match="chain is empty"): + guard.make_delegation_headers(chain=[]) + + def test_empty_badge_map_omits_header(self, guard, mock_rpc_client): + """Test that empty badge_map_header omits X-Capiscio-Badge-Map.""" + mock_rpc_client.simpleguard.build_transport_headers.return_value = ( + "eyJ.leaf.sig", + "base64url-chain", + "", # empty badge map header + None, + ) + + headers = guard.make_delegation_headers(chain=["eyJ.root.sig"]) + + # Empty badge_map_header means the header is not added (truthy check) + assert "X-Capiscio-Badge-Map" not in headers