Authentication
Etebase uses a challenge-response zero-knowledge proof for authentication. The client asks the server for an authentication challenge using the authentication/login_challenge/
HTTP endpoint, and then responds with a signed message to prove it posses the authentication private key.
This makes it possible for Etebase clients to authenticate in a secure manner without needing to send the server any secret information. This ensures that even if the connection was eavesdropped, or the server compromised, an attacker wouldn't be able to impersonate the user, login as them, or gain access to any secret information.
Signup
In order to use an Etebase server clients first need to signup (create an account). To do this, they first securely generate their cryptographic material and then upload the non-secret parts of it to the server.
The clients need to generate the following data:
salt
: securely generated random bytesmainKey
: the user's main encryption key derived using thesalt
, and the user password.accountKey
: a randomly generated symmetric encryption key.authenticationKeypair
: a deterministically generated keypair derived from a key that's in turn derived frommainKey
.identityKeypair
: a randomly generated keypair that's used for establishing trust between Etebase users.
It then encrypts identityKeypair
and accountKey
using mainKey
into encryptedContent
.
It then uploads the SigupBody
to the server using the authentication/signup/
HTTP endpoint as follows:
User {
username: str,
email: str,
}
salt = randombytes(32)
mainKey = pwhash(password, salt)
accountKey = randombytes(32)
encryptedContent = encrypt(mainKey, concat(accountKey, identityKeypair.privkey))
loginPubkey = aunthenticationKeypair.pubkey
pubkey = identityKeypair.pubkey
SignupBody {
user: User,
salt: [u8],
loginPubkey: [u8],
pubkey: [u8],
encryptedContent: [u8],
}
Login
In order to login to Etebase, a client first asks the server for the login challenge using the authentication/login_challenge/
endpoint. The response contains salt
and an encrypted challenge
.
The client then uses salt
and the user's password to generate mainKey
and from the authenticationKeypair
. The client then proceeds to sign challenge
as well as some extra data using a LoginResponse
object with the following fields:
username
: the username this login request is for.challenge
: the challenge sent from the server.host
: the host of the server we are logging into.action
: the stringlogin
.
The server then validates LoginResponse
and if successful returns an AuthenticationToken
that can be used to interact with the API.
The LoginResponse
is uploaded to the authentication/login/
HTTP endpoint as follows:
LoginResponse {
username: str,
challenge: [u8],
host: str, // The server's hostname
action: str, // "login"
}
response = msgpack(loginResponse)
signature = sign(authenticationKeypair, response)
LoginBody {
response: [u8],
signature: [u8],
}
Password Change
A password change is very similar to login as described above.
In order to change a user's login password, a client first asks the server for a challenge
, and then responds with a signed response
. The only differences are in the fields included in response
.
The client first generates new mainKey
and authenticationKeypair
similar to the signup section above.
action
: is set to the stringchangePassword
.
And the following fields are added (similar to signup):
loginPubkey
: theauthenticationKeypair
's public part.encryptedContent
: the encryptedidentityKeypair
andaccountKey
like in signup.
The Response
is uploaded to the authentication/change_password/
HTTP endpoint as follows:
Response {
username: str,
challenge: [u8],
host: str, // The server's hostname
action: str, // "changePassword"
loginPubkey: [u8],
encryptedContent: [u8],
}
packedResponse = msgpack(response)
signature = sign(authenticationKeypair, packedResponse)
Body {
response: [u8],
signature: [u8],
}