cardea

package module
v0.0.0-...-f671b16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 26, 2018 License: MIT Imports: 13 Imported by: 1

README

Cardea

She is the goddess of the hinge: by her divine power she opens what is closed, and closes what is open. — Ovid, Fasti

The cookie format is described in an Augmented Backus-Naur Form (ABNF), as described in rfc4234, with two additions:

  • The terminal expression notation /REGEXP/ means a string matching a Perl-compatible regular expression. The whole string should match the expression, so the anchors are ommitted for clarity; the actual regular expression would be /^(?:REGEXP)$/. Modificators (case-insensitiveness, multi-line match, etc) are also allowed (e.g. /ReGeXp/i).
  • Encoded values are defined in a "function notation", e.g. urlencode(X) = <any string X, percent-encoded as specified in RFC3986>

Some base token definitions that will be used later on:

WORD           = /[a-z_][a-z0-9_.-]*/i ; alphanumeric word, with reasonable punctuation allowed
NUMBER         = /\d+/
CRLF           = "\r\n"
ANY            = <arbitrary string, used as input for encoding or HMAC>
URL(ANY)       = <arbitrary string, percent-escaped as per RFC3986 section 2.1>
B64(ANY)       = <arbitrary string, escaped as URL-safe Base64 without padding, as per RFC4648 section 5>
HEX(ANY)       = <arbitrary string, likely binary, hex-encoded>

HMAC(HMAC_K,HMAC_M) = <HMAC-SHA256 of message HMAC_M, using key HMAC_K>
HMAC_K         = ANY
HMAC_M         = ANY

To compute the HMAC, a token is combined with HMAC Extras. It is an out-of-band value, based on request parameters, and designed to prevent cookie stealing / replay attacks. Originally it was value of the User-Agent HTTP header, postprocessed in a very stupid way. In Cardea, parameters used are open to interpretation: it can include User-Agent, client IP, "browser signature" based on headers, or an actual out-of-base value, such as a time-based token. Multiple values can be used as extras; if this is the case, they are joined together by CRLF ("\r\n"):

hmac_extras    = B64( hmac_extra *( CRLF hmac_extra ) )
hmac_extra     = ANY

HMAC is computed on a payload, which is cookie's text combined with the extras:

secret         = ANY ; preshared secret, provided in configuration
hmac           = HMAC(secret, payload)

There are two cookie formats, "Modern" (Cardea's), and optional "Legacy" (Odin Authenticator's). The two formats imply slightly different HMAC payload structure. As legacy format is optional, the base definition includes only modern format:

cookie         = modern_cookie
payload        = modern_payload

The default and recommended format for Cookie is the modern format. It is designed to resemble an URI (and utilise existing parsing code), and to be compatible with HTTP Basic authentication header. There is also an optional legacy format, which will be introduced later on.

modern_cookie  = modern_token "#" B64(hmac)
modern_payload = modern_token "#" hmac_extras
modern_token   = username ":" [ format "?" ] query
username       = WORD
format         = WORD

; The "query" is a set of key-value pairs like in HTTP query
; string, which seems to lack a formal definition, so we
; (re)define it here.
query          = kv_pair ( & kv_pair )*
kv_pair        = key "=" value
key            = WORD
value          = URL(ANY)
Legacy Format

Cardea also supports legacy format, used by her predecessor, Odin Authenticator. It should be used only in environment that mixes Odin Authenticator and Cardea. It is not flexible or extensible, and User-Agent header handling is, to put it mild, wrong. This format is disabled by default.

cookie        /= legacy_cookie
payload       /= legacy_payload

legacy_cookie  = legacy_token "," HEX(hmac)
legacy_payload = legacy_token "," hmac_extras
legacy_token   = B64(username) "," B64(legacy_groups) "," legacy_timestamp
legacy_timestamp = NUMBER ; UNIX time as decimal number

Odin Authenticator, which aimed at (at least partial) compatibility with GodAuth script, performs following transformation on User-Agent header before using it as a HMAC extra:

  • If the header includes substring "AppleWebKit", a hard-coded value "StupidAppleWebkitHacksGRRR" is used instead (don't ask)
  • s/ FirePHP\/\d+\.\d+//

Documentation

Index

Constants

View Source
const (
	TOKEN_RX_USERNAME         = iota
	TOKEN_RX_PAYLOAD          = iota
	TOKEN_RX_LEGACY_GROUPS    = iota
	TOKEN_RX_LEGACY_TIMESTAMP = iota
	TOKEN_RX_FORMAT           = iota
	TOKEN_RX_QUERY            = iota
	TOKEN_RX_HMAC             = iota
)

Variables

View Source
var (
	DEFAULT_COOKIE_NAME    = "ca"
	DEFAULT_EXPIRATION_SEC = uint64(36 * 3600)
)
View Source
var AUTHORIZATION_RX = regexp.MustCompile("^\\s*Basic\\s+(.*)$")
View Source
var TOKEN_RX = regexp.MustCompile("\\A[\\t-\\n\\f-\\r ]*(?P<USERNAME>[\\--9A-Z_a-z]+)(?P<PAYLOAD>,(?P<LEGACY_GROUPS>[\\--9A-Z_a-z]*),(?P<LEGACY_TIMESTAMP>[0-9]+),|:(?:(?P<FORMAT>[0-9A-Z_a-z]+)\\?)?(?P<QUERY>[^#]+)#)(?P<HMAC>[0-9a-f]+|[\\--9A-Z_a-z]+)[\\t-\\n\\f-\\r ]*(?-m:$)")

Functions

This section is empty.

Types

type Config

type Config struct {
	Secret        []byte
	Cookie        string
	ExpirationSec uint64
}

func NewConfig

func NewConfig(secret string) *Config

func (*Config) CheckAuthorization

func (c *Config) CheckAuthorization(auth string, hmac_extra string) (t *Token, err error)

func (*Config) CheckCookie

func (c *Config) CheckCookie(cookie string, hmac_extra string) (t *Token, err error)

func (*Config) CheckRequest

func (c *Config) CheckRequest(r *http.Request) (t *Token, err error)

func (*Config) CheckToken

func (c *Config) CheckToken(t *Token, hmac_extra string) (err error)

func (*Config) ServeHTTP

func (c *Config) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Token

type Token struct {
	User      string
	Timestamp time.Time
	Groups    []string
	url.Values
}

func DeserializeToken

func DeserializeToken(secret []byte, hmac_extra, serialized string) (t *Token, err error)

func ParseAuthorization

func ParseAuthorization(secret []byte, hmac_extra, auth string) (*Token, error)

func ParseCookie

func ParseCookie(secret []byte, hmac_extra, cookie string) (*Token, error)

func (*Token) Age

func (t *Token) Age() time.Duration

func (*Token) String

func (t *Token) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL