-
-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #682 from tdakkota/feat/add-more-auth-methods
feat(auth): add methods for updating and resetting password
- Loading branch information
Showing
25 changed files
with
954 additions
and
180 deletions.
There are no files selected for viewing
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package srp | ||
|
||
import ( | ||
"crypto/sha256" | ||
"crypto/sha512" | ||
"math/big" | ||
|
||
"github.com/go-faster/errors" | ||
"golang.org/x/crypto/pbkdf2" | ||
) | ||
|
||
// Hash computes user password hash using parameters from server. | ||
// | ||
// See https://core.telegram.org/api/srp#checking-the-password-with-srp. | ||
func (s SRP) Hash(password, srpB, random []byte, i Input) (Answer, error) { | ||
p := s.bigFromBytes(i.P) | ||
if err := checkInput(i.G, p); err != nil { | ||
return Answer{}, errors.Wrap(err, "validate algo") | ||
} | ||
|
||
g := big.NewInt(int64(i.G)) | ||
// It is safe to use FillBytes directly because we know that 64-bit G always smaller than | ||
// 256-bit destination array. | ||
var gBytes [256]byte | ||
g.FillBytes(gBytes[:]) | ||
|
||
// random 2048-bit number a | ||
a := s.bigFromBytes(random) | ||
|
||
// `g_a = pow(g, a) mod p` | ||
ga, ok := s.pad256FromBig(s.bigExp(g, a, p)) | ||
if !ok { | ||
return Answer{}, errors.New("g_a is too big") | ||
} | ||
|
||
// `g_b = srp_B` | ||
gb := s.pad256(srpB) | ||
|
||
// `u = H(g_a | g_b)` | ||
u := s.bigFromBytes(s.hash(ga[:], gb[:])) | ||
|
||
// `x = PH2(password, salt1, salt2)` | ||
// `v = pow(g, x) mod p` | ||
x, v := s.computeXV(password, i.Salt1, i.Salt2, g, p) | ||
|
||
// `k = (k * v) mod p` | ||
k := s.bigFromBytes(s.hash(i.P, gBytes[:])) | ||
|
||
// `k_v = (k * v) % p` | ||
kv := k.Mul(k, v).Mod(k, p) | ||
|
||
// `t = (g_b - k_v) % p` | ||
t := s.bigFromBytes(srpB) | ||
if t.Sub(t, kv).Cmp(big.NewInt(0)) == -1 { | ||
t.Add(t, p) | ||
} | ||
|
||
// `s_a = pow(t, a + u * x) mod p` | ||
sa, ok := s.pad256FromBig(s.bigExp(t, u.Mul(u, x).Add(u, a), p)) | ||
if !ok { | ||
return Answer{}, errors.New("s_a is too big") | ||
} | ||
|
||
// `k_a = H(s_a)` | ||
ka := sha256.Sum256(sa[:]) | ||
|
||
// `M1 = H(H(p) xor H(g) | H2(salt1) | H2(salt2) | g_a | g_b | k_a)` | ||
xorHpHg := xor32(sha256.Sum256(i.P), sha256.Sum256(gBytes[:])) | ||
M1 := s.hash( | ||
xorHpHg[:], | ||
s.hash(i.Salt1), | ||
s.hash(i.Salt2), | ||
ga[:], | ||
gb[:], | ||
ka[:], | ||
) | ||
|
||
return Answer{ | ||
A: ga[:], | ||
M1: M1, | ||
}, nil | ||
} | ||
|
||
// The main hashing function H is sha256: | ||
// | ||
// H(data) := sha256(data) | ||
func (s SRP) hash(data ...[]byte) []byte { | ||
h := sha256.New() | ||
for i := range data { | ||
h.Write(data[i]) | ||
} | ||
return h.Sum(nil) | ||
} | ||
|
||
// The salting hashing function SH is defined as follows: | ||
// | ||
// SH(data, salt) := H(salt | data | salt) | ||
func (s SRP) saltHash(data, salt []byte) []byte { | ||
return s.hash(salt, data, salt) | ||
} | ||
|
||
// The primary password hashing function is defined as follows: | ||
// | ||
// PH1(password, salt1, salt2) := SH(SH(password, salt1), salt2) | ||
func (s SRP) primary(password, salt1, salt2 []byte) []byte { | ||
return s.saltHash(s.saltHash(password, salt1), salt2) | ||
} | ||
|
||
// The secondary password hashing function is defined as follows: | ||
// | ||
// PH2(password, salt1, salt2) := SH(pbkdf2(sha512, PH1(password, salt1, salt2), salt1, 100000), salt2) | ||
func (s SRP) secondary(password, salt1, salt2 []byte) []byte { | ||
return s.saltHash(s.pbkdf2(s.primary(password, salt1, salt2), salt1, 100000), salt2) | ||
} | ||
|
||
func (s SRP) pbkdf2(ph1, salt1 []byte, n int) []byte { | ||
return pbkdf2.Key(ph1, salt1, n, 64, sha512.New) | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package srp | ||
|
||
import ( | ||
"io" | ||
"math/big" | ||
|
||
"github.com/go-faster/errors" | ||
) | ||
|
||
// computeXV computes following numbers | ||
// | ||
// `x = PH2(password, salt1, salt2)` | ||
// `v = pow(g, x) mod p` | ||
// | ||
// TDLib uses terms `clientSalt` for `salt1` and `serverSalt` for `salt2`. | ||
func (s SRP) computeXV(password, clientSalt, serverSalt []byte, g, p *big.Int) (x, v *big.Int) { | ||
// `x = PH2(password, salt1, salt2)` | ||
x = new(big.Int).SetBytes(s.secondary(password, clientSalt, serverSalt)) | ||
// `v = pow(g, x) mod p` | ||
v = new(big.Int).Exp(g, x, p) | ||
return x, v | ||
} | ||
|
||
// NewHash computes new user password hash using parameters from server. | ||
// | ||
// See https://core.telegram.org/api/srp#setting-a-new-2fa-password. | ||
// | ||
// TDLib implementation: | ||
// See https://github.com/tdlib/td/blob/fa8feefed70d64271945e9d5fd010b957d93c8cd/td/telegram/PasswordManager.cpp#L57. | ||
// | ||
// TDesktop implementation: | ||
// See https://github.com/telegramdesktop/tdesktop/blob/v3.4.8/Telegram/SourceFiles/core/core_cloud_password.cpp#L68. | ||
func (s SRP) NewHash(password []byte, i Input) (hash, newSalt []byte, _ error) { | ||
// Generate a new new_password_hash using the KDF algorithm specified in the new_settings, | ||
// just append 32 sufficiently random bytes to the salt1, first. Proceed as for checking passwords with SRP, | ||
// just stop at the generation of the v parameter, and use it as new_password_hash: | ||
p := new(big.Int).SetBytes(i.P) | ||
if err := checkInput(i.G, p); err != nil { | ||
return nil, nil, errors.Wrap(err, "validate algo") | ||
} | ||
|
||
// Make a copy. | ||
newClientSalt := append([]byte(nil), i.Salt1...) | ||
newClientSalt = append(newClientSalt, make([]byte, 32)...) | ||
// ... append 32 sufficiently random bytes to the salt1 ... | ||
if _, err := io.ReadFull(s.random, newClientSalt[len(newClientSalt)-32:]); err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
_, v := s.computeXV(password, newClientSalt, i.Salt2, big.NewInt(int64(i.G)), p) | ||
// As usual in big endian form, padded to 2048 bits. | ||
padded, _ := s.pad256FromBig(v) | ||
return padded[:], newClientSalt, nil | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package srp | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/gotd/td/internal/testutil" | ||
) | ||
|
||
func TestSRP_NewHash(t *testing.T) { | ||
password := []uint8{ | ||
110, 101, 119, 80, 97, 115, 115, 119, 111, 114, 100, | ||
} | ||
i := Input{ | ||
Salt1: []uint8{ | ||
230, 200, 149, 125, 223, 152, 141, 72, | ||
}, | ||
Salt2: []uint8{ | ||
159, 99, 68, 130, 43, 9, 108, 255, 135, 239, 164, 38, 245, 120, 87, 182, | ||
}, | ||
G: 3, | ||
P: []uint8{ | ||
199, 28, 174, 185, 198, 177, 201, 4, 142, 108, 82, 47, 112, 241, 63, 115, | ||
152, 13, 64, 35, 142, 62, 33, 193, 73, 52, 208, 55, 86, 61, 147, 15, | ||
72, 25, 138, 10, 167, 193, 64, 88, 34, 148, 147, 210, 37, 48, 244, 219, | ||
250, 51, 111, 110, 10, 201, 37, 19, 149, 67, 174, 212, 76, 206, 124, 55, | ||
32, 253, 81, 246, 148, 88, 112, 90, 198, 140, 212, 254, 107, 107, 19, 171, | ||
220, 151, 70, 81, 41, 105, 50, 132, 84, 241, 143, 175, 140, 89, 95, 100, | ||
36, 119, 254, 150, 187, 42, 148, 29, 91, 205, 29, 74, 200, 204, 73, 136, | ||
7, 8, 250, 155, 55, 142, 60, 79, 58, 144, 96, 190, 230, 124, 249, 164, | ||
164, 166, 149, 129, 16, 81, 144, 126, 22, 39, 83, 181, 107, 15, 107, 65, | ||
13, 186, 116, 216, 168, 75, 42, 20, 179, 20, 78, 14, 241, 40, 71, 84, | ||
253, 23, 237, 149, 13, 89, 101, 180, 185, 221, 70, 88, 45, 177, 23, 141, | ||
22, 156, 107, 196, 101, 176, 214, 255, 156, 163, 146, 143, 239, 91, 154, 228, | ||
228, 24, 252, 21, 232, 62, 190, 160, 248, 127, 169, 255, 94, 237, 112, 5, | ||
13, 237, 40, 73, 244, 123, 249, 89, 217, 86, 133, 12, 233, 41, 133, 31, | ||
13, 129, 21, 246, 53, 177, 5, 238, 46, 78, 21, 208, 75, 36, 84, 191, | ||
111, 79, 173, 240, 52, 177, 4, 3, 17, 156, 216, 227, 185, 47, 204, 91, | ||
}, | ||
} | ||
expectedHash := []uint8{ | ||
24, 106, 193, 141, 204, 87, 144, 191, 107, 186, 33, 189, 149, 141, 55, 94, | ||
229, 72, 26, 240, 2, 158, 155, 215, 169, 198, 142, 201, 38, 189, 81, 150, | ||
216, 31, 140, 216, 181, 142, 224, 108, 138, 16, 173, 234, 204, 127, 86, 232, | ||
25, 255, 81, 72, 37, 222, 177, 91, 31, 173, 236, 106, 174, 23, 162, 68, | ||
203, 35, 72, 141, 23, 52, 156, 212, 38, 26, 139, 164, 218, 123, 156, 44, | ||
229, 196, 0, 20, 221, 158, 54, 39, 80, 172, 243, 172, 137, 184, 184, 245, | ||
198, 24, 240, 182, 165, 114, 195, 143, 255, 58, 85, 77, 136, 24, 160, 184, | ||
231, 182, 1, 94, 24, 54, 18, 138, 30, 78, 45, 92, 249, 151, 29, 29, | ||
208, 72, 170, 24, 29, 134, 17, 82, 234, 231, 21, 83, 150, 38, 128, 99, | ||
35, 135, 184, 154, 193, 134, 95, 222, 215, 200, 195, 218, 166, 78, 211, 141, | ||
194, 80, 54, 102, 63, 160, 207, 119, 72, 197, 46, 161, 156, 24, 126, 112, | ||
167, 82, 168, 5, 62, 64, 157, 72, 148, 33, 138, 66, 147, 147, 208, 51, | ||
130, 228, 30, 80, 183, 65, 91, 59, 138, 208, 146, 253, 7, 144, 248, 141, | ||
137, 78, 132, 220, 167, 143, 71, 244, 33, 137, 55, 215, 170, 153, 216, 140, | ||
135, 192, 155, 203, 141, 168, 144, 229, 53, 2, 102, 35, 206, 166, 252, 139, | ||
61, 37, 219, 112, 203, 66, 170, 164, 131, 35, 146, 125, 135, 168, 252, 241, | ||
} | ||
expectedNewSalt := []uint8{ | ||
230, 200, 149, 125, 223, 152, 141, 72, 0, 0, 0, 0, 0, 0, 0, 0, | ||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||
0, 0, 0, 0, 0, 0, 0, 0, | ||
} | ||
|
||
a := require.New(t) | ||
s := NewSRP(testutil.ZeroRand{}) | ||
hash, newSalt, err := s.NewHash(password, i) | ||
a.NoError(err) | ||
a.Equal(expectedHash, hash) | ||
a.Equal(expectedNewSalt, newSalt) | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package srp | ||
|
||
import ( | ||
"math/big" | ||
|
||
"github.com/gotd/td/internal/crypto" | ||
) | ||
|
||
func (s SRP) pad256FromBig(i *big.Int) (b [256]byte, r bool) { | ||
r = crypto.FillBytes(i, b[:]) | ||
return b, r | ||
} | ||
|
||
func (s SRP) pad256(b []byte) [256]byte { | ||
if len(b) >= 256 { | ||
return *(*[256]byte)(b[len(b)-256:]) | ||
} | ||
|
||
var tmp [256]byte | ||
copy(tmp[256-len(b):], b) | ||
|
||
return tmp | ||
} |
Oops, something went wrong.