Revision 138d0e8b

/dev/null
1
#!/usr/bin/env python
2
##
3
##  d3des.py - DES implementation
4
##
5
##  Copyright (c) 2009 by Yusuke Shinyama
6
##
7

  
8
# This is a Python rewrite of d3des.c by Richard Outerbridge.
9
#
10
# I referred to the original VNC viewer code for the changes that
11
# is necessary to maintain the exact behavior of the VNC protocol.
12
# Two constants and two functions were added to the original d3des
13
# code.  These added parts were written in Python and marked
14
# below.  I believe that the added parts do not make this program
15
# a "derivative work" of the VNC viewer (which is GPL'ed and
16
# written in C), but if there's any problem, let me know.
17
#
18
# Yusuke Shinyama (yusuke at cs dot nyu dot edu)
19

  
20

  
21
#  D3DES (V5.09) -
22
#
23
#  A portable, public domain, version of the Data Encryption Standard.
24
#
25
#  Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
26
#  Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
27
#  code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
28
#  Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
29
#  for humouring me on.
30
#
31
#  Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
32
#  (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
33
#
34

  
35
from struct import pack, unpack
36

  
37

  
38
###################################################
39
###
40
###  start: changes made for VNC.
41
###
42

  
43
# This constant was taken from vncviewer/rfb/vncauth.c:
44
vnckey = [ 23,82,107,6,35,78,88,7 ]
45

  
46
# This is a departure from the original code.
47
#bytebit = [ 0200, 0100, 040, 020, 010, 04, 02, 01 ] # original
48
bytebit = [ 01, 02, 04, 010, 020, 040, 0100, 0200 ] # VNC version
49

  
50
# two password functions for VNC protocol.
51
def decrypt_passwd(data):
52
    dk = deskey(pack('8B', *vnckey), True)
53
    return desfunc(data, dk)
54

  
55
def generate_response(passwd, challange):
56
    ek = deskey((passwd+'\x00'*8)[:8], False)
57
    return desfunc(challange[:8], ek) + desfunc(challange[8:], ek)
58

  
59
###
60
###  end: changes made for VNC.
61
###
62
###################################################
63

  
64

  
65
bigbyte = [
66
  0x800000L,    0x400000L,      0x200000L,      0x100000L,
67
  0x80000L,     0x40000L,       0x20000L,       0x10000L,
68
  0x8000L,      0x4000L,        0x2000L,        0x1000L,
69
  0x800L,       0x400L,         0x200L,         0x100L,
70
  0x80L,        0x40L,          0x20L,          0x10L,
71
  0x8L,         0x4L,           0x2L,           0x1L
72
  ]
73

  
74
# Use the key schedule specified in the Standard (ANSI X3.92-1981).
75

  
76
pc1 = [
77
  56, 48, 40, 32, 24, 16,  8,    0, 57, 49, 41, 33, 25, 17,
78
   9,  1, 58, 50, 42, 34, 26,   18, 10,  2, 59, 51, 43, 35,
79
  62, 54, 46, 38, 30, 22, 14,    6, 61, 53, 45, 37, 29, 21,
80
  13,  5, 60, 52, 44, 36, 28,   20, 12,  4, 27, 19, 11,  3
81
  ]
82

  
83
totrot = [ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 ]
84

  
85
pc2 = [
86
  13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
87
  22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
88
  40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
89
  43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
90
  ]
91

  
92
def deskey(key, decrypt):      # Thanks to James Gillogly & Phil Karn!
93
    key = unpack('8B', key)
94

  
95
    pc1m = [0]*56
96
    pcr = [0]*56
97
    kn = [0L]*32
98

  
99
    for j in range(56):
100
        l = pc1[j]
101
        m = l & 07
102
        if key[l >> 3] & bytebit[m]:
103
            pc1m[j] = 1
104
        else:
105
            pc1m[j] = 0
106

  
107
    for i in range(16):
108
        if decrypt:
109
            m = (15 - i) << 1
110
        else:
111
            m = i << 1
112
        n = m + 1
113
        kn[m] = kn[n] = 0L
114
        for j in range(28):
115
            l = j + totrot[i]
116
            if l < 28:
117
                pcr[j] = pc1m[l]
118
            else:
119
                pcr[j] = pc1m[l - 28]
120
        for j in range(28, 56):
121
            l = j + totrot[i]
122
            if l < 56:
123
                pcr[j] = pc1m[l]
124
            else:
125
                pcr[j] = pc1m[l - 28]
126
        for j in range(24):
127
            if pcr[pc2[j]]:
128
                kn[m] |= bigbyte[j]
129
            if pcr[pc2[j+24]]:
130
                kn[n] |= bigbyte[j]
131

  
132
    return cookey(kn)
133

  
134
def cookey(raw):
135
    key = []
136
    for i in range(0, 32, 2):
137
        (raw0, raw1) = (raw[i], raw[i+1])
138
        k  = (raw0 & 0x00fc0000L) << 6
139
        k |= (raw0 & 0x00000fc0L) << 10
140
        k |= (raw1 & 0x00fc0000L) >> 10
141
        k |= (raw1 & 0x00000fc0L) >> 6
142
        key.append(k)
143
        k  = (raw0 & 0x0003f000L) << 12
144
        k |= (raw0 & 0x0000003fL) << 16
145
        k |= (raw1 & 0x0003f000L) >> 4
146
        k |= (raw1 & 0x0000003fL)
147
        key.append(k)
148
    return key
149

  
150
SP1 = [
151
  0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
152
  0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
153
  0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
154
  0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
155
  0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
156
  0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
157
  0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
158
  0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
159
  0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
160
  0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
161
  0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
162
  0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
163
  0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
164
  0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
165
  0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
166
  0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L
167
  ]
168

  
169
SP2 = [
170
  0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
171
  0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
172
  0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
173
  0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
174
  0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
175
  0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
176
  0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
177
  0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
178
  0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
179
  0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
180
  0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
181
  0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
182
  0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
183
  0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
184
  0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
185
  0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L
186
  ]
187

  
188
SP3 = [
189
  0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
190
  0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
191
  0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
192
  0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
193
  0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
194
  0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
195
  0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
196
  0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
197
  0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
198
  0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
199
  0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
200
  0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
201
  0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
202
  0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
203
  0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
204
  0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L
205
  ]
206

  
207
SP4 = [
208
  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
209
  0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
210
  0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
211
  0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
212
  0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
213
  0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
214
  0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
215
  0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
216
  0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
217
  0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
218
  0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
219
  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
220
  0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
221
  0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
222
  0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
223
  0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L
224
  ]
225

  
226
SP5 = [
227
  0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
228
  0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
229
  0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
230
  0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
231
  0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
232
  0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
233
  0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
234
  0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
235
  0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
236
  0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
237
  0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
238
  0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
239
  0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
240
  0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
241
  0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
242
  0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L
243
  ]
244

  
245
SP6 = [
246
  0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
247
  0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
248
  0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
249
  0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
250
  0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
251
  0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
252
  0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
253
  0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
254
  0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
255
  0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
256
  0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
257
  0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
258
  0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
259
  0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
260
  0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
261
  0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L
262
  ]
263

  
264
SP7 = [
265
  0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
266
  0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
267
  0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
268
  0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
269
  0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
270
  0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
271
  0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
272
  0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
273
  0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
274
  0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
275
  0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
276
  0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
277
  0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
278
  0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
279
  0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
280
  0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L
281
  ]
282

  
283
SP8 = [
284
  0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
285
  0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
286
  0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
287
  0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
288
  0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
289
  0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
290
  0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
291
  0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
292
  0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
293
  0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
294
  0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
295
  0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
296
  0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
297
  0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
298
  0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
299
  0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L
300
  ]
301

  
302
def desfunc(block, keys):
303
    (leftt, right) = unpack('>II', block)
304

  
305
    work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL
306
    right ^= work
307
    leftt ^= (work << 4)
308
    work = ((leftt >> 16) ^ right) & 0x0000ffffL
309
    right ^= work
310
    leftt ^= (work << 16)
311
    work = ((right >> 2) ^ leftt) & 0x33333333L
312
    leftt ^= work
313
    right ^= (work << 2)
314
    work = ((right >> 8) ^ leftt) & 0x00ff00ffL
315
    leftt ^= work
316
    right ^= (work << 8)
317
    right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL
318
    work = (leftt ^ right) & 0xaaaaaaaaL
319
    leftt ^= work
320
    right ^= work
321
    leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL
322

  
323
    for i in range(0, 32, 4):
324
        work  = (right << 28) | (right >> 4)
325
        work ^= keys[i]
326
        fval  = SP7[ work            & 0x3fL]
327
        fval |= SP5[(work >>  8) & 0x3fL]
328
        fval |= SP3[(work >> 16) & 0x3fL]
329
        fval |= SP1[(work >> 24) & 0x3fL]
330
        work  = right ^ keys[i+1]
331
        fval |= SP8[ work            & 0x3fL]
332
        fval |= SP6[(work >>  8) & 0x3fL]
333
        fval |= SP4[(work >> 16) & 0x3fL]
334
        fval |= SP2[(work >> 24) & 0x3fL]
335
        leftt ^= fval
336
        work  = (leftt << 28) | (leftt >> 4)
337
        work ^= keys[i+2]
338
        fval  = SP7[ work            & 0x3fL]
339
        fval |= SP5[(work >>  8) & 0x3fL]
340
        fval |= SP3[(work >> 16) & 0x3fL]
341
        fval |= SP1[(work >> 24) & 0x3fL]
342
        work  = leftt ^ keys[i+3]
343
        fval |= SP8[ work            & 0x3fL]
344
        fval |= SP6[(work >>  8) & 0x3fL]
345
        fval |= SP4[(work >> 16) & 0x3fL]
346
        fval |= SP2[(work >> 24) & 0x3fL]
347
        right ^= fval
348

  
349
    right = (right << 31) | (right >> 1)
350
    work = (leftt ^ right) & 0xaaaaaaaaL
351
    leftt ^= work
352
    right ^= work
353
    leftt = (leftt << 31) | (leftt >> 1)
354
    work = ((leftt >> 8) ^ right) & 0x00ff00ffL
355
    right ^= work
356
    leftt ^= (work << 8)
357
    work = ((leftt >> 2) ^ right) & 0x33333333L
358
    right ^= work
359
    leftt ^= (work << 2)
360
    work = ((right >> 16) ^ leftt) & 0x0000ffffL
361
    leftt ^= work
362
    right ^= (work << 16)
363
    work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL
364
    leftt ^= work
365
    right ^= (work << 4)
366

  
367
    leftt &= 0xffffffffL
368
    right &= 0xffffffffL
369
    return pack('>II', right, leftt)
370

  
371

  
372
# test
373
if __name__ == '__main__':
374
    key = '0123456789abcdef'.decode('hex')
375
    plain = '0123456789abcdef'.decode('hex')
376
    cipher = '6e09a37726dd560c'.decode('hex')
377
    ek = deskey(key, False)
378
    dk = deskey(key, True)
379
    assert desfunc(plain, ek) == cipher
380
    assert desfunc(desfunc(plain, ek), dk) == plain
381
    assert desfunc(desfunc(plain, dk), ek) == plain
382
    print 'test succeeded.'
/dev/null
1
#
2
#
3

  
4
# Copyright (c) 2010 GRNET SA
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21
import d3des
22
from struct import pack, unpack
23

  
24
RFB_AUTH_SUCCESS = 0
25
RFB_AUTH_ERROR = 1
26
RFB_AUTHTYPE_VNC = 2
27
RFB_AUTHTYPE_NONE = 1
28
RFB_AUTHTYPE_ERROR = 0
29
RFB_SUPPORTED_AUTHTYPES = [RFB_AUTHTYPE_NONE, RFB_AUTHTYPE_VNC]
30
RFB_VERSION_3_8 = "RFB 003.008"
31
RFB_VERSION_3_7 = "RFB 003.007"
32
RFB_VERSION_3_3 = "RFB 003.003"
33
RFB_VALID_VERSIONS = [
34
    RFB_VERSION_3_3,
35
#    RFB_VERSION_3_7,
36
    RFB_VERSION_3_8,
37
]
38

  
39
class RfbError(Exception):
40
    pass
41

  
42
def check_version(version):
43
    if version.strip()[:11] in RFB_VALID_VERSIONS:
44
        return version.strip()[:11]
45
    else:
46
        return None
47

  
48
def make_auth_request(*args, **kwargs):
49
    auth_methods = args
50
    version = kwargs['version']
51
    if version == RFB_VERSION_3_3:
52
        if len(auth_methods) != 1:
53
            raise RfbError("Only single authentication type may be specified for RFB 3.3")
54
    auth_methods = set(auth_methods)
55
    for method in auth_methods:
56
        if method not in RFB_SUPPORTED_AUTHTYPES:
57
            raise RfbError("Unsupported authentication type: %d" % method)
58
    if version == RFB_VERSION_3_3:
59
        return pack('>I', *auth_methods)
60
    else:
61
        return pack('B' + 'B' * len(auth_methods), len(auth_methods), *auth_methods)
62

  
63
def parse_auth_request(request):
64
    length = unpack('B', request[0])[0]
65
    if length == 0:
66
        return []
67
    return unpack('B' * length, request[1:])
68

  
69
def parse_client_authtype(authtype):
70
    return unpack('B', authtype[0])[0]
71

  
72
def from_u32(val):
73
    return unpack('>L', val)[0]
74

  
75
def to_u32(val):
76
    return pack('>L', val)
77

  
78
def from_u8(val):
79
    return unpack('B', val)[0]
80

  
81
def to_u8(val):
82
    return pack('B', val)
83

  
84
def check_password(challenge, response, password):
85
    return d3des.generate_response((password + '\0' * 8 )[:8],
86
                                   challenge) == response
/dev/null
1
#!/usr/bin/env python
2
#
3
# Copyright (c) 2010-2011 Greek Research and Technology Network S.A.
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
# 02110-1301, USA.
19

  
20
import sys
21
import socket
22

  
23
try:
24
    import simplejson as json
25
except ImportError:
26
    import json
27

  
28
CTRL_SOCKET = "/var/run/vncauthproxy/ctrl.sock"
29

  
30
def request_forwarding(sport, daddr, dport, password):
31
    assert(len(password) > 0)
32
    req = {
33
        "source_port": int(sport),
34
        "destination_address": daddr,
35
        "destination_port": int(dport),
36
        "password": password
37
    }
38

  
39
    ctrl = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
40
    ctrl.connect(CTRL_SOCKET)
41
    ctrl.send(json.dumps(req))
42

  
43
    response = ctrl.recv(1024)
44
    res = json.loads(response)
45
    return res
46

  
47
if __name__ == '__main__':
48
    res = request_forwarding(*sys.argv[1:])
49
    if res['status'] == "OK":
50
        sys.exit(0)
51
    else:
52
        sys.exit(1)
b/vncauthproxy.py
1 1
#!/usr/bin/env python
2
"""
3
vncauthproxy - a VNC authentication proxy
4
"""
2 5
#
3 6
# Copyright (c) 2010-2011 Greek Research and Technology Network S.A.
4 7
#
......
17 20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 21
# 02110-1301, USA.
19 22

  
20
DEFAULT_CTRL_SOCKET = "/var/run/vncauthproxy/ctrl.sock"
21
DEFAULT_LOG_FILE = "/var/log/vncauthproxy/vncauthproxy.log"
22
DEFAULT_PID_FILE = "/var/run/vncauthproxy/vncauthproxy.pid"
23
DEFAULT_CONNECT_TIMEOUT = 30
24
# Default values per http://www.iana.org/assignments/port-numbers
25
DEFAULT_MIN_PORT = 49152 
26
DEFAULT_MAX_PORT = 65535
23
from vncauthproxy import proxy
27 24

  
28
import os
29
import sys
30
import logging
31
import gevent
32
import daemon
33
import random
34
import daemon.pidlockfile
35

  
36
import rfb
37
 
38
try:
39
    import simplejson as json
40
except ImportError:
41
    import json
42

  
43
from gevent import socket
44
from signal import SIGINT, SIGTERM
45
from gevent import signal
46
from gevent.select import select
47

  
48
class VncAuthProxy(gevent.Greenlet):
49
    """
50
    Simple class implementing a VNC Forwarder with MITM authentication as a
51
    Greenlet
52

  
53
    VncAuthProxy forwards VNC traffic from a specified port of the local host
54
    to a specified remote host:port. Furthermore, it implements VNC
55
    Authentication, intercepting the client/server handshake and asking the
56
    client for authentication even if the backend requires none.
57

  
58
    It is primarily intended for use in virtualization environments, as a VNC
59
    ``switch''.
60

  
61
    """
62
    id = 1
63

  
64
    def __init__(self, logger, listeners, pool, daddr, dport, password, connect_timeout):
65
        """
66
        @type logger: logging.Logger
67
        @param logger: the logger to use
68
        @type listeners: list
69
        @param listeners: list of listening sockets to use for client connections
70
        @type pool: list
71
        @param pool: if not None, return the client port number into this port pool
72
        @type daddr: str
73
        @param daddr: destination address (IPv4, IPv6 or hostname)
74
        @type dport: int
75
        @param dport: destination port
76
        @type password: str
77
        @param password: password to request from the client
78
        @type connect_timeout: int
79
        @param connect_timeout: how long to wait for client connections
80
                                (seconds)
81

  
82
        """
83
        gevent.Greenlet.__init__(self)
84
        self.id = VncAuthProxy.id
85
        VncAuthProxy.id += 1
86
        self.log = logger
87
        self.listeners = listeners
88
        # All listening sockets are assumed to be on the same port
89
        self.sport = listeners[0].getsockname()[1]
90
        self.pool = pool
91
        self.daddr = daddr
92
        self.dport = dport
93
        self.password = password
94
        self.server = None
95
        self.client = None
96
        self.timeout = connect_timeout
97

  
98
    def _cleanup(self):
99
        """Close all active sockets and exit gracefully"""
100
        # Reintroduce the port number of the client socket in
101
        # the port pool, if applicable.
102
        if not self.pool is None:
103
            self.pool.append(self.sport)
104
            self.log.debug("Returned port %d to port pool, contains %d ports",
105
                self.sport, len(self.pool))
106

  
107
        while self.listeners:
108
            self.listeners.pop().close()
109
        if self.server:
110
            self.server.close()
111
        if self.client:
112
            self.client.close()
113

  
114
        raise gevent.GreenletExit
115

  
116
    def info(self, msg):
117
        self.log.info("[C%d] %s" % (self.id, msg))
118

  
119
    def debug(self, msg):
120
        self.log.debug("[C%d] %s" % (self.id, msg))
121

  
122
    def warn(self, msg):
123
        self.log.warn("[C%d] %s" % (self.id, msg))
124

  
125
    def error(self, msg):
126
        self.log.error("[C%d] %s" % (self.id, msg))
127

  
128
    def critical(self, msg):
129
        self.log.critical("[C%d] %s" % (self.id, msg))
130

  
131
    def __str__(self):
132
        return "VncAuthProxy: %d -> %s:%d" % (self.sport, self.daddr, self.dport)
133

  
134
    def _forward(self, source, dest):
135
        """
136
        Forward traffic from source to dest
137

  
138
        @type source: socket
139
        @param source: source socket
140
        @type dest: socket
141
        @param dest: destination socket
142

  
143
        """
144

  
145
        while True:
146
            d = source.recv(16384)
147
            if d == '':
148
                if source == self.client:
149
                    self.info("Client connection closed")
150
                else:
151
                    self.info("Server connection closed")
152
                break
153
            dest.sendall(d)
154
        # No need to close the source and dest sockets here.
155
        # They are owned by and will be closed by the original greenlet.
156

  
157
    def _handshake(self):
158
        """
159
        Perform handshake/authentication with a connecting client
160

  
161
        Outline:
162
        1. Client connects
163
        2. We fake RFB 3.8 protocol and require VNC authentication [also supports RFB 3.3]
164
        3. Client accepts authentication method
165
        4. We send an authentication challenge
166
        5. Client sends the authentication response
167
        6. We check the authentication
168
        7. We initiate a connection with the backend server and perform basic
169
           RFB 3.8 handshake with it.
170

  
171
        Upon return, self.client and self.server are sockets
172
        connected to the client and the backend server, respectively.
173

  
174
        """
175
        self.client.send(rfb.RFB_VERSION_3_8 + "\n")
176
        client_version_str = self.client.recv(1024)
177
        client_version = rfb.check_version(client_version_str)
178
        if not client_version:
179
            self.error("Invalid version: %s" % client_version_str)
180
            raise gevent.GreenletExit
181

  
182
        # Both for RFB 3.3 and 3.8
183
        self.debug("Requesting authentication")
184
        auth_request = rfb.make_auth_request(rfb.RFB_AUTHTYPE_VNC,
185
            version=client_version)
186
        self.client.send(auth_request)
187

  
188
        # The client gets to propose an authtype only for RFB 3.8
189
        if client_version == rfb.RFB_VERSION_3_8:
190
            res = self.client.recv(1024)
191
            type = rfb.parse_client_authtype(res)
192
            if type == rfb.RFB_AUTHTYPE_ERROR:
193
                self.warn("Client refused authentication: %s" % res[1:])
194
            else:
195
                self.debug("Client requested authtype %x" % type)
196

  
197
            if type != rfb.RFB_AUTHTYPE_VNC:
198
                self.error("Wrong auth type: %d" % type)
199
                self.client.send(rfb.to_u32(rfb.RFB_AUTH_ERROR))
200
                raise gevent.GreenletExit
201
        
202
        # Generate the challenge
203
        challenge = os.urandom(16)
204
        self.client.send(challenge)
205
        response = self.client.recv(1024)
206
        if len(response) != 16:
207
            self.error("Wrong response length %d, should be 16" % len(response))
208
            raise gevent.GreenletExit
209

  
210
        if rfb.check_password(challenge, response, password):
211
            self.debug("Authentication successful!")
212
        else:
213
            self.warn("Authentication failed")
214
            self.client.send(rfb.to_u32(rfb.RFB_AUTH_ERROR))
215
            raise gevent.GreenletExit
216

  
217
        # Accept the authentication
218
        self.client.send(rfb.to_u32(rfb.RFB_AUTH_SUCCESS))
219

  
220
        # Try to connect to the server
221
        tries = 50
222

  
223
        while tries:
224
            tries -= 1
225

  
226
            # Initiate server connection
227
            for res in socket.getaddrinfo(self.daddr, self.dport, socket.AF_UNSPEC,
228
                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
229
                af, socktype, proto, canonname, sa = res
230
                try:
231
                    self.server = socket.socket(af, socktype, proto)
232
                except socket.error, msg:
233
                    self.server = None
234
                    continue
235

  
236
                try:
237
                    self.debug("Connecting to %s:%s" % sa[:2])
238
                    self.server.connect(sa)
239
                    self.debug("Connection to %s:%s successful" % sa[:2])
240
                except socket.error, msg:
241
                    self.server.close()
242
                    self.server = None
243
                    continue
244

  
245
                # We succesfully connected to the server
246
                tries = 0
247
                break
248

  
249
            # Wait and retry
250
            gevent.sleep(0.2)
251

  
252
        if self.server is None:
253
            self.error("Failed to connect to server")
254
            raise gevent.GreenletExit
255

  
256
        version = self.server.recv(1024)
257
        if not rfb.check_version(version):
258
            self.error("Unsupported RFB version: %s" % version.strip())
259
            raise gevent.GreenletExit
260

  
261
        self.server.send(rfb.RFB_VERSION_3_8 + "\n")
262

  
263
        res = self.server.recv(1024)
264
        types = rfb.parse_auth_request(res)
265
        if not types:
266
            self.error("Error handshaking with the server")
267
            raise gevent.GreenletExit
268

  
269
        else:
270
            self.debug("Supported authentication types: %s" %
271
                           " ".join([str(x) for x in types]))
272

  
273
        if rfb.RFB_AUTHTYPE_NONE not in types:
274
            self.error("Error, server demands authentication")
275
            raise gevent.GreenletExit
276

  
277
        self.server.send(rfb.to_u8(rfb.RFB_AUTHTYPE_NONE))
278

  
279
        # Check authentication response
280
        res = self.server.recv(4)
281
        res = rfb.from_u32(res)
282

  
283
        if res != 0:
284
            self.error("Authentication error")
285
            raise gevent.GreenletExit
286
       
287
    def _run(self):
288
        try:
289
            self.log.debug("Waiting for client to connect")
290
            rlist, _, _ = select(listeners, [], [], timeout=self.timeout)
291

  
292
            if not rlist:
293
                self.info("Timed out, no connection after %d sec" % self.timeout)
294
                raise gevent.GreenletExit
295

  
296
            for sock in rlist:
297
                self.client, addrinfo = sock.accept()
298
                self.info("Connection from %s:%d" % addrinfo[:2])
299

  
300
                # Close all listening sockets, we only want a one-shot connection
301
                # from a single client.
302
                while self.listeners:
303
                    self.listeners.pop().close()
304
                break
305
       
306
            # Perform RFB handshake with the client and the backend server.
307
            # If all goes as planned, we have two connected sockets,
308
            # self.client and self.server.
309
            self._handshake()
310

  
311
            # Bridge both connections through two "forwarder" greenlets.
312
            self.workers = [gevent.spawn(self._forward, self.client, self.server),
313
                gevent.spawn(self._forward, self.server, self.client)]
314
            
315
            # If one greenlet goes, the other has to go too.
316
            self.workers[0].link(self.workers[1])
317
            self.workers[1].link(self.workers[0])
318
            gevent.joinall(self.workers)
319
            del self.workers
320
            raise gevent.GreenletExit
321
        except Exception, e:
322
            # Any unhandled exception in the previous block
323
            # is an error and must be logged accordingly
324
            if not isinstance(e, gevent.GreenletExit):
325
                self.log.exception(e)
326
            raise e
327
        finally:
328
            self._cleanup()
329

  
330

  
331
def fatal_signal_handler(signame):
332
    logger.info("Caught %s, will raise SystemExit" % signame)
333
    raise SystemExit
334

  
335
def get_listening_sockets(sport):
336
    sockets = []
337

  
338
    # Use two sockets, one for IPv4, one for IPv6. IPv4-to-IPv6 mapped
339
    # addresses do not work reliably everywhere (under linux it may have
340
    # been disabled in /proc/sys/net/ipv6/bind_ipv6_only).
341
    for res in socket.getaddrinfo(None, sport, socket.AF_UNSPEC,
342
                                  socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
343
        af, socktype, proto, canonname, sa = res
344
        try:
345
            s = None
346
            s = socket.socket(af, socktype, proto)
347
            if af == socket.AF_INET6:
348
                # Bind v6 only when AF_INET6, otherwise either v4 or v6 bind
349
                # will fail.
350
                s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
351
            s.bind(sa)
352
            s.listen(1)
353
            sockets.append(s)
354
            logger.debug("Listening on %s:%d" % sa[:2])
355
        except socket.error, msg:
356
            logger.error("Error binding to %s:%d: %s" %
357
                           (sa[0], sa[1], msg[1]))
358
            if s:
359
                s.close()
360
            while sockets:
361
                sockets.pop().close()
362
            
363
            # Make sure we fail immediately if we cannot get a socket
364
            raise msg
365
    
366
    return sockets
367

  
368
def parse_arguments(args):
369
    from optparse import OptionParser
370

  
371
    parser = OptionParser()
372
    parser.add_option("-s", "--socket", dest="ctrl_socket",
373
                      default=DEFAULT_CTRL_SOCKET,
374
                      metavar="PATH",
375
                      help="UNIX socket path for control connections (default: %s" %
376
                          DEFAULT_CTRL_SOCKET)
377
    parser.add_option("-d", "--debug", action="store_true", dest="debug",
378
                      help="Enable debugging information")
379
    parser.add_option("-l", "--log", dest="log_file",
380
                      default=DEFAULT_LOG_FILE,
381
                      metavar="FILE",
382
                      help="Write log to FILE instead of %s" % DEFAULT_LOG_FILE),
383
    parser.add_option('--pid-file', dest="pid_file",
384
                      default=DEFAULT_PID_FILE,
385
                      metavar='PIDFILE',
386
                      help="Save PID to file (default: %s)" %
387
                          DEFAULT_PID_FILE)
388
    parser.add_option("-t", "--connect-timeout", dest="connect_timeout",
389
                      default=DEFAULT_CONNECT_TIMEOUT, type="int", metavar="SECONDS",
390
                      help="How long to listen for clients to forward")
391
    parser.add_option("-p", "--min-port", dest="min_port",
392
                      default=DEFAULT_MIN_PORT, type="int", metavar="MIN_PORT",
393
                      help="The minimum port to use for automatically-allocated ephemeral ports")
394
    parser.add_option("-P", "--max-port", dest="max_port",
395
                      default=DEFAULT_MAX_PORT, type="int", metavar="MAX_PORT",
396
                      help="The minimum port to use for automatically-allocated ephemeral ports")
397

  
398
    return parser.parse_args(args)
399

  
400

  
401
if __name__ == '__main__':
402
    (opts, args) = parse_arguments(sys.argv[1:])
403

  
404
    # Create pidfile
405
    pidf = daemon.pidlockfile.TimeoutPIDLockFile(
406
        opts.pid_file, 10)
407
    
408
    # Initialize logger
409
    lvl = logging.DEBUG if opts.debug else logging.INFO
410
    logger = logging.getLogger("vncauthproxy")
411
    logger.setLevel(lvl)
412
    formatter = logging.Formatter("%(asctime)s %(module)s[%(process)d] %(levelname)s: %(message)s",
413
        "%Y-%m-%d %H:%M:%S")
414
    handler = logging.FileHandler(opts.log_file)
415
    handler.setFormatter(formatter)
416
    logger.addHandler(handler)
417

  
418
    # Become a daemon:
419
    # Redirect stdout and stderr to handler.stream to catch
420
    # early errors in the daemonization process [e.g., pidfile creation]
421
    # which will otherwise go to /dev/null.
422
    daemon_context = daemon.DaemonContext(
423
        pidfile=pidf,
424
        umask=0022,
425
        stdout=handler.stream,
426
        stderr=handler.stream,
427
        files_preserve=[handler.stream])
428
    daemon_context.open()
429
    logger.info("Became a daemon")
430

  
431
    # A fork() has occured while daemonizing,
432
    # we *must* reinit gevent
433
    gevent.reinit()
434

  
435
    if os.path.exists(opts.ctrl_socket):
436
        logger.critical("Socket '%s' already exists" % opts.ctrl_socket)
437
        sys.exit(1)
438

  
439
    # TODO: make this tunable? chgrp as well?
440
    old_umask = os.umask(0077)
441

  
442
    ctrl = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
443
    ctrl.bind(opts.ctrl_socket)
444

  
445
    os.umask(old_umask)
446

  
447
    ctrl.listen(1)
448
    logger.info("Initialized, waiting for control connections at %s" %
449
                 opts.ctrl_socket)
450

  
451
    # Catch signals to ensure graceful shutdown,
452
    # e.g., to make sure the control socket gets unlink()ed.
453
    #
454
    # Uses gevent.signal so the handler fires even during
455
    # gevent.socket.accept()
456
    gevent.signal(SIGINT, fatal_signal_handler, "SIGINT")
457
    gevent.signal(SIGTERM, fatal_signal_handler, "SIGTERM")
458

  
459
    # Init ephemeral port pool
460
    ports = range(opts.min_port, opts.max_port + 1) 
461

  
462
    while True:
463
        try:
464
            client, addr = ctrl.accept()
465
            logger.info("New control connection")
466
           
467
            # Receive and parse a client request.
468
            response = {
469
                "source_port": 0,
470
                "status": "FAILED"
471
            }
472
            try:
473
                # TODO: support multiple forwardings in the same message?
474
                # 
475
                # Control request, in JSON:
476
                #
477
                # {
478
                #     "source_port": <source port or 0 for automatic allocation>,
479
                #     "destination_address": <destination address of backend server>,
480
                #     "destination_port": <destination port>
481
                #     "password": <the password to use for MITM authentication of clients>
482
                # }
483
                # 
484
                # The <password> is used for MITM authentication of clients
485
                # connecting to <source_port>, who will subsequently be forwarded
486
                # to a VNC server at <destination_address>:<destination_port>
487
                #
488
                # Control reply, in JSON:
489
                # {
490
                #     "source_port": <the allocated source port>
491
                #     "status": <one of "OK" or "FAILED">
492
                # }
493
                buf = client.recv(1024)
494
                req = json.loads(buf)
495
                
496
                sport_orig = int(req['source_port'])
497
                daddr = req['destination_address']
498
                dport = int(req['destination_port'])
499
                password = req['password']
500
            except Exception, e:
501
                logger.warn("Malformed request: %s" % buf)
502
                client.send(json.dumps(response))
503
                client.close()
504
                continue
505
            
506
            # Spawn a new Greenlet to service the request.
507
            try:
508
                # If the client has so indicated, pick an ephemeral source port
509
                # randomly, and remove it from the port pool.
510
                if sport_orig == 0:
511
                    sport = random.choice(ports)
512
                    ports.remove(sport)
513
                    logger.debug("Got port %d from port pool, contains %d ports",
514
                        sport, len(ports))
515
                    pool = ports
516
                else:
517
                    sport = sport_orig
518
                    pool = None
519
                listeners = get_listening_sockets(sport)
520
                VncAuthProxy.spawn(logger, listeners, pool, daddr, dport,
521
                    password, opts.connect_timeout)
522
                logger.info("New forwarding [%d (req'd by client: %d) -> %s:%d]" %
523
                    (sport, sport_orig, daddr, dport))
524
                response = {
525
                    "source_port": sport,
526
                    "status": "OK"
527
                }
528
            except IndexError:
529
                logger.error("FAILED forwarding, out of ports for [req'd by "
530
                    "client: %d -> %s:%d]" % (sport_orig, daddr, dport))
531
            except socket.error, msg:
532
                logger.error("FAILED forwarding [%d (req'd by client: %d) -> %s:%d]" %
533
                    (sport, sport_orig, daddr, dport))
534
                if not pool is None:
535
                    pool.append(sport)
536
                    logger.debug("Returned port %d to port pool, contains %d ports",
537
                        sport, len(pool))
538
            finally:
539
                client.send(json.dumps(response))
540
                client.close()
541
        except Exception, e:
542
            logger.exception(e)
543
            continue
544
        except SystemExit:
545
            break
546
 
547
    logger.info("Unlinking control socket at %s" %
548
                 opts.ctrl_socket)
549
    os.unlink(opts.ctrl_socket)
550
    daemon_context.close()
551
    sys.exit(0)
25
if __name__ == "__main__":
26
    proxy.main()
b/vncauthproxy/client.py
1
#!/usr/bin/env python
2
#
3
# Copyright (c) 2010-2011 Greek Research and Technology Network S.A.
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
# General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
# 02110-1301, USA.
19

  
20
import sys
21
import socket
22

  
23
try:
24
    import simplejson as json
25
except ImportError:
26
    import json
27

  
28
CTRL_SOCKET = "/var/run/vncauthproxy/ctrl.sock"
29

  
30
def request_forwarding(sport, daddr, dport, password):
31
    assert(len(password) > 0)
32
    req = {
33
        "source_port": int(sport),
34
        "destination_address": daddr,
35
        "destination_port": int(dport),
36
        "password": password
37
    }
38

  
39
    ctrl = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
40
    ctrl.connect(CTRL_SOCKET)
41
    ctrl.send(json.dumps(req))
42

  
43
    response = ctrl.recv(1024)
44
    res = json.loads(response)
45
    return res
46

  
47
if __name__ == '__main__':
48
    res = request_forwarding(*sys.argv[1:])
49
    if res['status'] == "OK":
50
        sys.exit(0)
51
    else:
52
        sys.exit(1)
b/vncauthproxy/d3des.py
1
#!/usr/bin/env python
2
##
3
##  d3des.py - DES implementation
4
##
5
##  Copyright (c) 2009 by Yusuke Shinyama
6
##
7

  
8
# This is a Python rewrite of d3des.c by Richard Outerbridge.
9
#
10
# I referred to the original VNC viewer code for the changes that
11
# is necessary to maintain the exact behavior of the VNC protocol.
12
# Two constants and two functions were added to the original d3des
13
# code.  These added parts were written in Python and marked
14
# below.  I believe that the added parts do not make this program
15
# a "derivative work" of the VNC viewer (which is GPL'ed and
16
# written in C), but if there's any problem, let me know.
17
#
18
# Yusuke Shinyama (yusuke at cs dot nyu dot edu)
19

  
20

  
21
#  D3DES (V5.09) -
22
#
23
#  A portable, public domain, version of the Data Encryption Standard.
24
#
25
#  Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
26
#  Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
27
#  code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
28
#  Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
29
#  for humouring me on.
30
#
31
#  Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
32
#  (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
33
#
34

  
35
from struct import pack, unpack
36

  
37

  
38
###################################################
39
###
40
###  start: changes made for VNC.
41
###
42

  
43
# This constant was taken from vncviewer/rfb/vncauth.c:
44
vnckey = [ 23,82,107,6,35,78,88,7 ]
45

  
46
# This is a departure from the original code.
47
#bytebit = [ 0200, 0100, 040, 020, 010, 04, 02, 01 ] # original
48
bytebit = [ 01, 02, 04, 010, 020, 040, 0100, 0200 ] # VNC version
49

  
50
# two password functions for VNC protocol.
51
def decrypt_passwd(data):
52
    dk = deskey(pack('8B', *vnckey), True)
53
    return desfunc(data, dk)
54

  
55
def generate_response(passwd, challange):
56
    ek = deskey((passwd+'\x00'*8)[:8], False)
57
    return desfunc(challange[:8], ek) + desfunc(challange[8:], ek)
58

  
59
###
60
###  end: changes made for VNC.
61
###
62
###################################################
63

  
64

  
65
bigbyte = [
66
  0x800000L,    0x400000L,      0x200000L,      0x100000L,
67
  0x80000L,     0x40000L,       0x20000L,       0x10000L,
68
  0x8000L,      0x4000L,        0x2000L,        0x1000L,
69
  0x800L,       0x400L,         0x200L,         0x100L,
70
  0x80L,        0x40L,          0x20L,          0x10L,
71
  0x8L,         0x4L,           0x2L,           0x1L
72
  ]
73

  
74
# Use the key schedule specified in the Standard (ANSI X3.92-1981).
75

  
76
pc1 = [
77
  56, 48, 40, 32, 24, 16,  8,    0, 57, 49, 41, 33, 25, 17,
78
   9,  1, 58, 50, 42, 34, 26,   18, 10,  2, 59, 51, 43, 35,
79
  62, 54, 46, 38, 30, 22, 14,    6, 61, 53, 45, 37, 29, 21,
80
  13,  5, 60, 52, 44, 36, 28,   20, 12,  4, 27, 19, 11,  3
81
  ]
82

  
83
totrot = [ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 ]
84

  
85
pc2 = [
86
  13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
87
  22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
88
  40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
89
  43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
90
  ]
91

  
92
def deskey(key, decrypt):      # Thanks to James Gillogly & Phil Karn!
93
    key = unpack('8B', key)
94

  
95
    pc1m = [0]*56
96
    pcr = [0]*56
97
    kn = [0L]*32
98

  
99
    for j in range(56):
100
        l = pc1[j]
101
        m = l & 07
102
        if key[l >> 3] & bytebit[m]:
103
            pc1m[j] = 1
104
        else:
105
            pc1m[j] = 0
106

  
107
    for i in range(16):
108
        if decrypt:
109
            m = (15 - i) << 1
110
        else:
111
            m = i << 1
112
        n = m + 1
113
        kn[m] = kn[n] = 0L
114
        for j in range(28):
115
            l = j + totrot[i]
116
            if l < 28:
117
                pcr[j] = pc1m[l]
118
            else:
119
                pcr[j] = pc1m[l - 28]
120
        for j in range(28, 56):
121
            l = j + totrot[i]
122
            if l < 56:
123
                pcr[j] = pc1m[l]
124
            else:
125
                pcr[j] = pc1m[l - 28]
126
        for j in range(24):
127
            if pcr[pc2[j]]:
128
                kn[m] |= bigbyte[j]
129
            if pcr[pc2[j+24]]:
130
                kn[n] |= bigbyte[j]
131

  
132
    return cookey(kn)
133

  
134
def cookey(raw):
135
    key = []
136
    for i in range(0, 32, 2):
137
        (raw0, raw1) = (raw[i], raw[i+1])
138
        k  = (raw0 & 0x00fc0000L) << 6
139
        k |= (raw0 & 0x00000fc0L) << 10
140
        k |= (raw1 & 0x00fc0000L) >> 10
141
        k |= (raw1 & 0x00000fc0L) >> 6
142
        key.append(k)
143
        k  = (raw0 & 0x0003f000L) << 12
144
        k |= (raw0 & 0x0000003fL) << 16
145
        k |= (raw1 & 0x0003f000L) >> 4
146
        k |= (raw1 & 0x0000003fL)
147
        key.append(k)
148
    return key
149

  
150
SP1 = [
151
  0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
152
  0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
153
  0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
154
  0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
155
  0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
156
  0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
157
  0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
158
  0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
159
  0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
160
  0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
161
  0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
162
  0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
163
  0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
164
  0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
165
  0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
166
  0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L
167
  ]
168

  
169
SP2 = [
170
  0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
171
  0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
172
  0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
173
  0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
174
  0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
175
  0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
176
  0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
177
  0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
178
  0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
179
  0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
180
  0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
181
  0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
182
  0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
183
  0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
184
  0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
185
  0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L
186
  ]
187

  
188
SP3 = [
189
  0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
190
  0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
191
  0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
192
  0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
193
  0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
194
  0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
195
  0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
196
  0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
197
  0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
198
  0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
199
  0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
200
  0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
201
  0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
202
  0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
203
  0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
204
  0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L
205
  ]
206

  
207
SP4 = [
208
  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
209
  0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
210
  0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
211
  0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
212
  0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
213
  0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
214
  0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
215
  0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
216
  0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
217
  0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
218
  0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
219
  0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
220
  0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
221
  0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
222
  0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
223
  0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L
224
  ]
225

  
226
SP5 = [
227
  0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
228
  0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
229
  0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
230
  0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
231
  0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
232
  0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
233
  0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
234
  0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
235
  0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
236
  0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
237
  0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
238
  0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
239
  0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
240
  0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
241
  0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
242
  0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L
243
  ]
244

  
245
SP6 = [
246
  0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
247
  0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
248
  0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
249
  0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
250
  0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
251
  0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
252
  0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
253
  0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
254
  0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
255
  0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
256
  0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
257
  0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
258
  0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
259
  0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
260
  0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
261
  0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L
262
  ]
263

  
264
SP7 = [
265
  0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
266
  0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
267
  0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
268
  0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
269
  0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
270
  0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
271
  0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
272
  0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
273
  0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
274
  0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
275
  0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
276
  0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
277
  0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
278
  0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
279
  0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
280
  0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L
281
  ]
282

  
283
SP8 = [
284
  0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
285
  0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
286
  0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
287
  0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
288
  0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
289
  0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
290
  0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
291
  0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
292
  0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
293
  0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
294
  0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
295
  0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
296
  0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
297
  0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
298
  0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
299
  0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L
300
  ]
301

  
302
def desfunc(block, keys):
303
    (leftt, right) = unpack('>II', block)
304

  
305
    work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL
306
    right ^= work
307
    leftt ^= (work << 4)
308
    work = ((leftt >> 16) ^ right) & 0x0000ffffL
309
    right ^= work
310
    leftt ^= (work << 16)
311
    work = ((right >> 2) ^ leftt) & 0x33333333L
312
    leftt ^= work
313
    right ^= (work << 2)
314
    work = ((right >> 8) ^ leftt) & 0x00ff00ffL
315
    leftt ^= work
316
    right ^= (work << 8)
317
    right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL
318
    work = (leftt ^ right) & 0xaaaaaaaaL
319
    leftt ^= work
320
    right ^= work
321
    leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL
322

  
323
    for i in range(0, 32, 4):
324
        work  = (right << 28) | (right >> 4)
325
        work ^= keys[i]
326
        fval  = SP7[ work            & 0x3fL]
327
        fval |= SP5[(work >>  8) & 0x3fL]
328
        fval |= SP3[(work >> 16) & 0x3fL]
329
        fval |= SP1[(work >> 24) & 0x3fL]
330
        work  = right ^ keys[i+1]
331
        fval |= SP8[ work            & 0x3fL]
332
        fval |= SP6[(work >>  8) & 0x3fL]
333
        fval |= SP4[(work >> 16) & 0x3fL]
334
        fval |= SP2[(work >> 24) & 0x3fL]
335
        leftt ^= fval
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff