paseto

package
Version: v0.37.0 Latest Latest
Warning

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

Go to latest
Published: May 9, 2022 License: BSD-3-Clause Imports: 15 Imported by: 0

Documentation

Overview

Package paseto provide a simple, ready to use, opinionated implementation of Platform-Agnostic SEcurity TOkens (PASETOs) v2 as defined in draft of RFC 01 [1].

# Limitation

This implementation only support PASETO Protocol v2.

# Local mode

The local mode use crypto/rand package to generate random nonce and hashed with blake2b.

# Public mode

The public mode focus on signing and verifing data, everything else is handled and filled automatically.

Steps for sender when generating new token, the Pack() method,

- Prepare the JSON token claims, set
  ** Issuer "iss" to PublicMode.our.ID
  ** Subject "sub" to subject value from parameter
  ** Audience "aud" to audience value from parameter
  ** IssuedAt to current time
  ** NotBefore to current time
  ** ExpiredAt to current time + 60 seconds
  ** Data field to base64 encoded of data value from parameter
- Prepare the JSON footer, set
  ** Key ID "kid" to PublicMode.our.ID

The user's claims data is stored using key "data" inside the JSON token, encoded using base64 (with padding). Additional footer data can be added on the Data field.

Overall, the following JSONToken and JSONFooter is generated for each token,

JSONToken:{
	"iss": <Key.ID>,
	"sub": <Subject parameter>,
	"aud": <Audience parameter>
	"exp": <time.Now() + TTL>,
	"iat": <time.Now()>,
	"nbf": <time.Now()>,
	"data": <base64.StdEncoding.EncodeToString(userData)>,
}
JSONFooter:{
	"kid": <Key.ID>,
	"data": {}
}

On the receiver side, they will have list of registered peers Key (include ID, public Key, and list of allowed subject).

PublicMode:{
	peers: map[Key.ID]Key{
		Public: <ed25519.PublicKey>,
		AllowedSubjects: map[string]struct{}{
			"/api/x": struct{}{},
			"/api/y:read": struct{}{},
			"/api/z:write": struct{}{},
			...
		},
	},
}

Step for receiver to process the token, the Unpack() method,

- Decode the token footer
- Get the registered public key based on "kid" value in token footer
  ** If no peers key exist matched with "kid" value, reject the token
- Verify the token using the peer public key
  ** If verification failed, reject the token
- Validate the token
  ** The Issuer must equal to peer ID
  ** The Audience must equal to receiver ID
  ** If the peer AllowedSubjects is not empty, the Subject must be in
  one of them
  ** The current time must be after IssuedAt
  ** The current time must be after NotBefore
  ** The current time must be before ExpiredAt
  ** If one of the above condition is not passed, it will return an error.

# References

[1] https://github.com/paragonie/paseto/blob/master/docs/RFC/draft-paragon-paseto-rfc-01.txt

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultTTL = 60 * time.Second

DefaultTTL define the time-to-live of each token, by setting ExpiredAt to current time + DefaultTTL. If you want longer token, increase this value before using Pack().

Functions

func Decrypt

func Decrypt(aead cipher.AEAD, token string) (plain, footer []byte, err error)

Decrypt given a shared key and encrypted token, decrypt the token to get the message.

func Encrypt

func Encrypt(aead cipher.AEAD, plain, footer []byte) (token string, err error)

Encrypt given the shared key, encrypt the plain message and generate the "local" token with optional footer.

func Sign

func Sign(sk ed25519.PrivateKey, m, f []byte) (token string, err error)

Sign given an Ed25519 secret key "sk", a message "m", and optional footer "f" (which defaults to empty string); sign the message "m" and generate the public token.

func Verify

func Verify(pk ed25519.PublicKey, sm, f []byte) (msg []byte, err error)

Verify given a public key "pk", a signed message "sm" (that has been decoded from base64), and optional footer "f" (also that has been decoded from base64 string); verify that the signature is valid for the message.

Types

type JSONFooter

type JSONFooter struct {
	KID  string                 `json:"kid"`
	Data map[string]interface{} `json:"data,omitempty"`
}

type JSONToken

type JSONToken struct {
	Issuer    string     `json:"iss,omitempty"`
	Subject   string     `json:"sub,omitempty"`
	Audience  string     `json:"aud,omitempty"`
	ExpiredAt *time.Time `json:"exp,omitempty"`
	NotBefore *time.Time `json:"nbf,omitempty"`
	IssuedAt  *time.Time `json:"iat,omitempty"`
	TokenID   string     `json:"jti,omitempty"`
	Data      string     `json:"data"`
}

func (*JSONToken) Validate

func (jtoken *JSONToken) Validate(audience string, peer Key) (err error)

Validate the JSON token fields,

- The Issuer must equal to peer.ID
- The Audience must equal to received ID,
- If peer.AllowedSubjects is not empty, the Subject value must be in
  one of them,
- The current time must be after IssuedAt field,
- The current time must after NotBefore "nbf" field,
- The current time must before ExpiredAt field.

If one of the above condition is not passed, it will return an error.

type Key

type Key struct {
	// ID is a unique key ID.
	ID string

	// PrivateKey for signing public token.
	Private ed25519.PrivateKey

	// PublicKey for verifying public token.
	Public ed25519.PublicKey

	// AllowedSubjects contains list of subject that are allowed in the
	// token's claim "sub" to be signed by this public key.
	// This field is used by receiver to check the claim "sub" and compare
	// it with this list.
	// Empty list means allowing all subjects.
	AllowedSubjects map[string]struct{}
}

type LocalMode

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

LocalMode implement the PASETO encrypt and decrypt using shared key.

Example
//
// In local mode, we create sender and receiver using the same key.
//
key, _ := hex.DecodeString("707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f")
sender, _ := NewLocalMode(key)
receiver, _ := NewLocalMode(key)

token, err := sender.Pack([]byte("Hello receiver"), []byte(">>> footer"))
if err != nil {
	log.Fatal(err)
}

// Sender then send the encrypted token to receiver
// ...

// Receiver unpack the token from sender to get the plain text and
// footer.
plain, footer, err := receiver.Unpack(token)
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Receive data from sender: %s\n", plain)
fmt.Printf("Receive footer from sender: %s\n", footer)
Output:

Receive data from sender: Hello receiver
Receive footer from sender: >>> footer

func NewLocalMode

func NewLocalMode(key []byte) (local *LocalMode, err error)

NewLocalMode create and initialize new LocalMode using shared key.

func (*LocalMode) Pack

func (l *LocalMode) Pack(data, footer []byte) (token string, err error)

Pack encrypt the data and generate token with optional footer.

func (*LocalMode) Unpack

func (l *LocalMode) Unpack(token string) (data, footer []byte, err error)

Unpack decrypt the token and return the plain data and optional footer.

type PublicMode

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

PublicMode implement the PASETO public mode to signing and verifying data using private key and one or more shared public keys. The PublicMode contains list of peer public keys for verifying the incoming token.

Example
subjectMessage := "message"

senderSK, _ := hex.DecodeString("e9ae9c7eae2fce6fd6727b5ca8df0fbc0aa60a5ffb354d4fdee1729e4e1463688d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
senderPK, _ := hex.DecodeString("8d2160a4dc71a9a697d6ad6424da3f9dd18a259cdd51b0ae2b521e998b82d36e")
senderKey := Key{
	ID:      "sender",
	Private: ed25519.PrivateKey(senderSK),
	Public:  ed25519.PublicKey(senderPK),
	AllowedSubjects: map[string]struct{}{
		subjectMessage: struct{}{},
	},
}

receiverSK, _ := hex.DecodeString("4983da648bff1fd3e1892df9c56370215aa640829a5cab02d6616b115fa0bc5707c22e74ab9b181f8d87bdf03cf88476ec4c35e5517e173f236592f6695d59f5")
receiverPK, _ := hex.DecodeString("07c22e74ab9b181f8d87bdf03cf88476ec4c35e5517e173f236592f6695d59f5")
receiverKey := Key{
	ID:      "receiver",
	Private: ed25519.PrivateKey(receiverSK),
	Public:  ed25519.PublicKey(receiverPK),
}

//
// In the sender part, we register the sender key and the public key
// of receiver in the list of peers.
//
sender, err := NewPublicMode(senderKey)
if err != nil {
	log.Fatal(err)
}

err = sender.AddPeer(receiverKey)
if err != nil {
	log.Fatal(err)
}

footer := map[string]interface{}{
	"FOOTER": "HERE",
}
token, err := sender.Pack(receiverKey.ID, subjectMessage, []byte("hello receiver"), footer)
if err != nil {
	log.Fatal(err)
}
invalidToken, err := sender.Pack(receiverKey.ID, "unknown-subject", []byte("hello receiver"), footer)
if err != nil {
	log.Fatal(err)
}

// token generated by sender and send to receiver
// ...

//
// In the receiver part, we register the receiver key and the public key
// of sender in the list of peers.
//
receiver, err := NewPublicMode(receiverKey)
if err != nil {
	log.Fatal(err)
}

err = receiver.AddPeer(senderKey)
if err != nil {
	log.Fatal(err)
}

// receiver receive the token from sender and unpack it ...
got, err := receiver.Unpack(token)
if err != nil {
	log.Fatal(err)
}

fmt.Printf("Received data: %s\n", got.Data)
fmt.Printf("Received footer: %+v\n", got.Footer)

// receiver receive invalid token from sender and unpack it ...
got, err = receiver.Unpack(invalidToken)
if err != nil {
	fmt.Println(err)
}
Output:

Received data: hello receiver
Received footer: {KID:sender Data:map[FOOTER:HERE]}
token subject "unknown-subject" is not allowed for key "sender"

func NewPublicMode

func NewPublicMode(our Key) (auth *PublicMode, err error)

NewPublicMode create new PublicMode with our private key for signing outgoing token.

func (*PublicMode) AddPeer

func (auth *PublicMode) AddPeer(k Key) (err error)

AddPeer add a key to list of known peers for verifying incoming token. The Key.Public

func (*PublicMode) GetPeerKey

func (auth *PublicMode) GetPeerKey(id string) (k Key, ok bool)

GetPeerKey get the peer's key based on key ID.

func (*PublicMode) Pack

func (auth *PublicMode) Pack(audience, subject string, data []byte, footer map[string]interface{}) (
	token string, err error,
)

Pack the data into token.

func (*PublicMode) RemovePeer

func (auth *PublicMode) RemovePeer(id string)

RemovePeer remove peer's key from list.

func (*PublicMode) Unpack

func (auth *PublicMode) Unpack(token string) (publicToken *PublicToken, err error)

Unpack the token to get the JSONToken and the data.

func (*PublicMode) UnpackHTTPRequest

func (auth *PublicMode) UnpackHTTPRequest(req *http.Request) (
	publicToken *PublicToken, err error,
)

UnpackHTTPRequest unpack token from HTTP request header "Authorization" or from query parameter "access_token".

type PublicToken

type PublicToken struct {
	Token  JSONToken
	Data   []byte
	Footer JSONFooter
}

PublicToken contains the unpacked public token.

Jump to

Keyboard shortcuts

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