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 ¶
- Variables
- func Decrypt(aead cipher.AEAD, token string) (plain, footer []byte, err error)
- func Encrypt(aead cipher.AEAD, plain, footer []byte) (token string, err error)
- func Sign(sk ed25519.PrivateKey, m, f []byte) (token string, err error)
- func Verify(pk ed25519.PublicKey, sm, f []byte) (msg []byte, err error)
- type JSONFooter
- type JSONToken
- type Key
- type LocalMode
- type PublicMode
- func (auth *PublicMode) AddPeer(k Key) (err error)
- func (auth *PublicMode) GetPeerKey(id string) (k Key, ok bool)
- func (auth *PublicMode) Pack(audience, subject string, data []byte, footer map[string]interface{}) (token string, err error)
- func (auth *PublicMode) RemovePeer(id string)
- func (auth *PublicMode) Unpack(token string) (publicToken *PublicToken, err error)
- func (auth *PublicMode) UnpackHTTPRequest(req *http.Request) (publicToken *PublicToken, err error)
- type PublicToken
Examples ¶
Constants ¶
This section is empty.
Variables ¶
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 ¶
Decrypt given a shared key and encrypted token, decrypt the token to get the message.
func Encrypt ¶
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.
Types ¶
type JSONFooter ¶
type JSONFooter struct {}
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 ¶
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 ¶
NewLocalMode create and initialize new LocalMode using shared key.
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 ¶
PublicToken contains the unpacked public token.