Previous chapter: 7. Authentication in QKG Abstract and TOC Next chapter: Bibliography

Authentication in quantum key growing

This is my masters thesis in Applied Physics about the authentication step in quantum cryptography (a.k.a. quantum key distribution, quantum key growing, QKG, QKD). The major result is also available as the IEEE transactions on information theory paper Security Aspects of the Authentication Used in Quantum Cryptography (The PDF is available at arXiv) coauthored with Jan-Åke Larsson. See the press release.
It was also mentioned in some other news channels with more or less reliable descriptions, e.g. AFP, The Register, IDG.se, Der Standard, and of course Slashdot.
IEEE Spectrum had by far the best description.


A. Source code


entropies.py
1 #!/usr/bin/env python
2 # -*- coding: iso-8859-1 -*-
3
4 from __future__ import division
5 from math import *
6
7 # Yes, this is a quite ugly way to get an infinity constant, but it
8 # works, and we really get an IEEE 754 floating point infinity
9 # constant. Module fpconst should solve the problem.
10 infty = 1e300000000000000
11
12 def log2(x):
13 return log(x, 2)
14
15 def shannon_entropy(l):
16 """Return the Shannon entropy of random variable with probability
17 vector l."""
18 return sum([-p*log2(p) for p in l if p > 0])
19 def min_entropy(l):
20 """Return the min-entropy of random variable with probability
21 vector l."""
22 return -log2(max(l))
23 def entropy(l, alpha=1):
24 """Return the Rényi entropy of order alpha of random variable with
25 probability vector l."""
26 if abs(alpha - 1) < 10**-10:
27 return shannon_entropy(l)
28 elif alpha == infty:
29 return min_entropy(l)
30 try:
31 # "if p>0" saves us from 0**0 trouble.
32 return log2(sum([p**float(alpha) for p in l if p>0]))/(1-alpha)
33 except (ZeroDivisionError, OverflowError):
34 return min_entropy(l)
35
36 def guessing_entropy(l):
37 """Return the Shannon entropy of random variable with probability
38 vector l."""
39 tmp = l[:] # Copy the probability vector.
40 tmp.sort()
41 tmp.reverse() # Highest probability first.
42 return sum([p*i for (i,p) in enumerate(l)]) + 1
43
44 def normalize_inplace(l):
45 "Normalize a probability vector in-place."
46 s = sum(l)
47 for i, p in enumerate(l):
48 l[i] = p/s
49 def normalize(l):
50 "Return a normalized probability vector."
51 s = sum(l)
52 return [ p/s for p in l ]
53
54 def Q(p, n):
55 return [p]+[(1-p)/n]*n
hashfunctions.py
1 #!/usr/bin/env python
2 # -*- coding: iso-8859-1 -*-
3
4 from __future__ import division
5 from math import *
6 import Crypto.Util.number as cn
7
8 def log2(x):
9 return log(x, 2)
10
11 def logint(x, base, __cache=[(8, 2), 3]):
12 "Return int(ceil(log(x, base))) without rounding errors."
13 # Rounding error example:
14 #>>> log(2**96, 2**12)
15 #8.0000000000000018
16 if (x, base) == __cache[0]:
17 return __cache[1]
18 c = int(ceil(log(x, base))) # This works most of the time.
19 while x > base**c:
20 c += 1 # If not, this will fix it.
21 while x <= base**(c-1):
22 c -= 1 # Or this.
23 __cache[:] = (x, base), c
24 return c
25
26 def istwopower(x, __cache={}):
27 c = __cache.get(x)
28 if c is not None:
29 return c
30 c = 0L # This must be a long, or 1<<c will lose bits.
31 while x > 1<<c:
32 c += 1
33 if x != 1<<c:
34 return None
35 __cache[x] = c
36 return c
37
38 def int2list(x, xmax=None, s=256):
39 """Return the integer x as a little-endian list of bytes with s
40 states each. s is the number of states, not the number of bits.
41 If xmax is given, the returned list will be large enough to
42 contain xmax-1."""
43 if xmax is None:
44 xmax = x+1
45 assert 0 <= x < xmax
46 l = [0] * logint(xmax, s)
47 c = istwopower(s)
48 if c is None:
49 for i in xrange(len(l)):
50 l[i] = int(x%s)
51 x = x//s
52 else: # s is exactly 2**c==1<<c so we can optimize somewhat.
53 mask = (1<<c)-1
54 for i in xrange(len(l)):
55 l[i] = int(x&mask)
56 x = x>>c
57 return l
58
59 def list2int(l, s=256):
60 """Return list interpreted as a little-endian list of bytes with s
61 states each. s is the number of states, not the number of bits."""
62 while len(l) > 1:
63 l[-2] += l[-1]*s
64 del l[-1]
65 return l[0]
66
67 def nextprime(begin=1, __cache=[7,7]):
68 """Return the lowest prime >= begin."""
69 if begin == __cache[0]:
70 return __cache[1]
71 n = begin
72 if not n%2: n+=1
73 while not cn.isPrime(long(n)):
74 n += 2
75 __cache[:] = begin, n
76 return int(n)
77
78 # The following functions were first described by J. Lawrence Carter
79 # and Mark N. Wegman in their papers "Universal classes of hash
80 # functions" (1979) and "New hash functions and their use in
81 # authentication and set equality" (1981).
82 # All functions try to follow the Wegman-Carter papers as closely as
83 # possible and all quotes are from the papers.
84
85 def H1(a, b, key=None):
86 """Return a member of the hash family H_1 from Wegman-Carter 1979
87 Inputs:
88 a -- Number of input states
89 b -- Number of output states
90 key -- a tuple (p, m, n):
91 p -- A (non-secret) prime larger than or equal to a
92 q -- Half of the secret key. 0< q
93 r -- Half of the secret key. 0<=r
94 Output:
95 Normal mode:
96 A hash function mapping range(a) to range(b)
97 Parameter mode:
98 If H1 is called with no key, the smallest possible p is
99 returned to ease construction of a key.
100 """
101 if key is None:
102 return nextprime(a)
103 p, q, r = key
104 assert (a>b) and (p>=a) and (0and (0<=r
105 def g(x): # "A natural choice for g is the residue modulo b."
106 return x % b
107 def h(m):
108 return (q*m+r) % p
109 def f(m):
110 # Docstring is set below
111 assert 0<=m
112 return g(h(m))
113 f.__doc__ = \
114 """This is a hash function from the hash family H_1 from
115 Wegman-Carter 1979 using the key %s.
116 Inputs:
117 m -- The integer to be hashed in range(%d)
118 Output:
119 A hash value in range(%d)
120 """ % (str(key), a, b)
121 return f
122
123 def Hprime(aprime, bprime, flist=None):
124 """Return a member of the hash family H' from Wegman-Carter 1980
125 Inputs:
126 aprime -- Number of input bits
127 bprime -- Number of output states
128 flist -- A sequence of secret hash functions
129 Output:
130 Normal mode:
131 A hash function mapping range(2**aprime) to range(2**bprime)
132 Parameter mode:
133 Hprime needs a sequence flist of hash functions from a
134 universal_2 family. The number of functions, input states and
135 output states are dependent on the inner workings of Hprime
136 and need not be exposed to the outside. Instead, if Hprime is
137 called without flist those specifications are returned as a
138 tuple (a, b, len_f):
139 a -- Number of input states of each hash function
140 b -- Number of output states of each hash function
141 len_f -- Number of hash functions in flist
142 """
143 s = bprime + int(ceil(log2(log2(aprime))))
144 # "Let H be some strongly universal_2 class of functions which map
145 # bit strings of length 2s to ones of length s"
146 a = 2**(2*s)
147 b = 2**( s)
148 # The "or 1" is needed because the length calculation in W-C-80
149 # doesn't account for the extra padding when the message is
150 # smaller than s from the beginning.
151 len_f = int(ceil(log2(ceil(aprime/s)))) or 1
152 if flist is None:
153 return (a, b, len_f)
154 assert len(flist) == len_f
155 def f(substrings, hashfunction):
156 # Apply hashfunction to all substrings and concatenate pairwise
157 for i in xrange(len(substrings)//2):
158 substrings[i:i+2] = [hashfunction(substrings[i ]) +
159 hashfunction(substrings[i+1]) * 2**s]
160 if len(substrings)%2:
161 substrings[-1] = hashfunction(substrings[-1])
162 def fprime(m):
163 # Docstring is set below
164 assert 0 <= m < 2**aprime
165 # "The message is broken into substrings of length 2s."
166 substrings = int2list(m, xmax=2**aprime, s=2**(2*s))
167 for f_i in flist[:-1]:
168 assert len(substrings) > 1
169 f(substrings, f_i)
170 # "This process is repeated using f_2,f_3,... until only one
171 # substring of length s is left."
172 assert len(substrings) == 1
173 substring = flist[-1](substrings[0])
174 assert 0 <= substring < 2**s
175 # "The tag is the low-order b' bits of this substring."
176 return substring % 2**bprime
177 fprime.__doc__ = "This is a hash function from the hash family H' " \
178 "from Wegman-Carter 1980.\n" \
179 " Inputs:\n" \
180 " m -- A %d bit integer to be hashed\n" \
181 " Output:\n" \
182 " A %d bit hash value\n" \
183 % (aprime, bprime)
184 return fprime
185
186 def Hprime_H1(aprime, bprime, key=None):
187 """Return a member of the hash family H' from Wegman-Carter 1980
188 using the sub-hash family H_1 from Wegman-Carter 1979
189 Inputs:
190 aprime -- Number of input bits
191 bprime -- Number of output bits
192 key -- A secret key
193 Outputs:
194 Normal mode:
195 A hash function mapping range(2**aprime) to range(2**bprime)
196 Parameter mode:
197 If Hprime_H1 is called without a key an integer maxkey is
198 returned. The key should be in range(maxkey).
199 """
200 # Get key parameters
201 a, b, len_f = Hprime(aprime, bprime)
202 p = H1(a, b)
203 maxkey = ((p-1)*p)**len_f
204 if key is None:
205 return maxkey
206 assert 0 <= key < maxkey
207 flist = []
208 for thiskey in int2list(key, xmax=maxkey, s=(p-1)*p):
209 q, r = divmod(thiskey, p)
210 q += 1
211 flist.append(H1(a, b, (p, q, r)))
212 return Hprime(aprime, bprime, flist)
213
214 def Hprime_H1_compact(aprime, bprime, key=None):
215 """A more compact implementation of Wegman-Carter 1980 with H1
216 from W-C 1979.
217 The functions above are written to mimic the language of
218 Wegman-Carter as much as possible. Sometimes it might be easier to
219 understand a more compact language. This code should do exactly
220 the same as the one above, but in far less lines and with no error
221 checking. It is approximately three times faster than Hprime_H1().
222 Inputs:
223 aprime -- Number of input bits
224 bprime -- Number of output bits
225 key -- A secret key
226 Outputs:
227 Normal mode:
228 A hash function mapping range(2**aprime) to range(2**bprime)
229 Parameter mode:
230 If Hprime_H1_compact is called without a key an integer maxkey
231 is returned. The key should be in range(maxkey).
232 """
233 s = bprime + int(ceil(log2(log2(aprime))))
234 p = nextprime(2**(2*s))
235 len_f = int(ceil(log2(ceil(aprime/s)))) or 1
236 maxkey = ((p-1)*p)**len_f
237 if key is None:
238 return maxkey
239 keys = []
240 for thiskey in int2list(key, xmax=maxkey, s=(p-1)*p):
241 q, r = divmod(thiskey, p)
242 q += 1
243 keys.append( (q,r) )
244 def fprime(m):
245 "This is a hash function returned by Hprime_H1_compact()."
246 substrings = int2list(m, xmax=2**aprime, s=2**(2*s))
247 for q,r in keys:
248 for i in xrange(len(substrings)//2):
249 substrings[i:i+2] = [(((q*substrings[i ]+r)%p)%(2**s)) + \
250 (((q*substrings[i+1]+r)%p)%(2**s)) * 2**s]
251 if len(substrings)%2:
252 substrings[-1] = (((q*substrings[ -1]+r)%p)%(2**s))
253 return substrings[0] % 2**bprime
254 return fprime
Previous chapter: 7. Authentication in QKG Abstract and TOC Next chapter: Bibliography

AltStyle によって変換されたページ (->オリジナル) /