source: test/samples/password-reset/tokenutils.py @ 135

Last change on this file since 135 was 135, checked in by tim, 7 years ago

.

  • Property svn:executable set to *
File size: 2.6 KB
Line 
1#!/usr/bin/env python3
2
3'''
4Sample library to create tokens vulnerable to padding oracle attacks
5
6Copyright (C) 2016-2017 Blindspot Security LLC
7Author: Timothy D. Morgan
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License, version 3,
11 as published by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20'''
21
22import time
23import json
24
25from Crypto.Cipher import AES
26from Crypto import Random
27
28from bletchley.buffertools import pkcs7PadBuffer,stripPKCS7Pad
29from bletchley.blobtools import encodeChain,decodeChain
30
31
32key = Random.new().read(32) # Never breaking AES256!!!
33
34
35def encodeToken(ciphertext):
36    return encodeChain(['base64/rfc3548','percent/upper'], ciphertext)
37
38
39def decodeToken(token):
40    return decodeChain(['percent/upper','base64/rfc3548'], token)
41
42
43def _encrypt(plaintext):
44    iv = Random.new().read(AES.block_size)
45    cipher = AES.new(key, AES.MODE_CBC, iv)
46    ciphertext = iv + cipher.encrypt(pkcs7PadBuffer(plaintext.encode('utf-8'), AES.block_size))
47
48    return encodeToken(ciphertext)
49
50
51def _decrypt(token):
52    plaintext = None
53    ciphertext = decodeToken(token)
54    iv = ciphertext[0:AES.block_size]
55    cipher = AES.new(key, AES.MODE_CBC, iv)
56    plaintext = stripPKCS7Pad(cipher.decrypt(ciphertext[AES.block_size:]), AES.block_size)
57    if plaintext == None:
58        raise Exception('Padding Error')
59   
60    return plaintext
61
62
63def generateResetToken(user):
64    seven_days = 7*24*60*60
65    reset_info = {'user':user,'expires':int(time.time()+seven_days)}
66    return _encrypt(json.dumps(reset_info)).decode('utf-8')
67
68
69def validateResetToken(token):
70    try:
71        plaintext = _decrypt(token)
72    except Exception as e:
73        return (False, 'Reset Token Corrupt!')
74
75    try:
76        decoded = plaintext.decode('utf-8')
77    except Exception as e:
78        return (False, 'Bad Token!')
79
80    try:
81        reset_info = json.loads(decoded)
82    except Exception as e:
83        return (False, 'Parse Error!')
84
85    if reset_info.get('expires', 0) < int(time.time()):
86        return (False, 'Token Expired!')
87   
88    return (True, reset_info)
89
90
91if __name__ == "__main__":
92    token = generateResetToken('bob')
93    print(token)
94    print(validateResetToken(token))
Note: See TracBrowser for help on using the repository browser.