Accept all bytes-like objects as byte strings in the export subcommand
Marco Ricci

Marco Ricci commited on 2025-01-11 19:04:57
Zeige 1 geänderte Dateien mit 33 Einfügungen und 11 Löschungen.


This is a breaking API change, because some function return types have
been widened.
... ...
@@ -126,7 +126,10 @@ class MasterKeys(TypedDict):
126 126
     """"""
127 127
 
128 128
 
129
-def derive_master_keys_keys(password: str | bytes, iterations: int) -> KeyPair:
129
+def derive_master_keys_keys(
130
+    password: str | Buffer,
131
+    iterations: int,
132
+) -> KeyPair:
130 133
     """Derive encryption and signing keys for the master keys data.
131 134
 
132 135
     The master password is run through a key derivation function to
... ...
@@ -161,7 +164,7 @@ def derive_master_keys_keys(password: str | bytes, iterations: int) -> KeyPair:
161 164
         length=2 * KEY_SIZE,
162 165
         salt=STOREROOM_MASTER_KEYS_UUID,
163 166
         iterations=iterations,
164
-    ).derive(password)
167
+    ).derive(bytes(password))
165 168
     encryption_key, signing_key = struct.unpack(
166 169
         f'{KEY_SIZE}s {KEY_SIZE}s', master_keys_keys_blob
167 170
     )
... ...
@@ -183,7 +186,10 @@ def derive_master_keys_keys(password: str | bytes, iterations: int) -> KeyPair:
183 186
     }
184 187
 
185 188
 
186
-def decrypt_master_keys_data(data: bytes, keys: KeyPair) -> MasterKeys:
189
+def decrypt_master_keys_data(
190
+    data: Buffer,
191
+    keys: KeyPair,
192
+) -> MasterKeys:
187 193
     r"""Decrypt the master keys data.
188 194
 
189 195
     The master keys data contains:
... ...
@@ -230,6 +236,7 @@ def decrypt_master_keys_data(data: bytes, keys: KeyPair) -> MasterKeys:
230 236
         removal.
231 237
 
232 238
     """
239
+    data = memoryview(data).toreadonly().cast('c')
233 240
     ciphertext, claimed_mac = struct.unpack(
234 241
         f'{len(data) - MAC_SIZE}s {MAC_SIZE}s', data
235 242
     )
... ...
@@ -273,7 +280,10 @@ def decrypt_master_keys_data(data: bytes, keys: KeyPair) -> MasterKeys:
273 280
     }
274 281
 
275 282
 
276
-def decrypt_session_keys(data: bytes, master_keys: MasterKeys) -> KeyPair:
283
+def decrypt_session_keys(
284
+    data: Buffer,
285
+    master_keys: MasterKeys,
286
+) -> KeyPair:
277 287
     r"""Decrypt the bucket item's session keys.
278 288
 
279 289
     The bucket item's session keys are single-use keys for encrypting
... ...
@@ -318,6 +328,7 @@ def decrypt_session_keys(data: bytes, master_keys: MasterKeys) -> KeyPair:
318 328
         removal.
319 329
 
320 330
     """
331
+    data = memoryview(data).toreadonly().cast('c')
321 332
     ciphertext, claimed_mac = struct.unpack(
322 333
         f'{len(data) - MAC_SIZE}s {MAC_SIZE}s', data
323 334
     )
... ...
@@ -379,7 +390,10 @@ def decrypt_session_keys(data: bytes, master_keys: MasterKeys) -> KeyPair:
379 390
     return session_keys
380 391
 
381 392
 
382
-def decrypt_contents(data: bytes, session_keys: KeyPair) -> bytes:
393
+def decrypt_contents(
394
+    data: Buffer,
395
+    session_keys: KeyPair,
396
+) -> Buffer:
383 397
     """Decrypt the bucket item's contents.
384 398
 
385 399
     The data consists of:
... ...
@@ -420,6 +434,7 @@ def decrypt_contents(data: bytes, session_keys: KeyPair) -> bytes:
420 434
         removal.
421 435
 
422 436
     """
437
+    data = memoryview(data).toreadonly().cast('c')
423 438
     ciphertext, claimed_mac = struct.unpack(
424 439
         f'{len(data) - MAC_SIZE}s {MAC_SIZE}s', data
425 440
     )
... ...
@@ -463,7 +478,10 @@ def decrypt_contents(data: bytes, session_keys: KeyPair) -> bytes:
463 478
     return plaintext
464 479
 
465 480
 
466
-def decrypt_bucket_item(bucket_item: bytes, master_keys: MasterKeys) -> bytes:
481
+def decrypt_bucket_item(
482
+    bucket_item: Buffer,
483
+    master_keys: MasterKeys,
484
+) -> Buffer:
467 485
     """Decrypt a bucket item.
468 486
 
469 487
     Args:
... ...
@@ -491,6 +509,7 @@ def decrypt_bucket_item(bucket_item: bytes, master_keys: MasterKeys) -> bytes:
491 509
         removal.
492 510
 
493 511
     """
512
+    bucket_item = memoryview(bucket_item).toreadonly().cast('c')
494 513
     logger.debug(
495 514
         _msg.TranslatedString(
496 515
             _msg.DebugMsgTemplate.DECRYPT_BUCKET_ITEM_KEY_INFO,
... ...
@@ -518,7 +537,7 @@ def decrypt_bucket_file(
518 537
     master_keys: MasterKeys,
519 538
     *,
520 539
     root_dir: str | bytes | os.PathLike = '.',
521
-) -> Iterator[bytes]:
540
+) -> Iterator[Buffer]:
522 541
     """Decrypt a complete bucket.
523 542
 
524 543
     Args:
... ...
@@ -599,7 +618,7 @@ def _store(config: dict[str, Any], path: str, json_contents: bytes) -> None:
599 618
 
600 619
 def export_storeroom_data(  # noqa: C901,PLR0912,PLR0914,PLR0915
601 620
     storeroom_path: str | bytes | os.PathLike | None = None,
602
-    master_keys_key: str | bytes | None = None,
621
+    master_keys_key: str | Buffer | None = None,
603 622
 ) -> dict[str, Any]:
604 623
     """Export the full configuration stored in the storeroom.
605 624
 
... ...
@@ -632,6 +651,8 @@ def export_storeroom_data(  # noqa: C901,PLR0912,PLR0914,PLR0915
632 651
         storeroom_path = exporter.get_vault_path()
633 652
     if master_keys_key is None:
634 653
         master_keys_key = exporter.get_vault_key()
654
+    elif not isinstance(master_keys_key, str):
655
+        master_keys_key = memoryview(master_keys_key).toreadonly().cast('c')
635 656
     with open(
636 657
         os.path.join(os.fsdecode(storeroom_path), '.keys'), encoding='utf-8'
637 658
     ) as master_keys_file:
... ...
@@ -676,9 +697,10 @@ def export_storeroom_data(  # noqa: C901,PLR0912,PLR0914,PLR0915
676 697
                 bucket_number=file,
677 698
             )
678 699
         )
679
-        bucket_contents = list(
680
-            decrypt_bucket_file(file, master_keys, root_dir=storeroom_path)
681
-        )
700
+        bucket_contents = [
701
+            bytes(item)
702
+            for item in decrypt_bucket_file(file, master_keys, root_dir=storeroom_path)
703
+        ]
682 704
         bucket_index = json.loads(bucket_contents.pop(0))
683 705
         for pos, item in enumerate(bucket_index):
684 706
             json_contents[item] = bucket_contents[pos]
685 707