Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit b392030

Browse files
committed
Initial push of working IETF-116 Hackathon implementation of RKVST SCITT service
Signed-off-by: JAG-UK <jon.geater@gmail.com>
1 parent a1a3001 commit b392030

File tree

5 files changed

+156
-2
lines changed

5 files changed

+156
-2
lines changed

environment.yml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ dependencies:
1818
- pytest=7.2.1
1919
- python=3.8.16
2020
- rfc3986=1.5.0
21+
- rkvst-archivist=0.20.0
2122
- setuptools=66.1.1
2223
- wheel=0.38.4
2324
- pip=22.3.1

scitt_emulator/rkvst.py

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
from archivist.archivist import Archivist
5+
from typing import Optional
6+
from pathlib import Path
7+
import json
8+
import cbor2
9+
#from cose.messages import CoseMessage
10+
import base64
11+
from os import getenv
12+
from . import rkvst_mocks
13+
14+
15+
from scitt_emulator.scitt import SCITTServiceEmulator
16+
17+
18+
class RKVSTSCITTServiceEmulator(SCITTServiceEmulator):
19+
tree_alg = "RKVST"
20+
21+
def __init__(
22+
self, service_parameters_path: Path, storage_path: Optional[Path] = None
23+
):
24+
super().__init__(service_parameters_path, storage_path)
25+
if storage_path is not None:
26+
self._service_private_key_path = (
27+
self.storage_path / "service_private_key.pem"
28+
)
29+
30+
31+
def initialize_service(self):
32+
#########################
33+
# One time initial set-up
34+
#########################
35+
36+
# TODO: is there anything permanent?
37+
# * Credentials? We refresh creds and conn each time.
38+
# * Feeds? Feed is claim-specific
39+
40+
###################
41+
# Every time set-up
42+
###################
43+
44+
# Grab credentials from the enviroment
45+
# TODO: we should support container storage and protected local file storage too
46+
# TODO: we shold support unauthenticated connections for public read calls
47+
# TODO: we should support pass-through JWT from the main client
48+
self.rkvst_network_fqdn = getenv("RKVST_SCITT_URL") or "https://app.rkvst.io"
49+
client_id = getenv("RKVST_SCITT_CLIENT_ID") or rkvst_mocks.mock_client_id
50+
client_secret = getenv("RKVST_SCITT_CLIENT_SECRET") or rkvst_mocks.mock_client_secret
51+
52+
# Initialise RKVST session handler
53+
self.rkvst_connection = Archivist(
54+
self.rkvst_network_fqdn,
55+
(client_id, client_secret),
56+
max_time=300
57+
)
58+
59+
# TODO: We can download the certificate from RKVST but do we actually need to?
60+
self.service_parameters = {
61+
"serviceId": "RKVST",
62+
"treeAlgorithm": self.tree_alg,
63+
"signatureAlgorithm": "ES256",
64+
"serviceCertificate": None,
65+
}
66+
67+
def get_claim(self, entry_id: str):
68+
rkvst_claim= self.rkvst_connection.post(
69+
f"{self.rkvst_network_fqdn}/archivist/v1/notary/claims/events",
70+
{"transaction_id": entry_id},
71+
)
72+
#rkvst_claim=rkvst_mocks.mock_claim
73+
74+
# TODO: This is just neat debug. Get the JSON form of the claim
75+
print(json.dumps(rkvst_claim))
76+
return base64.b64decode(rkvst_claim["claim"])
77+
78+
def get_operation(self, operation_id: str):
79+
print(f'>>> Got Operation ID {operation_id}')
80+
81+
def create_receipt_contents(self, countersign_tbi: bytes, entry_id: str):
82+
# TODO: the emulator passes in the full countersigned thing but our interface
83+
# takes just the claim. Need to work out if this is the right place to strip
84+
# off the furniture or whether our receipt interface should change.
85+
# For the hackathon it's bodged to just pass the claim straight back in, and
86+
# we can work out how to do it properly during the event
87+
88+
# TODO: There are a number of inconsistencies between RKVST Cose and client Cose encoding of these things:
89+
# * RKVST: CBORTag(18, [b'\xa3\x01&\x03papplication/json\x19\x01\x87mapp.rkvst.com', {}, b'{"identity":"assets/71ae3f6e-6228-4e1b-9acb-7883d1b006ad/events/5b2bc2fc-929f-4858-964f-ff1d1ada6e5c"}', b'\xd6\xfe\xb54<\xfd\xea\x11\xe0\xd5@)\xab\x9c_\x18\xa3n=\x85d\xf8\x01\xd1d\xd3z$c<\xa4\xc2W\xf0\xb0\x11\x8e\xa1o\xee[h\x80\xf7hMw\x8f|\xd0\\\x84x\xd6\xd5\x9f\xf1z\x1e\xe0o4\xa7L'])
90+
# * TBI: ['CounterSignatureV2', b'\xa3\x01&\x03papplication/json\x19\x01\x87mapp.rkvst.com', b'\xa3jservice_ideRKVSThtree_algeRKVSTiissued_at\x1ad\x1e\x86"', b'', b'{"identity":"assets/71ae3f6e-6228-4e1b-9acb-7883d1b006ad/events/5b2bc2fc-929f-4858-964f-ff1d1ada6e5c"}', [b'\xd6\xfe\xb54<\xfd\xea\x11\xe0\xd5@)\xab\x9c_\x18\xa3n=\x85d\xf8\x01\xd1d\xd3z$c<\xa4\xc2W\xf0\xb0\x11\x8e\xa1o\xee[h\x80\xf7hMw\x8f|\xd0\\\x84x\xd6\xd5\x9f\xf1z\x1e\xe0o4\xa7L']]
91+
#
92+
# The important differences are:
93+
# * presence of a CBORTag outer envelope;
94+
# * more entries in the countersign;
95+
# * sigs are treated as a list in tbi, but a singleton in RKVST
96+
#
97+
# We beleive the RKVST implementation to be more correct so have left this here for compatibility until the emulator is fixed.
98+
[cstr, source, csissuer, phdr, payload, sig]=cbor2.loads(countersign_tbi)
99+
cose_contents=cbor2.loads(countersign_tbi)
100+
encoded_claim_str=countersign_tbi
101+
rkvst_claim_contents = [
102+
source,
103+
{},
104+
payload,
105+
sig[0],
106+
]
107+
# TODO: b'\xd2' is a hack to get the CBORTag(18) onto the front
108+
rkvst_claim_cbor = b'\xd2' + cbor2.dumps(rkvst_claim_contents)
109+
encoded_claim = base64.b64encode(rkvst_claim_cbor)
110+
encoded_claim_str = str(encoded_claim)
111+
encoded_claim_str = encoded_claim_str[2:-1]
112+
113+
rkvst_receipt = self.rkvst_connection.post(
114+
f"{self.rkvst_network_fqdn}/archivist/v1/notary/receipts",
115+
{"claim": encoded_claim_str},
116+
)
117+
#rkvst_receipt = rkvst_mocks.mock_receipt
118+
119+
# TODO: This is just neat debug. Get the JSON form of the receipt
120+
receipt_file_path = f'{entry_id}.receipt.json'
121+
with open(receipt_file_path, "w") as receipt_file:
122+
json.dump(rkvst_receipt, receipt_file)
123+
print(f"RKVST receipt written to {receipt_file_path}")
124+
receipt_data = str(base64.b64decode(rkvst_receipt["receipt"]))
125+
application_receipt = receipt_data.split('{"application_parameters"')
126+
compact_json = '{"application_parameters"' + application_receipt[1][:-1]
127+
receipt_structure = json.loads(compact_json)
128+
print(json.dumps(receipt_structure, sort_keys=True, indent=2))
129+
130+
# TODO: Make sure we return the right shape structure according to the superclass:
131+
# leaf_info = [internal_hash, internal_data]
132+
# receipt_contents = [signature, node_cert_der, proof, leaf_info]
133+
return base64.b64decode(rkvst_receipt["receipt"])
134+
135+
def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes):
136+
[signature, node_cert_der, proof, leaf_info] = receipt_contents
137+
[internal_hash, internal_data] = leaf_info
138+
139+
# Get RKVST to verify the receipts
140+
# TODO: later. Is this actiually needed since we have offline verification already
141+

scitt_emulator/rkvst_mocks.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
mock_client_id=""
2+
3+
mock_client_secret=""
4+
5+
mock_event_id=""
6+
7+
mock_claim={}
8+
9+
mock_receipt={}

scitt_emulator/tree_algs.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from typing import Mapping
55
from scitt_emulator.scitt import SCITTServiceEmulator
66
from scitt_emulator.ccf import CCFSCITTServiceEmulator
7+
from scitt_emulator.rkvst import RKVSTSCITTServiceEmulator
78

89
TREE_ALGS: Mapping[str, SCITTServiceEmulator] = {
9-
"CCF": CCFSCITTServiceEmulator
10+
"CCF": CCFSCITTServiceEmulator,
11+
"RKVST": RKVSTSCITTServiceEmulator,
1012
}

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"cbor2",
1919
"pycose",
2020
"httpx",
21-
"flask"
21+
"flask",
22+
"rkvst-archivist"
2223
],
2324
)

0 commit comments

Comments
 (0)