From 92c641fb5d17995566b1975544915b181209c367 Mon Sep 17 00:00:00 2001 From: FreezB11 Date: Tue, 2 Jun 2026 04:01:25 +0530 Subject: [PATCH] go/login: add README for login and server packages Adds README.md to go/login and go/login/server documenting the package overview, key types, usage examples, and error types. Closes #63. --- go/login/README.md | 136 +++++++++++++++++++++++++++++++ go/login/server/README.md | 165 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 go/login/README.md create mode 100644 go/login/server/README.md diff --git a/go/login/README.md b/go/login/README.md new file mode 100644 index 00000000..bdcc6637 --- /dev/null +++ b/go/login/README.md @@ -0,0 +1,136 @@ +# go/login + +Package `login` implements the **client and server sides of the GLOME Login protocol** in Go. It builds on top of the [`glome`](../glome) package to handle URL construction, handshake parsing, tag verification, and authorization code validation as described in the [GLOME Login protocol spec](../../docs/glome-login.md). + +> **Note:** This API is Alpha and may be subject to breaking changes. + +## Overview + +GLOME Login is a challenge-response authentication mechanism. A client (e.g. a machine at a serial console) generates a URL encoding a *handshake* and a *message* (host identity + requested action). An operator forwards this URL to a GLOME Login server, which validates the request and returns an authorization code. The client verifies the code and grants access if it matches. + +This package provides Go types for both sides of that exchange: + +| Role | Type | Responsibility | +|--------|----------|-------------------------------------------------------------------| +| Client | `Client` | Build the challenge URL, validate the server's authorization code | +| Server | `Server` | Parse an incoming challenge URL, verify the client's tag | + +## Package Layout + +``` +go/login/ +├── login.go # Core types: Client, Server, URLResponse, Message, Handshake +└── server/ # Higher-level HTTP server framework (see go/login/server) +``` + +## Key Types + +### `Message` + +Encodes what is being authorized: a target host and an action. + +```go +type Message struct { + HostIDType string // optional type qualifier (e.g. "serial", "hostname") + HostID string // identity of the target host + Action string // action being requested (e.g. "shell/root") +} +``` + +`Message.Construct(esc bool)` serializes this to the wire format `[:][/]`, with optional URL-escaping. + +### `Handshake` + +Contains the cryptographic material exchanged in the URL path. + +```go +type Handshake struct { + Prefix byte // encodes the server key ID (or its lower 7 bits) + UserKey glome.PublicKey // client's ephemeral public key + MessageTagPrefix []byte // prefix of the tag computed over the Message +} +``` + +### `URLResponse` + +Represents a fully constructed GLOME Login URL. Created by either `NewResponse` (server-side) or internally by `Client.Construct`. + +```go +type URLResponse struct { + V byte // URL format version (currently 1) + HandshakeInfo Handshake + Msg Message + // unexported: dialog, sendingKey +} +``` + +Useful methods: +- `Tag(len uint) []byte` — compute the GLOME tag over the message (used by the server to produce an auth code). +- `EncToken() string` — base64url-encoded response token (the authorization code to return to the client). +- `ValidateAuthCode(tag []byte) bool` — check whether a received tag is correct. +- `String() string` — render the full URL string. + +### `Client` + +Implements the **client side** of the protocol (e.g. embedded in `glome-login`). + +```go +client := login.NewClient(serverPublicKey, userPrivateKey, serverKeyID, tagLen) + +// Build the challenge URL to display to the operator +url, err := client.Construct(1, "", "myhost", "shell/root") + +// After receiving the authorization code from the operator: +valid, err := client.ValidateAuthCode(receivedTag) +``` + +### `Server` + +Implements the **server side** of the protocol. Requires a `KeyFetcher` callback to look up private keys by version ID. + +```go +srv := login.Server{ + KeyFetcher: func(id uint8) (*glome.PrivateKey, error) { + return keyStore.Lookup(id) + }, +} + +response, err := srv.ParseURLResponse(incomingURL) +if err != nil { + // handle: ErrInvalidURLFormat, ErrServerKeyNotFound, ErrIncorrectTag, ... +} + +// Send the authorization code back to the operator: +authCode := response.EncToken() +``` + +`ParseURLResponse` validates: +1. The URL is well-formed (returns `ErrInvalidURLFormat` otherwise). +2. The server key corresponding to the handshake prefix exists (returns `ErrServerKeyNotFound` otherwise). +3. The client's embedded tag is valid (returns `ErrIncorrectTag` otherwise). + +## Error Types + +| Error | Meaning | +|-------------------------|----------------------------------------------------------| +| `ErrInvalidURLFormat` | The URL does not conform to the GLOME Login URL format | +| `ErrServerKeyNotFound` | No server private key matches the prefix in the URL | +| `ErrVersionNotSupported`| The URL format version `V` is not supported | +| `ErrInvalidHandshakeLen`| The handshake segment is too short | +| `ErrInvalidPrefixType` | The prefix type byte is invalid | +| `ErrIncorrectTag` | The client's embedded tag failed verification | +| `ErrResponseNotInitialized` | `ValidateAuthCode` called before `Construct` | + +## Running the Tests + +```sh +cd go +go test ./login/... +``` + +## Related Packages + +- [`go/glome`](../glome) — core GLOME cryptographic primitives (key generation, tag computation) +- [`go/login/server`](./server) — HTTP server framework built on top of this package +- [`login/`](../../login) — C implementation of `glome-login` (the `login(1)` replacement) +- [GLOME Login protocol spec](../../docs/glome-login.md) diff --git a/go/login/server/README.md b/go/login/server/README.md new file mode 100644 index 00000000..e61db006 --- /dev/null +++ b/go/login/server/README.md @@ -0,0 +1,165 @@ +# go/login/server + +Package `server` is an HTTP server framework for the **GLOME Login server side**. It builds on top of [`go/login`](../) to provide a ready-to-use `http.Handler` that validates incoming GLOME Login challenge URLs, authorizes users, and returns authorization codes. + +> **Note:** This API is Alpha and may be subject to breaking changes. + +## Overview + +In the GLOME Login flow, the *server* receives a challenge URL (forwarded by a human operator from a target machine), verifies the embedded handshake, checks whether the requesting user is authorized to perform the action, and responds with an authorization code. + +This package handles the HTTP plumbing so you only need to supply: +1. A **key manager** with your service private keys. +2. An **authorizer** that decides whether a given `(user, host, action)` tuple is permitted. + +## Package Layout + +``` +go/login/server/ +├── server.go # LoginServer, Authorizer interface, option functions +└── keymanager.go # KeyManager, PrivateKey, PublicKey types +``` + +## Quick Start + +```go +import ( + "net/http" + "github.com/google/glome/go/login/server" +) + +func main() { + // 1. Create a key manager and load your service key(s). + km := server.NewKeyManager() + km.Add(myPrivateKey, 1 /* key index 0-127 */) + + // 2. Define an authorization policy. + authz := server.AuthorizerFunc(func(user, hostID, hostIDType, action string) (bool, error) { + // Allow root shell on any host for all authenticated users. + return action == "shell/root" && user != "", nil + }) + + // 3. Build the server. + srv, err := server.NewLoginServer(authz, + server.ResponseLen(16), // shorter auth codes (optional) + server.UserHeader("x-user-id"), // custom header name (optional) + ) + if err != nil { + panic(err) + } + srv.Keys = km + + // 4. Register as a standard http.Handler. + http.ListenAndServe(":8080", srv) +} +``` + +## Key Types + +### `LoginServer` + +The central type. Implements `http.Handler`. + +```go +type LoginServer struct { + Keys *KeyManager + // ... +} +``` + +**Endpoints served by `ServeHTTP`:** + +| Path | Behaviour | +|------|-----------| +| `/` | Returns a list of the server's service public keys (so clients can configure target machines). | +| `/v1///` | Validates the GLOME Login URL, runs the authorizer, and returns the authorization code or an error. | + +**Constructor:** + +```go +srv, err := server.NewLoginServer(authorizer, ...options) +``` + +**Options** (functional-options pattern): + +| Option | Default | Description | +|--------|---------|-------------| +| `ResponseLen(n uint8)` | `44` (32 bytes, base64) | Length of the returned auth code in base64 characters. Must be in `[1, 44]`. | +| `UserHeader(name string)` | `"authenticated-user"` | HTTP request header from which the authenticated username is read. | + +**Updating the authorizer at runtime:** + +```go +srv.Authorizer(newAuthorizerImpl) // concurrency-safe swap +``` + +### `KeyManager` + +A concurrency-safe store for the server's private keys, indexed by an integer ID in `[0, 127]`. + +```go +km := server.NewKeyManager() + +// Add a key. +err := km.Add(privateKey, 1) + +// Atomically replace all keys (e.g. after a key rotation). +err = km.DropAllReplace([]server.PrivateKey{ + {Value: newPrivKey, Index: 2}, +}) + +// Look up a key by index. +privKey, ok := km.Read(2) + +// List all current public keys (for publishing to clients). +pubKeys := km.ServiceKeys() +``` + +Key indexes must be unique and in `[0, 127]`. The `LoginServer` uses `KeyManager` to resolve the key ID embedded in an incoming challenge URL. + +### `Authorizer` interface + +You implement this to encode your authorization policy: + +```go +type Authorizer interface { + GrantLogin(user, hostID, hostIDType, action string) (bool, error) +} +``` + +For simple cases, `AuthorizerFunc` lets you use a plain function: + +```go +authz := server.AuthorizerFunc(func(user, hostID, hostIDType, action string) (bool, error) { + return myPolicyCheck(user, hostID, action) +}) +``` + +**Contract for implementors:** +- An empty `user` string means no user identity could be extracted from the request (treat as unauthenticated). +- An empty `action` is a valid input; decide whether to allow it explicitly. +- Both `hostID` and `hostIDType` may be empty strings. +- The returned `bool` is acted upon even when a non-nil `error` is also returned. + +### Key and error types + +| Type | Description | +|------|-------------| +| `PrivateKey` | A `{Value glome.PrivateKey, Index uint8}` pair | +| `PublicKey` | A `{Value glome.PublicKey, Index uint8}` pair | +| `ErrInvalidKeyIndex` | Key index is outside `[0, 127]` | +| `ErrDuplicatedKeyIndex` | Key index is already registered | +| `ErrInvalidResponseLen` | Response length is outside `[1, 44]` | + +## Running the Tests + +```sh +cd go +go test ./login/server/... +``` + +## Related Packages + +- [`go/login`](../) — core protocol types (`Client`, `Server`, `URLResponse`) +- [`go/glome`](../../glome) — GLOME cryptographic primitives +- [GLOME Login protocol spec](../../../docs/glome-login.md)