package authenticator import ( "sync" "time" "git.wxccs.org/iceking2nd/winauth-go/internal/global" ) // Authenticator is the interface implemented by every concrete OTP type. // It deliberately mirrors the surface of the C# Authenticator base class // rather than introducing a wider/cleaner Go interface, so the port can // stay close to the original. type Authenticator interface { // Name returns a short identifier for logging ("google", "battlenet", ...). Name() string // CurrentCode returns the OTP that should be shown to the user right now. CurrentCode() (string, error) // SecretData returns the serialized form of the secret + parameters, // matching the field stored inside the original XML config under // . SecretData() string // SetSecretData reverses SecretData, populating the receiver. SetSecretData(value string) error // Sync re-aligns the server time offset using whatever network call the // concrete authenticator supports. Sync() error } // Base holds fields shared by every concrete authenticator. Embed it in // your subtype to inherit the field set and common helpers. type Base struct { SecretKey []byte CodeDigits int HMACType HMACType Period int ServerTimeDiff int64 // ms LastServerTime int64 // ms (Unix-millis here, not .NET ticks) mu sync.Mutex } // NewBase returns a Base seeded with the project-wide defaults. func NewBase() Base { return Base{ CodeDigits: DefaultCodeDigits, HMACType: HMACSHA1, Period: DefaultPeriod, } } // NowMillis is the milliseconds since the Unix epoch, equivalent to the // CurrentTime helper in the original C#. func NowMillis() int64 { return time.Now().UnixMilli() } // ServerTime returns the server's notion of "now" in milliseconds. func (b *Base) ServerTime() int64 { return NowMillis() + b.ServerTimeDiff } // CodeInterval returns the TOTP step number for the current server time. func (b *Base) CodeInterval() uint64 { if b.Period <= 0 { b.Period = DefaultPeriod } return uint64(b.ServerTime() / int64(b.Period*1000)) } // CalculateTOTP runs the TOTP algorithm using the receiver's fields. func (b *Base) CalculateTOTP() string { const fn = "internal.authenticator.Base.CalculateTOTP" digits := b.CodeDigits if digits == 0 { digits = DefaultCodeDigits } code := hotpCode(b.SecretKey, b.CodeInterval(), digits, b.HMACType) global.Log.WithField("func", fn). WithField("interval", b.CodeInterval()). WithField("digits", digits). Trace("computed TOTP") return code }