]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/util/hashutil.py
8a8dc4c619e8dd65c74cb391336c303641bbfaa7
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / util / hashutil.py
1 from pycryptopp.hash.sha256 import SHA256
2 import os
3
4 # Various crypto values are this size: hash outputs (from SHA-256),
5 # randomly-generated secrets such as the lease secret, and symmetric encryption
6 # keys.  In the near future we will add DSA private keys, and salts of various
7 # kinds.
8 CRYPTO_VAL_SIZE=32
9
10 class IntegrityCheckError(Exception):
11     pass
12
13 def netstring(s):
14     assert isinstance(s, str), s
15     return "%d:%s," % (len(s), s,)
16
17 def tagged_hash(tag, val):
18     s = SHA256()
19     s.update(netstring(tag))
20     s.update(val)
21     return s.digest()
22
23 def tagged_hash_256d(tag, val, truncate_to=None):
24     # use SHA-256d, as defined by Ferguson and Schneier: hash the output
25     # again to prevent length-extension attacks
26     s = SHA256()
27     s.update(netstring(tag))
28     s.update(val)
29     h = s.digest()
30     h2 = SHA256(h).digest()
31     if truncate_to:
32         h2 = h2[:truncate_to]
33     return h2
34
35 class SHA256d_Hasher:
36     def __init__(self, truncate_to=None):
37         self.h = SHA256()
38         self.truncate_to = truncate_to
39     def update(self, data):
40         self.h.update(data)
41     def digest(self):
42         h1 = self.h.digest()
43         del self.h
44         h2 = SHA256(h1).digest()
45         if self.truncate_to:
46             h2 = h2[:self.truncate_to]
47         return h2
48
49 def tagged_hasher_256d(tag, truncate_to=None):
50     hasher = SHA256d_Hasher(truncate_to)
51     hasher.update(netstring(tag))
52     return hasher
53
54 def tagged_pair_hash(tag, val1, val2):
55     s = SHA256()
56     s.update(netstring(tag))
57     s.update(netstring(val1))
58     s.update(netstring(val2))
59     return s.digest()
60
61 # specific hash tags that we use
62
63 def tagged_hasher(tag):
64     return SHA256(netstring(tag))
65
66 def storage_index_hash(key):
67     # storage index is truncated to 128 bits (16 bytes). We're only hashing a
68     # 16-byte value to get it, so there's no point in using a larger value.
69     # TODO: remove the word "CHK" from this tag since we use this same tagged
70     # hash for random-keyed immutable files, mutable files, content-hash-keyed
71     # immutabie files.  Or, define two other tagged hashes, one for each kind.
72     # (Either way is fine -- we can never have collisions of storage indexes
73     # anyway, since we can't have collisions of keys.)
74     return tagged_hash("allmydata_CHK_storage_index_v1", key)[:16]
75
76 def block_hash(data):
77     return tagged_hash("allmydata_encoded_subshare_v1", data)
78 def block_hasher():
79     return tagged_hasher("allmydata_encoded_subshare_v1")
80
81 def uri_extension_hash(data):
82     return tagged_hash("allmydata_uri_extension_v1", data)
83 def uri_extension_hasher():
84     return tagged_hasher("allmydata_uri_extension_v1")
85
86 def plaintext_hash(data):
87     return tagged_hash("allmydata_plaintext_hash_v1", data)
88 def plaintext_hasher():
89     return tagged_hasher("allmydata_plaintext_hash_v1")
90
91 def crypttext_hash(data):
92     return tagged_hash("allmydata_crypttext_hash_v1", data)
93 def crypttext_hasher():
94     return tagged_hasher("allmydata_crypttext_hash_v1")
95
96 def crypttext_segment_hash(data):
97     return tagged_hash("allmydata_crypttext_segment_v1", data)
98 def crypttext_segment_hasher():
99     return tagged_hasher("allmydata_crypttext_segment_v1")
100
101 def plaintext_segment_hash(data):
102     return tagged_hash("allmydata_plaintext_segment_v1", data)
103 def plaintext_segment_hasher():
104     return tagged_hasher("allmydata_plaintext_segment_v1")
105
106 def key_hash(data):
107     return tagged_hash("allmydata_encryption_key_v1", data)
108 def key_hasher():
109     return tagged_hasher("allmydata_encryption_key_v1")
110
111 KEYLEN = 16
112 def random_key():
113     return os.urandom(KEYLEN)
114
115 def my_renewal_secret_hash(my_secret):
116     return tagged_hash(my_secret, "bucket_renewal_secret")
117 def my_cancel_secret_hash(my_secret):
118     return tagged_hash(my_secret, "bucket_cancel_secret")
119
120 def file_renewal_secret_hash(client_renewal_secret, storage_index):
121     return tagged_pair_hash("file_renewal_secret",
122                             client_renewal_secret, storage_index)
123
124 def file_cancel_secret_hash(client_cancel_secret, storage_index):
125     return tagged_pair_hash("file_cancel_secret",
126                             client_cancel_secret, storage_index)
127
128 def bucket_renewal_secret_hash(file_renewal_secret, peerid):
129     assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
130     return tagged_pair_hash("bucket_renewal_secret",
131                             file_renewal_secret, peerid)
132
133 def bucket_cancel_secret_hash(file_cancel_secret, peerid):
134     assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
135     return tagged_pair_hash("bucket_cancel_secret",
136                             file_cancel_secret, peerid)
137
138 def dir_write_enabler_hash(write_key):
139     return tagged_hash("allmydata_dir_write_enabler_v1", write_key)
140 def dir_read_key_hash(write_key):
141     return tagged_hash("allmydata_dir_read_key_v1", write_key)[:KEYLEN]
142 def dir_index_hash(read_key):
143     return tagged_hash("allmydata_dir_index_v1", read_key)
144 def dir_name_hash(readkey, name):
145     return tagged_pair_hash("allmydata_dir_name_v1", readkey, name)
146
147 def generate_dirnode_keys_from_writekey(write_key):
148     readkey = dir_read_key_hash(write_key)
149     write_enabler = dir_write_enabler_hash(write_key)
150     index = dir_index_hash(readkey)
151     return write_key, write_enabler, readkey, index
152
153 def generate_dirnode_keys_from_readkey(read_key):
154     index = dir_index_hash(read_key)
155     return None, None, read_key, index
156
157 def _xor(a, b):
158     return "".join([chr(ord(c) ^ ord(b)) for c in a])
159
160 def hmac(tag, data):
161     ikey = _xor(tag, "\x36")
162     okey = _xor(tag, "\x5c")
163     h1 = SHA256(ikey + data).digest()
164     h2 = SHA256(okey + h1).digest()
165     return h2
166
167 def mutable_rwcap_key_hash(iv, writekey):
168     return tagged_pair_hash("allmydata_mutable_rwcap_key_v1", iv, writekey)[:16]
169 def ssk_writekey_hash(privkey):
170     return tagged_hash("allmydata_mutable_writekey_v1", privkey)[:16]
171 def ssk_write_enabler_master_hash(writekey):
172     return tagged_hash("allmydata_mutable_write_enabler_master_v1", writekey)
173 def ssk_write_enabler_hash(writekey, peerid):
174     assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
175     wem = ssk_write_enabler_master_hash(writekey)
176     return tagged_pair_hash("allmydata_mutable_write_enabler_v1", wem, peerid)
177
178 def ssk_pubkey_fingerprint_hash(pubkey):
179     return tagged_hash("allmydata_mutable_pubkey_v1", pubkey)
180
181 def ssk_readkey_hash(writekey):
182     return tagged_hash("allmydata_mutable_readkey_v1", writekey)[:16]
183 def ssk_readkey_data_hash(IV, readkey):
184     return tagged_pair_hash("allmydata_mutable_readkey_data_v1", IV, readkey)[:16]
185 def ssk_storage_index_hash(readkey):
186     return tagged_hash("allmydata_mutable_storage_index_v1", readkey)[:16]