isokey

package module
v2.0.0+incompatible Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2016 License: MIT Imports: 13 Imported by: 0

README

Isokey

Isokey allows you to make and verify self-contained API keys without a database via HMAC signatures.

Features

  • Important information such as userID, key expire time, and flags are stored within the key.
  • Use mutliple secrets simultaneously
  • Invalidate secrets and compromised keys

Table Of Contents

Install

Always use gopkg to install, the repository may be in a broken midway state.

go get gopkg.in/ammario/isokey.v1

Symmetric Keys

Make a key service

    ks := SymKeyService{
		Secret: []byte("super_secure111"),
	}

Make key digest

	key := &Key{
		UserID:  1,
		Expires: time.Now().AddDate(0, 1, 0),
	}

	digest, err := ks.Digest(key)

	if err != nil {
		log.Fatalf("Error making digest: %v", err)
	}
	fmt.Printf("Digest is %v\n", digest)

Verify key

    key, err = ks.Verify(digest)

	if err != nil {
		log.Fatalf("Error reading digest: %v", err)
	}
    //Key authenticated
	fmt.Printf("Key: %+v\n", key)

Using multiple secrets

The SecretVersion is in included in each key to enable implementors to use multiple secrets.

Use a map

    ks.SecretMap = map[uint32][]byte{
        1: []byte("sec1"),
        2: []byte("sec2"),
    }

Alternatively get full control with a function

    ks.GetSecret = function(key *Key)(secret []byte){
        if key.SecretVersion == 1 {
            return []byte("sec1") 
        }
        return nil
    }

Digest Structure

All binary values are big endian.

Field Type
Signature [16]byte
Made Time (Unix epoch timestamp) uint32
Expire Time (Unix epoch timestamp) uint32
Secret Version uint32
User ID uint32
Flags uint32

Digests are encoded with Bitcoin's base58 alphabet.

It may seem intuitive to put the signature at the end of the digest. It's located at the beginning as it makes eyeballing different keys more easy due to the avalanche effect.

Asymmetric Keys

Make a key pair

Make your private key openssl ecparam -genkey -name prime256v1 -outform DER -noout -out privatekey.der

Make your public key openssl ec -in privatekey.der -inform DER -outform DER -pubout -out publickey.der

Make key digest

    privKey, _ = isokey.LoadPrivateKey("priv.key")

    ks := AsymKeySigner{
		PrivateKey: privKey,
	}

    key := &Key{
        User: 1,
        Expires: time.Now().Add(time.Hour)
    }

    digest, _ := ks.Digest(key)

    fmt.Printf("Digest: %v", digest)

Verify key

	pubKey, _ = isokey.LoadPublicKey("pub.key")

	kv := AsymKeyVerifier{
        PublicKey: pubKey,
    }

    key, err := kv.Verify(digest)
    if err != nil {
        log.Fatalf("Error verifying key: %v", err)
    }
	fmt.Printf("Key verified %v\n", key)

Using multiple keys

Similar to symmetric keys, you can use multiple public or private keys. Refer to the godoc for specifc usage.

Digest Structure

All binary values are big endian.

Field Type
R len uint8
R []byte
S Len uint8
S []byte
Made Time (Unix epoch timestamp) uint32
Expire Time (Unix epoch timestamp) uint32
Secret Version uint32
User ID uint32
Flags uint32

Digests are encoded with Bitcoin's base58 alphabet.

Invalidating keys

Custom invalidation can be useful if you'd like to support cases where the client has been compromised.

You can invalidate keys like so

ks.CustomInvalidate = function(key *isokey.Key) bool {
    if key.UserID == 10 && key.Made.Before(time.Date(2015, time.November, 10, 23, 0, 0, 0, time.UTC)) {
        return true
    }
    //Make sure to handle expired keys when overriding
    if key.Expires.Before(time.Now()) {
        return true
    }
    return false
}

Remember when overriding Invalidate to handle expired keys

Documentation

Overview

Package isokey allows you to make and verify API keys without a database connection via HMAC signatures. The keys are scalable and persistent. All information is stored in the key, and with the client.

Package isokey allows you to make and verify API keys without a database connection via HMAC signatures. The keys are scalable and persistent. All information is stored in the key, and with the client.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNoSecret   = errors.New("No secret was found for the secret version.")
	ErrSymKeySize = fmt.Errorf("Key is not %v bytes long.", symKeyDigestSize)
	ErrBadSecret  = errors.New("Secret is incorrect")
	ErrInvalid    = errors.New("Key is expired or invalid.")
)

Common errors

View Source
var (
	ErrNoAsymKey       = errors.New("No asym key was found for that SecretVersion")
	ErrNotECPublicKey  = errors.New("Not elliptic curve public key")
	ErrAsymMessageSize = errors.New("Message portion not 20 bytes")
	ErrBadSignature    = errors.New("Bad signature or message.")
)

Asymmetric key errors

Functions

func LoadPrivateKey

func LoadPrivateKey(filename string) (privKey *ecdsa.PrivateKey, err error)

LoadPrivateKey loads an ASN.1 ECDSA private key from a file.

func LoadPublicKey

func LoadPublicKey(filename string) (publicKey *ecdsa.PublicKey, err error)

LoadPublicKey loads an ASN.1 ECDSA public key from a file.

Types

type AsymKeySigner

type AsymKeySigner struct {
	//PrivateKey is used if GetPrivateKey and KeyMap is nil
	PrivateKey *ecdsa.PrivateKey

	//PrivateKeyMap maps secret versions to secrets
	PrivateKeyMap map[uint32]*ecdsa.PrivateKey

	//GetPrivateKey allows you to dynamically use secrets.
	//Returning nil indicates that no secret was found for the version
	GetPrivateKey func(key *Key) *ecdsa.PrivateKey
}

AsymKeySigner facilitates the creation ECDSA API keys

func (*AsymKeySigner) Digest

func (ks *AsymKeySigner) Digest(key *Key) (digest string, err error)

Digest signs the API key and digests it into it's base58 form. An error will only be returned if the corresponding key cannot be found from SecretVersion. if key.Made is zero it is set to the current time.

type AsymKeyVerifier

type AsymKeyVerifier struct {
	//PublicKey is used if GetPublicKey and KeyMap is nil
	PublicKey *ecdsa.PublicKey

	//PublicKeyMap maps secret versions to secrets
	PublicKeyMap map[uint32]*ecdsa.PublicKey

	//GetPublicKey allows you to dynamically use secrets.
	//Returning nil indicates that no secret was found for the version
	GetPublicKey func(key *Key) *ecdsa.PublicKey

	//CustomInvalidate allows you to invalidate certain keys based off the Key's parameters (e.g when it was made.)
	//CustomInvalidate is ran after the key's signature has been validated.
	//This is useful to deal with cases revolving compromised users.
	CustomInvalidate func(*Key) bool
}

AsymKeyVerifier verifies ECDSA signed API keys

func (*AsymKeyVerifier) Invalidate

func (kv *AsymKeyVerifier) Invalidate(key *Key) bool

Invalidate invalidates a key

func (*AsymKeyVerifier) Verify

func (kv *AsymKeyVerifier) Verify(digest string) (key *Key, err error)

Verify verifies and parses a key

type Key

type Key struct {
	Made          time.Time
	Expires       time.Time
	SecretVersion uint32
	UserID        uint32
	Flags         uint32
}

Key is a self-contained algorithm agnostic API key

type SymKeyService

type SymKeyService struct {
	//Secret is used if GetSecret and SecretMap is nil
	Secret []byte

	//SecretMap maps secret versions to secrets
	SecretMap map[uint32][]byte

	//GetSecret allows you to dynamically use secrets.
	//Returning nil indicates that no secret was found for the version
	GetSecret func(key *Key) (secret []byte)

	//CustomInvalidate allows you to invalidate certain keys based off the Key's parameters (e.g when it was made.)
	//CustomInvalidate is ran after the key's signature has been validated.
	//This is useful to deal with cases revolving compromised users.
	CustomInvalidate func(*Key) bool
}

SymKeyService facilitates the creation and verification of symmetricly signed (HMAC) keys

func (*SymKeyService) Digest

func (ks *SymKeyService) Digest(key *Key) (digest string, err error)

Digest converts the key into it's base58 form. An error will only be returned if the secret cannot be found from SecretVersion. if key.Made is zero it is set to the current time.

func (*SymKeyService) Invalidate

func (ks *SymKeyService) Invalidate(key *Key) bool

Invalidate invalidates a key

func (*SymKeyService) Verify

func (ks *SymKeyService) Verify(digest string) (*Key, error)

Verify securely validates a digest or API. If Invalidate is not set with a custom handler, expired keys will invoke an error.

Jump to

Keyboard shortcuts

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