Core modules (M1-M6): - pkg/protocol: message format, encoding, NAT type enums - pkg/config: server/client config structs, env vars, validation - pkg/auth: CRC64 token, TOTP gen/verify, one-time relay tokens - pkg/nat: UDP/TCP STUN client and server - pkg/signal: WSS message dispatch, sync request/response - pkg/punch: UDP/TCP hole punching + priority chain - pkg/mux: stream multiplexer (7B frame: StreamID+Flags+Len) - pkg/tunnel: mux-based port forwarding with stats - pkg/relay: relay manager with TOTP auth + session bridging - internal/server: signaling server (login/heartbeat/report/coordinator) - internal/client: client (NAT detect/login/punch/relay/reconnect) - cmd/inp2ps + cmd/inp2pc: main entrypoints with graceful shutdown All tests pass: 16 tests across 5 packages Code: 3559 lines core + 861 lines tests = 19 source files
93 lines
2.4 KiB
Go
93 lines
2.4 KiB
Go
// Package auth provides TOTP and token authentication for INP2P.
|
|
package auth
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash/crc64"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// TOTPStep is the time window in seconds for TOTP validity.
|
|
// A code is valid for ±1 step to allow for clock drift.
|
|
TOTPStep int64 = 60
|
|
)
|
|
|
|
var crcTable = crc64.MakeTable(crc64.ECMA)
|
|
|
|
// MakeToken generates a token from user+password using CRC64.
|
|
func MakeToken(user, password string) uint64 {
|
|
return crc64.Checksum([]byte(user+password), crcTable)
|
|
}
|
|
|
|
// GenTOTP generates a TOTP code for relay authentication.
|
|
func GenTOTP(token uint64, ts int64) uint64 {
|
|
step := ts / TOTPStep
|
|
buf := make([]byte, 16)
|
|
binary.BigEndian.PutUint64(buf[:8], token)
|
|
binary.BigEndian.PutUint64(buf[8:], uint64(step))
|
|
|
|
mac := hmac.New(sha256.New, buf[:8])
|
|
mac.Write(buf[8:])
|
|
sum := mac.Sum(nil)
|
|
|
|
return binary.BigEndian.Uint64(sum[:8])
|
|
}
|
|
|
|
// VerifyTOTP verifies a TOTP code with ±1 step tolerance.
|
|
func VerifyTOTP(code uint64, token uint64, ts int64) bool {
|
|
for delta := int64(-1); delta <= 1; delta++ {
|
|
expected := GenTOTP(token, ts+delta*TOTPStep)
|
|
if code == expected {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RelayToken generates a one-time relay token signed by the server.
|
|
// Used for cross-user super relay authentication.
|
|
type RelayToken struct {
|
|
SessionID string `json:"sessionID"`
|
|
From string `json:"from"`
|
|
To string `json:"to"`
|
|
Relay string `json:"relay"`
|
|
Expires int64 `json:"expires"`
|
|
Signature []byte `json:"signature"`
|
|
}
|
|
|
|
// SignRelayToken creates a signed one-time relay token.
|
|
func SignRelayToken(secret []byte, sessionID, from, to, relay string, ttl time.Duration) RelayToken {
|
|
rt := RelayToken{
|
|
SessionID: sessionID,
|
|
From: from,
|
|
To: to,
|
|
Relay: relay,
|
|
Expires: time.Now().Add(ttl).Unix(),
|
|
}
|
|
|
|
msg := fmt.Sprintf("%s:%s:%s:%s:%d", rt.SessionID, rt.From, rt.To, rt.Relay, rt.Expires)
|
|
mac := hmac.New(sha256.New, secret)
|
|
mac.Write([]byte(msg))
|
|
rt.Signature = mac.Sum(nil)
|
|
|
|
return rt
|
|
}
|
|
|
|
// VerifyRelayToken validates a signed relay token.
|
|
func VerifyRelayToken(secret []byte, rt RelayToken) bool {
|
|
if time.Now().Unix() > rt.Expires {
|
|
return false
|
|
}
|
|
|
|
msg := fmt.Sprintf("%s:%s:%s:%s:%d", rt.SessionID, rt.From, rt.To, rt.Relay, rt.Expires)
|
|
mac := hmac.New(sha256.New, secret)
|
|
mac.Write([]byte(msg))
|
|
expected := mac.Sum(nil)
|
|
|
|
return hmac.Equal(rt.Signature, expected)
|
|
}
|