paseto

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2018 License: MIT Imports: 22 Imported by: 913

README

Golang implementation of PASETO: Platform-Agnostic Security Tokens

License GoDoc Build Status Coverage Status Go Report Card

This is 100% compatible pure Go (Golang) implementation of PASETO tokens.

Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards.

Contents

What is Paseto?

Paseto (Platform-Agnostic SEcurity TOkens) is a specification and reference implementation for secure stateless tokens.

Key Differences between Paseto and JWT

Unlike JSON Web Tokens (JWT), which gives developers more than enough rope with which to hang themselves, Paseto only allows secure operations. JWT gives you "algorithm agility", Paseto gives you "versioned protocols". It's incredibly unlikely that you'll be able to use Paseto in an insecure way.

Caution: Neither JWT nor Paseto were designed for stateless session management. Paseto is suitable for tamper-proof cookies, but cannot prevent replay attacks by itself.

Installation

To install the library use the following command:

$ go get -u github.com/o1egl/paseto

Usage

This library contains a predefined JsonToken struct for using as payload, but you are free to use any data types and structs you want.

During the encoding process a payload of type string and []byte is used without transformation. For other data types, the library tries to encode the payload to json.

Use general parser to parse all supported token versions:

b, err := hex.DecodeString("2d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0d0a4d494942496a414e42676b71686b6947397730424151454641414f43415138414d49494243674b43415145417878636e47724e4f6136426c41523458707050640d0a746146576946386f7279746c4b534d6a66446831314c687956627a4335416967556b706a457274394d7649482f46384d444a72324f39486b36594b454b574b6f0d0a72333566364b6853303679357a714f722b7a4e34312b39626a52365633322b527345776d5a737a3038375258764e41334e687242633264593647736e57336c5a0d0a34356f5341564a755639553667335a334a574138355972362b6350776134793755632f56726f6d7a674679627355656e33476f724254626a783142384f514a440d0a73652f4b6b6855433655693358384264514f473974523455454775742f6c39703970732b3661474d4c57694357495a54615456784d4f75653133596b777038740d0a3148467635747a6872493055635948687638464a6b315a6435386759464158634e797975737834346e6a6152594b595948646e6b4f6a486e33416b534c4d306b0d0a6c774944415141420d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d")
block, _ := pem.Decode(b)
rsaPubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
v1PublicKey := rsaPubInterface.(*rsa.PublicKey)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
v2PublicKey := ed25519.PublicKey(b)


var payload JSONToken
var footer string
version, err := paseto.Parse(token, &payload, &footer, symmetricKey, map[paseto.Version]crypto.PublicKey{paseto.V1: v1PublicKey, paseto.V2: v2PublicKey})

Create token using symmetric key (local mode):

symmetricKey := []byte("YELLOW SUBMARINE, BLACK WIZARDRY") // Must be 32 bytes
now := time.Now()
exp := now.Add(24 * time.Hour)
nbt := now

jsonToken := paseto.JSONToken{
		Audience:   "test",
		Issuer:     "test_service",
		Jti:        "123",
		Subject:    "test_subject",
		IssuedAt:   now,
		Expiration: exp,
		NotBefore:  nbt,
		}
// Add custom claim	to the token	
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

v2 := paseto.NewV2()

// Encrypt data
token, err := v2.Encrypt(symmetricKey, jsonToken, paseto.WithFooter(footer))
// token = "v2.local.E42A2iMY9SaZVzt-WkCi45_aebky4vbSUJsfG45OcanamwXwieieMjSjUkgsyZzlbYt82miN1xD-X0zEIhLK_RhWUPLZc9nC0shmkkkHS5Exj2zTpdNWhrC5KJRyUrI0cupc5qrctuREFLAvdCgwZBjh1QSgBX74V631fzl1IErGBgnt2LV1aij5W3hw9cXv4gtm_jSwsfee9HZcCE0sgUgAvklJCDO__8v_fTY7i_Regp5ZPa7h0X0m3yf0n4OXY9PRplunUpD9uEsXJ_MTF5gSFR3qE29eCHbJtRt0FFl81x-GCsQ9H9701TzEjGehCC6Bhw.c29tZSBmb290ZXI"

// Decrypt data
var newJsonToken paseto.JSONToken
var newFooter string
err := v2.Decrypt(token, symmetricKey, &newJsonToken, &newFooter)

Create token using asymetric key (public mode):

b, _ := hex.DecodeString("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
privateKey := ed25519.PrivateKey(b)

b, _ = hex.DecodeString("1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2")
publicKey := ed25519.PublicKey(b)

// or create a new keypair 
// publicKey, privateKey, err := ed25519.GenerateKey(nil)

jsonToken := paseto.JSONToken{
		Expiration: time.Now().Add(24 * time.Hour),
		}
		
// Add custom claim	to the token	
jsonToken.Set("data", "this is a signed message")
footer := "some footer"

v2 := paseto.NewV2()

// Sign data
token, err := v2.Sign(privateKey, jsonToken, paseto.WithFooter(footer))
// token = "v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAxOC0wMy0xMlQxOTowODo1NCswMTowMCJ9Ojv0uXlUNXSFhR88KXb568LheLRdeGy2oILR3uyOM_-b7r7i_fX8aljFYUiF-MRr5IRHMBcWPtM0fmn9SOd6Aw.c29tZSBmb290ZXI"

// Verify data
var newJsonToken paseto.JSONToken
var newFooter string
err := v2.Verify(token, publicKey, &newJsonToken, &newFooter)

For more information see *_test.go files.

Benchmarks

MacBook Pro (Retina, 15-inch, Late 2013) CPU: 2,3 GHz Intel Core i7 RAM: 16 GB 1600 MHz DDR3 OS: macOS 10.13.3 GO: 1.10

$ go test -bench . -benchmem

Benchmark_V2_JSONToken_Encrypt-8          100000             11729 ns/op            5808 B/op         63 allocs/op
Benchmark_V2_JSONToken_Decrypt-8          100000             11795 ns/op            3104 B/op         61 allocs/op
Benchmark_V2_JSONToken_Sign-8              20000             71034 ns/op            5136 B/op         60 allocs/op
Benchmark_V2_JSONToken_Verify-8            10000            167387 ns/op            2672 B/op         58 allocs/op
Benchmark_V2_String_Encrypt-8             300000              4295 ns/op            2240 B/op         32 allocs/op
Benchmark_V2_String_Decrypt-8            1000000              1854 ns/op            1512 B/op         22 allocs/op
Benchmark_V2_String_Sign-8                 20000             60374 ns/op            1296 B/op         28 allocs/op
Benchmark_V2_String_Verify-8               10000            156859 ns/op             776 B/op         18 allocs/op

Supported Paseto Versions

Version 2

Version 2 (the recommended version by the specification) is fully supported.

Version 1

Version 1 (the compatability version) is fully supported.

Documentation

Overview

Package paseto provides a Go implementation of PASETO, a secure alternative to the JOSE standards (JWT, JWE, JWS). See https://paseto.io/

Index

Constants

View Source
const (
	// Version1 defines protocol version 1
	Version1 = Version("v1")
	// Version2 defines protocol version 2
	Version2 = Version("v2")
)

Variables

View Source
var (
	// ErrUnsupportedTokenVersion unsupported parser version
	ErrUnsupportedTokenVersion = errors.New("unsupported parser version")
	// ErrUnsupportedTokenType unsupported token type
	ErrUnsupportedTokenType = errors.New("unsupported token type")
	// ErrIncorrectPrivateKeyType incorrect private key type
	ErrIncorrectPrivateKeyType = errors.New("incorrect private key type")
	// ErrIncorrectPublicKeyType incorrect public key type
	ErrIncorrectPublicKeyType = errors.New("incorrect public key type")
	// ErrPublicKeyNotFound public key for this version not found
	ErrPublicKeyNotFound = errors.New("public key for this version not found")
	// ErrIncorrectTokenFormat incorrect token format
	ErrIncorrectTokenFormat = errors.New("incorrect token format")
	// ErrIncorrectTokenHeader incorrect token header
	ErrIncorrectTokenHeader = errors.New("incorrect token header")
	// ErrInvalidTokenAuth invalid token authentication
	ErrInvalidTokenAuth = errors.New("invalid token authentication")
	// ErrInvalidSignature invalid signature
	ErrInvalidSignature = errors.New("invalid signature")
	// ErrDataUnmarshal can't unmarshal token data to the given type of value
	ErrDataUnmarshal = errors.New("can't unmarshal token data to the given type of value")
	// ErrTokenValidationError invalid token data
	ErrTokenValidationError = errors.New("token validation error")
)

Functions

func GetTokenInfo

func GetTokenInfo(token string) (Version, Purpose, error)

GetTokenInfo returns the token version (paseto.Version1 or paseto.Version2) and purpose (paseto.LOCAL or paseto.PUBLIC).

func ParseFooter

func ParseFooter(token string, footer interface{}) error

ParseFooter parses the footer from the token and returns it.

Types

type JSONToken added in v0.2.0

type JSONToken struct {
	// Audience identifies the intended recipients of the token.
	// It should be a string or a URI and is case sensitive.
	Audience string
	// Issuer identifies the entity which issued the token.
	// It should be a string or a URI and is case sensitive.
	Issuer string
	// JTI is a globally unique identifier for the token. It must be created in
	// such a way as to ensure that there is negligible probability that the same
	// value will be used in another token.
	Jti string
	// Subject identifies the principal entity that is the subject of the token.
	// For example, for an authentication token, the subject might be the user ID
	// of a person.
	Subject string
	// Expiration is a time on or after which the token must not be accepted for processing.
	Expiration time.Time
	// IssuedAt is the time at which the token was issued.
	IssuedAt time.Time
	// NotBefore is a time on or before which the token must not be accepted for
	// processing.
	NotBefore time.Time
	// contains filtered or unexported fields
}

JSONToken defines standard token payload claims and allows for additional claims to be added. All of the standard claims are optional.

func (*JSONToken) Get added in v0.2.0

func (t *JSONToken) Get(key string) string

Get returns the value of a custom claim, as a string. If there is no such claim, an empty string is returned.

func (JSONToken) MarshalJSON added in v0.2.0

func (t JSONToken) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler interface

func (*JSONToken) Set added in v0.2.0

func (t *JSONToken) Set(key string, value string)

Set sets the value of a custom claim to the string value provided.

func (*JSONToken) UnmarshalJSON added in v0.2.0

func (t *JSONToken) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler interface

func (*JSONToken) Validate added in v0.2.0

func (t *JSONToken) Validate(validators ...Validator) error

Validate validates a token with the given validators. If no validators are specified, then by default it validates the token with ValidAt(time.Now()), which checks IssuedAt, NotBefore and Expiration fields against the current time.

type Protocol

type Protocol interface {

	// Encrypt encrypts a token with a symmetric key. The key should be a byte
	// slice of 32 bytes, regardless of whether PASETO v1 or v2 is being used.
	Encrypt(key []byte, payload interface{}, footer interface{}) (string, error)

	// Decrypt decrypts a token which was encrypted with a symmetric key.
	Decrypt(token string, key []byte, payload interface{}, footer interface{}) error

	// Sign signs a token with the given private key. For PASETO v1, the key should
	// be an rsa.PrivateKey. For v2, the key should be an ed25519.PrivateKey.
	Sign(privateKey crypto.PrivateKey, payload interface{}, footer interface{}) (string, error)

	// Verify verifies a token against the given public key. For PASETO v1, the key
	// key should be an rsa.PublicKey. For v2, the key should be an
	// ed25519.PublicKey.
	Verify(token string, publicKey crypto.PublicKey, value interface{}, footer interface{}) error
}

Protocol defines the PASETO token protocol interface.

type Purpose

type Purpose int

Purpose defines the token type by its intended purpose.

const (
	// LOCAL defines symmetric encrypted token type
	LOCAL Purpose = iota
	// PUBLIC defines asymmetric signed token type
	PUBLIC
)

type V1

type V1 struct {
	// contains filtered or unexported fields
}

V1 is a v1 implementation of PASETO tokens

func NewV1

func NewV1() *V1

NewV1 returns a v1 implementation of PASETO tokens. You should not use PASETO v1 unless you need interoperability with for legacy systems that cannot use modern cryptography.

func (*V1) Decrypt added in v1.0.0

func (p *V1) Decrypt(token string, key []byte, payload interface{}, footer interface{}) error

Decrypt implements Protocol.Decrypt

func (*V1) Encrypt added in v1.0.0

func (p *V1) Encrypt(key []byte, payload interface{}, footer interface{}) (string, error)

Encrypt implements Protocol.Encrypt

func (*V1) Sign added in v1.0.0

func (p *V1) Sign(privateKey crypto.PrivateKey, payload interface{}, footer interface{}) (string, error)

Sign implements Protocol.Sign. privateKey should be of type *rsa.PrivateKey

func (*V1) Verify added in v1.0.0

func (p *V1) Verify(token string, publicKey crypto.PublicKey, payload interface{}, footer interface{}) error

Verify implements Protocol.Verify. publicKey should be of type *rsa.PublicKey

type V2

type V2 struct {
	// contains filtered or unexported fields
}

V2 is a v2 implementation of PASETO tokens

func NewV2

func NewV2() *V2

NewV2 returns a v2 implementation of PASETO tokens.

func (*V2) Decrypt added in v1.0.0

func (*V2) Decrypt(token string, key []byte, payload interface{}, footer interface{}) error

Decrypt implements Protocol.Decrypt

func (*V2) Encrypt added in v1.0.0

func (p *V2) Encrypt(key []byte, payload interface{}, footer interface{}) (string, error)

Encrypt implements Protocol.Encrypt

func (*V2) Sign added in v1.0.0

func (*V2) Sign(privateKey crypto.PrivateKey, payload interface{}, footer interface{}) (string, error)

Sign implements Protocol.Sign

func (*V2) Verify added in v1.0.0

func (*V2) Verify(token string, publicKey crypto.PublicKey, payload interface{}, footer interface{}) error

Verify implements Protocol.Verify

type Validator added in v0.2.0

type Validator func(token *JSONToken) error

Validator defines a JSONToken validator function.

func ForAudience added in v0.2.0

func ForAudience(audience string) Validator

ForAudience validates that the JSONToken audience has the specified value.

func IdentifiedBy added in v0.2.0

func IdentifiedBy(jti string) Validator

IdentifiedBy validates that the JSONToken JTI has the specified value.

func IssuedBy added in v0.2.0

func IssuedBy(issuer string) Validator

IssuedBy validates that the JSONToken issuer has the specified value.

func Subject added in v0.2.0

func Subject(subject string) Validator

Subject validates that the JSONToken subject has the specified value.

func ValidAt added in v0.2.0

func ValidAt(t time.Time) Validator

ValidAt validates whether the token is valid at the specified time, based on the values of the IssuedAt, NotBefore and Expiration claims in the token.

type Version

type Version string

Version defines the token version.

func Parse

func Parse(token string, payload interface{}, footer interface{},
	symmetricKey []byte, publicKeys map[Version]crypto.PublicKey) (Version, error)

Parse extracts the payload and footer from the token by calling either Decrypt() or Verify(), depending on whether the token is public or private. To parse public tokens you need to provide a map containing v1 and/or v2 public keys, depending on the version of the token. To parse private tokens you need to provide the symmetric key.

Jump to

Keyboard shortcuts

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