Ctrl K
ComunidadGuías…

internal/domain/auth_delivery_report.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package domain

import (
	"crypto/sha256"
	"encoding/json"
	"errors"
	"time"
	"unicode/utf8"
)

const (
	MaxAuthDeliveryMNCBytes           = 8
	MaxAuthDeliveryClientTypeBytes    = 32
	MaxAuthDeliveryIDBytes            = 128
	MaxAuthDeliveryReportsPerHour     = 10
	MaxAuthDeliveryReportsPerPhoneDay = 20
)

var (
	ErrAuthDeliveryReportInvalid = errors.New("auth delivery report invalid")
	ErrAuthDeliveryRateLimited   = errors.New("auth delivery report rate limited")
)

// AuthDeliveryReport is operational delivery telemetry, not an abuse report.
// It deliberately stores only hashes of the phone and phone_code_hash and
// never stores the authentication code.
type AuthDeliveryReport struct {
	ID           int64
	AuthKeyID    [8]byte
	SessionID    int64
	ClientType   string
	PhoneHash    [sha256.Size]byte
	CodeHash     [sha256.Size]byte
	IssuedUserID int64
	DeliveryID   string
	Channel      AuthCodeDeliveryKind
	MNC          string
	Fingerprint  [sha256.Size]byte
	CreatedAt    time.Time
}

type AuthMissingCodeReportRequest struct {
	AuthKeyID     [8]byte
	SessionID     int64
	ClientType    string
	Phone         string
	PhoneCodeHash string
	MNC           string
	CreatedAt     time.Time
}

func NewAuthDeliveryReport(authKeyID [8]byte, sessionID int64, clientType, phone, phoneCodeHash string, issuedUserID int64, deliveryID string, channel AuthCodeDeliveryKind, mnc string, createdAt time.Time) (AuthDeliveryReport, error) {
	report := AuthDeliveryReport{
		AuthKeyID: authKeyID, SessionID: sessionID, ClientType: clientType,
		PhoneHash: sha256.Sum256([]byte(phone)), CodeHash: sha256.Sum256([]byte(phoneCodeHash)),
		IssuedUserID: issuedUserID, DeliveryID: deliveryID,
		Channel: channel, MNC: mnc, CreatedAt: createdAt,
	}
	raw, err := json.Marshal(struct {
		Version    int
		AuthKeyID  [8]byte
		SessionID  int64
		PhoneHash  [sha256.Size]byte
		CodeHash   [sha256.Size]byte
		DeliveryID string
		Channel    AuthCodeDeliveryKind
		MNC        string
	}{
		Version: 1, AuthKeyID: authKeyID, SessionID: sessionID,
		PhoneHash: report.PhoneHash, CodeHash: report.CodeHash,
		DeliveryID: deliveryID, Channel: channel, MNC: mnc,
	})
	if err != nil {
		return AuthDeliveryReport{}, ErrAuthDeliveryReportInvalid
	}
	report.Fingerprint = sha256.Sum256(raw)
	if err := report.Validate(); err != nil {
		return AuthDeliveryReport{}, err
	}
	return report, nil
}

func (r AuthDeliveryReport) Validate() error {
	if r.ID < 0 || r.AuthKeyID == ([8]byte{}) || r.SessionID == 0 ||
		r.PhoneHash == ([sha256.Size]byte{}) || r.CodeHash == ([sha256.Size]byte{}) ||
		r.Fingerprint == ([sha256.Size]byte{}) || r.IssuedUserID < 0 ||
		len(r.ClientType) > MaxAuthDeliveryClientTypeBytes || !utf8.ValidString(r.ClientType) ||
		len(r.DeliveryID) > MaxAuthDeliveryIDBytes || !utf8.ValidString(r.DeliveryID) ||
		!validAuthDeliveryChannel(r.Channel) || !validMNC(r.MNC) || r.CreatedAt.IsZero() {
		return ErrAuthDeliveryReportInvalid
	}
	return nil
}

func validAuthDeliveryChannel(channel AuthCodeDeliveryKind) bool {
	switch channel {
	case AuthCodeDeliveryPhone, AuthCodeDeliverySMS:
		return true
	default:
		return false
	}
}

func validMNC(mnc string) bool {
	if len(mnc) > MaxAuthDeliveryMNCBytes {
		return false
	}
	for _, r := range mnc {
		if r < '0' || r > '9' {
			return false
		}
	}
	return true
}