diff options
Diffstat (limited to 'lib/paseto')
| -rw-r--r-- | lib/paseto/v4/local_mode.go | 205 | ||||
| -rw-r--r-- | lib/paseto/v4/local_mode_example_test.go | 36 | ||||
| -rw-r--r-- | lib/paseto/v4/local_mode_test.go | 119 | ||||
| -rw-r--r-- | lib/paseto/v4/pasetov4.go | 9 | ||||
| -rw-r--r-- | lib/paseto/v4/testdata/local.json | 135 | ||||
| -rw-r--r-- | lib/paseto/v4/testdata/local.json.license | 2 |
6 files changed, 506 insertions, 0 deletions
diff --git a/lib/paseto/v4/local_mode.go b/lib/paseto/v4/local_mode.go new file mode 100644 index 00000000..507a1484 --- /dev/null +++ b/lib/paseto/v4/local_mode.go @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package pasetov4 + +import ( + "bytes" + "crypto/rand" + "encoding/base64" + "encoding/hex" + "fmt" + "slices" + "strings" + + "git.sr.ht/~shulhan/pakakeh.go/lib/paseto" + "golang.org/x/crypto/blake2b" + "golang.org/x/crypto/chacha20" +) + +const localHeader = `v4.local.` +const localKeyPrefixEncryption = `paseto-encryption-key` +const localKeyPrefixAuth = `paseto-auth-key-for-aead` + +// LocalMode contains the secret for Paseto v4 local protocol. +type LocalMode struct { + v [32]byte +} + +// NewLocalMode returns new local key. +func NewLocalMode(key [32]byte) (lmode *LocalMode) { + lmode = &LocalMode{} + copy(lmode.v[:], key[:]) + return lmode +} + +// Encrypt encrypts the plain text with optional footer and implicit using +// LocalMode as defined in [paseto-v4-encrypt]. +// +// [paseto-v4-encrypt]: https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Version4.md#encrypt +func (lmode LocalMode) Encrypt(plain, footer, implicit []byte) (token string, err error) { + logp := `Encrypt` + + var salt [32]byte + rand.Read(salt[:]) + + token, err = lmode.encrypt(salt, plain, footer, implicit) + if err != nil { + return ``, fmt.Errorf(`%s: %w`, logp, err) + } + return token, nil +} + +func (lmode LocalMode) encrypt(salt [32]byte, plain, footer, implicit []byte) (string, error) { + // Step 4: generate encryption key, authentication key, and nonce. + encKey, authKey, nonce, err := lmode.gen(salt) + if err != nil { + return ``, err + } + + // Step 5: encrpyt the plain text. + cipher, err := chacha20.NewUnauthenticatedCipher(encKey, nonce) + if err != nil { + return ``, err + } + + ciphertext := make([]byte, len(plain)) + cipher.XORKeyStream(ciphertext, plain) + + // Step 6: generate pre-authentication encoding. + pae, err := paseto.PreAuthEncode([]byte(localHeader), salt[:], ciphertext, footer, implicit) + if err != nil { + return ``, err + } + + // Step 7: MAC the pae. + hash, err := blake2b.New256(authKey) + if err != nil { + return ``, err + } + hash.Write(pae) + mac := hash.Sum(nil) + + // Step 8: create the paseto token from cipher text and mac. + var buf bytes.Buffer + + buf.WriteString(localHeader) + + payload := slices.Concat(salt[:], ciphertext, mac) + n := base64.RawURLEncoding.EncodedLen(len(payload)) + b64 := make([]byte, n) + base64.RawURLEncoding.Encode(b64, payload) + buf.Write(b64) + + if len(footer) != 0 { + buf.WriteByte('.') + n = base64.RawURLEncoding.EncodedLen(len(footer)) + b64 = make([]byte, n) + base64.RawURLEncoding.Encode(b64, footer) + buf.Write(b64) + } + + return buf.String(), nil +} + +// Decrypt returns the plain text of encrypted message inside token with +// optional footer. +func (lmode LocalMode) Decrypt(token string, implicit []byte) (plain, footer []byte, err error) { + logp := `Decrypt` + + // Step 3: verify the header. + token, found := strings.CutPrefix(token, localHeader) + if !found { + return nil, nil, fmt.Errorf(`%s: invalid header, want %s`, logp, localHeader) + } + + token, footerb64, found := strings.Cut(token, `.`) + if found { + footer, err = base64.RawURLEncoding.DecodeString(footerb64) + if err != nil { + return nil, nil, fmt.Errorf(`%s: invalid footer: %w`, logp, err) + } + } + + payload, err := base64.RawURLEncoding.DecodeString(token) + if err != nil { + return nil, nil, fmt.Errorf(`%s: %w`, logp, err) + } + + // Step 4: decode the + if len(payload) <= 64 { // 64 is size of nonce + mac. + return nil, nil, fmt.Errorf(`%s: invalid payload length`, logp) + } + + var salt [32]byte + copy(salt[:], payload[:32]) + mac := payload[len(payload)-32:] + ciphertext := payload[32 : len(payload)-32] + + // Step 5: generate encryption key, authentication key, and nonce. + encKey, authKey, nonce, err := lmode.gen(salt) + if err != nil { + return nil, nil, err + } + + // Step 6: generate pre-authentication encoding. + pae, err := paseto.PreAuthEncode([]byte(localHeader), salt[:], ciphertext, footer, implicit) + if err != nil { + return nil, nil, err + } + + // Step 7: MAC the pae. + hash, err := blake2b.New256(authKey) + if err != nil { + return nil, nil, err + } + hash.Write(pae) + gotmac := hash.Sum(nil) + + // Step 8: Compare MAC. + if !bytes.Equal(mac, gotmac) { + return nil, nil, fmt.Errorf(`%s: MAC mismatch`, logp) + } + + // Step 9: Decrypt the cipher text. + cipher, err := chacha20.NewUnauthenticatedCipher(encKey, nonce) + if err != nil { + return nil, nil, fmt.Errorf(`%s: %w`, logp, err) + } + + plain = make([]byte, len(ciphertext)) + cipher.XORKeyStream(plain, ciphertext) + + return plain, footer, nil +} + +// gen generates new encryption key, authentication key, and nonce. +func (lmode LocalMode) gen(salt [32]byte) (encKey, authKey, nonce []byte, err error) { + logp := `gen` + + hash, err := blake2b.New(56, lmode.v[:]) + if err != nil { + return nil, nil, nil, fmt.Errorf(`%s: %w`, logp, err) + } + hash.Write([]byte(localKeyPrefixEncryption)) + hash.Write(salt[:]) + out := hash.Sum(nil) + encKey = out[:32] + nonce = out[32:56] + + // Create new hash with 32 bytes size. + hash, err = blake2b.New256(lmode.v[:]) + if err != nil { + return nil, nil, nil, fmt.Errorf(`%s: %w`, logp, err) + } + hash.Write([]byte(localKeyPrefixAuth)) + hash.Write(salt[:]) + authKey = hash.Sum(nil) + + return encKey, authKey, nonce, nil +} + +// Hex returns the key as hexadecimal string. +func (lmode LocalMode) Hex() string { + return hex.EncodeToString(lmode.v[:]) +} diff --git a/lib/paseto/v4/local_mode_example_test.go b/lib/paseto/v4/local_mode_example_test.go new file mode 100644 index 00000000..a9708935 --- /dev/null +++ b/lib/paseto/v4/local_mode_example_test.go @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package pasetov4 + +import ( + "encoding/hex" + "fmt" + "log" +) + +func ExampleLocalMode() { + secret := `707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f` + key, err := hex.DecodeString(secret) + if err != nil { + log.Fatal(err) + } + lmode := NewLocalMode([32]byte(key)) + plain := []byte(`{"data":"Hello, secret!"}`) + footer := []byte(`{"kid":1000}`) + token, err := lmode.Encrypt(plain, footer, nil) + if err != nil { + log.Fatal(err) + } + + gotPlain, gotFooter, err := lmode.Decrypt(token, nil) + if err != nil { + log.Fatal(err) + } + fmt.Println(string(gotPlain)) + fmt.Println(string(gotFooter)) + + // Output: + // {"data":"Hello, secret!"} + // {"kid":1000} +} diff --git a/lib/paseto/v4/local_mode_test.go b/lib/paseto/v4/local_mode_test.go new file mode 100644 index 00000000..144b8c74 --- /dev/null +++ b/lib/paseto/v4/local_mode_test.go @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package pasetov4 + +import ( + "encoding/hex" + "encoding/json" + "log" + "os" + "testing" + + "git.sr.ht/~shulhan/pakakeh.go/lib/test" +) + +type testVectorLocal struct { + Name string + Key string + Nonce string + Token string + Payload string + Footer string + Implicit string `json:"implicit-assertion"` + key [32]byte + salt [32]byte + ExpectFail bool `json:"expect-fail"` +} + +func (tvl *testVectorLocal) init() { + v, err := hex.DecodeString(tvl.Key) + if err != nil { + log.Fatalf(`key: %s`, err) + } + copy(tvl.key[:], v) + + v, err = hex.DecodeString(tvl.Nonce) + if err != nil { + log.Fatalf(`key: %s`, err) + } + copy(tvl.salt[:], v) +} + +func TestNewLocalMode(t *testing.T) { + v, err := hex.DecodeString(`707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f`) + if err != nil { + t.Fatal(err) + } + lmode := NewLocalMode([32]byte(v)) + hex := lmode.Hex() + test.Assert(t, `LocalMode hex length`, 64, len(hex)) +} + +func TestLocalMode_Encrypt(t *testing.T) { + logp := `TestLocalMode_Encrypt` + + localb, err := os.ReadFile(`testdata/local.json`) + if err != nil { + t.Fatalf(`%s: %s`, logp, err) + } + listCase := []testVectorLocal{} + err = json.Unmarshal(localb, &listCase) + if err != nil { + t.Fatalf(`%s: %s`, logp, err) + } + + for _, tc := range listCase { + tc.init() + lmode := NewLocalMode(tc.key) + got, err := lmode.encrypt(tc.salt, []byte(tc.Payload), + []byte(tc.Footer), []byte(tc.Implicit)) + if err != nil { + if tc.ExpectFail { + continue + } + t.Fatalf(`%s: %s`, logp, err) + } + if tc.Token == string(got) { + continue + } + if tc.ExpectFail { + continue + } + t.Fatalf(`%s: token not match`, tc.Name) + } +} + +func TestLocalMode_Decrypt(t *testing.T) { + logp := `TestLocalMode_Decrypt` + + localb, err := os.ReadFile(`testdata/local.json`) + if err != nil { + t.Fatalf(`%s: %s`, logp, err) + } + listCase := []testVectorLocal{} + err = json.Unmarshal(localb, &listCase) + if err != nil { + t.Fatalf(`%s: %s`, logp, err) + } + + for _, tc := range listCase { + tc.init() + lmode := NewLocalMode(tc.key) + gotPlain, gotFooter, err := lmode.Decrypt(tc.Token, []byte(tc.Implicit)) + if err != nil { + if tc.ExpectFail { + continue + } + t.Fatalf(`%s: %s`, logp, err) + } + if tc.Payload != string(gotPlain) && tc.ExpectFail { + continue + } + if tc.Footer != string(gotFooter) && tc.ExpectFail { + continue + } + test.Assert(t, tc.Name+` payload`, tc.Payload, string(gotPlain)) + test.Assert(t, tc.Name+` footer`, tc.Footer, string(gotFooter)) + } +} diff --git a/lib/paseto/v4/pasetov4.go b/lib/paseto/v4/pasetov4.go new file mode 100644 index 00000000..3897186c --- /dev/null +++ b/lib/paseto/v4/pasetov4.go @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +// Package pasetov4 provides a simple, ready to use, opinionated +// implementation of Platform-Agnostic SEcurity TOkens (PASETO) version 4 as +// defined in [paseto-v4]. +// +// [paseto-v4]: https://github.com/paseto-standard/paseto-spec/blob/master/docs/01-Protocol-Versions/Version4.md +package pasetov4 diff --git a/lib/paseto/v4/testdata/local.json b/lib/paseto/v4/testdata/local.json new file mode 100644 index 00000000..ae73f38a --- /dev/null +++ b/lib/paseto/v4/testdata/local.json @@ -0,0 +1,135 @@ +[ + { + "name": "4-E-1", + "expect-fail": false, + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "nonce": "0000000000000000000000000000000000000000000000000000000000000000", + "token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvSwscFlAl1pk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XJ5hOb_4v9RmDkneN0S92dx0OW4pgy7omxgf3S8c3LlQg", + "payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "", + "implicit-assertion": "" + }, + { + "name": "4-E-2", + "expect-fail": false, + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "nonce": "0000000000000000000000000000000000000000000000000000000000000000", + "token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvS2csCgglvpk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XIemu9chy3WVKvRBfg6t8wwYHK0ArLxxfZP73W_vfwt5A", + "payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "", + "implicit-assertion": "" + }, + { + "name": "4-E-3", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6-tyebyWG6Ov7kKvBdkrrAJ837lKP3iDag2hzUPHuMKA", + "payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "", + "implicit-assertion": "" + }, + { + "name": "4-E-4", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4gt6TiLm55vIH8c_lGxxZpE3AWlH4WTR0v45nsWoU3gQ", + "payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "", + "implicit-assertion": "" + }, + { + "name": "4-E-5", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4x-RMNXtQNbz7FvFZ_G-lFpk5RG3EOrwDL6CgDqcerSQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", + "payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}", + "implicit-assertion": "" + }, + { + "name": "4-E-6", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6pWSA5HX2wjb3P-xLQg5K5feUCX4P2fpVK3ZLWFbMSxQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", + "payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}", + "implicit-assertion": "" + }, + { + "name": "4-E-7", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t40KCCWLA7GYL9KFHzKlwY9_RnIfRrMQpueydLEAZGGcA.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", + "payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}", + "implicit-assertion": "{\"test-vector\":\"4-E-7\"}" + }, + { + "name": "4-E-8", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t5uvqQbMGlLLNYBc7A6_x7oqnpUK5WLvj24eE4DVPDZjw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", + "payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}", + "implicit-assertion": "{\"test-vector\":\"4-E-8\"}" + }, + { + "name": "4-E-9", + "expect-fail": false, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6tybdlmnMwcDMw0YxA_gFSE_IUWl78aMtOepFYSWYfQA.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24", + "payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}", + "footer": "arbitrary-string-that-isn't-json", + "implicit-assertion": "{\"test-vector\":\"4-E-9\"}" + }, + { + "name": "4-F-1", + "expect-fail": true, + "public-key": "1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2", + "secret-key": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2", + "secret-key-seed": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a3774", + "secret-key-pem": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----", + "public-key-pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----", + "token": "v4.local.vngXfCISbnKgiP6VWGuOSlYrFYU300fy9ijW33rznDYgxHNPwWluAY2Bgb0z54CUs6aYYkIJ-bOOOmJHPuX_34Agt_IPlNdGDpRdGNnBz2MpWJvB3cttheEc1uyCEYltj7wBQQYX.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24", + "payload": null, + "footer": "arbitrary-string-that-isn't-json", + "implicit-assertion": "{\"test-vector\":\"4-F-1\"}" + }, + { + "name": "4-F-3", + "expect-fail": true, + "nonce": "26f7553354482a1d91d4784627854b8da6b8042a7966523c2b404e8dbbe7f7f2", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v3.local.23e_2PiqpQBPvRFKzB0zHhjmxK3sKo2grFZRRLM-U7L0a8uHxuF9RlVz3Ic6WmdUUWTxCaYycwWV1yM8gKbZB2JhygDMKvHQ7eBf8GtF0r3K0Q_gF1PXOxcOgztak1eD1dPe9rLVMSgR0nHJXeIGYVuVrVoLWQ.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24", + "payload": null, + "footer": "arbitrary-string-that-isn't-json", + "implicit-assertion": "{\"test-vector\":\"4-F-3\"}" + }, + { + "name": "4-F-4", + "expect-fail": true, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvSwscFlAl1pk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XJ5hOb_4v9RmDkneN0S92dx0OW4pgy7omxgf3S8c3LlQh", + "payload": null, + "footer": "", + "implicit-assertion": "" + }, + { + "name": "4-F-5", + "expect-fail": true, + "nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8", + "key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f", + "token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4x-RMNXtQNbz7FvFZ_G-lFpk5RG3EOrwDL6CgDqcerSQ==.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9", + "payload": null, + "footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}", + "implicit-assertion": "" + } +] diff --git a/lib/paseto/v4/testdata/local.json.license b/lib/paseto/v4/testdata/local.json.license new file mode 100644 index 00000000..870a8126 --- /dev/null +++ b/lib/paseto/v4/testdata/local.json.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: BSD-3-Clause +SPDX-FileCopyrightText: 2021 Paragon Initiative Enterprises <security at paragonie dot com> |
