SCRAM-SHA-1
A basic overview of the operation of this mechanism:
- The client sends the username that wants to authenticate as.
- The server sends back the salt for this user and the number of iterations (either generating them, or browsing them in his database for the given username).
- The client sets a password with the specified salt for a given number of iterations.
- The client sends the result back.
- The server performs a hash variation and sends the result back to the client, so the client can also verify that the server had a password / password hash.
The cryptographic algorithms you will need are SHA-1, HMAC with SHA-1, and PBKDF2 with SHA-1. You should see how to use them in your language / framework, as I do not recommend them to implement them from scratch.
More details
- First, normalize the password (using SASLprep ), this will be
normalizedPassword . This is necessary so that the UTF8 encoding cannot contain changes to the same password. - Select a random string (for example, 32 hexadecimal bytes). This will be
clientNonce . initialMessage - "n=" .. username .. ",r=" .. clientNonce (I use .. to concatenate strings).The client adds the GS2 header ( "n,," ) to the initial Message and base64 encodes the result. He sends this as his first message:
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1"> biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl </auth>
The server answers the call. The call data is encoded by base64:
<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY= </challenge>
Client base64 decodes it:
r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
The client analyzes this:
r= This is serverNonce . The client MUST ensure that it starts with the clientNonce that it sent in its original message.s= This is the salt encoding, base64 (yes, this is the base64 encoding twice!)i= This is the number of iterations, i .
The client calculates:
clientFinalMessageBare = "c=biws,r=" .. serverNonce saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i) clientKey = HMAC-SHA-1(saltedPassword, "Client Key") storedKey = SHA-1(clientKey) authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare clientSignature = HMAC-SHA-1(storedKey, authMessage) clientProof = clientKey XOR clientSignature serverKey = HMAC-SHA-1(saltedPassword, "Server Key") serverSignature = HMAC-SHA-1(serverKey, authMessage) clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
The base64 client encodes clientFinalMessage and sends it as a response:
<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0= </response>
If all goes well, you will receive a response from <success> from the server:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289 </success>
Base64 base decoding contains:
v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
The client MUST verify that the value of v is base64 encoded by serverSignature .
Additionally
This is the basic version of the algorithm. You can expand it to do:
- Linking channels. This mixes some information from the TLS connection with the procedure to prevent MitM attacks.
Hashing If the server always sends the same salt and i values, then the client can only store saltedPassword instead of the user password. This is more secure (since the client does not need to store a password, it is simply difficult to cancel the salty hash) and faster, since the client does not need to do all the key stretching every time.
The server can also use hashed storage: the server can only store salt , i , storedKey and serverKey . Read more about it here .
- Perhaps the addition of SCRAM-SHA-256 (although server support seems to be non-existent).
Trap
Some common mistakes:
- Do not think about the length of nonces or
salt (although if you create them, make sure they are long enough and cryptographically random). salt is encoded by base64 and can contain any data (embedded NUL s).- Not using SASLprep may work fine for users using ASCII passwords, but it can completely upset registration for people using other scripts.
- The
initialMessage authMessage does not include the GS2 header (in most cases it is "n,," ).
Test vectors
If you want to test your implementation, here are all the intermediate results for an example from the RFC:
Username: user
Password: pencil
The client creates a random value nonce fyko+d2lbbFgONRv9qkxdawL
Original post: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
Server generates random value nonce 3rfcNHYJY1ZVvWVs7j
Server responses: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
Salt (hex): 4125c247e43ab1e93c6dff76
Final client message bare: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Salty password (hexadecimal): 1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
Client key (hex): e234c47bf6c36696dd6d852b99aaa2ba26555728
Saved Key (hex): e9d94660c39d65c38fbad91c358f14da0eef2bd6
Error message: n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Client Signature (hex): 5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
Client Certificate (Hex): bf45fcbf7073d93d022466c94321745fe1c8e13b
Server key (hexadecimal): 0fe09258b3ac852ba502cc62ba903eaacdbf7d31
Server Signature (hex): ae617da6a57c4bbb2e0286568dae1d251905b0a4
The final client message: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
Final server message: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
Server Server Signature (hex): ae617da6a57c4bbb2e0286568dae1d251905b0a4
xnyhps
source share