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:
http://seqrentry.net/{action}#{param1}={value1}&{param2}=…
- The scheme part is always
http
.
- The authority part is always
seqrentry.net
.
- 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.
Parameters
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 https://seqrentry.net/proxy
, you should
post your response to the URL
https://seqrentry.net/proxy.json
.
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.
Actions
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
seqrentry.net
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 seqrentry.net/demo
and
user@example.com
.
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.
Parameters
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:
- Start by Base64-decoding the
k
URL
parameter. The resulting 16 bytes is the 128-bit
derivation key.
- UTF-8-encode the parameter value to be encrypted into
a sequence of bytes, if required.
- 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).
- 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.
- For each byte in the UTF-8-encoded parameter value,
XOR it with the corresponding byte in from the HMAC-SHA256
result.
- 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
.
- Finally, Base64-encode the result (again, using the
URL-safe alphabet).
Example
Here is a complete example of a login request from the demo page.

The SeQRentry code to process is shown
above. Once decoded, we end up with the following URL (split
into multiple lines for readability) :
http://seqrentry.net/login
#p=https%3A%2F%2Fproxy.seqrentry.net%2Fproxy
&t=SCLq6g6cjSpN
&r=seqrentry.net%2Fdemo
&k=KbmRJaAeFLNzdoCs75AjKQ
We identify it as a SeQRentry URL, since the
protocol is http
and the authority is
seqrentry.net
. 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
https://proxy.seqrentry.net/proxy.json
. When
posting, we will supply the token
SCLq6g6cjSpN
.
The realm, from the r
parameter, is
URL-decoded into seqrentry.net/demo
. 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 user@example.com
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, user@example.com,
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 = 'user@example.com'
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 = hmac.new(k, msg=message,
digestmod=hashlib.sha256).digest()
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): ' + \
base64.urlsafe_b64encode(res).rstrip('=')
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
Host: proxy.seqrentry.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 90
token=SCLq6g6cjSpN&username=9wIasH7QkONvdLDxiEU2yw&
password=R0UN4CDCjNsASg7f25cLajIsjETEVA
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.