2007-11-09 21:40:13 +00:00
|
|
|
from pycryptopp.hash.sha256 import SHA256
|
2007-06-25 20:23:51 +00:00
|
|
|
import os
|
2007-03-30 01:11:30 +00:00
|
|
|
|
2007-12-18 01:34:11 +00:00
|
|
|
# Various crypto values are this size: hash outputs (from SHA-256),
|
|
|
|
# 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.
|
|
|
|
CRYPTO_VAL_SIZE=32
|
|
|
|
|
2007-12-03 21:52:42 +00:00
|
|
|
class IntegrityCheckError(Exception):
|
|
|
|
pass
|
|
|
|
|
2007-03-30 01:11:30 +00:00
|
|
|
def netstring(s):
|
2007-12-03 21:52:42 +00:00
|
|
|
assert isinstance(s, str), s
|
2007-03-30 01:11:30 +00:00
|
|
|
return "%d:%s," % (len(s), s,)
|
|
|
|
|
|
|
|
def tagged_hash(tag, val):
|
2007-11-09 21:40:13 +00:00
|
|
|
s = SHA256()
|
2007-03-30 01:11:30 +00:00
|
|
|
s.update(netstring(tag))
|
|
|
|
s.update(val)
|
|
|
|
return s.digest()
|
2007-11-01 22:25:09 +00:00
|
|
|
|
2008-02-07 02:36:43 +00:00
|
|
|
def tagged_hash_256d(tag, val, truncate_to=None):
|
|
|
|
# use SHA-256d, as defined by Ferguson and Schneier: hash the output
|
|
|
|
# again to prevent length-extension attacks
|
|
|
|
s = SHA256()
|
|
|
|
s.update(netstring(tag))
|
|
|
|
s.update(val)
|
|
|
|
h = s.digest()
|
|
|
|
h2 = SHA256(h).digest()
|
|
|
|
if truncate_to:
|
|
|
|
h2 = h2[:truncate_to]
|
|
|
|
return h2
|
|
|
|
|
|
|
|
class SHA256d_Hasher:
|
|
|
|
def __init__(self, truncate_to=None):
|
|
|
|
self.h = SHA256()
|
|
|
|
self.truncate_to = truncate_to
|
|
|
|
def update(self, data):
|
|
|
|
self.h.update(data)
|
|
|
|
def digest(self):
|
|
|
|
h1 = self.h.digest()
|
|
|
|
del self.h
|
|
|
|
h2 = SHA256(h1).digest()
|
|
|
|
if self.truncate_to:
|
|
|
|
h2 = h2[:self.truncate_to]
|
|
|
|
return h2
|
|
|
|
|
|
|
|
def tagged_hasher_256d(tag, truncate_to=None):
|
|
|
|
hasher = SHA256d_Hasher(truncate_to)
|
|
|
|
hasher.update(netstring(tag))
|
|
|
|
return hasher
|
|
|
|
|
2007-03-30 01:11:30 +00:00
|
|
|
def tagged_pair_hash(tag, val1, val2):
|
2007-11-09 21:40:13 +00:00
|
|
|
s = SHA256()
|
2007-03-30 01:11:30 +00:00
|
|
|
s.update(netstring(tag))
|
|
|
|
s.update(netstring(val1))
|
|
|
|
s.update(netstring(val2))
|
|
|
|
return s.digest()
|
|
|
|
|
2007-06-08 04:47:21 +00:00
|
|
|
# specific hash tags that we use
|
|
|
|
|
|
|
|
def tagged_hasher(tag):
|
2007-11-09 21:40:13 +00:00
|
|
|
return SHA256(netstring(tag))
|
2007-06-08 04:47:21 +00:00
|
|
|
|
2008-02-01 19:27:37 +00:00
|
|
|
def storage_index_hash(key):
|
2007-07-23 02:48:44 +00:00
|
|
|
# storage index is truncated to 128 bits (16 bytes). We're only hashing a
|
|
|
|
# 16-byte value to get it, so there's no point in using a larger value.
|
2008-02-07 02:50:47 +00:00
|
|
|
# We use this same tagged hash to go from encryption key to storage index
|
|
|
|
# for random-keyed immutable files and content-hash-keyed immutabie
|
|
|
|
# files. Mutable files use ssk_storage_index_hash().
|
|
|
|
return tagged_hash_256d("allmydata_immutable_storage_index_v2", key, 16)
|
2007-07-20 01:21:44 +00:00
|
|
|
|
2007-06-08 04:47:21 +00:00
|
|
|
def block_hash(data):
|
|
|
|
return tagged_hash("allmydata_encoded_subshare_v1", data)
|
|
|
|
def block_hasher():
|
|
|
|
return tagged_hasher("allmydata_encoded_subshare_v1")
|
|
|
|
|
2007-06-08 22:59:16 +00:00
|
|
|
def uri_extension_hash(data):
|
|
|
|
return tagged_hash("allmydata_uri_extension_v1", data)
|
|
|
|
def uri_extension_hasher():
|
|
|
|
return tagged_hasher("allmydata_uri_extension_v1")
|
2007-06-08 04:47:21 +00:00
|
|
|
|
2007-06-10 03:46:04 +00:00
|
|
|
def plaintext_hash(data):
|
|
|
|
return tagged_hash("allmydata_plaintext_hash_v1", data)
|
|
|
|
def plaintext_hasher():
|
|
|
|
return tagged_hasher("allmydata_plaintext_hash_v1")
|
|
|
|
|
|
|
|
def crypttext_hash(data):
|
|
|
|
return tagged_hash("allmydata_crypttext_hash_v1", data)
|
|
|
|
def crypttext_hasher():
|
|
|
|
return tagged_hasher("allmydata_crypttext_hash_v1")
|
2007-06-08 04:47:21 +00:00
|
|
|
|
|
|
|
def crypttext_segment_hash(data):
|
|
|
|
return tagged_hash("allmydata_crypttext_segment_v1", data)
|
|
|
|
def crypttext_segment_hasher():
|
|
|
|
return tagged_hasher("allmydata_crypttext_segment_v1")
|
|
|
|
|
|
|
|
def plaintext_segment_hash(data):
|
|
|
|
return tagged_hash("allmydata_plaintext_segment_v1", data)
|
|
|
|
def plaintext_segment_hasher():
|
|
|
|
return tagged_hasher("allmydata_plaintext_segment_v1")
|
|
|
|
|
2008-02-07 02:50:47 +00:00
|
|
|
def content_hash_key_hash(k, n, segsize, data):
|
2008-02-15 20:11:02 +00:00
|
|
|
# This is defined to return a 16-byte AES key. We use SHA-256d here.
|
2008-02-07 02:50:47 +00:00
|
|
|
# we'd like to use it everywhere, but we're only switching algorithms
|
|
|
|
# when we can hide the compatibility breaks in other necessary changes.
|
|
|
|
param_tag = netstring("%d,%d,%d" % (k, n, segsize))
|
|
|
|
h = tagged_hash_256d("allmydata_encryption_key_v2+" + param_tag, data, 16)
|
|
|
|
return h
|
|
|
|
def content_hash_key_hasher(k, n, segsize):
|
|
|
|
param_tag = netstring("%d,%d,%d" % (k, n, segsize))
|
|
|
|
return tagged_hasher_256d("allmydata_encryption_key_v2+" + param_tag, 16)
|
2007-06-08 04:47:21 +00:00
|
|
|
|
2007-06-25 20:23:51 +00:00
|
|
|
KEYLEN = 16
|
|
|
|
def random_key():
|
|
|
|
return os.urandom(KEYLEN)
|
|
|
|
|
2007-08-28 02:00:18 +00:00
|
|
|
def my_renewal_secret_hash(my_secret):
|
|
|
|
return tagged_hash(my_secret, "bucket_renewal_secret")
|
|
|
|
def my_cancel_secret_hash(my_secret):
|
|
|
|
return tagged_hash(my_secret, "bucket_cancel_secret")
|
|
|
|
|
|
|
|
def file_renewal_secret_hash(client_renewal_secret, storage_index):
|
|
|
|
return tagged_pair_hash("file_renewal_secret",
|
|
|
|
client_renewal_secret, storage_index)
|
|
|
|
|
|
|
|
def file_cancel_secret_hash(client_cancel_secret, storage_index):
|
|
|
|
return tagged_pair_hash("file_cancel_secret",
|
|
|
|
client_cancel_secret, storage_index)
|
|
|
|
|
|
|
|
def bucket_renewal_secret_hash(file_renewal_secret, peerid):
|
2007-11-07 01:49:59 +00:00
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2007-08-28 02:00:18 +00:00
|
|
|
return tagged_pair_hash("bucket_renewal_secret",
|
|
|
|
file_renewal_secret, peerid)
|
|
|
|
|
|
|
|
def bucket_cancel_secret_hash(file_cancel_secret, peerid):
|
2007-11-07 01:49:59 +00:00
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2007-08-28 02:00:18 +00:00
|
|
|
return tagged_pair_hash("bucket_cancel_secret",
|
|
|
|
file_cancel_secret, peerid)
|
2007-08-28 00:28:51 +00:00
|
|
|
|
2007-06-25 20:23:51 +00:00
|
|
|
def dir_write_enabler_hash(write_key):
|
|
|
|
return tagged_hash("allmydata_dir_write_enabler_v1", write_key)
|
|
|
|
def dir_read_key_hash(write_key):
|
|
|
|
return tagged_hash("allmydata_dir_read_key_v1", write_key)[:KEYLEN]
|
|
|
|
def dir_index_hash(read_key):
|
|
|
|
return tagged_hash("allmydata_dir_index_v1", read_key)
|
|
|
|
def dir_name_hash(readkey, name):
|
|
|
|
return tagged_pair_hash("allmydata_dir_name_v1", readkey, name)
|
|
|
|
|
|
|
|
def generate_dirnode_keys_from_writekey(write_key):
|
|
|
|
readkey = dir_read_key_hash(write_key)
|
|
|
|
write_enabler = dir_write_enabler_hash(write_key)
|
|
|
|
index = dir_index_hash(readkey)
|
|
|
|
return write_key, write_enabler, readkey, index
|
|
|
|
|
|
|
|
def generate_dirnode_keys_from_readkey(read_key):
|
|
|
|
index = dir_index_hash(read_key)
|
|
|
|
return None, None, read_key, index
|
2007-06-26 19:36:21 +00:00
|
|
|
|
|
|
|
def _xor(a, b):
|
|
|
|
return "".join([chr(ord(c) ^ ord(b)) for c in a])
|
|
|
|
|
|
|
|
def hmac(tag, data):
|
|
|
|
ikey = _xor(tag, "\x36")
|
|
|
|
okey = _xor(tag, "\x5c")
|
2007-11-09 21:40:13 +00:00
|
|
|
h1 = SHA256(ikey + data).digest()
|
|
|
|
h2 = SHA256(okey + h1).digest()
|
2007-06-26 19:36:21 +00:00
|
|
|
return h2
|
2007-11-01 22:15:29 +00:00
|
|
|
|
|
|
|
def mutable_rwcap_key_hash(iv, writekey):
|
2007-12-04 00:27:46 +00:00
|
|
|
return tagged_pair_hash("allmydata_mutable_rwcap_key_v1", iv, writekey)[:16]
|
2007-11-01 22:15:29 +00:00
|
|
|
def ssk_writekey_hash(privkey):
|
2007-12-04 00:27:46 +00:00
|
|
|
return tagged_hash("allmydata_mutable_writekey_v1", privkey)[:16]
|
2007-11-06 04:51:08 +00:00
|
|
|
def ssk_write_enabler_master_hash(writekey):
|
|
|
|
return tagged_hash("allmydata_mutable_write_enabler_master_v1", writekey)
|
2007-11-07 01:49:59 +00:00
|
|
|
def ssk_write_enabler_hash(writekey, peerid):
|
|
|
|
assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
|
2007-11-06 04:51:08 +00:00
|
|
|
wem = ssk_write_enabler_master_hash(writekey)
|
2007-11-07 01:49:59 +00:00
|
|
|
return tagged_pair_hash("allmydata_mutable_write_enabler_v1", wem, peerid)
|
2007-11-06 04:51:08 +00:00
|
|
|
|
2007-11-01 22:15:29 +00:00
|
|
|
def ssk_pubkey_fingerprint_hash(pubkey):
|
|
|
|
return tagged_hash("allmydata_mutable_pubkey_v1", pubkey)
|
|
|
|
|
|
|
|
def ssk_readkey_hash(writekey):
|
2007-12-04 00:27:46 +00:00
|
|
|
return tagged_hash("allmydata_mutable_readkey_v1", writekey)[:16]
|
2007-11-03 03:51:39 +00:00
|
|
|
def ssk_readkey_data_hash(IV, readkey):
|
2007-12-04 00:27:46 +00:00
|
|
|
return tagged_pair_hash("allmydata_mutable_readkey_data_v1", IV, readkey)[:16]
|
2007-11-01 22:15:29 +00:00
|
|
|
def ssk_storage_index_hash(readkey):
|
2007-11-07 01:54:34 +00:00
|
|
|
return tagged_hash("allmydata_mutable_storage_index_v1", readkey)[:16]
|