// Package auth implements an interface for providing CFSSL // authentication. This is meant to authenticate a client CFSSL to a // remote CFSSL in order to prevent unauthorised use of the signature // capabilities. This package provides both the interface and a // standard HMAC-based implementation. package auth import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io/ioutil" "os" "strings" ) // An AuthenticatedRequest contains a request and authentication // token. The Provider may determine whether to validate the timestamp // and remote address. type AuthenticatedRequest struct { // An Authenticator decides whether to use this field. Timestamp int64 `json:"timestamp,omitempty"` RemoteAddress []byte `json:"remote_address,omitempty"` Token []byte `json:"token"` Request []byte `json:"request"` } // A Provider can generate tokens from a request and verify a // request. The handling of additional authentication data (such as // the IP address) is handled by the concrete type, as is any // serialisation and state-keeping. type Provider interface { Token(req []byte) (token []byte, err error) Verify(aReq *AuthenticatedRequest) bool } // Standard implements an HMAC-SHA-256 authentication provider. It may // be supplied additional data at creation time that will be used as // request || additional-data with the HMAC. type Standard struct { key []byte ad []byte } // New generates a new standard authentication provider from the key // and additional data. The additional data will be used when // generating a new token. func New(key string, ad []byte) (*Standard, error) { if splitKey := strings.SplitN(key, ":", 2); len(splitKey) == 2 { switch splitKey[0] { case "env": key = os.Getenv(splitKey[1]) case "file": data, err := ioutil.ReadFile(splitKey[1]) if err != nil { return nil, err } key = string(data) default: return nil, fmt.Errorf("unknown key prefix: %s", splitKey[0]) } } keyBytes, err := hex.DecodeString(key) if err != nil { return nil, err } return &Standard{keyBytes, ad}, nil } // Token generates a new authentication token from the request. func (p Standard) Token(req []byte) (token []byte, err error) { h := hmac.New(sha256.New, p.key) h.Write(req) h.Write(p.ad) return h.Sum(nil), nil } // Verify determines whether an authenticated request is valid. func (p Standard) Verify(ad *AuthenticatedRequest) bool { if ad == nil { return false } // Standard token generation returns no error. token, _ := p.Token(ad.Request) if len(ad.Token) != len(token) { return false } return hmac.Equal(token, ad.Token) }