pangea-sdk 3.8.0__py3-none-any.whl → 5.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pangea/__init__.py +2 -1
- pangea/asyncio/__init__.py +1 -0
- pangea/asyncio/file_uploader.py +39 -0
- pangea/asyncio/request.py +46 -23
- pangea/asyncio/services/__init__.py +2 -0
- pangea/asyncio/services/audit.py +46 -20
- pangea/asyncio/services/authn.py +123 -61
- pangea/asyncio/services/authz.py +57 -31
- pangea/asyncio/services/base.py +21 -2
- pangea/asyncio/services/embargo.py +2 -2
- pangea/asyncio/services/file_scan.py +24 -9
- pangea/asyncio/services/intel.py +104 -30
- pangea/asyncio/services/redact.py +52 -3
- pangea/asyncio/services/sanitize.py +217 -0
- pangea/asyncio/services/share.py +733 -0
- pangea/asyncio/services/vault.py +1709 -766
- pangea/crypto/rsa.py +135 -0
- pangea/deep_verify.py +7 -1
- pangea/dump_audit.py +9 -8
- pangea/file_uploader.py +35 -0
- pangea/request.py +70 -49
- pangea/response.py +36 -17
- pangea/services/__init__.py +2 -0
- pangea/services/audit/audit.py +57 -29
- pangea/services/audit/models.py +12 -3
- pangea/services/audit/signing.py +6 -5
- pangea/services/audit/util.py +3 -3
- pangea/services/authn/authn.py +120 -66
- pangea/services/authn/models.py +167 -11
- pangea/services/authz.py +53 -30
- pangea/services/base.py +16 -2
- pangea/services/embargo.py +2 -2
- pangea/services/file_scan.py +32 -15
- pangea/services/intel.py +155 -30
- pangea/services/redact.py +132 -3
- pangea/services/sanitize.py +388 -0
- pangea/services/share/file_format.py +170 -0
- pangea/services/share/share.py +1440 -0
- pangea/services/vault/models/asymmetric.py +120 -18
- pangea/services/vault/models/common.py +439 -141
- pangea/services/vault/models/keys.py +94 -0
- pangea/services/vault/models/secret.py +27 -3
- pangea/services/vault/models/symmetric.py +68 -22
- pangea/services/vault/vault.py +1690 -766
- pangea/tools.py +6 -7
- pangea/utils.py +94 -33
- pangea/verify_audit.py +270 -83
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/METADATA +21 -29
- pangea_sdk-5.3.0.dist-info/RECORD +56 -0
- {pangea_sdk-3.8.0.dist-info → pangea_sdk-5.3.0.dist-info}/WHEEL +1 -1
- pangea_sdk-3.8.0.dist-info/RECORD +0 -46
pangea/services/audit/audit.py
CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
import datetime
|
6
6
|
import json
|
7
|
-
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Union
|
7
|
+
from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
|
8
8
|
|
9
9
|
import pangea.exceptions as pexc
|
10
10
|
from pangea.config import PangeaConfig
|
@@ -199,7 +199,7 @@ class AuditBase:
|
|
199
199
|
|
200
200
|
# verify consistency proofs
|
201
201
|
if self.can_verify_consistency_proof(search_event):
|
202
|
-
if self.verify_consistency_proof(
|
202
|
+
if search_event.leaf_index is not None and self.verify_consistency_proof(search_event.leaf_index):
|
203
203
|
search_event.consistency_verification = EventVerification.PASS
|
204
204
|
else:
|
205
205
|
search_event.consistency_verification = EventVerification.FAIL
|
@@ -222,7 +222,7 @@ class AuditBase:
|
|
222
222
|
tree_sizes.difference_update(self.pub_roots.keys())
|
223
223
|
|
224
224
|
if tree_sizes:
|
225
|
-
arweave_roots = get_arweave_published_roots(result.root.tree_name,
|
225
|
+
arweave_roots = get_arweave_published_roots(result.root.tree_name, tree_sizes)
|
226
226
|
else:
|
227
227
|
arweave_roots = {}
|
228
228
|
|
@@ -284,7 +284,7 @@ class AuditBase:
|
|
284
284
|
"""
|
285
285
|
return event.published and event.leaf_index is not None and event.leaf_index >= 0 # type: ignore[return-value]
|
286
286
|
|
287
|
-
def verify_consistency_proof(self,
|
287
|
+
def verify_consistency_proof(self, tree_size: int) -> bool:
|
288
288
|
"""
|
289
289
|
Verify consistency proof
|
290
290
|
|
@@ -293,18 +293,17 @@ class AuditBase:
|
|
293
293
|
Read more at: [What is a consistency proof?](https://pangea.cloud/docs/audit/merkle-trees#what-is-a-consistency-proof)
|
294
294
|
|
295
295
|
Args:
|
296
|
-
|
297
|
-
event (SearchEvent): Audit event to be verified.
|
296
|
+
leaf_index (int): The tree size of the root to be verified.
|
298
297
|
|
299
298
|
Returns:
|
300
299
|
bool: True if consistency proof is verified, False otherwise.
|
301
300
|
"""
|
302
301
|
|
303
|
-
if
|
302
|
+
if tree_size == 0:
|
304
303
|
return True
|
305
304
|
|
306
|
-
curr_root = pub_roots.get(
|
307
|
-
prev_root = pub_roots.get(
|
305
|
+
curr_root = self.pub_roots.get(tree_size + 1)
|
306
|
+
prev_root = self.pub_roots.get(tree_size)
|
308
307
|
|
309
308
|
if not curr_root or not prev_root:
|
310
309
|
return False
|
@@ -314,9 +313,12 @@ class AuditBase:
|
|
314
313
|
):
|
315
314
|
return False
|
316
315
|
|
316
|
+
if curr_root.consistency_proof is None:
|
317
|
+
return False
|
318
|
+
|
317
319
|
curr_root_hash = decode_hash(curr_root.root_hash)
|
318
320
|
prev_root_hash = decode_hash(prev_root.root_hash)
|
319
|
-
proof = decode_consistency_proof(curr_root.consistency_proof)
|
321
|
+
proof = decode_consistency_proof(curr_root.consistency_proof)
|
320
322
|
|
321
323
|
return verify_consistency_proof(curr_root_hash, prev_root_hash, proof)
|
322
324
|
|
@@ -490,7 +492,7 @@ class Audit(ServiceBase, AuditBase):
|
|
490
492
|
verbose: Optional[bool] = None,
|
491
493
|
) -> PangeaResponse[LogResult]:
|
492
494
|
"""
|
493
|
-
Log an
|
495
|
+
Log an event
|
494
496
|
|
495
497
|
Create a log entry in the Secure Audit Log.
|
496
498
|
|
@@ -499,6 +501,7 @@ class Audit(ServiceBase, AuditBase):
|
|
499
501
|
verify (bool, optional): True to verify logs consistency after response.
|
500
502
|
sign_local (bool, optional): True to sign event with local key.
|
501
503
|
verbose (bool, optional): True to get a more verbose response.
|
504
|
+
|
502
505
|
Raises:
|
503
506
|
AuditException: If an audit based api exception happens
|
504
507
|
PangeaAPIException: If an API Error happens
|
@@ -509,17 +512,13 @@ class Audit(ServiceBase, AuditBase):
|
|
509
512
|
Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log).
|
510
513
|
|
511
514
|
Examples:
|
512
|
-
|
513
|
-
log_response = audit.log({"message": "hello world"}, verbose=True)
|
514
|
-
print(f"Response. Hash: {log_response.result.hash}")
|
515
|
-
except pe.PangeaAPIException as e:
|
516
|
-
print(f"Request Error: {e.response.summary}")
|
517
|
-
for err in e.errors:
|
518
|
-
print(f"\\t{err.detail} \\n")
|
515
|
+
response = audit.log_event({"message": "hello world"}, verbose=True)
|
519
516
|
"""
|
520
517
|
|
521
518
|
input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
|
522
|
-
response: PangeaResponse[LogResult] = self.request.post(
|
519
|
+
response: PangeaResponse[LogResult] = self.request.post(
|
520
|
+
"v1/log", LogResult, data=input.model_dump(exclude_none=True)
|
521
|
+
)
|
523
522
|
if response.success and response.result is not None:
|
524
523
|
self._process_log_result(response.result, verify=verify)
|
525
524
|
return response
|
@@ -559,7 +558,7 @@ class Audit(ServiceBase, AuditBase):
|
|
559
558
|
|
560
559
|
input = self._get_log_request(events, sign_local=sign_local, verify=False, verbose=verbose)
|
561
560
|
response: PangeaResponse[LogBulkResult] = self.request.post(
|
562
|
-
"v2/log", LogBulkResult, data=input.
|
561
|
+
"v2/log", LogBulkResult, data=input.model_dump(exclude_none=True)
|
563
562
|
)
|
564
563
|
|
565
564
|
if response.success and response.result is not None:
|
@@ -603,7 +602,7 @@ class Audit(ServiceBase, AuditBase):
|
|
603
602
|
try:
|
604
603
|
# Calling to v2 methods will return always a 202.
|
605
604
|
response: PangeaResponse[LogBulkResult] = self.request.post(
|
606
|
-
"v2/log_async", LogBulkResult, data=input.
|
605
|
+
"v2/log_async", LogBulkResult, data=input.model_dump(exclude_none=True), poll_result=False
|
607
606
|
)
|
608
607
|
except pexc.AcceptedRequestException as e:
|
609
608
|
return e.response
|
@@ -626,6 +625,7 @@ class Audit(ServiceBase, AuditBase):
|
|
626
625
|
verbose: Optional[bool] = None,
|
627
626
|
verify_consistency: bool = False,
|
628
627
|
verify_events: bool = True,
|
628
|
+
return_context: Optional[bool] = None,
|
629
629
|
) -> PangeaResponse[SearchOutput]:
|
630
630
|
"""
|
631
631
|
Search the log
|
@@ -655,6 +655,7 @@ class Audit(ServiceBase, AuditBase):
|
|
655
655
|
verbose (bool, optional): If true, response include root and membership and consistency proofs.
|
656
656
|
verify_consistency (bool): True to verify logs consistency
|
657
657
|
verify_events (bool): True to verify hash events and signatures
|
658
|
+
return_context (bool): Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
658
659
|
|
659
660
|
Raises:
|
660
661
|
AuditException: If an audit based api exception happens
|
@@ -688,10 +689,11 @@ class Audit(ServiceBase, AuditBase):
|
|
688
689
|
max_results=max_results,
|
689
690
|
search_restriction=search_restriction,
|
690
691
|
verbose=verbose,
|
692
|
+
return_context=return_context,
|
691
693
|
)
|
692
694
|
|
693
695
|
response: PangeaResponse[SearchOutput] = self.request.post(
|
694
|
-
"v1/search", SearchOutput, data=input.
|
696
|
+
"v1/search", SearchOutput, data=input.model_dump(exclude_none=True)
|
695
697
|
)
|
696
698
|
if verify_consistency and response.result is not None:
|
697
699
|
self.update_published_roots(response.result)
|
@@ -705,6 +707,7 @@ class Audit(ServiceBase, AuditBase):
|
|
705
707
|
assert_search_restriction: Optional[Dict[str, Sequence[str]]] = None,
|
706
708
|
verify_consistency: bool = False,
|
707
709
|
verify_events: bool = True,
|
710
|
+
return_context: Optional[bool] = None,
|
708
711
|
) -> PangeaResponse[SearchResultOutput]:
|
709
712
|
"""
|
710
713
|
Results of a search
|
@@ -720,6 +723,7 @@ class Audit(ServiceBase, AuditBase):
|
|
720
723
|
assert_search_restriction (Dict[str, Sequence[str]], optional): Assert the requested search results were queried with the exact same search restrictions, to ensure the results comply to the expected restrictions.
|
721
724
|
verify_consistency (bool): True to verify logs consistency
|
722
725
|
verify_events (bool): True to verify hash events and signatures
|
726
|
+
return_context (bool): Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
723
727
|
Raises:
|
724
728
|
AuditException: If an audit based api exception happens
|
725
729
|
PangeaAPIException: If an API Error happens
|
@@ -743,9 +747,10 @@ class Audit(ServiceBase, AuditBase):
|
|
743
747
|
limit=limit,
|
744
748
|
offset=offset,
|
745
749
|
assert_search_restriction=assert_search_restriction,
|
750
|
+
return_context=return_context,
|
746
751
|
)
|
747
752
|
response: PangeaResponse[SearchResultOutput] = self.request.post(
|
748
|
-
"v1/results", SearchResultOutput, data=input.
|
753
|
+
"v1/results", SearchResultOutput, data=input.model_dump(exclude_none=True)
|
749
754
|
)
|
750
755
|
if verify_consistency and response.result is not None:
|
751
756
|
self.update_published_roots(response.result)
|
@@ -807,7 +812,7 @@ class Audit(ServiceBase, AuditBase):
|
|
807
812
|
)
|
808
813
|
try:
|
809
814
|
return self.request.post(
|
810
|
-
"v1/export", PangeaResponseResult, data=input.
|
815
|
+
"v1/export", PangeaResponseResult, data=input.model_dump(exclude_none=True), poll_result=False
|
811
816
|
)
|
812
817
|
except pexc.AcceptedRequestException as e:
|
813
818
|
return e.response
|
@@ -874,13 +879,14 @@ class Audit(ServiceBase, AuditBase):
|
|
874
879
|
response = audit.root(tree_size=7)
|
875
880
|
"""
|
876
881
|
input = RootRequest(tree_size=tree_size)
|
877
|
-
return self.request.post("v1/root", RootResult, data=input.
|
882
|
+
return self.request.post("v1/root", RootResult, data=input.model_dump(exclude_none=True))
|
878
883
|
|
879
884
|
def download_results(
|
880
885
|
self,
|
881
886
|
result_id: Optional[str] = None,
|
882
887
|
format: DownloadFormat = DownloadFormat.CSV,
|
883
888
|
request_id: Optional[str] = None,
|
889
|
+
return_context: Optional[bool] = None,
|
884
890
|
) -> PangeaResponse[DownloadResult]:
|
885
891
|
"""
|
886
892
|
Download search results
|
@@ -893,6 +899,7 @@ class Audit(ServiceBase, AuditBase):
|
|
893
899
|
result_id: ID returned by the search API.
|
894
900
|
format: Format for the records.
|
895
901
|
request_id: ID returned by the export API.
|
902
|
+
return_context (bool): Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
896
903
|
|
897
904
|
Returns:
|
898
905
|
URL where search results can be downloaded.
|
@@ -911,8 +918,10 @@ class Audit(ServiceBase, AuditBase):
|
|
911
918
|
if request_id is None and result_id is None:
|
912
919
|
raise ValueError("must pass one of `request_id` or `result_id`")
|
913
920
|
|
914
|
-
input = DownloadRequest(
|
915
|
-
|
921
|
+
input = DownloadRequest(
|
922
|
+
request_id=request_id, result_id=result_id, format=format, return_context=return_context
|
923
|
+
)
|
924
|
+
return self.request.post("v1/download_results", DownloadResult, data=input.model_dump(exclude_none=True))
|
916
925
|
|
917
926
|
def update_published_roots(self, result: SearchResultOutput):
|
918
927
|
"""Fetches series of published root hashes from Arweave
|
@@ -937,12 +946,31 @@ class Audit(ServiceBase, AuditBase):
|
|
937
946
|
for tree_size in tree_sizes:
|
938
947
|
pub_root = None
|
939
948
|
if tree_size in arweave_roots:
|
940
|
-
pub_root = PublishedRoot(**arweave_roots[tree_size].
|
949
|
+
pub_root = PublishedRoot(**arweave_roots[tree_size].model_dump(exclude_none=True))
|
941
950
|
pub_root.source = RootSource.ARWEAVE
|
942
951
|
elif self.allow_server_roots:
|
943
952
|
resp = self.root(tree_size=tree_size)
|
944
953
|
if resp.success and resp.result is not None:
|
945
|
-
pub_root = PublishedRoot(**resp.result.data.
|
954
|
+
pub_root = PublishedRoot(**resp.result.data.model_dump(exclude_none=True))
|
946
955
|
pub_root.source = RootSource.PANGEA
|
947
956
|
if pub_root is not None:
|
948
957
|
self.pub_roots[tree_size] = pub_root
|
958
|
+
|
959
|
+
self._fix_consistency_proofs(tree_sizes)
|
960
|
+
|
961
|
+
def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
|
962
|
+
# on very rare occasions, the consistency proof in Arweave may be wrong
|
963
|
+
# override it with the proof from pangea (not the root hash, just the proof)
|
964
|
+
for tree_size in tree_sizes:
|
965
|
+
if tree_size not in self.pub_roots or tree_size - 1 not in self.pub_roots:
|
966
|
+
continue
|
967
|
+
|
968
|
+
if self.pub_roots[tree_size].source == RootSource.PANGEA:
|
969
|
+
continue
|
970
|
+
|
971
|
+
if self.verify_consistency_proof(tree_size):
|
972
|
+
continue
|
973
|
+
|
974
|
+
resp = self.root(tree_size=tree_size)
|
975
|
+
if resp.success and resp.result is not None and resp.result.data is not None:
|
976
|
+
self.pub_roots[tree_size].consistency_proof = resp.result.data.consistency_proof
|
pangea/services/audit/models.py
CHANGED
@@ -6,7 +6,7 @@ import datetime
|
|
6
6
|
import enum
|
7
7
|
from typing import Any, Dict, List, Optional, Sequence, Union
|
8
8
|
|
9
|
-
from pangea.response import APIRequestModel, APIResponseModel, PangeaResponseResult
|
9
|
+
from pangea.response import APIRequestModel, APIResponseModel, PangeaDateTime, PangeaResponseResult
|
10
10
|
|
11
11
|
|
12
12
|
class EventVerification(str, enum.Enum):
|
@@ -126,7 +126,7 @@ class EventEnvelope(APIResponseModel):
|
|
126
126
|
event: Dict[str, Any]
|
127
127
|
signature: Optional[str] = None
|
128
128
|
public_key: Optional[str] = None
|
129
|
-
received_at:
|
129
|
+
received_at: PangeaDateTime
|
130
130
|
|
131
131
|
|
132
132
|
class LogRequest(APIRequestModel):
|
@@ -271,6 +271,7 @@ class SearchRequest(APIRequestModel):
|
|
271
271
|
max_results -- Maximum number of results to return.
|
272
272
|
search_restriction -- A list of keys to restrict the search results to. Useful for partitioning data available to the query string.
|
273
273
|
verbose -- If true, include root, membership and consistency proofs in response.
|
274
|
+
return_context -- Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
274
275
|
"""
|
275
276
|
|
276
277
|
query: str
|
@@ -283,6 +284,7 @@ class SearchRequest(APIRequestModel):
|
|
283
284
|
max_results: Optional[int] = None
|
284
285
|
search_restriction: Optional[Dict[str, Sequence[str]]] = None
|
285
286
|
verbose: Optional[bool] = None
|
287
|
+
return_context: Optional[bool] = None
|
286
288
|
|
287
289
|
|
288
290
|
class RootRequest(APIRequestModel):
|
@@ -363,6 +365,7 @@ class SearchEvent(APIResponseModel):
|
|
363
365
|
consistency_verification -- Consistency verification calculated if required.
|
364
366
|
membership_verification -- Membership verification calculated if required.
|
365
367
|
signature_verification -- Signature verification calculated if required.
|
368
|
+
fpe_context -- The context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
366
369
|
"""
|
367
370
|
|
368
371
|
envelope: EventEnvelope
|
@@ -373,6 +376,7 @@ class SearchEvent(APIResponseModel):
|
|
373
376
|
consistency_verification: EventVerification = EventVerification.NONE
|
374
377
|
membership_verification: EventVerification = EventVerification.NONE
|
375
378
|
signature_verification: EventVerification = EventVerification.NONE
|
379
|
+
fpe_context: Optional[str] = None
|
376
380
|
|
377
381
|
|
378
382
|
class SearchResultOutput(PangeaResponseResult):
|
@@ -406,7 +410,7 @@ class SearchOutput(SearchResultOutput):
|
|
406
410
|
"""
|
407
411
|
|
408
412
|
id: str
|
409
|
-
expires_at:
|
413
|
+
expires_at: PangeaDateTime
|
410
414
|
|
411
415
|
|
412
416
|
class SearchResultRequest(APIRequestModel):
|
@@ -418,12 +422,14 @@ class SearchResultRequest(APIRequestModel):
|
|
418
422
|
limit -- Number of audit records to include from the first page of the results.
|
419
423
|
offset -- Offset from the start of the result set to start returning results from.
|
420
424
|
assert_search_restriction -- Assert the requested search results were queried with the exact same search restrictions, to ensure the results comply to the expected restrictions.
|
425
|
+
return_context -- Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption.
|
421
426
|
"""
|
422
427
|
|
423
428
|
id: str
|
424
429
|
limit: Optional[int] = 20
|
425
430
|
offset: Optional[int] = 0
|
426
431
|
assert_search_restriction: Optional[Dict[str, Sequence[str]]] = None
|
432
|
+
return_context: Optional[bool] = None
|
427
433
|
|
428
434
|
|
429
435
|
class DownloadFormat(str, enum.Enum):
|
@@ -450,6 +456,9 @@ class DownloadRequest(APIRequestModel):
|
|
450
456
|
format: Optional[str] = None
|
451
457
|
"""Format for the records."""
|
452
458
|
|
459
|
+
return_context: Optional[bool] = None
|
460
|
+
"""Return the context data needed to decrypt secure audit events that have been redacted with format preserving encryption."""
|
461
|
+
|
453
462
|
|
454
463
|
class DownloadResult(PangeaResponseResult):
|
455
464
|
dest_url: str
|
pangea/services/audit/signing.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Copyright 2022 Pangea Cyber Corporation
|
2
2
|
# Author: Pangea Cyber Corporation
|
3
|
+
from __future__ import annotations
|
4
|
+
|
3
5
|
from abc import ABC, abstractmethod
|
4
6
|
from typing import Optional
|
5
7
|
|
@@ -10,7 +12,7 @@ from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes, Pub
|
|
10
12
|
|
11
13
|
from pangea.exceptions import PangeaException
|
12
14
|
from pangea.services.audit.util import b64decode, b64decode_ascii, b64encode_ascii
|
13
|
-
from pangea.services.vault.models.
|
15
|
+
from pangea.services.vault.models.asymmetric import AsymmetricKeySigningAlgorithm
|
14
16
|
|
15
17
|
|
16
18
|
class AlgorithmSigner(ABC):
|
@@ -43,7 +45,7 @@ class ED25519Signer(AlgorithmSigner):
|
|
43
45
|
)
|
44
46
|
|
45
47
|
def get_algorithm(self) -> str:
|
46
|
-
return
|
48
|
+
return AsymmetricKeySigningAlgorithm.ED25519.value
|
47
49
|
|
48
50
|
|
49
51
|
signers = {
|
@@ -96,7 +98,7 @@ class Signer:
|
|
96
98
|
|
97
99
|
for func in (serialization.load_pem_private_key, serialization.load_ssh_private_key):
|
98
100
|
try:
|
99
|
-
return func(private_key, None)
|
101
|
+
return func(private_key, None)
|
100
102
|
except exceptions.UnsupportedAlgorithm as e:
|
101
103
|
raise e
|
102
104
|
except ValueError:
|
@@ -144,8 +146,7 @@ class Verifier:
|
|
144
146
|
for cls, verifier in verifiers.items():
|
145
147
|
if isinstance(pubkey, cls):
|
146
148
|
return verifier(pubkey).verify(message_bytes, signature_bytes)
|
147
|
-
|
148
|
-
raise PangeaException(f"Not supported public key type: {type(pubkey)}")
|
149
|
+
raise PangeaException(f"Not supported public key type: {type(pubkey)}")
|
149
150
|
|
150
151
|
def _decode_public_key(self, public_key: bytes):
|
151
152
|
"""Parse a public key in PEM or ssh format"""
|
pangea/services/audit/util.py
CHANGED
@@ -7,7 +7,7 @@ from binascii import hexlify, unhexlify
|
|
7
7
|
from dataclasses import dataclass
|
8
8
|
from datetime import datetime
|
9
9
|
from hashlib import sha256
|
10
|
-
from typing import Dict, List, Optional
|
10
|
+
from typing import Collection, Dict, List, Optional
|
11
11
|
|
12
12
|
import requests
|
13
13
|
|
@@ -61,7 +61,7 @@ def verify_hash(hash1: str, hash2: str) -> bool:
|
|
61
61
|
|
62
62
|
|
63
63
|
def verify_envelope_hash(envelope: EventEnvelope, hash: str):
|
64
|
-
return verify_hash(hash_dict(normalize_log(envelope.
|
64
|
+
return verify_hash(hash_dict(normalize_log(envelope.model_dump(exclude_none=True))), hash)
|
65
65
|
|
66
66
|
|
67
67
|
def canonicalize_event(event: Event) -> bytes:
|
@@ -192,7 +192,7 @@ def arweave_graphql_url():
|
|
192
192
|
return f"{ARWEAVE_BASE_URL}/graphql"
|
193
193
|
|
194
194
|
|
195
|
-
def get_arweave_published_roots(tree_name: str, tree_sizes:
|
195
|
+
def get_arweave_published_roots(tree_name: str, tree_sizes: Collection[int]) -> Dict[int, PublishedRoot]:
|
196
196
|
if len(tree_sizes) == 0:
|
197
197
|
return {}
|
198
198
|
|