Security Info
Account Creation
During account creation, an Account Seed is created for the user, that seed is used to generate a recovery phrase, a Master Key signing pair, and common use signing and encryption keys. The user's username and password are stretched to create login information and a "bootstrap" encryption key.
The derived login information is then passed via SRP to the server along with the encrypted bootstrap file, the public portion of the Master Key, and the public portions of the common use signing and encryption keys to create the account.
Once the account creation process is complete, the private portion of the Master Key is removed from memory, and at no point is the private key ever saved to persistent storage (ie: LocalStorage) or transmitted to the server.
Loss of the user's recovery phrase (the only place the private Master Key is stored) will prevent the user from making certain changes to their accounts, like resetting passwords, changing usernames, etc.
Account Seed Generation
TweetNaCl.js's
nacl.randomBytes()
is called with a length of32
to create a new 256 bit seed. Before returning the seen, the last byte (8 bits) of the seed is replaced with aCRC
checksum to ensure BIP39 phrases generated elsewhere will not be recognized as valid BirdCalls recovery phrases.
Recovery Phrase Generation
The Account Seed is converted using BIP39 to a Recovery Phrase, and the user is asked to record it locally.
The seed is passed to BIP39's
entropyToMnemonic()
to generate a 24 word recovery phrase, which is shown to the user to be recorded, and the user is asked to verify that they've properly recorded the phrase by verifying random words.
Key Generation
Once the user has verified that they have properly recorded the Account Seed, several key pairs are created using the seed and derivation paths.
Keys are generated from the Account Seed the Futoin
hkdf()
method, using SHA-512 for the hash function and the derivation path (below) for theinfo
field. No salt is passed in, since the recovery phrase needs to reliably generate the same keys without any external information.The derivation paths used to generate keys from the Account Seed follow the format
/<path-version>/<context>/<index>
where<path-version>
is currently0
(but allows for different path formats in the future,<context>
is the type of key/string to be created, currently eithersigning
orencryption
, and<index>
is a counter starting at0
, allowing easy key rotation from the same Account Seed.
A Master Signing Key pair is created from the Account Seed, the public portion of which is used as the account identifier and is passed to the server during account creation. The private portion of the Master Signing Key pair is only ever stored in RAM and is used to make account modifications (like username or password changes) and to sign new keys when required.
The Master Signing Key pair uses the index
0
for it's derivation path.
A set of "common use" keys is then generated from the Account Seed, using the same method as the Master Key Pair, and are signed by the Master Key to verify that they belong to / are authorized to act on behalf of this account. These keys are used for daily use encryption, signing, etc, and the public portions are passed to the server during account creation.
"Common use" keys use indexes greater than
0
for their the derivation paths.
Password Strengthening
To help prevent weak passwords from compromising account security, passwords are passed though PBKDF2 to generate login information and bootstrap encryption keys, which dramatically slows down brute force attempts, even with offline data. The resulting "derived password" is then split and used for both login and bootstrap encryption/decryption.
window.crypto.subtle.deriveBits()
is used with the user's username as a salt and 10,000,000 (10 million) iterations ofPBKDF2
, to generate a 64 byte (512 bit) key. This 512 bit derived key is split into a 256 bit encryption secret (used to encrypt the bootstrap file) and a 256 bit login secret (used in place of the raw password during SRP login).PBKDF2 was chosen mainly because it's built into the browser's native crypto library, allowing many more rounds to be completed than would be possible using a non native / JS based method. If/when
window.crypto
supports additional algorithms we may update this.In local testing, 10 millions rounds of PBKDF2 can be completed in around 3-10 seconds on modern user devices, and was chosen as a tradeoff between security and usability (the user has to wait every time they enter their password for this process to complete).
Bootstrap File
While the Recovery Key is a good way to store the user's Account Seed, which can be used to recover their Master Key and Common Keys, it takes significant time to enter and may not be at hand whenever the user wishes to log in. To provide a better user experience, while still maintaining security, the Bird Calls client stores commonly used information (namely, the "common use" private encryption and signing keys) in an encrypted file that's downloaded whenever the user logs in, which we refer to as "the bootstrap file". This data is encrypted on the user's own computer, using a key that never leaves the user's computer.
The bootstrap file is a JSON encoded string, encrypted with
nacl.secretbox()
using the first 256 bits of the PBKDF2 strengthened password, and a 24 bit randomly generated nonce. The nonce is then prepended to the encrypted data.
The bootstrap file does NOT contain the Account Seed or private Master Key, which are never stored and only accessible via the user's recovery phrase.
SRP Registration
To prevent even the PBKDF2 strengthened password from being sent to the server, SRP (Secure Remote Password protocol) is used to register the account.
Thinbus's
generateRandomSalt()
function is used to generate a random salt for the SRP registration, andgenerateVerifier()
is called with the generated salt, the user's username, and the PBKDF2 strengthened password to generate a verifier. The username, salt, and verifier are then passed to the server along with the public Master Key, public common signing key, and public common encryption key to the server to create the new account.
Bootstrap Retrieval (Login)
The user's bootstrap file contains the information necessary to access Bird Calls resources by authenticating with the server to make requests, as well as the keys necessary to decrypt data stored by users. However, if the user is accessing the service from a new device, or from a device where previously cached data has been removed, the user needs a way to retrieve that bootstrap file without being able to use the keys contained in the file to authenticate their requests.
To accomplish this the user is asked to provide their username and password, which is passed through the same password strengthening method described in the Account Creation section, providing a 256 bit login secret and a 256 bit encryption secret. The login secret is passed to the SRP client along with the username to authenticate the request and retrieve the bootstrap file. At which point the client can decrypt the encrypted bootstrap file using the encryption secret generated during the password strengthening step and access the user's common keys.
Post Login Resource Authentication
General use requests to the server (aside from Account Creation and Bootstrap Retrieval/Login) are authenticated using HTTP Signatures using the user's common use signing key pair, normally cached on the user's device and in the user's bootstrap file.
Bird Calls' implementation follows the HTTP Signatures spec, except that we use ed25519 signatures to sign the requests (as the spec as currently written does not support the signatures used in our chosen crypto library, TweetNaCl.js).
To secure the request with a signature, a
Digest
header is included that contains a SHA-512 hash of the request contents. TheSignature-Input
header is then generated that includes asig1
value ofsig1=(*request-target, *created, host, digest, signature-input);
, and that string is signed with the user's common public signing key usingnacl.sign.detached()
.The server then retrieves the
keyID
from theSignature-Input
header, matches it to an existing user, and verifies that the values and signature match in order to authenticate the request.
Security Related Third Party Libraries
Client Side (Web App / birdcalls.app)
TweetNaCl.js https://github.com/dchest/tweetnacl-js
The core encryption library used to generate secure random data, encrypt, decrypt, sign, and verify. Internally usesx25519-xsalsa20-poly1305
for public key / asymmetric encryption,xsalsa20-poly1305
for secret key / symmetric encryption, anded25519
for public key signatures. For generating random data, TweetNaCl.js useswindow.crypto.getRandomValues
to generate secure random data.Thinbus Javascript Secure Remote Password (SRP) https://github.com/simbo1905/thinbus-srp-npm
Used to negotiate SRP login with the server without providing the server with the (derived) login password.BIP39 https://github.com/bitcoinjs/bip39
Used to convert between recovery phrases and master key.Futoin HKDF https://github.com/futoin/util-js-hkdf
Used to derive sub-keys from the account's mater key.crypto-js https://github.com/brix/crypto-js
Used for SHA256 and HMAC generation / verification, mostly to hash namespaced strings so that the strings don't collide between users.zxcvbn https://github.com/dropbox/zxcvbn
Used to estimate password strength and prompt the user for tips on creating better passwords. If the users' passwords do not meet some minimal criteria, they are not permitted to use it.
Server Side
Laravel
crypt::
https://laravel.com/docs/encryption
To encrypt / decrypt / verify data server side. Internally this library usesAES-256-CBC
with a MAC for verification.Thinbus SRP PHP https://bitbucket.org/simon_massey/thinbus-php
Used for server side SRP negotiation with the client during login.
Privacy
What the Server Knows
The following is a list of all the information the server knows (or can determine) about users registered on the system.
Username
Provided by the user during signup.Email Address
Provided by the user during signup.Public Master Key
Generated from the user's recovery phrase during signup and used as a global account ID, to sign sub-keys, and to authenticate account change requests (like username/password changes).Public Common Signing Key
Generated from the user's recovery phrase during signup and used to authenticate API requests and to sign data stored on the server to prove ownership by this account.Public Common Encryption Key
Generated from the user's recovery phrase during signup and used to verify the integrity of data encrypted by the user (without being able to decrypt it).The time, length, and number of calls you created.
The number of devices that joined a call and when they joined and left the call.
Which encrypted files you've stored on the server (but not the contents of them).
IP addresses, device types of visitors to our site (web server logs) We do not match these up to user accounts, but in theory we could.
What the Server Does NOT Know
Plain Text Password
Account Seed
Recovery Phrase
Private Keys
Plain text contents of the bootstrap file
Keys used to encrypt the bootstrap file
Contents of files you've stored on the server
The contents of your calls
Who owns the devices that joined a call