Skip to content

Commit

Permalink
feat(auth): add methods for updating and resetting password
Browse files Browse the repository at this point in the history
  • Loading branch information
tdakkota committed Jan 29, 2022
1 parent dc9347d commit c2a6619
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 26 deletions.
10 changes: 3 additions & 7 deletions internal/crypto/cipher_decrypt_test.go
Expand Up @@ -11,13 +11,9 @@ import (
"github.com/gotd/td/internal/testutil"
)

type Zero struct{}

func (Zero) Read(p []byte) (n int, err error) { return len(p), nil }

func TestDecrypt(t *testing.T) {
// Test vector from grammers.
c := NewClientCipher(Zero{})
c := NewClientCipher(testutil.ZeroRand{})
var msg EncryptedMessage
b := &bin.Buffer{Buf: []byte{
122, 113, 131, 194, 193, 14, 79, 77, 249, 69, 250, 154, 154, 189, 53, 231, 195, 132,
Expand Down Expand Up @@ -57,8 +53,8 @@ func TestCipher_Decrypt(t *testing.T) {
t.Fatal(err)
}

c := NewClientCipher(Zero{})
s := NewServerCipher(Zero{})
c := NewClientCipher(testutil.ZeroRand{})
s := NewServerCipher(testutil.ZeroRand{})
tests := []struct {
name string
data []byte
Expand Down
8 changes: 3 additions & 5 deletions internal/crypto/srp/new_hash_test.go
Expand Up @@ -4,11 +4,9 @@ import (
"testing"

"github.com/stretchr/testify/require"
)

type Zero struct{}

func (Zero) Read(p []byte) (n int, err error) { return len(p), nil }
"github.com/gotd/td/internal/testutil"
)

func TestSRP_NewHash(t *testing.T) {
password := []uint8{
Expand Down Expand Up @@ -66,7 +64,7 @@ func TestSRP_NewHash(t *testing.T) {
}

a := require.New(t)
s := NewSRP(Zero{})
s := NewSRP(testutil.ZeroRand{})
hash, newSalt, err := s.NewHash(password, i)
a.NoError(err)
a.Equal(expectedHash, hash)
Expand Down
2 changes: 1 addition & 1 deletion internal/crypto/srp/srp_test.go
Expand Up @@ -62,7 +62,7 @@ func TestSRP(t *testing.T) {
}
for i := range tests {
tcase := tests[i]
t.Run(fmt.Sprintf("#%v", i), func(t *testing.T) {
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
random := setByte(256, 1)
srp := NewSRP(rand.Reader)
got, err := srp.Hash(tcase.args.password, tcase.args.srpB, random, tcase.args.mp)
Expand Down
7 changes: 2 additions & 5 deletions internal/mtproto/handle_message_fuzz.go
Expand Up @@ -13,14 +13,11 @@ import (
"github.com/gotd/td/internal/mt"
"github.com/gotd/td/internal/proto"
"github.com/gotd/td/internal/rpc"
"github.com/gotd/td/internal/testutil"
"github.com/gotd/td/internal/tmap"
"github.com/gotd/td/tg"
)

type Zero struct{}

func (Zero) Read(p []byte) (n int, err error) { return len(p), nil }

type fuzzHandler struct {
types *tmap.Constructor
}
Expand Down Expand Up @@ -67,7 +64,7 @@ func init() {
),
}
c := &Conn{
rand: Zero{},
rand: testutil.ZeroRand{},
rpc: rpc.New(rpc.NopSend, rpc.Options{}),
log: zap.NewNop(),
messageID: proto.NewMessageIDGen(time.Now),
Expand Down
4 changes: 2 additions & 2 deletions internal/mtproto/zero_test.go
Expand Up @@ -3,6 +3,6 @@

package mtproto

type Zero struct{}
import "github.com/gotd/td/internal/testutil"

func (Zero) Read(p []byte) (n int, err error) { return len(p), nil }
type Zero = testutil.ZeroRand
6 changes: 6 additions & 0 deletions internal/testutil/rand.go
Expand Up @@ -5,6 +5,12 @@ import (
"math/rand"
)

// ZeroRand is zero random source.
type ZeroRand struct{}

// Read implements io.Reader.
func (ZeroRand) Read(p []byte) (n int, err error) { return len(p), nil }

func randSeed(data []byte) int64 {
if len(data) == 0 {
return 0
Expand Down
72 changes: 66 additions & 6 deletions telegram/auth/password.go
Expand Up @@ -2,6 +2,8 @@ package auth

import (
"context"
"fmt"
"time"

"github.com/go-faster/errors"

Expand Down Expand Up @@ -62,11 +64,21 @@ var (
emptyPassword tg.InputCheckPasswordSRPClass = &tg.InputCheckPasswordEmpty{}
)

// UpdatePassword sets new password for this account.
// UpdatePasswordOptions is options structure for UpdatePassword.
type UpdatePasswordOptions struct {
// Hint is new password hint.
Hint string
// Password is password callback.
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,
hint, newPassword string,
pass func(ctx context.Context) (string, error),
newPassword string,
opts UpdatePasswordOptions,
) error {
p, err := c.api.AccountGetPassword(ctx)
if err != nil {
Expand All @@ -85,11 +97,11 @@ func (c *Client) UpdatePassword(

var old = emptyPassword
if p.HasPassword {
if pass == nil {
if opts.Password == nil {
return ErrPasswordNotProvided
}

oldPassword, err := pass(ctx)
oldPassword, err := opts.Password(ctx)
if err != nil {
return errors.Wrap(err, "get password")
}
Expand All @@ -106,10 +118,58 @@ func (c *Client) UpdatePassword(
NewSettings: tg.AccountPasswordInputSettings{
NewAlgo: algo,
NewPasswordHash: newHash,
Hint: hint,
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.
//
// 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
}

0 comments on commit c2a6619

Please sign in to comment.