source file: /home/buildslave/tahoe/edgy/build/src/allmydata/util/hashutil.py
file stats: 141 lines, 141 executed: 100.0% covered
1. from pycryptopp.hash.sha256 import SHA256
2. import os
3.
4. # Be very very cautious when modifying this file. Almost any change will
5. # cause a compatibility break, invalidating all outstanding URIs and making
6. # any previously uploaded files become inaccessible. BE CONSERVATIVE AND TEST
7. # AGAINST OLD DATA!
8.
9. # Various crypto values are this size: hash outputs (from SHA-256d),
10. # randomly-generated secrets such as the lease secret, and symmetric encryption
11. # keys. In the near future we will add DSA private keys, and salts of various
12. # kinds.
13. CRYPTO_VAL_SIZE=32
14.
15. class IntegrityCheckError(Exception):
16. pass
17.
18. def netstring(s):
19. assert isinstance(s, str), s # no unicode here
20. return "%d:%s," % (len(s), s,)
21.
22. class _SHA256d_Hasher:
23. # use SHA-256d, as defined by Ferguson and Schneier: hash the output
24. # again to prevent length-extension attacks
25. def __init__(self, truncate_to=None):
26. self.h = SHA256()
27. self.truncate_to = truncate_to
28. self._digest = None
29. def update(self, data):
30. assert isinstance(data, str) # no unicode
31. self.h.update(data)
32. def digest(self):
33. if self._digest is None:
34. h1 = self.h.digest()
35. del self.h
36. h2 = SHA256(h1).digest()
37. if self.truncate_to:
38. h2 = h2[:self.truncate_to]
39. self._digest = h2
40. return self._digest
41.
42.
43.
44. def tagged_hasher(tag, truncate_to=None):
45. hasher = _SHA256d_Hasher(truncate_to)
46. hasher.update(netstring(tag))
47. return hasher
48.
49. def tagged_hash(tag, val, truncate_to=None):
50. hasher = tagged_hasher(tag, truncate_to)
51. hasher.update(val)
52. return hasher.digest()
53.
54. def tagged_pair_hash(tag, val1, val2, truncate_to=None):
55. s = _SHA256d_Hasher(truncate_to)
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. # immutable
64. STORAGE_INDEX_TAG = "allmydata_immutable_key_to_storage_index_v1"
65. BLOCK_TAG = "allmydata_encoded_subshare_v1"
66. UEB_TAG = "allmydata_uri_extension_v1"
67. PLAINTEXT_TAG = "allmydata_plaintext_v1"
68. CIPHERTEXT_TAG = "allmydata_crypttext_v1"
69. CIPHERTEXT_SEGMENT_TAG = "allmydata_crypttext_segment_v1"
70. PLAINTEXT_SEGMENT_TAG = "allmydata_plaintext_segment_v1"
71. CONVERGENT_ENCRYPTION_TAG = "allmydata_immutable_content_to_key_with_added_secret_v1+"
72.
73. CLIENT_RENEWAL_TAG = "allmydata_client_renewal_secret_v1"
74. CLIENT_CANCEL_TAG = "allmydata_client_cancel_secret_v1"
75. FILE_RENEWAL_TAG = "allmydata_file_renewal_secret_v1"
76. FILE_CANCEL_TAG = "allmydata_file_cancel_secret_v1"
77. BUCKET_RENEWAL_TAG = "allmydata_bucket_renewal_secret_v1"
78. BUCKET_CANCEL_TAG = "allmydata_bucket_cancel_secret_v1"
79.
80. # mutable
81. MUTABLE_WRITEKEY_TAG = "allmydata_mutable_privkey_to_writekey_v1"
82. MUTABLE_WRITE_ENABLER_MASTER_TAG = "allmydata_mutable_writekey_to_write_enabler_master_v1"
83. MUTABLE_WRITE_ENABLER_TAG = "allmydata_mutable_write_enabler_master_and_nodeid_to_write_enabler_v1"
84. MUTABLE_PUBKEY_TAG = "allmydata_mutable_pubkey_to_fingerprint_v1"
85. MUTABLE_READKEY_TAG = "allmydata_mutable_writekey_to_readkey_v1"
86. MUTABLE_DATAKEY_TAG = "allmydata_mutable_readkey_to_datakey_v1"
87. MUTABLE_STORAGEINDEX_TAG = "allmydata_mutable_readkey_to_storage_index_v1"
88.
89. # dirnodes
90. DIRNODE_CHILD_WRITECAP_TAG = "allmydata_mutable_writekey_and_salt_to_dirnode_child_capkey_v1"
91.
92. def storage_index_hash(key):
93. # storage index is truncated to 128 bits (16 bytes). We're only hashing a
94. # 16-byte value to get it, so there's no point in using a larger value. We
95. # use this same tagged hash to go from encryption key to storage index for
96. # random-keyed immutable files and convergent-encryption immutabie
97. # files. Mutable files use ssk_storage_index_hash().
98. return tagged_hash(STORAGE_INDEX_TAG, key, 16)
99.
100. def block_hash(data):
101. return tagged_hash(BLOCK_TAG, data)
102. def block_hasher():
103. return tagged_hasher(BLOCK_TAG)
104.
105. def uri_extension_hash(data):
106. return tagged_hash(UEB_TAG, data)
107. def uri_extension_hasher():
108. return tagged_hasher(UEB_TAG)
109.
110. def plaintext_hash(data):
111. return tagged_hash(PLAINTEXT_TAG, data)
112. def plaintext_hasher():
113. return tagged_hasher(PLAINTEXT_TAG)
114.
115. def crypttext_hash(data):
116. return tagged_hash(CIPHERTEXT_TAG, data)
117. def crypttext_hasher():
118. return tagged_hasher(CIPHERTEXT_TAG)
119.
120. def crypttext_segment_hash(data):
121. return tagged_hash(CIPHERTEXT_SEGMENT_TAG, data)
122. def crypttext_segment_hasher():
123. return tagged_hasher(CIPHERTEXT_SEGMENT_TAG)
124.
125. def plaintext_segment_hash(data):
126. return tagged_hash(PLAINTEXT_SEGMENT_TAG, data)
127. def plaintext_segment_hasher():
128. return tagged_hasher(PLAINTEXT_SEGMENT_TAG)
129.
130. KEYLEN = 16
131.
132. def convergence_hash(k, n, segsize, data, convergence):
133. h = convergence_hasher(k, n, segsize, convergence)
134. h.update(data)
135. return h.digest()
136. def convergence_hasher(k, n, segsize, convergence):
137. assert isinstance(convergence, str)
138. param_tag = netstring("%d,%d,%d" % (k, n, segsize))
139. tag = CONVERGENT_ENCRYPTION_TAG + netstring(convergence) + param_tag
140. return tagged_hasher(tag, KEYLEN)
141.
142. def random_key():
143. return os.urandom(KEYLEN)
144.
145. def my_renewal_secret_hash(my_secret):
146. return tagged_hash(my_secret, CLIENT_RENEWAL_TAG)
147. def my_cancel_secret_hash(my_secret):
148. return tagged_hash(my_secret, CLIENT_CANCEL_TAG)
149.
150. def file_renewal_secret_hash(client_renewal_secret, storage_index):
151. return tagged_pair_hash(FILE_RENEWAL_TAG,
152. client_renewal_secret, storage_index)
153.
154. def file_cancel_secret_hash(client_cancel_secret, storage_index):
155. return tagged_pair_hash(FILE_CANCEL_TAG,
156. client_cancel_secret, storage_index)
157.
158. def bucket_renewal_secret_hash(file_renewal_secret, peerid):
159. assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
160. return tagged_pair_hash(BUCKET_RENEWAL_TAG, file_renewal_secret, peerid)
161.
162. def bucket_cancel_secret_hash(file_cancel_secret, peerid):
163. assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
164. return tagged_pair_hash(BUCKET_CANCEL_TAG, file_cancel_secret, peerid)
165.
166.
167. def _xor(a, b):
168. return "".join([chr(ord(c) ^ ord(b)) for c in a])
169.
170. def hmac(tag, data):
171. ikey = _xor(tag, "\x36")
172. okey = _xor(tag, "\x5c")
173. h1 = SHA256(ikey + data).digest()
174. h2 = SHA256(okey + h1).digest()
175. return h2
176.
177. def mutable_rwcap_key_hash(iv, writekey):
178. return tagged_pair_hash(DIRNODE_CHILD_WRITECAP_TAG, iv, writekey, KEYLEN)
179.
180. def ssk_writekey_hash(privkey):
181. return tagged_hash(MUTABLE_WRITEKEY_TAG, privkey, KEYLEN)
182. def ssk_write_enabler_master_hash(writekey):
183. return tagged_hash(MUTABLE_WRITE_ENABLER_MASTER_TAG, writekey)
184. def ssk_write_enabler_hash(writekey, peerid):
185. assert len(peerid) == 20, "%s: %r" % (len(peerid), peerid) # binary!
186. wem = ssk_write_enabler_master_hash(writekey)
187. return tagged_pair_hash(MUTABLE_WRITE_ENABLER_TAG, wem, peerid)
188.
189. def ssk_pubkey_fingerprint_hash(pubkey):
190. return tagged_hash(MUTABLE_PUBKEY_TAG, pubkey)
191.
192. def ssk_readkey_hash(writekey):
193. return tagged_hash(MUTABLE_READKEY_TAG, writekey, KEYLEN)
194. def ssk_readkey_data_hash(IV, readkey):
195. return tagged_pair_hash(MUTABLE_DATAKEY_TAG, IV, readkey, KEYLEN)
196. def ssk_storage_index_hash(readkey):
197. return tagged_hash(MUTABLE_STORAGEINDEX_TAG, readkey, KEYLEN)