package domain import ( "crypto/hmac" "crypto/sha256" "crypto/subtle" "encoding/base64" "encoding/json" "errors" "strings" "time" "git.cin.su/m/Assistant/contracts/identity" ) var ErrInvalidCreatorSession = errors.New("invalid creator session") func IssueCreatorSession(secret []byte, now time.Time, ttl time.Duration) (string, identity.CreatorSession, error) { if len(secret) < 32 || ttl <= 0 { return "", identity.CreatorSession{}, ErrInvalidCreatorSession } session := identity.CreatorSession{ActorID: "domovoy-creator-actor", PrincipalID: "domovoy-creator-principal", ExpiresAtUnix: now.Add(ttl).Unix()} payload, err := json.Marshal(session) if err != nil { return "", identity.CreatorSession{}, err } encoded := base64.RawURLEncoding.EncodeToString(payload) return encoded + "." + signature(secret, encoded), session, nil } func ValidateCreatorSession(secret []byte, token string, now time.Time) (identity.CreatorSession, error) { parts := strings.Split(token, ".") if len(parts) != 2 || len(secret) < 32 || subtle.ConstantTimeCompare([]byte(signature(secret, parts[0])), []byte(parts[1])) != 1 { return identity.CreatorSession{}, ErrInvalidCreatorSession } payload, err := base64.RawURLEncoding.DecodeString(parts[0]) if err != nil { return identity.CreatorSession{}, ErrInvalidCreatorSession } var session identity.CreatorSession if json.Unmarshal(payload, &session) != nil || session.ActorID != "domovoy-creator-actor" || session.PrincipalID != "domovoy-creator-principal" || now.Unix() >= session.ExpiresAtUnix { return identity.CreatorSession{}, ErrInvalidCreatorSession } return session, nil } func BootstrapMatches(expected, supplied string) bool { return expected != "" && supplied != "" && subtle.ConstantTimeCompare([]byte(expected), []byte(supplied)) == 1 } func CreatorCredentialsMatch(expectedUsername, expectedPassword string, supplied identity.CreatorCredentials) bool { if expectedUsername == "" || expectedPassword == "" || supplied.Username == "" || supplied.Password == "" { return false } usernameMatches := subtle.ConstantTimeCompare([]byte(expectedUsername), []byte(supplied.Username)) passwordMatches := subtle.ConstantTimeCompare([]byte(expectedPassword), []byte(supplied.Password)) return usernameMatches&passwordMatches == 1 } func signature(secret []byte, payload string) string { mac := hmac.New(sha256.New, secret) mac.Write([]byte(payload)) return base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) }