|
1 | 1 | local v2 = { |
2 | | - _VERSION = 'paseto v0.1.0', |
| 2 | + _VERSION = 'paseto v0.2.0', |
3 | 3 | _DESCRIPTION = 'PASETO (Platform-Agnostic Security Tokens) for Lua', |
4 | 4 | _URL = 'https://github.com/peter-evans/paseto-lua', |
5 | 5 | _LICENSE = [[ |
@@ -27,121 +27,121 @@ local v2 = { |
27 | 27 | ]] |
28 | 28 | } |
29 | 29 |
|
30 | | -local PROTOCOL_VERSION = "v2" |
31 | | -local luasodium = require("luasodium") |
32 | | -local basexx = require("basexx") |
33 | | -local struct = require("struct") |
| 30 | +local paseto = require "paseto.v2.core" |
| 31 | +local json = require "cjson" |
34 | 32 |
|
35 | | -function v2.__pre_auth_encode(...) |
36 | | - local encoded = struct.pack("<L", #{...}) |
37 | | - for _, piece in ipairs({...}) |
38 | | - do |
39 | | - encoded = encoded .. struct.pack("<L", #piece) .. piece |
| 33 | +local function encode_json(data) |
| 34 | + local ok, encoded = pcall(function() |
| 35 | + return json.encode(data) |
| 36 | + end) |
| 37 | + if not ok then |
| 38 | + return nil, "Invalid data" |
40 | 39 | end |
41 | 40 | return encoded |
42 | 41 | end |
43 | 42 |
|
44 | | -function v2.__validate_and_remove_footer(token, footer) |
45 | | - if not footer or footer == "" then |
46 | | - return token |
| 43 | +local function decode_json(data) |
| 44 | + local ok, decoded = pcall(function() |
| 45 | + return json.decode(data) |
| 46 | + end) |
| 47 | + if not ok then |
| 48 | + return nil, "Invalid JSON" |
47 | 49 | end |
48 | | - footer = basexx.to_url64(footer) |
49 | | - local trailing = string.sub(token, #token - #footer + 1, #token) |
50 | | - if trailing ~= footer then |
51 | | - return nil, "Invalid message footer" |
52 | | - end |
53 | | - return string.sub(token, 1, #token - #footer - 1) |
| 50 | + return decoded |
54 | 51 | end |
55 | 52 |
|
56 | | -local function aead_encrypt(key, payload, header, footer, nonce_key) |
57 | | - if #nonce_key == 0 then |
58 | | - nonce_key = luasodium.randombytes(luasodium.SYMMETRIC_NONCEBYTES) |
59 | | - end |
60 | | - local nonce = luasodium.generichash(payload, nonce_key, luasodium.SYMMETRIC_NONCEBYTES) |
61 | | - local additional_data = v2.__pre_auth_encode(header, nonce, footer) |
62 | | - local ciphertext, err = luasodium.aead_encrypt(payload, additional_data, nonce, key) |
63 | | - if not ciphertext then |
64 | | - return nil, err |
65 | | - end |
66 | | - local token = header .. basexx.to_url64(nonce .. ciphertext) .. |
67 | | - (#footer > 0 and "." .. basexx.to_url64(footer) or "") |
| 53 | +-- API -- |
68 | 54 |
|
69 | | - return token |
70 | | -end |
71 | | - |
72 | | -local function aead_decrypt(key, encrypted, header, footer) |
73 | | - if header ~= string.sub(encrypted, 1, #header) then |
74 | | - return nil, "Invalid message header" |
75 | | - end |
76 | | - local decoded = basexx.from_url64(string.sub(encrypted, #header + 1)) |
77 | | - local nonce = string.sub(decoded, 1, luasodium.SYMMETRIC_NONCEBYTES) |
78 | | - local ciphertext = string.sub(decoded, luasodium.SYMMETRIC_NONCEBYTES + 1, #decoded) |
79 | | - local additional_data = v2.__pre_auth_encode(header, nonce, footer) |
80 | | - local decrypted, err = luasodium.aead_decrypt(ciphertext, additional_data, nonce, key) |
81 | | - return decrypted, err |
82 | | -end |
83 | | - |
84 | | -function v2.get_symmetric_key_byte_length() |
85 | | - return luasodium.SYMMETRIC_KEYBYTES |
86 | | -end |
| 55 | +v2.SYMMETRIC_KEY_BYTES = paseto.SYMMETRIC_KEY_BYTES |
| 56 | +v2.ASYMMETRIC_PUBLIC_KEY_BYTES = paseto.ASYMMETRIC_PUBLIC_KEY_BYTES |
| 57 | +v2.ASYMMETRIC_SECRET_KEY_BYTES = paseto.ASYMMETRIC_SECRET_KEY_BYTES |
87 | 58 |
|
88 | 59 | function v2.generate_symmetric_key() |
89 | | - return luasodium.randombytes(luasodium.SYMMETRIC_KEYBYTES) |
| 60 | + return paseto.generate_symmetric_key() |
90 | 61 | end |
91 | 62 |
|
92 | 63 | function v2.generate_asymmetric_secret_key() |
93 | | - return luasodium.sign_keypair() |
| 64 | + return paseto.generate_asymmetric_secret_key() |
| 65 | +end |
| 66 | + |
| 67 | +function v2.extract_version_purpose(token) |
| 68 | + return paseto.extract_version_purpose(token) |
94 | 69 | end |
95 | 70 |
|
96 | | -function v2.__encrypt(key, payload, footer, nonce) |
97 | | - nonce = nonce or "" |
98 | | - return aead_encrypt(key, payload, PROTOCOL_VERSION .. ".local.", footer, nonce) |
| 71 | +function v2.extract_footer_claims(token) |
| 72 | + local footer, err = paseto.extract_footer(token) |
| 73 | + if footer == nil then |
| 74 | + return nil, nil, err |
| 75 | + end |
| 76 | + if footer == "" then |
| 77 | + return {}, footer |
| 78 | + end |
| 79 | + local footer_claims |
| 80 | + footer_claims, err = decode_json(footer) |
| 81 | + if footer_claims == nil then |
| 82 | + return nil, nil, err |
| 83 | + end |
| 84 | + return footer_claims, footer |
99 | 85 | end |
100 | 86 |
|
101 | | -function v2.encrypt(key, payload, footer) |
102 | | - footer = footer or "" |
103 | | - return v2.__encrypt(key, payload, footer) |
| 87 | +function v2.encrypt(key, payload_claims, footer_claims) |
| 88 | + local payload, footer |
| 89 | + payload = encode_json(payload_claims) |
| 90 | + if payload == nil or type(payload_claims) ~= "table" then |
| 91 | + return nil, "Invalid payload claims" |
| 92 | + end |
| 93 | + if footer_claims == nil then |
| 94 | + footer = "" |
| 95 | + else |
| 96 | + footer = encode_json(footer_claims) |
| 97 | + if footer == nil or type(footer_claims) ~= "table" then |
| 98 | + return nil, "Invalid footer claims" |
| 99 | + end |
| 100 | + end |
| 101 | + return paseto.encrypt(key, payload, footer) |
104 | 102 | end |
105 | 103 |
|
106 | 104 | function v2.decrypt(key, token, footer) |
107 | | - footer = footer or "" |
108 | | - local encrypted_payload, err = v2.__validate_and_remove_footer(token, footer) |
109 | | - if not encrypted_payload then |
| 105 | + local err, decrypted, payload_claims |
| 106 | + decrypted, err = paseto.decrypt(key, token, footer) |
| 107 | + if decrypted == nil then |
110 | 108 | return nil, err |
111 | 109 | end |
112 | | - return aead_decrypt(key, encrypted_payload, PROTOCOL_VERSION .. ".local.", footer) |
| 110 | + payload_claims, err = decode_json(decrypted) |
| 111 | + if payload_claims == nil then |
| 112 | + return nil, err |
| 113 | + end |
| 114 | + return payload_claims |
113 | 115 | end |
114 | 116 |
|
115 | | -function v2.sign(secret_key, message, footer) |
116 | | - footer = footer or "" |
117 | | - local header = PROTOCOL_VERSION .. ".public." |
118 | | - local data = v2.__pre_auth_encode(header, message, footer) |
119 | | - local signature = luasodium.sign_detached(data, secret_key) |
120 | | - local token = header .. basexx.to_url64(message .. signature) .. |
121 | | - (#footer > 0 and "." .. basexx.to_url64(footer) or "") |
122 | | - return token |
| 117 | +function v2.sign(secret_key, payload_claims, footer_claims) |
| 118 | + local payload, footer |
| 119 | + payload = encode_json(payload_claims) |
| 120 | + if payload == nil or type(payload_claims) ~= "table" then |
| 121 | + return nil, "Invalid payload claims" |
| 122 | + end |
| 123 | + if footer_claims == nil then |
| 124 | + footer = "" |
| 125 | + else |
| 126 | + footer = encode_json(footer_claims) |
| 127 | + if footer == nil or type(footer_claims) ~= "table" then |
| 128 | + return nil, "Invalid footer claims" |
| 129 | + end |
| 130 | + end |
| 131 | + return paseto.sign(secret_key, payload, footer) |
123 | 132 | end |
124 | 133 |
|
125 | 134 | function v2.verify(public_key, token, footer) |
126 | | - footer = footer or "" |
127 | | - local signed_payload, err = v2.__validate_and_remove_footer(token, footer) |
128 | | - if not signed_payload then |
| 135 | + local err, verified_claims, payload_claims |
| 136 | + verified_claims, err = paseto.verify(public_key, token, footer) |
| 137 | + if verified_claims == nil then |
129 | 138 | return nil, err |
130 | 139 | end |
131 | | - local header = PROTOCOL_VERSION .. ".public." |
132 | | - if header ~= string.sub(signed_payload, 1, #header) then |
133 | | - return nil, "Invalid message header" |
134 | | - end |
135 | | - local decoded = basexx.from_url64(string.sub(signed_payload, #header + 1)) |
136 | | - local message = string.sub(decoded, 1, #decoded - luasodium.SIGN_BYTES) |
137 | | - local signature = string.sub(decoded, #decoded - luasodium.SIGN_BYTES + 1) |
138 | | - local data = v2.__pre_auth_encode(header, message, footer) |
139 | | - local verified |
140 | | - verified, err = luasodium.sign_verify_detached(data, signature, public_key) |
141 | | - if not verified then |
| 140 | + payload_claims, err = decode_json(verified_claims) |
| 141 | + if payload_claims == nil then |
142 | 142 | return nil, err |
143 | 143 | end |
144 | | - return message |
| 144 | + return payload_claims |
145 | 145 | end |
146 | 146 |
|
147 | 147 | return v2 |
0 commit comments