Skip to content

Commit 7b145e2

Browse files
committed
Add support for digest API
Signed-off-by: Nicholas Vinson <[email protected]>
1 parent f17af6e commit 7b145e2

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

libarchive/entry.py

+79
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ def modify(self, header_codec=None, **attributes):
8686
rdev (int | Tuple[int, int]): device number, if the file is a device
8787
rdevmajor (int): major part of the device number
8888
rdevminor (int): minor part of the device number
89+
md5Digest (bytes): MD5 digest
90+
rmd160Digest (bytes): RMD160 digest
91+
sha1Digest (bytes): SHA1 digest
92+
sha256Digest (bytes): SHA256 digest
93+
sha384Digest (bytes): SHA384 digest
94+
sha512Digest (bytes): SHA512 digest
8995
"""
9096
if header_codec:
9197
self.header_codec = header_codec
@@ -433,6 +439,79 @@ def rdevminor(self):
433439
def rdevminor(self, value):
434440
ffi.entry_set_rdevminor(self._entry_p, value)
435441

442+
@property
443+
def md5Digest(self):
444+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_MD5)
445+
446+
@md5Digest.setter
447+
def md5Digest(self, value):
448+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_MD5, value)
449+
450+
@property
451+
def rmd160Digest(self):
452+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_RMD160)
453+
454+
@rmd160Digest.setter
455+
def rmd160Digest(self, value):
456+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_RMD160, value)
457+
458+
@property
459+
def sha1Digest(self):
460+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA1)
461+
462+
@sha1Digest.setter
463+
def sha1Digest(self, value):
464+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA1, value)
465+
466+
@property
467+
def sha256Digest(self):
468+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA256)
469+
470+
@sha256Digest.setter
471+
def sha256Digest(self, value):
472+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA256, value)
473+
474+
@property
475+
def sha384Digest(self):
476+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA384)
477+
478+
@sha384Digest.setter
479+
def sha384Digest(self, value):
480+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA384, value)
481+
482+
@property
483+
def sha512Digest(self):
484+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA512)
485+
486+
@sha512Digest.setter
487+
def sha512Digest(self, value):
488+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA512, value)
489+
490+
def _digest(self, digestType):
491+
try:
492+
ptr = ffi.entry_digest(self._entry_p, digestType)
493+
if ptr:
494+
return bytes(ptr[:ffi._DIGEST_LENGTHS[digestType - 1]])
495+
except AttributeError:
496+
raise NotImplementedError(f"the libarchive being used (version "
497+
f"{ffi.version_number()} path {ffi.libarchive_path}) doesn't "
498+
f"support read-only digest APIs")
499+
return None
500+
501+
def _set_digest(self, digestType, value):
502+
try:
503+
digestLen = ffi._DIGEST_LENGTHS[digestType - 1]
504+
if len(value) != digestLen:
505+
raise ValueError(f"Invalid input digest Expected {digestLen} "
506+
f"bytes. Got {len(value)}.")
507+
buffer = (digestLen * ffi.c_ubyte)(*value)
508+
ffi.entry_set_digest(self._entry_p, digestType, buffer)
509+
except AttributeError:
510+
raise NotImplementedError(f"the libarchive being used (version "
511+
f"{ffi.version_number()} path {ffi.libarchive_path}) doesn't "
512+
f"support writable digest APIs")
513+
return None
514+
436515

437516
class ConsumedArchiveEntry(ArchiveEntry):
438517

libarchive/ffi.py

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from ctypes import (
22
c_char_p, c_int, c_uint, c_long, c_longlong, c_size_t, c_int64,
3-
c_void_p, c_wchar_p, CFUNCTYPE, POINTER,
3+
c_ubyte, c_void_p, c_wchar_p, CFUNCTYPE, POINTER,
44
)
55

6+
c_ubyte_p = POINTER(c_ubyte)
7+
68
try:
79
from ctypes import c_ssize_t
810
except ImportError:
@@ -362,3 +364,39 @@ def get_write_filter_function(filter_name):
362364
f"the libarchive being used (version {version_number()}, "
363365
f"path {libarchive_path}) doesn't support encryption"
364366
)
367+
368+
# archive digest API
369+
try:
370+
ffi('entry_digest', [c_archive_entry_p, c_int], c_ubyte_p)
371+
372+
ARCHIVE_ENTRY_DIGEST_MD5 = 1
373+
ARCHIVE_ENTRY_DIGEST_RMD160 = 2
374+
ARCHIVE_ENTRY_DIGEST_SHA1 = 3
375+
ARCHIVE_ENTRY_DIGEST_SHA256 = 4
376+
ARCHIVE_ENTRY_DIGEST_SHA384 = 5
377+
ARCHIVE_ENTRY_DIGEST_SHA512 = 6
378+
379+
_DIGEST_LENGTHS = [
380+
16, # MD5
381+
20, # RMD160
382+
20, # SHA1
383+
32, # SHA256
384+
48, # SHA384
385+
64, # SHA512
386+
]
387+
388+
except AttributeError:
389+
logger.info(
390+
f"the libarchive being used (version {version_number()}, "
391+
f"path {libarchive_path}) doesn't support read-only message digest API"
392+
)
393+
394+
try:
395+
ffi('entry_set_digest',
396+
[ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_ubyte)],
397+
ctypes.c_int)
398+
except AttributeError:
399+
logger.info(
400+
f"the libarchive being used (version {version_number()}, "
401+
f"path {libarchive_path}) doesn't support mutable message digest API"
402+
)

0 commit comments

Comments
 (0)