24 from hashlib
import sha512
29 from Crypto.Cipher
import AES
30 from functools
import reduce
33 DBLOCK_SIZE = (32 * 1024)
43 CHK_QUERY_SIZE = CHK_HASH_SIZE
45 GNUNET_FS_URI_PREFIX =
"gnunet://fs/"
47 GNUNET_FS_URI_CHK_INFIX =
"chk/"
51 """Returns an ASCII encoding of the given data block like
52 GNUNET_STRINGS_data_to_string() function.
54 data: A bytearray representing the block of data which has to be encoded
56 echart =
"0123456789ABCDEFGHIJKLMNOPQRSTUV"
58 assert (bytearray ==
type(data))
66 while (rpos < size)
or (vbit > 0):
67 if (rpos < size)
and (vbit < 5):
68 bits = (bits << 8) | data[rpos]
73 assert (vbit == ((size * 8) % 5))
75 out += echart[(bits >> (vbit - 5)) & 31]
83 """ Returns the sha512 hash of the given data.
89 return hash_obj.digest()
93 """Class for AES Keys. Contains the main key and the initialization
100 IV_SIZE = AES.block_size
103 """Creates a new AES key.
105 passphrase: string containing the passphrase to get the AES key and
106 initialization vector
108 passphrase = bytearray(passphrase)
110 self.
iviv = bytearray(self.
IV_SIZEIV_SIZE)
113 passphrase = passphrase[self.
KEY_SIZEKEY_SIZE:]
115 self.
iviv = passphrase[:self.
IV_SIZEIV_SIZE]
117 self.
iviv[0:
len(passphrase)] = passphrase
119 self.
keykey[0:
len(passphrase)] = passphrase
120 self.
keykey = str(self.
keykey)
121 self.
iviv = str(self.
iviv)
127 """Initializes the AES object with settings similar to those in GNUnet.
129 aes_key: the AESKey object
130 Returns the newly initialized AES object
132 return AES.new(aes_key.key, AES.MODE_CFB, aes_key.iv, segment_size=128)
136 """Adds padding to the data such that the size of the data is a multiple of
139 data: the data string
140 Returns a tuple:(pad_len, data). pad_len denotes the number of bytes added
141 as padding; data is the new data string with padded bytes at the end
143 pad_len =
len(data) % 16
145 pad_len = 16 - pad_len
146 pad_bytes = bytearray(15)
147 data += str(pad_bytes[:pad_len])
148 return (pad_len, data)
152 """Encrypts the given data using AES.
154 aes_key: the AESKey object to use for AES encryption
155 data: the data string to encrypt
159 enc_data = cipher.encrypt(data)
161 enc_data = enc_data[:-pad_len]
166 """Decrypts the given data using AES
168 aes_key: the AESKey object to use for AES decryption
169 data: the data string to decrypt
173 ptext = cipher.decrypt(data)
175 ptext = ptext[:-pad_len]
180 """Class for the content hash key."""
186 assert (
len(key) == CHK_HASH_SIZE)
187 assert (
len(query) == CHK_QUERY_SIZE)
189 self.
queryquery = query
192 self.
fsizefsize = size
195 sizestr = repr(self.
fsizefsize)
196 if isinstance(self.
fsizefsize, int):
197 sizestr = sizestr[:-1]
198 return GNUNET_FS_URI_PREFIX + GNUNET_FS_URI_CHK_INFIX + \
205 """Computes the depth of the hash tree.
207 size: the size of the file whose tree's depth has to be computed
208 Returns the depth of the tree. Always > 0.
214 if ((fl * CHK_PER_INODE) < fl):
216 fl = fl * CHK_PER_INODE
221 """Calculate how many bytes of payload a block tree of the given depth MAY
222 correspond to at most (this function ignores the fact that some blocks will
223 only be present partially due to the total file size cutting some blocks
226 depth: depth of the block. depth==0 is a DBLOCK.
227 Returns the number of bytes of payload a subtree of this depth may
231 for cnt
in range(0, depth):
232 rsize *= CHK_PER_INODE
237 """Compute the offset of the CHK for the current block in the IBlock
240 depth: depth of the IBlock in the tree (aka overall number of tree levels
241 minus depth); 0 == DBLOCK
242 end_offset: current offset in the overall file, at the *beginning* of the
243 block for DBLOCK (depth == 0), otherwise at the *end* of the
245 Returns the offset in the list of CHKs in the above IBlock
250 ret = end_offset // bds
251 return ret % CHK_PER_INODE
255 """Compute the size of the current IBLOCK. The encoder is triggering the
256 calculation of the size of an IBLOCK at the *end* (hence end_offset) of its
257 construction. The IBLOCK maybe a full or a partial IBLOCK, and this
258 function is to calculate how long it should be.
260 depth: depth of the IBlock in the tree, 0 would be a DBLOCK, must be > 0
261 (this function is for IBLOCKs only!)
262 offset: current offset in the payload (!) of the overall file, must be > 0
263 (since this function is called at the end of a block).
264 Returns the number of elements to be in the corresponding IBlock
275 if (mod % bds)
is not 0:
281 """Returns the content hash key after generating the hash tree for the given
284 readin: the stream where to read data from
285 size: the size of data to be read
289 chks = [
None] * (depth * CHK_PER_INODE)
291 logging.debug(
"Beginning to calculate tree hash with depth: " + repr(depth))
293 if (depth == current_depth):
294 off = CHK_PER_INODE * (depth - 1)
295 assert (chks[off]
is not None)
296 logging.debug(
"Encoding done, reading CHK `" + chks[off].query + \
297 "' from " + repr(off) +
"\n")
299 assert (size == read_offset)
300 uri_chk.setSize(size)
302 if (0 == current_depth):
303 pt_size =
min(DBLOCK_SIZE, size - read_offset)
305 pt_block = readin.read(pt_size)
307 logging.warning(
"Error reading input file stream")
313 reduce((
lambda ba, chk:
314 ba + (chk.key + chk.query)),
315 chks[(current_depth - 1) * CHK_PER_INODE:][:pt_elements],
317 pt_size = pt_elements * (CHK_HASH_SIZE + CHK_QUERY_SIZE)
318 assert (
len(pt_block) == pt_size)
319 assert (pt_size <= DBLOCK_SIZE)
321 logging.debug(
"Encoding data at offset " + repr(read_offset) + \
322 " and depth " + repr(current_depth) +
" with block " \
323 "size " + repr(pt_size) +
" and target CHK offset " + \
324 repr(current_depth * CHK_PER_INODE))
326 pt_aes_key =
AESKey(pt_hash)
329 chk =
Chk(pt_hash, pt_enc_hash)
330 chks[(current_depth * CHK_PER_INODE) + off] = chk
331 if (0 == current_depth):
332 read_offset += pt_size
333 if (read_offset == size)
or \
334 (0 == (read_offset % (CHK_PER_INODE * DBLOCK_SIZE))):
337 if (CHK_PER_INODE == off)
or (read_offset == size):
344 """Returns the CHK URI of the file at the given path.
346 path: the path of the file whose CHK has to be calculated
348 size = os.path.getsize(path)
349 readin = open(path,
"rb")
356 """Prints help about using this script."""
359 Usage: gnunet-chk.py [options] file
360 Prints the Content Hash Key of given file in GNUNET-style URI.
363 -h, --help : prints this message
368 if '__main__' == __name__:
370 opts, args = getopt.getopt(sys.argv[1:],
"h", [
"help"])
371 except getopt.GetoptError
as err:
373 print(
"Exception occurred")
376 for option, value
in opts:
377 if option
in (
"-h",
"--help"):
381 print(
"Incorrect number of arguments passed")
def __init__(self, passphrase)
def __init__(self, key, query)
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
def compute_tree_size_(depth)
def aes_encrypt(aes_key, data)
def compute_iblock_size_(depth, offset)
def setup_aes_cipher_(aes_key)
def chkuri_from_path(path)
def encode_data_to_string(data)
def compute_chk_offset_(depth, end_offset)
def compute_rootchk(readin, size)
def aes_decrypt(aes_key, data)
enum GNUNET_TESTBED_UnderlayLinkModelType type
the type of this model