diff --git a/README.md b/README.md index 3489ed0..66bec28 100644 --- a/README.md +++ b/README.md @@ -46,23 +46,7 @@ $ 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: -```go -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}) -``` +During the encoding process a payload of type string and []byte is used without transformation. For other data types, the library encodes the payload to json. ## Create token using symmetric key (local mode): ```go @@ -84,16 +68,14 @@ jsonToken := paseto.JSONToken{ jsonToken.Set("data", "this is a signed message") footer := "some footer" -v2 := paseto.NewV2() - // Encrypt data -token, err := v2.Encrypt(symmetricKey, jsonToken, footer) +token, err := paseto.Encrypt(symmetricKey, jsonToken, 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) +err := paseto.Decrypt(token, symmetricKey, &newJsonToken, &newFooter) ``` ## Create token using asymetric key (public mode): @@ -115,16 +97,32 @@ jsonToken := paseto.JSONToken{ jsonToken.Set("data", "this is a signed message") footer := "some footer" -v2 := paseto.NewV2() - // Sign data -token, err := v2.Sign(privateKey, jsonToken, footer) +token, err := paseto.Sign(privateKey, jsonToken, 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) +err := paseto.Verify(token, publicKey, &newJsonToken, &newFooter) +``` + +## Use Parse() function to parse all supported token versions: +**IMPORTANT**: Version 1 of protocol is deprecated + +```go +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}) ``` For more information see *_test.go files. diff --git a/parser.go b/parser.go index 978b864..4bea018 100644 --- a/parser.go +++ b/parser.go @@ -33,9 +33,33 @@ var availableVersions = map[Version]Protocol{ VersionV2: NewV2(), } +// Encrypt encrypts a token with a symmetric key. The key length must be 32. +// Uses V2 protocol as default +func Encrypt(key []byte, payload, footer interface{}) (string, error) { + return NewV2().Encrypt(key, payload, footer) +} + +// Decrypt decrypts a token. +// Uses V2 protocol as default. +func Decrypt(token string, key []byte, payload, footer interface{}) error { + return NewV2().Decrypt(token, key, payload, footer) +} + +// Sign signs a token with the given private key. The key should be an ed25519.PrivateKey. +// Uses V2 protocol as default. +func Sign(privateKey crypto.PrivateKey, payload, footer interface{}) (string, error) { + return NewV2().Sign(privateKey, payload, footer) +} + +// Verify verifies a token against the given public key. The key should be an ed25519.PublicKey. +// Uses V2 protocol as default. +func Verify(token string, publicKey crypto.PublicKey, value, footer interface{}) error { + return NewV2().Verify(token, publicKey, value, footer) +} + // 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 +// 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. func Parse(token string, payload, footer interface{}, diff --git a/parser_test.go b/parser_test.go index 1ce3283..6d57ed8 100644 --- a/parser_test.go +++ b/parser_test.go @@ -9,6 +9,82 @@ import ( "golang.org/x/crypto/ed25519" ) +func TestEncrypt(t *testing.T) { + key := []byte("YELLOW SUBMARINE, BLACK WIZARDRY") + payload := []byte("payload") + footer := []byte("footer") + + token, err := Encrypt(key, payload, footer) + if assert.NoError(t, err) { + var ( + decryptedPayload []byte + decryptedFooter []byte + ) + if err = NewV2().Decrypt(token, key, &decryptedPayload, &decryptedFooter); assert.NoError(t, err) { + assert.Equal(t, payload, decryptedPayload) + assert.Equal(t, footer, decryptedFooter) + } + } +} + +func TestDecrypt(t *testing.T) { + key := []byte("YELLOW SUBMARINE, BLACK WIZARDRY") + payload := []byte("payload") + footer := []byte("footer") + + token, err := NewV2().Encrypt(key, payload, footer) + if assert.NoError(t, err) { + var ( + decryptedPayload []byte + decryptedFooter []byte + ) + if err = Decrypt(token, key, &decryptedPayload, &decryptedFooter); assert.NoError(t, err) { + assert.Equal(t, payload, decryptedPayload) + assert.Equal(t, footer, decryptedFooter) + } + } +} + +func TestSign(t *testing.T) { + b, _ := hex.DecodeString("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2") + privateKey := ed25519.PrivateKey(b) + + payload := []byte("payload") + footer := []byte("footer") + + token, err := Sign(privateKey, payload, footer) + if assert.NoError(t, err) { + var ( + decryptedPayload []byte + decryptedFooter []byte + ) + if err := NewV2().Verify(token, privateKey.Public(), &decryptedPayload, &decryptedFooter); assert.NoError(t, err) { + assert.Equal(t, payload, decryptedPayload) + assert.Equal(t, footer, decryptedFooter) + } + } +} + +func TestVerify(t *testing.T) { + b, _ := hex.DecodeString("b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2") + privateKey := ed25519.PrivateKey(b) + + payload := []byte("payload") + footer := []byte("footer") + + token, err := NewV2().Sign(privateKey, payload, footer) + if assert.NoError(t, err) { + var ( + decryptedPayload []byte + decryptedFooter []byte + ) + if err := Verify(token, privateKey.Public(), &decryptedPayload, &decryptedFooter); assert.NoError(t, err) { + assert.Equal(t, payload, decryptedPayload) + assert.Equal(t, footer, decryptedFooter) + } + } +} + func TestParse(t *testing.T) { symmetricKey, _ := hex.DecodeString("707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f")