schnellburger

package module
v0.0.0-...-9952fd3 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2014 License: MIT Imports: 13 Imported by: 0

README

schnellburger

This library provides HMAC Verification of HTTP requests in Golang. It is compatible with the net/http package.

View the full documentation here

Documentation

Overview

This package allows you to add HMAC security to an existing http server. This implementations builds on the existing "net/http" package.

The first step is create an instance of Schnellburger. Next, select the cryptographic algorithm that is used to secure endpoints. This is done by setting the `Algorithm` member of the Schnellburger structure to a function like `md5.new`. Finally, implement a KeyProvider. A KeyProvider allows the Schnellburger instance to lookup keys by index. The HTTP client sends the key index as part of the request. The value of the key used constitutes a preshared secret between the server and the client. It is used to prove the authenticity of the request by the client.

To prove the authenticity of a request, the client sends the signature for the request and an optional key index. If the key index is not provided the key at index zero is used. This value is placed in the Authorization header sent by the client. The Authorization header is base64 encoded by the client. The server decodes this and uses the resulting byte slice as follows

|----X----|---------------Y-------------------|

Where X is 0,1,2,4,6, or 8 bytes. These values correspond to the
following types
0 - No type. The key at index 0 is used.
1 - uint8
2 - uint16
4 - uint32
8 - uint64
Integers are always considered to be in big endian byte order

Where Y is the cryptographic signature, having exactly the same length
as the number of bytes as the slice returned by Algorithm.Sum(nil)

The cryptographic signature is computed as follows using the HMAC algorithm and the chosen cryptogrpaphic hash implementation.

  1. The request method ("GET","POST",etc.)
  2. The path of the request. There is no concept of an empty path, a request for the root resource has a path of "/".
  3. The raw request query with the leading question mark.
  4. The body of the request, if any.

The server responds with 403 if for any reason the value of the header does not prove the authenticity of the request.

This example shows protecting http.FileServer with this package.

package main

import "net/http"

import "github.com/hydrogen18/schnellburger"
import "crypto/md5"

//Simple implementation of key provider
type DictKeyProvider map[uint64][]byte

func (dkp DictKeyProvider) GetKey(index uint64) ([]byte, error) {
	k, ok := dkp[index]
	if !ok {
		return nil, nil
	}

	return k, nil
}

func main() {

	kp := DictKeyProvider{}

	//Configure keys
	kp[0] = []byte{0x1, 0x2, 0x3, 0x4}

	//Create an instance of schnellburger
	sb := schnellburger.Schnellburger{}

	//Use the MD5 hash
	sb.Algorithm = md5.New
	//Use the dictionary key provider
	sb.Kp = kp

	server := http.Server{}
	server.Addr = "0.0.0.0:8080"
	//Wrap the http.Handler to protect it
	//with HMAC
	server.Handler = sb.WrapHttpHandler(http.FileServer(http.Dir("/tmp")))

	//serve forever

	server.ListenAndServe()

}

Using curl we can see the request is denied

ericu@eric-phenom-linux:~$ curl -D - http://localhost:8080
HTTP/1.1 403 Forbidden
Content-Type: text/plain
X-Schnellburger-Algo: *md5.digest
X-Schnellburger-Doc: http://godoc.org/github.com/hydrogen18/schnellburger
X-Schnellburger-Error-Code: 0
Date: Sun, 22 Jun 2014 02:07:37 GMT
Content-Length: 92

Missing Header
---
You must supply the authorization header "Authorization" to this endpoint

After creating a file and repeating the same curl command, but this time with the correct header

ericu@eric-phenom-linux:/tmp$ echo 'Hello Crypto' > /tmp/x
ericu@eric-phenom-linux:/tmp$ curl -D - -H "Authorization: ADVij+gEOkh2xRqsTAeGcyg=" http://192.168.12.10:8080/x
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Length: 13
Content-Type: text/plain; charset=utf-8
Last-Modified: Sun, 22 Jun 2014 02:14:01 GMT
Date: Sun, 22 Jun 2014 02:14:04 GMT

Hello Crypto

The request is allowed.

The preferred implementation is to use (*Schnellburger).WrapHttpHandler. However, this makes an in-memory of the body sent by the client. For requests that cannot possibly have a body, this is of no impact. For requests that have a body, this only matters if the body is very large. If the body is very large (*Schnellburger).WrapHandler should be called to add HMAC security. The handler must be of type schnellburger.Handler. This type requires that the verify closure be called before taking any action on the request but after reading the body entirely. Any implementation not honoring this contract results in a call to "Printf" from the "log" package of the caller. This process allows the handler to write the body of the request to an alternate location rather than requiring it to be buffered in memory.

Index

Constants

View Source
const ALGORITHM_HEADER = "X-Schnellburger-Algo"

Header returned to clients to indicate what algorithm to sign requests with

View Source
const DOCUMENTATION_URL = "http://godoc.org/github.com/hydrogen18/schnellburger"
View Source
const DOCUMENTATION_URL_HEADER = "X-Schnellburger-Doc"

Header returned to clients to link to documentation

View Source
const ERROR_CODE_HEADER = "X-Schnellburger-Error-Code"

Header returned to clients to uniquely identify this error

View Source
const ERROR_HEADER = "X-Schnellburger-Error"

Header returned to clients to indicate the error message, if any

View Source
const ERROR_TYPE_HEADER = "X-Schnellburger-Error-Type"

Header returned to client to idnicate the type of the error, if any

View Source
const HMAC_HEADER = "Authorization"

Variables

View Source
var ErrHeaderTooShort = errors.New("Header is too short")
View Source
var ErrKeyIndexWrongLength = errors.New("Key index is the wrong length")
View Source
var ErrMissingHeader = errors.New("Missing header")
View Source
var HeaderTooShort = newHelp(http.StatusForbidden, "Header Too Short",
	fmt.Sprintf("The value supplied as the header %q did not have enough bytes after base64 decoding", HMAC_HEADER))
View Source
var InvalidHeader = newHelp(http.StatusForbidden, "Invalid Header",
	fmt.Sprintf("The value supplied as the header %q must the base64 encoding of a signature", HMAC_HEADER))
View Source
var KeyIndexWrongLength = newHelp(http.StatusForbidden, "Key Index Wrong Length",
	"The key index at the beginning of the authorization bytes must be exactly 0,2,4, or 8 bytes representing an unsigned integer")
View Source
var KeyLookupFailure = newHelp(http.StatusInternalServerError, "Key Lookup Failure",
	"Failure while attempting to lookup the key by index")
View Source
var MissingHeader = newHelp(http.StatusForbidden, "Missing Header",
	fmt.Sprintf("You must supply the authorization header %q to this endpoint", HMAC_HEADER))
View Source
var NotAuthentic = newHelp(http.StatusForbidden, "Not Authentic",
	"The request you have made is not authentic")
View Source
var SignatureWrongSize = newHelp(http.StatusForbidden, "Signature Wrong Size",
	"The bytes after the key index must exactly match the output of the algorithm in use")

Functions

This section is empty.

Types

type Handler

type Handler interface {
	ServeHTTP(rw http.ResponseWriter, req *http.Request, verify func() bool)
}

A handler as used by this package. The third parameter must be called before taking any action based off the request but after req.Body is consumed completely. If false is returned the implementation must return immediately without taking any other action. If true is returned the request is authentic and the handler should proceed as normal.

See: WrapHandler WrapHttpHandler

type HandlerFunc

type HandlerFunc func(rw http.ResponseWriter, req *http.Request, verify func() bool)

func (HandlerFunc) ServeHTTP

func (f HandlerFunc) ServeHTTP(rw http.ResponseWriter, req *http.Request, verify func() bool)

type KeyProvider

type KeyProvider interface {
	GetKey(index uint64) ([]byte, error)
}

Returns (nil, nil) if no such key exists. Returning an error results in that error being show to the client and a status code of http.StatusInternalServerError begin returned

type KeyProviderFunc

type KeyProviderFunc func(index uint64) ([]byte, error)

Wrapper to allow functions to be used as KeyProvider

func (KeyProviderFunc) GetKey

func (kpf KeyProviderFunc) GetKey(index uint64) ([]byte, error)

type RoundTripper

type RoundTripper struct {
	Next      http.RoundTripper
	Key       []byte
	KeyId     uint64
	Algorithm func() hash.Hash
}

func (RoundTripper) RoundTrip

func (this RoundTripper) RoundTrip(originalRequest *http.Request) (*http.Response, error)

type Schnellburger

type Schnellburger struct {
	//Interface used to lookup keys from key indices. If the client
	//does not provide a key index, the 0'th key is looked up.
	Kp KeyProvider

	//Algorithm used to verify message authenticity
	Algorithm func() hash.Hash
	// contains filtered or unexported fields
}

func (Schnellburger) FindHmacHeader

func (sb Schnellburger) FindHmacHeader(req *http.Request) (uint64, []byte, error)

func (Schnellburger) ServeHTTP

func (sb Schnellburger) ServeHTTP(rw http.ResponseWriter, req *http.Request)

func (Schnellburger) WrapHandler

func (sb Schnellburger) WrapHandler(handler Handler) http.Handler

Protects a handler with HMAC verification. If the handler only responds to requests with a body, this is the preferred way to use this package.

func (Schnellburger) WrapHttpHandler

func (sb Schnellburger) WrapHttpHandler(handler http.Handler) http.Handler

Protects a traditional http.Handler instance with HMAC verification. If the handler only responds to requests without a body, this is the preferred way to use this package.

Jump to

Keyboard shortcuts

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