18 | Temporal.PW can convert a password into a unique temporary secure URL that is safe to send via E-Mail.
19 |
20 | I made this because I needed a simple way to send passwords to people with whom I didn't already have end-to-end encryption established.
21 |
22 |
23 |
24 |
How to use it:
25 |
26 |
27 |
Enter a good, long, random password into temporal.pw, or click "Generate a random password".
28 |
E-Mail the temporary URL to someone:
29 |
30 |
If they're able to view the password: you know that nobody else was able to view it, and you know that that URL can't be used again later to determine the password.
31 |
If they see a "This password doesn't exist" error: that password was likely viewed by someone else, the recipient needs to tell you to generate a new password and send them a new temporal.pw URL.
32 |
If they see an "Invalid password URL" error: the URL they're attempting to view isn't the exact URL that you generated for them.
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
How it works:
41 |
42 |
43 |
The unencrypted password is NEVER sent anywhere.
44 |
The password encryption key is generated by your browser and is NEVER sent anywhere.
45 |
The browser generates a random 128 bit AES encryption/decryption key, encrypts the password with it, sends (only) the encrypted version of the password to the server (so that the URL can be rendered useless after viewing the password once, and the URL doesn't expose the password to brute force attack), and then the browser builds a unique temporary secure URL that contains the ID of the encrypted password plus the decryption key.
46 |
The password encryption/decryption key only exists in the 'fragment' part of the URL (after the '#' hash symbol) and is never sent to the server.
47 |
The encrypted password is only sent to the server so that the unique temporary secure URL can be rendered useless after it has been viewed once or it has expired. (the encrypted password is sent instead of the key so that the password can't be brute forced out of the URL, and so that the URL is a fixed short length independent of the password size)
48 |
The encrypted password cannot be decrypted without the decryption key thats in the unique temporary secure URL.
49 |
50 |
All key generation, encryption, and decryption is 100% done in the browser using common public cryptographic libraries (AES-JS).
51 |
The encrypted password is deleted from the server after it expires, or immediately after being viewed a single time by someone who has the unique temporary secure URL.
52 |
Each password is only viewable a single time so that the intended recipient will NOT be able to view the password if someone else has intercepted and viewed it first.
53 |
Optionally, you can choose to tell the server to only allow a password to be viewed from your same IP address (useful for sending passwords to someone in the same office / network).
54 |
55 |
56 |
57 |
58 |
59 |
60 |
Other Information:
61 |
62 |
63 |
Easy to audit: only ~150 lines of python for the backend, and ~200 lines of JavaScript for the fronend.
If you're paranoid, and don't trust that I won't change the JavaScript to send your password or encryption key somewhere (and you don't want to just view the source of the page) : you can use this copy of the JavaScript hosted on github.io: https://tkooda.github.io/temporalpw/ instead, where (you can track any changes to the Javascript you're running at https://github.com/tkooda/temporalpw/commits/gh-pages).
183 |
184 |
185 |
186 | (Do not include any information that identifies what the password is for)
187 |
188 |
189 |
190 | Send another password | About | Source
191 |
192 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/vendor.py:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2014 Jon Wayne Parrott, [proppy], Michael R. Bernstein
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | # Notes:
18 | # - Imported from https://github.com/jonparrott/Darth-Vendor/.
19 | # - Added license header.
20 | # - Renamed `darth.vendor` to `vendor.add` to match upcoming SDK interface.
21 | # - Renamed `position` param to `index` to match upcoming SDK interface.
22 | # - Removed funny arworks docstring.
23 |
24 | import site
25 | import os.path
26 | import sys
27 |
28 |
29 | def add(folder, index=1):
30 | """
31 | Adds the given folder to the python path. Supports namespaced packages.
32 | By default, packages in the given folder take precedence over site-packages
33 | and any previous path manipulations.
34 |
35 | Args:
36 | folder: Path to the folder containing packages, relative to ``os.getcwd()``
37 | position: Where in ``sys.path`` to insert the vendor packages. By default
38 | this is set to 1. It is inadvisable to set it to 0 as it will override
39 | any modules in the current working directory.
40 | """
41 |
42 | # Check if the path contains a virtualenv.
43 | site_dir = os.path.join(folder, 'lib', 'python' + sys.version[:3], 'site-packages')
44 | if os.path.exists(site_dir):
45 | folder = site_dir
46 | # Otherwise it's just a normal path, make it absolute.
47 | else:
48 | folder = os.path.join(os.path.dirname(__file__), folder)
49 |
50 | # Use site.addsitedir() because it appropriately reads .pth
51 | # files for namespaced packages. Unfortunately, there's not an
52 | # option to choose where addsitedir() puts its paths in sys.path
53 | # so we have to do a little bit of magic to make it play along.
54 |
55 | # We're going to grab the current sys.path and split it up into
56 | # the first entry and then the rest. Essentially turning
57 | # ['.', '/site-packages/x', 'site-packages/y']
58 | # into
59 | # ['.'] and ['/site-packages/x', 'site-packages/y']
60 | # The reason for this is we want '.' to remain at the top of the
61 | # list but we want our vendor files to override everything else.
62 | sys.path, remainder = sys.path[:1], sys.path[1:]
63 |
64 | # Now we call addsitedir which will append our vendor directories
65 | # to sys.path (which was truncated by the last step.)
66 | site.addsitedir(folder)
67 |
68 | # Finally, we'll add the paths we removed back.
69 | # The final product is something like this:
70 | # ['.', '/vendor-folder', /site-packages/x', 'site-packages/y']
71 | sys.path.extend(remainder)
72 |
--------------------------------------------------------------------------------