A Safer Internet


We're here to make the Internet a safer place. Nothing more.

Specifically, our mission is to make hacker attacks against password databases a thing of the past. As a side effect, we'll also make break-ins by password guessing a thing of the past as well.


A web site's password database is often only interesting because the passwords found inside might be used to unlock accounts at other, more sensitive, services.

Our goal is to make any stolen password obtained from a data breach unusable and uninteresting, by making sure all passwords are cryptographically strong and never, ever reused between different accounts.


The SeQRentry (secure entry) app generates and stores strong, random and unique passwords for you, on the only device you always have at hand: your phone.

These passwords are then automatically sent — encrypted, of course — from your phone to the login dialog every time you log in.

We believe in evolution over revolution and we're pragmatic enough to accept that passwords will be with us for a long, long time. Any system trying to replace a simple username and password box that is difficult to use, difficult to implement, expensive or not flexible enough is doomed to fail.

Fortunately, SeQRentry is nothing of the kind. Here's an example of the not difficult to use part: Scan a QR code. Enter PIN. Boom — you're logged in.

Head over to the demo section to see SeQRentry in action.


You can help us achieve our goal in several ways:

  • Site owners can implement it on their web site.
  • Developers can help by improving the code, writing browser extensions or writing plugins for non-web systems such as Windows or Gnome.
  • 3rd party developers can help by adding support in their existing key ring applications.
  • Artists can make this web site and the apps pretty.
  • End-users can spread the word, help us improve the usability and find bugs.
  • Security experts can shoot holes in the idea and then suggest how to plug them.

Last, but not least: you're all encouraged to donate to the project.

Bitcoin 1FgNWrWUZL6KhJHyhbcnzdFoJgbdPoFgJW

Android App on Google Play
Coming soon

Available on the App Store
Now available!

Windows Phone Store
Coming soon


For Site Owners

Quick Start

First of all: this is pre-release software. Before we hit version 1.0, any aspect of the information below may change!

SeQRentry is designed to plug right into your existing infrastructure, without modification. There is really nothing new here; if you are currently using usernames and passwords for authentication, you're ready to go. The system even plays nice with the browsers' built-in capability to remember web site credentials!

Adding SeQRentry to a web site is easy. Begin by importing the CSS and JavaScript file inside your <head> section:

  <link href='//' rel='stylesheet' type='text/css' />
  <script src='//' type='text/javascript' async='async'></script>

A word of caution: in production, you should download the CSS and JavaScript files and serve them from your own site. When it comes to your user's credentials, you should trust nobody except yourself.

Next, you need to mark up your input fields. This is done by adding a data-seqrentry-type attribute to your username and password input fields. The attribute's value should be one of the following:

  • username
  • password
  • new-password

Finally, you need to add a SeQRentry action button. We recommend that you use a <button> element, but anything that can contain HTML markup is fine. Add data-seqrentry-type attribute with one of the following values:

  • register
  • login
  • change

If the element is empty, the SeQRentry logo will be inserted into the element as an <img> element. Otherwise, the content will be left as-is. This allows you to style the action buttons in any way you would like.

  <input type="text"     name="username" size="30" data-seqrentry-type="username" /> <br/>
  <input type="password" name="password" size="30" data-seqrentry-type="password" /> <br/>
  <button data-seqrentry-type="login"></button> <br/>
  <input type="submit" value="Log in" />

And that's it. When the user clicks on the SeQRentry button, a QR code will be shown. Once scanned, the marked-up input fields are automatically populated with the credentials from the user's phone.

Note that all account activity in the app is triggered by a SeQRentry code. Users cannot, for instance, add accounts on their own; instead, the web site must provide a register or new-password QR code for this to happen.

Check out the demo section and look at the source code of this page for a live example.

How It Works

When the user clicks on a SeQRentry action button, a random 128-bit key is generated and a long-polling connection to one of the SeQRentry proxy servers is opened. The key, details about the proxy server and a few more things are then encoded as a QR code and displayed on-screen.

When the phone app has retrieved or generated a password, it derives a new key based on the one from the QR code, encrypts the password and sends it to the proxy server. The proxy relays the information back to the browser, which decrypts it and inserts it into the username and password boxes.

The important thing to remember here is that the credentials that are sent from the phone to the web browser (via the proxy server) are encrypted with a one-time pad, derived from a random 128-bit key, which is never reused. That key is never transmitted electronically over the network; instead, it is sent over the air directly from the computer's screen to the phone's camera using plain ol' visible light.

The entropy for the random keys is collected from various device properties and input events such as mouse movements, orientation sensors etc. If available, window.crypto.getRandomValues() is also used to add another 256 bits of true randomness.

Note: just because SeQRentry passwords are cryptographically strong and never reused doesn't mean all your users' passwords are. Of course you will still be using PBKDF2, bcrypt or scrypt with a high iteration count before storing any password in a database!

Advanced Configuration

A SeQRentry account is made up of three components:

  • A username, which can be any string.
  • A randomly generated password, which is just 22 digits and letters from the (URL safe) Base64 alphabet, encoding a 128-bit random number.
  • A realm, which identifies your site. By default, the realm is equal to the host part of your web page's URL.

These credentials are relayed via a proxy server to the user's browser. By default, a public proxy server that we provide is used.

The credentials are automatically inserted into <input> fields that you tag using the data-seqrentry-type attribute. When searching for such <input> fields, only elements belonging to the same <form> that contained the button triggering the QR code are considered. This allows you to have multiple SeQRentry forms on the same page.

These are the defaults. You may change these in several ways.

Changing The Realm

To set the realm (for instance, to change to Your Site), add a global JavaScript variable named SEQRENTRY_REALM, like this:

<script type='text/javascript'>
  var SEQRENTRY_REALM = "Your Site";

Another option is to add a data-seqrentry-realm attribute to your <form> element:

<form data-seqrentry-realm="Your Site" action="…" …>

Strange Forms

Speaking of <form> elements, it could be that there are no <form> elements at all on your pages. In this case, use a data-seqrentry-type attribute on any element surrounding your input fields and action button:

<div data-seqrentry-type="form">
  <input data-seqrentry-type="username" … />

Fixing the Username

Some systems only have one username, or maybe the username is determined by some other means, such as a client certificate. In this case, you can specify the username manually, by adding a data-seqrentry-username attribute to your <form> element:

<form data-seqrentry-username="Administrator" action="…" …>

Running Your Own Proxy Servers

Depending on a third party in order to let your users log in to your site is probably simply not an option. Fortunately, the SeQRentry proxy server is open source and written in Python, which means you can — and should — run it almost anywhere. Then, simply let the JavaScript code know where to find it by setting a global variable named SEQRENTRY_PROXY_URL:

<script type='text/javascript'>

You can use round-robin DNS to spread the load between several proxy servers, if required.


You can instruct SeQRentry to invoke a JavaScript code snippet for almost any action it takes, which is very powerful and can be used for many things, including:

  • Getting informed when the QR banner is opened or cancelled.
  • Automatically submitting the form when the username and password has been filled in, making the login sequence even more attractive to your users.
  • Handling the received credentials in more advanced ways, instead of just having them automatically inserted into <input> fields. You could even use SeQRentry as the only way to log in to your site.
  • Working around password policy restrictions. Some system (God forbid) might only handle passwords shorter than 8 or 16 characters, or maybe a certain number of special characters or digits are required. Callbacks allow you to programmatically transpose the SeQRentry-generates passwords in any way you'd like.

You can add a callback on any element marked with a data-seqrentry-type attribute, plus the actual <form> element itself.

Callbacks receive two arguments: type and value. type is set to whatever value the data-seqrentry-type attribute had, and value is the actual value (for instance, a username or a password). Additionally, the this variable is bound to the actual HTML DOM element.

For <form> callbacks, the type is simply set to form and the value is one of open, cancel or success.

Callbacks can modify the processing in three ways:

  • A callback that returns nothing or undefined does not modify the processing in any way.
  • If a callback returns null, processing for this field simply stops and nothing is inserted into the input field in question.
  • Any other value is treated as the value to be inserted into the current input field.
<form data-seqrentry-func='alert(type + ": " + value);'>
  <input data-seqrentry-type="username"
         data-seqrentry-func='return value.toLowerCase();'/>

The small example above logs form events and makes sure that any username contains lower case letters only.


For 3rd Party Developers

The SeQRentry Codes

A SeQRentry code is just a regular QR code that encodes a specially constructed URL. The URL is designed to work in a web browser if clicked, but it is normally expected that it will be parsed by the SeQRentry app, or a 3rd party key ring application.

The URL has the following format:{action}#{param1}={value1}&{param2}=…
  • The scheme part is always http.
  • The authority part is always
  • The path part is one of the supported actions: register, login or change.
  • There is no query part!
  • The fragment part holds key-value pairs in the same format that is normally used in the query part.

The reason we pass parameters in the fragment part and not the query part is that the fragment never leaves the browser. It is never sent over the Internet. In contrast, normal query parameters end up in server logs, web analytics tools and can easily be sniffed, if the transport channel is not encrypted.

Since one of the parameters we need to transmit is the unique, secret key that is used to derive password and parameter encryption keys, it is essential that this key is never, ever sent over the network. Putting the parameters in the fragment part solves this problem neatly.


The following sections will describe the parameters that are currently in use. More parameters may be defined in the future. When parsing the URL, unknown parameters should always be ignored.

Paramters names are kept as short as possible, in order to keep the complexity of the QR code to a minimum.

Proxy URL: p

The p parameter is used to pass the location of the proxy to use. You are supposed to append .json, .xml or .js to this string to indicate in what format you would like to receive the response.

Example: If you prefer JSON, and the p parameter is, you should post your response to the URL

Note that the format only defines the format of the response. Everything posted to the proxy should be encoded as application/x-www-form-urlencoded.

Proxy Token: t

This is a unique, randomly generated proxy token, which specifies the channel that the client (browser) is listening to. This value must be sent as-is when posting the response to the proxy.

Account Realm: r

This parameter specifies the realm of the service being authenticated. Should be a domain name, but it could also be just a company or service name.

Account Username: u

This parameter, if present, holds the name of the account. It could be a username or an email address.

One-Time Derivation Key: k

This is the secret, random derivation key that the browser generates and which is used to derive keys for encrypting all parameters that are sent via the proxy to the browser. It is a Base64-encoded 128-bit random number, encoded using the URL-safe alphabet and with the trailing = symbols removed.


There are currently three defined actions: register, login and change. The application must reject unknown actions with a failure message. You are also encouraged to open the full URL in a browser window in this case, using the web site as a fall-back for handling the action.

Create a New Account: register

This action is used to register a new account. The following URL parameters are present: p, t, r, u and k.

r and u defines the account. Example values are and

You are expected to generate a new 128-bit random number using a cryptographically strong random number generator, but a 3rd party key ring application could also prompt the user for a password to use; this is, however, strongly discouraged, as it kind of defeats the purpose of the SeQRentry system.

Convert the generated key to printable characters by Base64-encoding it (using the URL-safe alphabet) and removing the trailing = symbols. Or use another algorithm that you like. The important thing is that the password is strong and that it can be pasted into an HTML input field.

Once you have stored the credentials securely locally, you should post the following parameters to the proxy URL (from the p URL parameter, with the desired format extension added):

  • token: The value of the t URL parameter, unchanged.
  • ident: An optional request identification that will be sent back to you, unchanged, as part of the response.
  • username: The username (from the u URL parameter), encrypted and Base64-encoded.
  • new-password: The generated password, encrypted and Base64-encoded.

See below for information about how the parameters are encrypted and encoded.

Sign In: login

The most common action is the login action. The following URL parameters are present: p, t, r and k. It is possible that the u parameter is also present. In that case, you should use it to select what password to send, in case there are multiple accounts stored from this realm.

Once you have retrieved the credentials from your key ring, post the following parameters:

  • token: The value of the t URL parameter, unchanged.
  • ident: An optional request identification that will be sent back to you, unchanged, as part of the response.
  • username: The username (from the u URL parameter or selected by the user), encrypted and Base64-encoded.
  • password: The account password, encrypted and Base64-encoded.

Reset Password: change

This action is used to generate a new password for an existing account. It is an important action, since making it easy for users to regularly change passwords improves security.

The following URL parameters are present: p, t, r and k. It is possible that the u parameter is also present. In that case, you should use it to select what password to send, in case there are multiple accounts stored from this realm.

Generate a new 128-bit random number using a cryptographically strong random number generator, convert it to printable characters by Base64-encoding it (using the URL-safe alphabet) and post the following parameters to the proxy:

  • token: The value of the t URL parameter, unchanged.
  • ident: An optional request identification that will be sent back to you, unchanged, as part of the response.
  • username: The username (from the u URL parameter or selected by the user), encrypted and Base64-encoded.
  • password: The old account password, encrypted and Base64-encoded.
  • new-password: The newly generated password, encrypted and Base64-encoded.

Proxy responses

When you have posted the encrypted credentials to the proxy, you will receive a response in the format you have requested. Responses are made up of three components: HTTP status codes, response types and parameters.

HTTP Status Codes and Response Types

The HTTP status code informs you about the general success or failure of the request:

  • 200/proxyNotified: The credentials were successfully posted and relayed to the client (browser).
  • 202/proxyNotified: The credentials were successfully posted, but there were no client listening on the channel right now. The credentials will be kept by the proxy for a couple of minutes, so it's possible, even likely, that they will eventually reach the client.
  • 402/proxyNotFound: The specified token is invalid. Most probably, the QR code is too old and the channel has been closed. Ask the user to try again.


No response parameters except the ident parameter are currently defined.

The JSON Response Format

The JSON response format encodes the response type and the parameters as a two-element long array:

["type", {"param1": "value1", …}]

The XML Response Format

XML responses are encoded by creating an element with the name of the response type and adding all parameters as attributes:

<type param1="value" …/>

The JavaScript Response Format

This response format is mainly used when the browser is communicating with the proxy, but it can also be used by key ring applications (even if this is discouraged).

The format calls a method with the same name as the response type on the SeQRentry global object, passing the HTTP status code as the first argument and the parameters as the second (as an object literal):

SeQRentry.type(http-status, {"param1": "value1", …});

Responses in this format are always sent using HTTP status code 200. The actual status code is sent as the first argument to the function.

Parameter encryption

All parameters that are to be sent to the client (browser) must be encrypted. Every time a QR code is created, the client generates a random 128-bit key, Base64-encodes it and sends it to the key ring application via the k URL parameter.

Note that the URL-safe Base64 alphabet is used, and that trailing = symbols have been removed. This is done so that the string that is encoded in the QR code is as short as possible. The less data in the QR code, the easier it will be to scan it.

(These Base64-encoded strings can be converted into regular Base64 by replacing all - symbols with + and all _ symbols with /. Finally, add one or two = symbols to make the final length a multiple of 4.)

Using this key, you derive parameter keys (more like one-time pads, actually) using HMAC-SHA256:

  1. Start by Base64-decoding the k URL parameter. The resulting 16 bytes is the 128-bit derivation key.
  2. UTF-8-encode the parameter value to be encrypted into a sequence of bytes, if required.
  3. Construct a message by taking the ASCII/Latin-1/UTF-8-encoded parameter name (it should not contain non-ASCII symbols, so these encodings are all equivalent) and appending an ASCII 0 digit (ASCII value 0x30).
  4. Calculate the keyed-hash message authentication code using the HMAC-SHA256 algorithm, using the 128-bit derivation key and the constructed message from the previous steps. This generates 32 new bytes, which will be used to encode the parameter.
  5. For each byte in the UTF-8-encoded parameter value, XOR it with the corresponding byte in from the HMAC-SHA256 result.
  6. If the parameter is longer than 32 bytes, generate a new sequence of XOR bytes by encoding a new message, this time appending the ASCII digit 1 instead. And so on. In the rare case you need to encode more than 320 bytes, the 11th XOR block should be generated by appending two ASCII digits, 1 and 0.
  7. Finally, Base64-encode the result (again, using the URL-safe alphabet).


Here is a complete example of a login request from the demo page.

Example SeQRentry Code

The SeQRentry code to process is shown above. Once decoded, we end up with the following URL (split into multiple lines for readability) :

We identify it as a SeQRentry URL, since the protocol is http and the authority is The path is login, which means that we should fetch credentials from our key ring and post them to the proxy.

The proxy to use is taken from the p parameter from the fragment part. We prefer JSON, so the proxy URL we'll use is When posting, we will supply the token SCLq6g6cjSpN.

The realm, from the r parameter, is URL-decoded into As it happens, we have an account with this realm in our key ring, so let's post the credentials!

The user name for the account is and the password is SIqDSphiNaOYVgJUzrJk1Q.

The first thing we need to do is to decode the derivation key. Base64-decode the value of the k parameter, KbmRJaAeFLNzdoCs75AjKQ, and you end up with the following 16 bytes:

29 b9 91 25 a0 1e 14 b3 73 76 80 ac ef 90 23

Let's continue with the username. To derive the XOR block, we first construct the message username0 and take the ASCII values from the string:

75 73 65 72 6e 61 6d 65 30

Then we feed the message into HMAC-SHA256 together with the derivation key. After that, we end up with the following 32 bytes:

82 71 7f c2 3e b5 e8 82 02 04 dc 94 a6 26 59 a6
5d c2 f5 70 76 c0 79 c1 51 fd 39 91 bf 1b 0a cd

The username to encrypt,, is UTF-8-encoded into the following 16 bytes:

75 73 65 72 40 65 78 61 6d 70 6c 65 2e 63 6f 6d

Now simply XOR each byte from the UTF-8-encoded username and first 16 of the above 32 bytes:

Input:  75 73 65 72 40 65 78 61 6d 70 6c 65 2e 63 6f 6d
Xor:    82 71 7f c2 3e b5 e8 82 02 04 dc 94 a6 26 59 a6
Result: f7 02 1a b0 7e d0 90 e3 6f 74 b0 f1 88 45 36 cb

Base64-encode the result, and you end up with 9wIasH7QkONvdLDxiEU2yw. This is the value we will post to the proxy as the username parameter.

Here's a small Python example that highlights the math:

#!/usr/bin/env python

import hmac
import hashlib
import base64

username = ''
password = 'SIqDSphiNaOYVgJUzrJk1Q'

k = 'KbmRJaAeFLNzdoCs75AjKQ'
print 'Original k: ' + k

k = k.ljust((len(k) + 3) / 4 * 4, '=')
k = base64.urlsafe_b64decode(k)
print 'b64-decoded k (hex): ' + k.encode('hex')

message = 'username0'
print 'Message (hex): ' + message.encode('hex')

xor =, msg=message,
print 'HMAC (hex): ' + xor.encode('hex')

print 'Username: ' + username
print 'Usename (hex): ' + username.encode('hex')

res = ''.join(chr(ord(a) ^ ord(b))
              for a,b in zip(username, xor))
print 'Username ^ HMAC (hex): ' + res.encode('hex')
print 'Username ^ HMAC (b64): ' + \

Perform the same calculations for the password. Then post the token, username and password parameters to the proxy URL:

POST /proxy.json HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 90


In response, the proxy sends the following message, indicating that all went well:

HTTP/1.0 200 OK
Date: Sat, 18 May 2013 20:55:44 GMT
Cache-Control: private, max-age=0
Content-Type: application/json

["proxyNotified", {"ident": ""}]

And that's all there is to it.




This form is used to register an account in the SeQRentry phone app. Enter a username and click on the large button to try. When registering, the username is required.

(Leave the password field empty; the generated password will automatically be pasted there.)

The action button in this form is an empty <div> element.


Sign In

Once you have registered an account, it's easy to sign in. Just click the button and sign in with the app!

It's optional to fill in the username. If left empty. SeQRentry will let you select from a list of usernames used at this site.

The action button in this form is a <button> element with custom content.

Log in


Another action is to change the password for an existing account.

Like with sign in actions, providing a username is optional.

The action button in this form is an empty <button> element.

Change password

Event Log

Wondering what's going on? This page uses the data-seqrentry-func attribute to get notified when the forms fields are populated, and displays each action in the following event log.

Event log