2020-07-15 14:55:56 -04:00
|
|
|
"""
|
|
|
|
Hashing utilities.
|
2020-07-15 15:00:29 -04:00
|
|
|
|
|
|
|
Ported to Python 3.
|
2020-07-15 14:55:56 -04:00
|
|
|
"""
|
2020-07-15 15:00:29 -04:00
|
|
|
from __future__ import absolute_import
|
|
|
|
from __future__ import division
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
from future.utils import PY2
|
|
|
|
if PY2:
|
2020-08-05 11:53:23 -04:00
|
|
|
from builtins import filter, map, zip, ascii, chr, hex, input, next, oct, open, pow, round, super, bytes, dict, list, object, range, str, max, min # noqa: F401
|
2020-07-15 14:55:56 -04:00
|
|
|
|
|
|
|
from past.builtins import chr as byteschr
|
|
|
|
|
2007-06-25 13:23:51 -07:00
|
|
|
import os
|
2016-09-26 20:42:42 -07:00
|
|
|
import hashlib
|
2008-09-25 21:38:24 -07:00
|
|
|
from allmydata.util.netstring import netstring
|
2007-03-29 18:11:30 -07:00
|
|
|
|
2008-02-14 19:58:01 -07:00
|
|
|
# Be very very cautious when modifying this file. Almost any change will
|
|
|
|
# cause a compatibility break, invalidating all outstanding URIs and making
|
|
|
|
# any previously uploaded files become inaccessible. BE CONSERVATIVE AND TEST
|
|
|
|
# AGAINST OLD DATA!
|
|
|
|
|
|
|
|
# Various crypto values are this size: hash outputs (from SHA-256d),
|
2007-12-17 18:34:11 -07:00
|
|
|
# randomly-generated secrets such as the lease secret, and symmetric encryption
|
|
|
|
# keys. In the near future we will add DSA private keys, and salts of various
|
|
|
|
# kinds.
|
2019-05-24 15:10:18 +02:00
|
|
|
CRYPTO_VAL_SIZE = 32
|
|
|
|
|
2007-12-17 18:34:11 -07:00
|
|
|
|
2019-05-15 08:17:44 +02:00
|
|
|
class _SHA256d_Hasher(object):
|
2008-02-06 19:36:43 -07:00
|
|
|
# use SHA-256d, as defined by Ferguson and Schneier: hash the output
|
|
|
|
# again to prevent length-extension attacks
|
|
|
|
def __init__(self, truncate_to=None):
|
2019-05-24 15:10:18 +02:00
|
|
|
self.h = hashlib.sha256()
|
2008-02-06 19:36:43 -07:00
|
|
|
self.truncate_to = truncate_to
|
2008-02-14 19:58:01 -07:00
|
|
|
self._digest = None
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-06 19:36:43 -07:00
|
|
|
def update(self, data):
|
2019-07-08 14:16:37 -06:00
|
|
|
assert isinstance(data, bytes) # no unicode
|
2008-02-06 19:36:43 -07:00
|
|
|
self.h.update(data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-06 19:36:43 -07:00
|
|
|
def digest(self):
|
2008-02-14 19:58:01 -07:00
|
|
|
if self._digest is None:
|
|
|
|
h1 = self.h.digest()
|
|
|
|
del self.h
|
2019-05-24 15:10:18 +02:00
|
|
|
h2 = hashlib.sha256(h1).digest()
|
2008-02-14 19:58:01 -07:00
|
|
|
if self.truncate_to:
|
|
|
|
h2 = h2[:self.truncate_to]
|
|
|
|
self._digest = h2
|
|
|
|
return self._digest
|
|
|
|
|
|
|
|
|
|
|
|
def tagged_hasher(tag, truncate_to=None):
|
|
|
|
hasher = _SHA256d_Hasher(truncate_to)
|
2008-02-06 19:36:43 -07:00
|
|
|
hasher.update(netstring(tag))
|
|
|
|
return hasher
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-14 19:58:01 -07:00
|
|
|
def tagged_hash(tag, val, truncate_to=None):
|
|
|
|
hasher = tagged_hasher(tag, truncate_to)
|
|
|
|
hasher.update(val)
|
|
|
|
return hasher.digest()
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-14 19:58:01 -07:00
|
|
|
def tagged_pair_hash(tag, val1, val2, truncate_to=None):
|
|
|
|
s = _SHA256d_Hasher(truncate_to)
|
2007-03-29 18:11:30 -07:00
|
|
|
s.update(netstring(tag))
|
|
|
|
s.update(netstring(val1))
|
|
|
|
s.update(netstring(val2))
|
|
|
|
return s.digest()
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
# specific hash tags that we use
|
|
|
|
|
2008-02-14 19:58:01 -07:00
|
|
|
|
|
|
|
# immutable
|
2020-07-15 14:55:56 -04:00
|
|
|
STORAGE_INDEX_TAG = b"allmydata_immutable_key_to_storage_index_v1"
|
|
|
|
BLOCK_TAG = b"allmydata_encoded_subshare_v1"
|
|
|
|
UEB_TAG = b"allmydata_uri_extension_v1"
|
|
|
|
PLAINTEXT_TAG = b"allmydata_plaintext_v1"
|
|
|
|
CIPHERTEXT_TAG = b"allmydata_crypttext_v1"
|
|
|
|
CIPHERTEXT_SEGMENT_TAG = b"allmydata_crypttext_segment_v1"
|
|
|
|
PLAINTEXT_SEGMENT_TAG = b"allmydata_plaintext_segment_v1"
|
|
|
|
CONVERGENT_ENCRYPTION_TAG = b"allmydata_immutable_content_to_key_with_added_secret_v1+"
|
|
|
|
|
|
|
|
CLIENT_RENEWAL_TAG = b"allmydata_client_renewal_secret_v1"
|
|
|
|
CLIENT_CANCEL_TAG = b"allmydata_client_cancel_secret_v1"
|
|
|
|
FILE_RENEWAL_TAG = b"allmydata_file_renewal_secret_v1"
|
|
|
|
FILE_CANCEL_TAG = b"allmydata_file_cancel_secret_v1"
|
|
|
|
BUCKET_RENEWAL_TAG = b"allmydata_bucket_renewal_secret_v1"
|
|
|
|
BUCKET_CANCEL_TAG = b"allmydata_bucket_cancel_secret_v1"
|
2008-02-14 19:58:01 -07:00
|
|
|
|
|
|
|
# mutable
|
2020-07-15 14:55:56 -04:00
|
|
|
MUTABLE_WRITEKEY_TAG = b"allmydata_mutable_privkey_to_writekey_v1"
|
|
|
|
MUTABLE_WRITE_ENABLER_MASTER_TAG = b"allmydata_mutable_writekey_to_write_enabler_master_v1"
|
|
|
|
MUTABLE_WRITE_ENABLER_TAG = b"allmydata_mutable_write_enabler_master_and_nodeid_to_write_enabler_v1"
|
|
|
|
MUTABLE_PUBKEY_TAG = b"allmydata_mutable_pubkey_to_fingerprint_v1"
|
|
|
|
MUTABLE_READKEY_TAG = b"allmydata_mutable_writekey_to_readkey_v1"
|
|
|
|
MUTABLE_DATAKEY_TAG = b"allmydata_mutable_readkey_to_datakey_v1"
|
|
|
|
MUTABLE_STORAGEINDEX_TAG = b"allmydata_mutable_readkey_to_storage_index_v1"
|
2008-02-14 19:58:01 -07:00
|
|
|
|
|
|
|
# dirnodes
|
2020-07-15 14:55:56 -04:00
|
|
|
DIRNODE_CHILD_WRITECAP_TAG = b"allmydata_mutable_writekey_and_salt_to_dirnode_child_capkey_v1"
|
|
|
|
DIRNODE_CHILD_SALT_TAG = b"allmydata_dirnode_child_rwcap_to_salt_v1"
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-01 12:27:37 -07:00
|
|
|
def storage_index_hash(key):
|
2007-07-22 19:48:44 -07:00
|
|
|
# storage index is truncated to 128 bits (16 bytes). We're only hashing a
|
2008-03-24 09:46:06 -07:00
|
|
|
# 16-byte value to get it, so there's no point in using a larger value. We
|
|
|
|
# use this same tagged hash to go from encryption key to storage index for
|
|
|
|
# random-keyed immutable files and convergent-encryption immutabie
|
2008-02-06 19:50:47 -07:00
|
|
|
# files. Mutable files use ssk_storage_index_hash().
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(STORAGE_INDEX_TAG, key, 16)
|
2007-07-19 18:21:44 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def block_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(BLOCK_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def block_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(BLOCK_TAG)
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-08 15:59:16 -07:00
|
|
|
def uri_extension_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(UEB_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-08 15:59:16 -07:00
|
|
|
def uri_extension_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(UEB_TAG)
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-09 20:46:04 -07:00
|
|
|
def plaintext_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(PLAINTEXT_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-09 20:46:04 -07:00
|
|
|
def plaintext_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(PLAINTEXT_TAG)
|
2007-06-09 20:46:04 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-09 20:46:04 -07:00
|
|
|
def crypttext_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(CIPHERTEXT_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-09 20:46:04 -07:00
|
|
|
def crypttext_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(CIPHERTEXT_TAG)
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def crypttext_segment_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(CIPHERTEXT_SEGMENT_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def crypttext_segment_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(CIPHERTEXT_SEGMENT_TAG)
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def plaintext_segment_hash(data):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(PLAINTEXT_SEGMENT_TAG, data)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-06-07 21:47:21 -07:00
|
|
|
def plaintext_segment_hasher():
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(PLAINTEXT_SEGMENT_TAG)
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-02-14 19:58:01 -07:00
|
|
|
KEYLEN = 16
|
2009-07-04 19:48:15 -07:00
|
|
|
IVLEN = 16
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2008-03-24 09:46:06 -07:00
|
|
|
def convergence_hash(k, n, segsize, data, convergence):
|
|
|
|
h = convergence_hasher(k, n, segsize, convergence)
|
|
|
|
h.update(data)
|
|
|
|
return h.digest()
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2008-03-24 09:46:06 -07:00
|
|
|
def convergence_hasher(k, n, segsize, convergence):
|
2020-07-15 14:55:56 -04:00
|
|
|
assert isinstance(convergence, bytes)
|
|
|
|
param_tag = netstring(b"%d,%d,%d" % (k, n, segsize))
|
2008-03-24 09:46:06 -07:00
|
|
|
tag = CONVERGENT_ENCRYPTION_TAG + netstring(convergence) + param_tag
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hasher(tag, KEYLEN)
|
2007-06-07 21:47:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-25 13:23:51 -07:00
|
|
|
def random_key():
|
|
|
|
return os.urandom(KEYLEN)
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def my_renewal_secret_hash(my_secret):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(my_secret, CLIENT_RENEWAL_TAG)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def my_cancel_secret_hash(my_secret):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(my_secret, CLIENT_CANCEL_TAG)
|
2007-08-27 19:00:18 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def file_renewal_secret_hash(client_renewal_secret, storage_index):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(FILE_RENEWAL_TAG,
|
2007-08-27 19:00:18 -07:00
|
|
|
client_renewal_secret, storage_index)
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def file_cancel_secret_hash(client_cancel_secret, storage_index):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(FILE_CANCEL_TAG,
|
2007-08-27 19:00:18 -07:00
|
|
|
client_cancel_secret, storage_index)
|
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def bucket_renewal_secret_hash(file_renewal_secret, peerid):
|
2019-05-24 15:10:18 +02:00
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(BUCKET_RENEWAL_TAG, file_renewal_secret, peerid)
|
2007-08-27 19:00:18 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-08-27 19:00:18 -07:00
|
|
|
def bucket_cancel_secret_hash(file_cancel_secret, peerid):
|
2019-05-24 15:10:18 +02:00
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(BUCKET_CANCEL_TAG, file_cancel_secret, peerid)
|
|
|
|
|
2007-06-26 12:36:21 -07:00
|
|
|
|
|
|
|
def _xor(a, b):
|
2020-07-15 14:55:56 -04:00
|
|
|
return b"".join([byteschr(c ^ b) for c in a])
|
2007-06-26 12:36:21 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-06-26 12:36:21 -07:00
|
|
|
def hmac(tag, data):
|
2020-07-15 14:55:56 -04:00
|
|
|
tag = bytes(tag) # Make sure it matches Python 3 behavior
|
|
|
|
ikey = _xor(tag, 0x36)
|
|
|
|
okey = _xor(tag, 0x5c)
|
2019-05-24 15:10:18 +02:00
|
|
|
h1 = hashlib.sha256(ikey + data).digest()
|
|
|
|
h2 = hashlib.sha256(okey + h1).digest()
|
2007-06-26 12:36:21 -07:00
|
|
|
return h2
|
2007-11-01 15:15:29 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-11-01 15:15:29 -07:00
|
|
|
def mutable_rwcap_key_hash(iv, writekey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(DIRNODE_CHILD_WRITECAP_TAG, iv, writekey, KEYLEN)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2009-07-12 17:13:20 -07:00
|
|
|
def mutable_rwcap_salt_hash(writekey):
|
|
|
|
return tagged_hash(DIRNODE_CHILD_SALT_TAG, writekey, IVLEN)
|
2008-02-14 19:58:01 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-11-01 15:15:29 -07:00
|
|
|
def ssk_writekey_hash(privkey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(MUTABLE_WRITEKEY_TAG, privkey, KEYLEN)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-11-05 21:51:08 -07:00
|
|
|
def ssk_write_enabler_master_hash(writekey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(MUTABLE_WRITE_ENABLER_MASTER_TAG, writekey)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-11-06 18:49:59 -07:00
|
|
|
def ssk_write_enabler_hash(writekey, peerid):
|
2019-05-24 15:10:18 +02:00
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2007-11-05 21:51:08 -07:00
|
|
|
wem = ssk_write_enabler_master_hash(writekey)
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(MUTABLE_WRITE_ENABLER_TAG, wem, peerid)
|
2007-11-05 21:51:08 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-11-01 15:15:29 -07:00
|
|
|
def ssk_pubkey_fingerprint_hash(pubkey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(MUTABLE_PUBKEY_TAG, pubkey)
|
2007-11-01 15:15:29 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2007-11-01 15:15:29 -07:00
|
|
|
def ssk_readkey_hash(writekey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(MUTABLE_READKEY_TAG, writekey, KEYLEN)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-11-02 20:51:39 -07:00
|
|
|
def ssk_readkey_data_hash(IV, readkey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_pair_hash(MUTABLE_DATAKEY_TAG, IV, readkey, KEYLEN)
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2007-11-01 15:15:29 -07:00
|
|
|
def ssk_storage_index_hash(readkey):
|
2008-02-14 19:58:01 -07:00
|
|
|
return tagged_hash(MUTABLE_STORAGEINDEX_TAG, readkey, KEYLEN)
|
2009-03-22 20:20:55 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2014-02-24 20:21:24 +00:00
|
|
|
def timing_safe_compare(a, b):
|
2014-02-24 20:23:39 +00:00
|
|
|
n = os.urandom(32)
|
2009-03-22 20:20:55 -07:00
|
|
|
return bool(tagged_hash(n, a) == tagged_hash(n, b))
|
2009-11-26 15:42:57 -08:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2020-07-15 14:55:56 -04:00
|
|
|
BACKUPDB_DIRHASH_TAG = b"allmydata_backupdb_dirhash_v1"
|
2019-05-24 15:10:18 +02:00
|
|
|
|
|
|
|
|
2009-11-26 15:42:57 -08:00
|
|
|
def backupdb_dirhash(contents):
|
|
|
|
return tagged_hash(BACKUPDB_DIRHASH_TAG, contents)
|
2016-09-26 20:42:42 -07:00
|
|
|
|
2019-05-24 15:10:18 +02:00
|
|
|
|
2016-09-26 20:42:42 -07:00
|
|
|
def permute_server_hash(peer_selection_index, server_permutation_seed):
|
|
|
|
return hashlib.sha1(peer_selection_index + server_permutation_seed).digest()
|