perf: sync protocol and core hardening updates
This commit is contained in:
parent
152fed3b87
commit
4390ebf5a9
283 changed files with 29231 additions and 2295 deletions
67
internal/store/login_code_delivery.go
Normal file
67
internal/store/login_code_delivery.go
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPhoneCodeHashBytes = 512
|
||||
maxLoginCodeBytes = 64
|
||||
)
|
||||
|
||||
// LoginCodeDeliveryStore atomically creates the recipient message box, dialog,
|
||||
// account update event and online-dispatch task for a 777000 login code.
|
||||
type LoginCodeDeliveryStore interface {
|
||||
DeliverLoginCodeMessage(ctx context.Context, req domain.LoginCodeDeliveryRequest) (domain.LoginCodeDeliveryResult, error)
|
||||
}
|
||||
|
||||
// LoginCodeDeliveryKey is the only phone-code-hash representation permitted at
|
||||
// rest. The raw phone_code_hash stays at the auth/store call boundary.
|
||||
func LoginCodeDeliveryKey(phoneCodeHash string) ([sha256.Size]byte, error) {
|
||||
if phoneCodeHash == "" || len(phoneCodeHash) > maxPhoneCodeHashBytes {
|
||||
return [sha256.Size]byte{}, domain.ErrLoginCodeDeliveryInvalid
|
||||
}
|
||||
return sha256.Sum256([]byte(phoneCodeHash)), nil
|
||||
}
|
||||
|
||||
// LoginCodeFingerprint binds a delivery receipt to its immutable secret code.
|
||||
// It is keyed by the high-entropy raw phone_code_hash, which is never stored;
|
||||
// unlike a bare digest of a short login code this cannot be brute-forced from
|
||||
// the compact receipt alone after the message itself has been deleted.
|
||||
func LoginCodeFingerprint(phoneCodeHash, code string) ([sha256.Size]byte, error) {
|
||||
if phoneCodeHash == "" || len(phoneCodeHash) > maxPhoneCodeHashBytes || code == "" || len(code) > maxLoginCodeBytes {
|
||||
return [sha256.Size]byte{}, domain.ErrLoginCodeDeliveryInvalid
|
||||
}
|
||||
mac := hmac.New(sha256.New, []byte(phoneCodeHash))
|
||||
_, _ = mac.Write([]byte(code))
|
||||
var fingerprint [sha256.Size]byte
|
||||
copy(fingerprint[:], mac.Sum(nil))
|
||||
return fingerprint, nil
|
||||
}
|
||||
|
||||
func SameLoginCodeFingerprint(stored []byte, expected [sha256.Size]byte) bool {
|
||||
return len(stored) == sha256.Size && subtle.ConstantTimeCompare(stored, expected[:]) == 1
|
||||
}
|
||||
|
||||
// RestoreLoginCodeDeliveryMessage reconstructs the immutable first result from
|
||||
// a compact receipt. The secret code is not duplicated in the receipt: exact
|
||||
// replay has already proven the supplied code fingerprint matches.
|
||||
func RestoreLoginCodeDeliveryMessage(userID int64, code string, date int, privateMessageID int64, messageBoxID, pts int) (domain.Message, error) {
|
||||
if privateMessageID <= 0 || messageBoxID <= 0 || messageBoxID > domain.MaxMessageBoxID || pts <= 0 {
|
||||
return domain.Message{}, fmt.Errorf("restore login code delivery: %w: uid=%d box=%d pts=%d", domain.ErrLoginCodeDeliveryInvalid, privateMessageID, messageBoxID, pts)
|
||||
}
|
||||
msg, err := domain.OfficialLoginCodeMessage(userID, code, date)
|
||||
if err != nil {
|
||||
return domain.Message{}, err
|
||||
}
|
||||
msg.ID = messageBoxID
|
||||
msg.UID = privateMessageID
|
||||
msg.Pts = pts
|
||||
return msg, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue