move gotd fork into repo. (#111)
- update to latest telegram layer - remove some references to fields in tg.Entities that don't exist in the schema - originally added here: https://github.com/beeper/td/commit/820929062a2ba0104397bc01235ab58a9cff780e - referenced here - https://github.com/mautrix/telegramgo/commit/124f0967ed195b5a380c9bd02e170ada9710dde3 - https://github.com/mautrix/telegramgo/commit/4205047aab2e0639217148b5d125bfaab668bd8e
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-faster/errors"
|
||||
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/crypto"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/crypto/srp"
|
||||
"go.mau.fi/mautrix-telegram/pkg/gotd/tg"
|
||||
)
|
||||
|
||||
// PasswordHash computes password hash to log in.
|
||||
//
|
||||
// See https://core.telegram.org/api/srp#checking-the-password-with-srp.
|
||||
func PasswordHash(
|
||||
password []byte,
|
||||
srpID int64,
|
||||
srpB, secureRandom []byte,
|
||||
alg tg.PasswordKdfAlgoClass,
|
||||
) (*tg.InputCheckPasswordSRP, error) {
|
||||
s := srp.NewSRP(crypto.DefaultRand())
|
||||
|
||||
algo, ok := alg.(*tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unsupported algo: %T", alg)
|
||||
}
|
||||
|
||||
a, err := s.Hash(password, srpB, secureRandom, srp.Input(*algo))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create SRP answer")
|
||||
}
|
||||
|
||||
return &tg.InputCheckPasswordSRP{
|
||||
SRPID: srpID,
|
||||
A: a.A,
|
||||
M1: a.M1,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewPasswordHash computes new password hash to update password.
|
||||
//
|
||||
// Notice that NewPasswordHash mutates given alg.
|
||||
//
|
||||
// See https://core.telegram.org/api/srp#setting-a-new-2fa-password.
|
||||
func NewPasswordHash(
|
||||
password []byte,
|
||||
algo *tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow,
|
||||
) (hash []byte, _ error) {
|
||||
s := srp.NewSRP(crypto.DefaultRand())
|
||||
|
||||
hash, newSalt, err := s.NewHash(password, srp.Input(*algo))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create SRP answer")
|
||||
}
|
||||
algo.Salt1 = newSalt
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
var (
|
||||
emptyPassword tg.InputCheckPasswordSRPClass = &tg.InputCheckPasswordEmpty{}
|
||||
)
|
||||
|
||||
// UpdatePasswordOptions is options structure for UpdatePassword.
|
||||
type UpdatePasswordOptions struct {
|
||||
// Hint is new password hint.
|
||||
Hint string
|
||||
// Password is password callback.
|
||||
//
|
||||
// If password was requested and Password is nil, ErrPasswordNotProvided error will be returned.
|
||||
Password func(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
// UpdatePassword sets new cloud password for this account.
|
||||
//
|
||||
// See https://core.telegram.org/api/srp#setting-a-new-2fa-password.
|
||||
func (c *Client) UpdatePassword(
|
||||
ctx context.Context,
|
||||
newPassword string,
|
||||
opts UpdatePasswordOptions,
|
||||
) error {
|
||||
p, err := c.api.AccountGetPassword(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get SRP parameters")
|
||||
}
|
||||
|
||||
algo, ok := p.NewAlgo.(*tg.PasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow)
|
||||
if !ok {
|
||||
return errors.Errorf("unsupported algo: %T", p.NewAlgo)
|
||||
}
|
||||
|
||||
newHash, err := NewPasswordHash([]byte(newPassword), algo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "compute new password hash")
|
||||
}
|
||||
|
||||
var old = emptyPassword
|
||||
if p.HasPassword {
|
||||
if opts.Password == nil {
|
||||
return ErrPasswordNotProvided
|
||||
}
|
||||
|
||||
oldPassword, err := opts.Password(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get password")
|
||||
}
|
||||
|
||||
hash, err := PasswordHash([]byte(oldPassword), p.SRPID, p.SRPB, p.SecureRandom, p.CurrentAlgo)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "compute old password hash")
|
||||
}
|
||||
old = hash
|
||||
}
|
||||
|
||||
if _, err := c.api.AccountUpdatePasswordSettings(ctx, &tg.AccountUpdatePasswordSettingsRequest{
|
||||
Password: old,
|
||||
NewSettings: tg.AccountPasswordInputSettings{
|
||||
NewAlgo: algo,
|
||||
NewPasswordHash: newHash,
|
||||
Hint: opts.Hint,
|
||||
},
|
||||
}); err != nil {
|
||||
return errors.Wrap(err, "update password")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResetFailedWaitError reports that you recently requested a password reset that was cancel and need to wait until the
|
||||
// specified date before requesting another reset.
|
||||
type ResetFailedWaitError struct {
|
||||
Result tg.AccountResetPasswordFailedWait
|
||||
}
|
||||
|
||||
// Until returns time required to wait.
|
||||
func (r ResetFailedWaitError) Until() time.Duration {
|
||||
retryDate := time.Unix(int64(r.Result.RetryDate), 0)
|
||||
return time.Until(retryDate)
|
||||
}
|
||||
|
||||
// Error implements error.
|
||||
func (r *ResetFailedWaitError) Error() string {
|
||||
return fmt.Sprintf("wait to reset password (%s)", r.Until())
|
||||
}
|
||||
|
||||
// ResetPassword resets cloud password and returns time to wait until reset be performed.
|
||||
// If time is zero, password was successfully reset.
|
||||
//
|
||||
// May return ResetFailedWaitError.
|
||||
//
|
||||
// See https://core.telegram.org/api/srp#password-reset.
|
||||
func (c *Client) ResetPassword(ctx context.Context) (time.Time, error) {
|
||||
r, err := c.api.AccountResetPassword(ctx)
|
||||
if err != nil {
|
||||
return time.Time{}, errors.Wrap(err, "reset password")
|
||||
}
|
||||
switch v := r.(type) {
|
||||
case *tg.AccountResetPasswordFailedWait:
|
||||
return time.Time{}, &ResetFailedWaitError{Result: *v}
|
||||
case *tg.AccountResetPasswordRequestedWait:
|
||||
return time.Unix(int64(v.UntilDate), 0), nil
|
||||
case *tg.AccountResetPasswordOk:
|
||||
return time.Time{}, nil
|
||||
default:
|
||||
return time.Time{}, errors.Errorf("unexpected type %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
// CancelPasswordReset cancels password reset.
|
||||
//
|
||||
// See https://core.telegram.org/api/srp#password-reset.
|
||||
func (c *Client) CancelPasswordReset(ctx context.Context) error {
|
||||
if _, err := c.api.AccountDeclinePasswordReset(ctx); err != nil {
|
||||
return errors.Wrap(err, "cancel password reset")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user