You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
119 lines
2.8 KiB
119 lines
2.8 KiB
3 years ago
|
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)
|
||
|
}
|