Last active
August 5, 2019 14:41
-
-
Save Sc00bz/ef0951ab98e8e1bac4810f65a42eab1a to your computer and use it in GitHub Desktop.
Description of BSPAKE with all optional features and implementation ways explicitly pointed out
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BSPAKE | |
For an easier read that glosses over a few details see: | |
https://gist.github.com/Sc00bz/09b5836923ad986921b905723b0d0c02 | |
Both have: | |
G = generator | |
idS = server identity | |
H() is a KDF or hash that serializes the inputs and outputs enough bits. | |
hashToPoint() is either Elligator or SWU depending on curve. | |
pwKdf() is a password KDF like Argon2, scrypt, etc. | |
The modifiers are for generating different keys from a base key. These | |
could be implemented as info strings for HKDF or used as an offset | |
from large output like "a || b || c || d = H(baseKey)" or | |
"a || b || c || d = baseKey". | |
For authModifier and optional encryptModifier, the output from pwKdf() | |
should be passed through a KDF because of PBKDF2's footgun. Each hash | |
size block of output runs through the full amount of work. An | |
attacker, that dumps the database, would generate the block that | |
overlaps the most with k3 or block(s) for the authKey. This gives the | |
attacker an advantage of doing much less work to check a password. | |
For clientModifier, serverModifier, keyModifier, and verifyModifier, | |
another way to implement this is like: | |
v = H(authKey, verifyModifier) | |
k3 = H(authKey, keyModifier) | |
clientBlind = H(k3, clientModifier) | |
serverBlind = H(k3, serverModifier) | |
Thus you can give the server k3 and v*G. Then the server can choose | |
to expand k3 on the fly or expand and save BlindC and BlindS. | |
extraKeyData is any already established or co-established session key. | |
Such as a TLS session key. | |
Client has: | |
idC = client identity | |
secretSalt = an optional secret salt stored on the client's device | |
Server has these for "idC": | |
salt | |
settings | |
BlindC = hashToPoint(H(authKey, clientModifier)) | |
BlindS = hashToPoint(H(authKey, serverModifier)) | |
k3 = H(authKey, keyModifier) | |
V = H(authKey, verifyModifier) * G | |
C: r = random() | |
C: R = r * hashToPoint(H(password, idC, idS)) | |
C->S: idC, R | |
S: b = random() | |
S: B = b * G + BlindS | |
S: R' = H(salt) * R | |
C<-S: B, R', settings | |
C: BlindSalt = (1/r) * R' | |
C: pwKey = pwKdf(password, BlindSalt, 1[secretSalt], idC, idS, settings) | |
C: authKey = H(pwKey, authModifier) | |
C: a = random() | |
C: A = a * G + hashToPoint(H(authKey, clientModifier)) | |
C: B' = B - hashToPoint(H(authKey, serverModifier)) | |
C: k3 = H(authKey, keyModifier) | |
C: v = H(authKey, verifyModifier) | |
C: K_c = H(idC, idS, A, B, a * B', k3, v * B') | |
C: verifierC_c = H(K_c, verifyCModifier) | |
C: 2[verifierS_c = H(K_c, verifySModifier)] | |
C: 3[keyC = H(K_c, extraKeyData, encryptCModifier)] | |
C: 4[keyS = H(K_c, extraKeyData, encryptSModifier)] | |
C: 3[encryptedDataC = encrypt(dataC, keyC)] | |
C->S: A, verifierC_c, 3[encryptedDataC] | |
S: A' = A - BlindC | |
S: K_s = H(idC, idS, A, B, b * A', k3, b * V) | |
S: verifierC_s = H(K_s, verifyCModifier) | |
S: Checks verifierC_c == verifierC_s | |
S: 2[verifierS_s = H(K_s, verifySModifier)] | |
S: 3[keyC = H(K_s, extraKeyData, encryptCModifier)] | |
S: 4[keyS = H(K_s, extraKeyData, encryptSModifier)] | |
S: 4[encryptedDataS = encrypt(dataS, keyS)] | |
C<-S: 2[verifierS_s], 4[encryptedDataS] | |
C: 2[Checks verifierS_c == verifierS_s] | |
C: 5[secretKey = H(pwKey, encryptModifier)] | |
#[This denotes optional steps that are needed for the numbered "#" feature]. If | |
you don't need encryption keys don't do 3 and 4. Don't need server auth skip 2. | |
Not authenticating to an encrypted service skip 5. Don't have a secretSalt skip | |
1. | |
On success K_c == K_s, thus derived verifiers and encryption keys are the same. | |
When receiving a point, you must check it is valid and not a low order point. | |
After blinding and unblinding, check the point is not the point at infinity. | |
When using H() or random() to generate a scalar, you should generate a larger | |
value and modulo by one less than the order then add 1. This makes sure it is | |
uniformly distributed and not zero. Similar should be done for H() when | |
generating fields to avoid bad values. | |
Note "H(salt) * R" vs "salt * R", this is so you don't need to store a large | |
salt. A salt of just 128 to 256 bits is fine once expanded to the required | |
length. | |
Note client can do "secretKey = H(pwKey, encryptModifier)" to generate an | |
encryption key that only the client knows. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment