Package bootlegger :: Module api
[hide private]
[frames] | no frames]

Source Code for Module bootlegger.api

  1  import requests 
  2  from Crypto.PublicKey import RSA 
  3  from Crypto import Random 
  4  import os 
  5  from base64 import b64encode, b64decode, b16encode 
  6  import json 
  7  from .cryptfile import encrypt_file, decrypt_file 
  8   
  9  rng = Random.new().read 
 10  DEFAULT_HOST = 'localhost' 
 11   
12 -class SecurityException(BaseException):
13 - def __init__(self, msg):
14 self.msg = msg
15 - def __str__(self):
16 return self.msg
17 - def __repr__(self):
18 return 'SecurityException: ' + self.msg
19
20 -class BootLegger(object):
21 """The base class for interacting with speakeasy"""
22 - def __init__(self, username, pubkey, privkey, 23 host = DEFAULT_HOST, password = '', auth = True):
24 """username is the username of the speakeasy user 25 pubkey is the user's public key as a string 26 privkey is the user's private key as a string 27 host is the host that speakeasy is running on (default is localhost) 28 password is the password for the private key (default to blank / no password) 29 auth determines whether to connect and authenticate immediately (default to true)""" 30 self.username = username 31 self.password = password 32 self.host = host 33 34 self.pubkey = pubkey 35 self.privkey = privkey 36 37 if auth: 38 self.authenticate()
39
40 - def authenticate(self):
41 """Authenticate to speakeasy. You do not need to call this yourself 42 if you set auth = True in the constructor""" 43 cookiefname = os.path.expanduser('~/.bootlegger/cookiejar.json') 44 write_cookies = True 45 46 if os.path.isfile(cookiefname): 47 f = open(cookiefname) 48 cookies = json.load(f) 49 f.close() 50 if cookies['username'] != self.username: 51 cookies = self._real_auth() 52 else: 53 write_cookies = False 54 else: 55 cookies = self._real_auth() 56 57 self.cookies = dict([(str(key), str(val)) for (key, val) in cookies.items()]) 58 59 if write_cookies: 60 f = open(cookiefname, 'w') 61 json.dump(self.cookies, f) 62 f.close()
63
64 - def upload(self, fname, rname = None):
65 """Encrypt and upload the file given by fname to the server. 66 fname should be the full path to the file. 67 You can set the optional argument rname to give the file a 68 different name on the server""" 69 rsakey = RSA.importKey(self.pubkey) 70 71 aes_key = rng(32) 72 tempname = '/tmp/' + b16encode(rng(16)) + '.bootleg' 73 encrypt_file(fname, tempname, aes_key) 74 aes_key = rsakey.encrypt(aes_key, rng(384))[0] 75 aes_key = b64encode(aes_key) 76 77 url = 'http://' + self.host + '/file/upload' 78 79 cryptf = open(tempname) 80 81 if not rname: 82 rname = os.path.basename(fname) 83 84 files = {'file': (rname, cryptf)} 85 headers = {'Symmetric-Key': str(aes_key)} 86 87 r = requests.post(url, cookies=self.cookies, files=files, headers=headers) 88 89 cryptf.close() 90 91 if r.status_code != 200: 92 r.raise_for_status()
93
94 - def download(self, fname, lname = None):
95 """Download and decrypt the file given by fname from the server. 96 By default it creates the file in the current directory with 97 the same name as on the server. To give the downloaded file a 98 different name or download it to a different location, set the 99 lname parameter to where you want the file downloaded.""" 100 url = 'http://' + self.host + '/file/download/' + fname 101 tempname = '/tmp/' + b16encode(rng(16)) + '.bootleg' 102 r = requests.get(url, cookies=self.cookies) 103 104 if not lname: 105 lname = fname 106 107 if r.status_code != 200: 108 r.raise_for_status() 109 110 with open(tempname, 'wb') as tempf: 111 for chunk in r.iter_content(): 112 tempf.write(chunk) 113 114 aes_key = b64decode(r.headers['Symmetric-Key']) 115 rsakey = RSA.importKey(self.privkey, self.password) 116 aes_key = rsakey.decrypt(aes_key) 117 118 decrypt_file(tempname, lname, aes_key)
119
120 - def list_files(self, pattern = None):
121 """Get a list of the names of the files stored on the server. 122 If the optional argument pattern is given, the method will 123 only list files matching the pattern. 124 Pattern should by a unix-style file glob.""" 125 url = 'http://' + self.host + '/file/list' 126 127 if pattern: 128 url += '/' + pattern 129 130 r = requests.get(url, cookies=self.cookies) 131 132 if r.status_code != 200: 133 r.raise_for_status() 134 135 resp = json.loads(r.text) 136 137 return resp['files']
138
139 - def get_info(self, fname):
140 """Get more detailed information about the file called fname 141 from the server.""" 142 url = 'http://' + self.host + '/file/info/' + fname 143 144 r = requests.get(url, cookies=self.cookies) 145 146 if r.status_code != 200: 147 r.raise_for_status() 148 149 resp = json.loads(r.text) 150 151 return resp['fileinfo']
152
153 - def share(self, fname, recipient):
154 """Share a file called fname stored on the server with the recipient""" 155 finfo = self.get_info(fname) 156 157 rsakey = RSA.importKey(self.privkey, self.password) 158 aes_key = rsakey.decrypt(b64decode(finfo['aes_key'])) 159 160 pubkey = self.get_pubkey(recipient) 161 rsakey = RSA.importKey(pubkey) 162 163 aes_key = rsakey.encrypt(aes_key, rng(384))[0] 164 aes_key = b64encode(aes_key) 165 166 headers = {'Symmetric-Key': aes_key} 167 data = {'recipient': recipient, 'filename': fname} 168 169 url = 'http://' + self.host + '/file/share' 170 171 r = requests.post(url, headers=headers, data=data, cookies=self.cookies) 172 173 if r.status_code != 200: 174 r.raise_for_status()
175
176 - def versions(self, fname):
177 """Get the dates of previous modifications to the file.""" 178 url = 'http://' + self.host + '/file/versions/' + fname 179 180 r = requests.get(url, cookies=self.cookies) 181 182 if r.status_code != 200: 183 r.raise_for_status() 184 185 resp = json.loads(r.text) 186 187 return resp['dates']
188
189 - def delete(self, fname):
190 """Delete a file from the server.""" 191 url = 'http://' + self.host + '/file/delete/' + fname 192 193 r = requests.post(url, cookies=self.cookies) 194 195 if r.status_code != 200: 196 r.raise_for_status()
197
198 - def get_pubkey(self, username):
199 """Get the public key of a user. 200 Takes the user's username as an argument. 201 Returns the public key as a string.""" 202 fname = os.path.expanduser('~/.bootlegger/' + username + '_public.pem') 203 204 if os.path.isfile(fname): 205 f = open(fname) 206 s = f.read() 207 f.close() 208 return s 209 210 url = 'http://' + self.host + '/pubkey/' + username 211 r = requests.get(url) 212 213 if r.status_code != 200: 214 r.raise_for_status() 215 216 f = open(fname, 'w') 217 f.write(r.text) 218 f.close() 219 220 return r.text
221
222 - def _real_auth(self):
223 url = 'http://' + self.host + '/authenticate' 224 rsakey = RSA.importKey(self.privkey, self.password) 225 shibboleth = 'Mom sent me' 226 signature = rsakey.sign(shibboleth, rng(384))[0] 227 data = {'username': self.username, 228 'shibboleth': shibboleth, 229 'signature': str(signature)} 230 231 r = requests.post(url, data=data) 232 233 if r.status_code != 200: 234 r.raise_for_status() 235 236 if not r.cookies['signature']: 237 raise SecurityException('Server did not return cookie.') 238 239 servkey = self.get_pubkey('server') 240 rsakey = RSA.importKey(servkey) 241 servsig = int(r.cookies['signature']) 242 243 if not rsakey.verify(self.username, (servsig,)): 244 raise SecurityException('Could not verify server signature') 245 246 return r.cookies
247
248 - def add_pubkey(self):
249 """Add a public key to the server.""" 250 rsakey = RSA.importKey(self.privkey, self.password) 251 shibboleth = 'Rosie sent me' 252 signature = rsakey.sign(shibboleth, rng(384))[0] 253 data = {'username': self.username, 254 'shibboleth': shibboleth, 255 'signature': str(signature), 256 'pubkey': self.pubkey} 257 258 url = 'http://' + self.host + '/pubkey/add' 259 260 r = requests.post(url, data=data) 261 262 if r.status_code != 200: 263 r.raise_for_status() 264 265 if not r.cookies['signature']: 266 raise SecurityException('Server did not return cookie') 267 268 servkey = self.get_pubkey('server') 269 rsakey = RSA.importKey(servkey) 270 servsig = int(r.cookies['signature']) 271 272 if not rsakey.verify(self.username, (servsig,)): 273 raise SecurityException('Could not verify server signature') 274 275 return r.cookies
276