Skip to content

Commit db0cf40

Browse files
authored
Implement v4.local (#11)
1 parent 60895de commit db0cf40

File tree

3 files changed

+444
-0
lines changed

3 files changed

+444
-0
lines changed

testdata/v4.json

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
{
2+
"name": "PASETO v4 Test Vectors",
3+
"tests": [
4+
{
5+
"name": "4-E-1",
6+
"expect-fail": false,
7+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
8+
"nonce": "0000000000000000000000000000000000000000000000000000000000000000",
9+
"token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvSwscFlAl1pk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XJ5hOb_4v9RmDkneN0S92dx0OW4pgy7omxgf3S8c3LlQg",
10+
"payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
11+
"footer": "",
12+
"implicit-assertion": ""
13+
},
14+
{
15+
"name": "4-E-2",
16+
"expect-fail": false,
17+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
18+
"nonce": "0000000000000000000000000000000000000000000000000000000000000000",
19+
"token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvS2csCgglvpk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XIemu9chy3WVKvRBfg6t8wwYHK0ArLxxfZP73W_vfwt5A",
20+
"payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
21+
"footer": "",
22+
"implicit-assertion": ""
23+
},
24+
{
25+
"name": "4-E-3",
26+
"expect-fail": false,
27+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
28+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
29+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6-tyebyWG6Ov7kKvBdkrrAJ837lKP3iDag2hzUPHuMKA",
30+
"payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
31+
"footer": "",
32+
"implicit-assertion": ""
33+
},
34+
{
35+
"name": "4-E-4",
36+
"expect-fail": false,
37+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
38+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
39+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4gt6TiLm55vIH8c_lGxxZpE3AWlH4WTR0v45nsWoU3gQ",
40+
"payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
41+
"footer": "",
42+
"implicit-assertion": ""
43+
},
44+
{
45+
"name": "4-E-5",
46+
"expect-fail": false,
47+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
48+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
49+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4x-RMNXtQNbz7FvFZ_G-lFpk5RG3EOrwDL6CgDqcerSQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
50+
"payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
51+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
52+
"implicit-assertion": ""
53+
},
54+
{
55+
"name": "4-E-6",
56+
"expect-fail": false,
57+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
58+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
59+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6pWSA5HX2wjb3P-xLQg5K5feUCX4P2fpVK3ZLWFbMSxQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
60+
"payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
61+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
62+
"implicit-assertion": ""
63+
},
64+
{
65+
"name": "4-E-7",
66+
"expect-fail": false,
67+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
68+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
69+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t40KCCWLA7GYL9KFHzKlwY9_RnIfRrMQpueydLEAZGGcA.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
70+
"payload": "{\"data\":\"this is a secret message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
71+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
72+
"implicit-assertion": "{\"test-vector\":\"4-E-7\"}"
73+
},
74+
{
75+
"name": "4-E-8",
76+
"expect-fail": false,
77+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
78+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
79+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t5uvqQbMGlLLNYBc7A6_x7oqnpUK5WLvj24eE4DVPDZjw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
80+
"payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
81+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
82+
"implicit-assertion": "{\"test-vector\":\"4-E-8\"}"
83+
},
84+
{
85+
"name": "4-E-9",
86+
"expect-fail": false,
87+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
88+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
89+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WiA8rd3wgFSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t6tybdlmnMwcDMw0YxA_gFSE_IUWl78aMtOepFYSWYfQA.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24",
90+
"payload": "{\"data\":\"this is a hidden message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
91+
"footer": "arbitrary-string-that-isn't-json",
92+
"implicit-assertion": "{\"test-vector\":\"4-E-9\"}"
93+
},
94+
{
95+
"name": "4-S-1",
96+
"expect-fail": false,
97+
"public-key": "1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
98+
"secret-key": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
99+
"secret-key-seed": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a3774",
100+
"secret-key-pem": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----",
101+
"public-key-pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----",
102+
"token": "v4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9bg_XBBzds8lTZShVlwwKSgeKpLT3yukTw6JUz3W4h_ExsQV-P0V54zemZDcAxFaSeef1QlXEFtkqxT1ciiQEDA",
103+
"payload": "{\"data\":\"this is a signed message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
104+
"footer": "",
105+
"implicit-assertion": ""
106+
},
107+
{
108+
"name": "4-S-2",
109+
"expect-fail": false,
110+
"public-key": "1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
111+
"secret-key": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
112+
"secret-key-seed": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a3774",
113+
"secret-key-pem": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----",
114+
"public-key-pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----",
115+
"token": "v4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9v3Jt8mx_TdM2ceTGoqwrh4yDFn0XsHvvV_D0DtwQxVrJEBMl0F2caAdgnpKlt4p7xBnx1HcO-SPo8FPp214HDw.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
116+
"payload": "{\"data\":\"this is a signed message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
117+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
118+
"implicit-assertion": ""
119+
},
120+
{
121+
"name": "4-S-3",
122+
"expect-fail": false,
123+
"public-key": "1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
124+
"secret-key": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
125+
"secret-key-seed": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a3774",
126+
"secret-key-pem": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----",
127+
"public-key-pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----",
128+
"token": "v4.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAyMi0wMS0wMVQwMDowMDowMCswMDowMCJ9NPWciuD3d0o5eXJXG5pJy-DiVEoyPYWs1YSTwWHNJq6DZD3je5gf-0M4JR9ipdUSJbIovzmBECeaWmaqcaP0DQ.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
129+
"payload": "{\"data\":\"this is a signed message\",\"exp\":\"2022-01-01T00:00:00+00:00\"}",
130+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
131+
"implicit-assertion": "{\"test-vector\":\"4-S-3\"}"
132+
},
133+
{
134+
"name": "4-F-1",
135+
"expect-fail": true,
136+
"public-key": "1eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
137+
"secret-key": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a37741eb9dbbbbc047c03fd70604e0071f0987e16b28b757225c11f00415d0e20b1a2",
138+
"secret-key-seed": "b4cbfb43df4ce210727d953e4a713307fa19bb7d9f85041438d9e11b942a3774",
139+
"secret-key-pem": "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEILTL+0PfTOIQcn2VPkpxMwf6Gbt9n4UEFDjZ4RuUKjd0\n-----END PRIVATE KEY-----",
140+
"public-key-pem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAHrnbu7wEfAP9cGBOAHHwmH4Wsot1ciXBHwBBXQ4gsaI=\n-----END PUBLIC KEY-----",
141+
"token": "v4.local.vngXfCISbnKgiP6VWGuOSlYrFYU300fy9ijW33rznDYgxHNPwWluAY2Bgb0z54CUs6aYYkIJ-bOOOmJHPuX_34Agt_IPlNdGDpRdGNnBz2MpWJvB3cttheEc1uyCEYltj7wBQQYX.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24",
142+
"payload": null,
143+
"footer": "arbitrary-string-that-isn't-json",
144+
"implicit-assertion": "{\"test-vector\":\"4-F-1\"}"
145+
},
146+
{
147+
"name": "4-F-2",
148+
"expect-fail": true,
149+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
150+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
151+
"token": "v4.public.eyJpbnZhbGlkIjoidGhpcyBzaG91bGQgbmV2ZXIgZGVjb2RlIn22Sp4gjCaUw0c7EH84ZSm_jN_Qr41MrgLNu5LIBCzUr1pn3Z-Wukg9h3ceplWigpoHaTLcwxj0NsI1vjTh67YB.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
152+
"payload": null,
153+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
154+
"implicit-assertion": "{\"test-vector\":\"4-F-2\"}"
155+
},
156+
{
157+
"name": "4-F-3",
158+
"expect-fail": true,
159+
"nonce": "26f7553354482a1d91d4784627854b8da6b8042a7966523c2b404e8dbbe7f7f2",
160+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
161+
"token": "v3.local.23e_2PiqpQBPvRFKzB0zHhjmxK3sKo2grFZRRLM-U7L0a8uHxuF9RlVz3Ic6WmdUUWTxCaYycwWV1yM8gKbZB2JhygDMKvHQ7eBf8GtF0r3K0Q_gF1PXOxcOgztak1eD1dPe9rLVMSgR0nHJXeIGYVuVrVoLWQ.YXJiaXRyYXJ5LXN0cmluZy10aGF0LWlzbid0LWpzb24",
162+
"payload": null,
163+
"footer": "arbitrary-string-that-isn't-json",
164+
"implicit-assertion": "{\"test-vector\":\"4-F-3\"}"
165+
},
166+
{
167+
"name": "4-F-4",
168+
"expect-fail": true,
169+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
170+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
171+
"token": "v4.local.AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAr68PS4AXe7If_ZgesdkUMvSwscFlAl1pk5HC0e8kApeaqMfGo_7OpBnwJOAbY9V7WU6abu74MmcUE8YWAiaArVI8XJ5hOb_4v9RmDkneN0S92dx0OW4pgy7omxgf3S8c3LlQh",
172+
"payload": null,
173+
"footer": "",
174+
"implicit-assertion": ""
175+
},
176+
{
177+
"name": "4-F-5",
178+
"expect-fail": true,
179+
"nonce": "df654812bac492663825520ba2f6e67cf5ca5bdc13d4e7507a98cc4c2fcc3ad8",
180+
"key": "707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f",
181+
"token": "v4.local.32VIErrEkmY4JVILovbmfPXKW9wT1OdQepjMTC_MOtjA4kiqw7_tcaOM5GNEcnTxl60WkwMsYXw6FSNb_UdJPXjpzm0KW9ojM5f4O2mRvE2IcweP-PRdoHjd5-RHCiExR1IK6t4x-RMNXtQNbz7FvFZ_G-lFpk5RG3EOrwDL6CgDqcerSQ==.eyJraWQiOiJ6VmhNaVBCUDlmUmYyc25FY1Q3Z0ZUaW9lQTlDT2NOeTlEZmdMMVc2MGhhTiJ9",
182+
"payload": null,
183+
"footer": "{\"kid\":\"zVhMiPBP9fRf2snEcT7gFTioeA9COcNy9DfgL1W60haN\"}",
184+
"implicit-assertion": ""
185+
}
186+
]
187+
}

v4loc.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package paseto
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/rand"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"strings"
10+
11+
"golang.org/x/crypto/blake2b"
12+
"golang.org/x/crypto/chacha20"
13+
)
14+
15+
const (
16+
v4locHeader = "v4.local."
17+
v4locKey = 32
18+
v4locNonce = 32
19+
v4locMac = 32
20+
v4locKDF = 32
21+
)
22+
23+
func V4Encrypt(key []byte, payload, footer any, implicit string, randBytes []byte) (string, error) {
24+
payloadBytes, err := toBytes(payload)
25+
if err != nil {
26+
return "", fmt.Errorf("encode payload: %w", err)
27+
}
28+
29+
footerBytes, err := toBytes(footer)
30+
if err != nil {
31+
return "", fmt.Errorf("encode footer: %w", err)
32+
}
33+
34+
if randBytes == nil {
35+
randBytes = make([]byte, v4locNonce)
36+
if _, err := io.ReadFull(rand.Reader, randBytes); err != nil {
37+
return "", fmt.Errorf("read from crypto/rand.Reader: %w", err)
38+
}
39+
}
40+
41+
// step 0.
42+
m := payloadBytes
43+
k := key
44+
f := footerBytes
45+
i := []byte(implicit)
46+
47+
// step 1.
48+
if !constTimeEq(int32(len(k)), v4locKey) {
49+
return "", errors.New("bad key")
50+
}
51+
52+
// step 2.
53+
h := []byte(v4locHeader)
54+
55+
// step 3.
56+
n := randBytes
57+
if n == nil {
58+
n = make([]byte, v4locNonce)
59+
if _, err := io.ReadFull(rand.Reader, n); err != nil {
60+
return "", fmt.Errorf("read from crypto/rand.Reader: %w", err)
61+
}
62+
}
63+
64+
// step 4.
65+
ek, n2, ak, err := v4locSplitKey(key, n)
66+
if err != nil {
67+
return "", fmt.Errorf("create enc and auth keys: %w", err)
68+
}
69+
70+
// step 5.
71+
c := make([]byte, len(m))
72+
73+
ciph, err := chacha20.NewUnauthenticatedCipher(ek, n2)
74+
if err != nil {
75+
return "", fmt.Errorf("create chacha20 cipher: %w", err)
76+
}
77+
ciph.XORKeyStream(c, m)
78+
79+
// step 6.
80+
preAuth := pae(h, n, c, f, i)
81+
82+
// step 7.
83+
mac, err := blake2b.New(v4locMac, ak)
84+
if err != nil {
85+
return "", fmt.Errorf("unable to in initialize MAC kdf: %w", err)
86+
}
87+
mac.Write(preAuth)
88+
t := mac.Sum(nil)
89+
90+
// step 8.
91+
body := make([]byte, 0, len(n)+len(c)+len(t))
92+
body = append(body, n...)
93+
body = append(body, c...)
94+
body = append(body, t...)
95+
96+
return buildToken(h, body, f), nil
97+
}
98+
99+
func V4Decrypt(token string, key []byte, payload, footer any, implicit string) error {
100+
// step 0.
101+
k := key
102+
i := []byte(implicit)
103+
104+
// step 1.
105+
// step 2.
106+
107+
// step 3.
108+
if !strings.HasPrefix(token, v4locHeader) {
109+
return ErrIncorrectTokenFormat
110+
}
111+
h := []byte(v4locHeader)
112+
113+
// step 4.
114+
body, footerBytes, err := splitToken(token, v4locHeader)
115+
if err != nil {
116+
return fmt.Errorf("decode token: %w", err)
117+
}
118+
if len(body) < v4locNonce+v4locMac {
119+
return ErrIncorrectTokenFormat
120+
}
121+
f := footerBytes
122+
n := body[:v4locNonce]
123+
c := body[v4locNonce : len(body)-v4locMac]
124+
t := body[v4locNonce+len(c):]
125+
126+
// step 5.
127+
ek, n2, ak, err := v4locSplitKey(k, n)
128+
if err != nil {
129+
return fmt.Errorf("create enc and auth keys: %w", err)
130+
}
131+
132+
// step 6.
133+
preAuth := pae(h, n, c, f, i)
134+
135+
// step 7.
136+
hasher, err := blake2b.New(v4locMac, ak)
137+
if err != nil {
138+
return fmt.Errorf("create blake2b hash: %w", err)
139+
}
140+
hasher.Write(preAuth)
141+
t2 := hasher.Sum(nil)
142+
143+
// step 8.
144+
if !hmac.Equal(t, t2) {
145+
return ErrInvalidTokenAuth
146+
}
147+
148+
// step 9.
149+
ciph, err := chacha20.NewUnauthenticatedCipher(ek, n2)
150+
if err != nil {
151+
return fmt.Errorf("create chacha20 cipher: %w", err)
152+
}
153+
154+
p := make([]byte, len(c))
155+
ciph.XORKeyStream(p, c)
156+
157+
// step 10.
158+
if payload != nil {
159+
if err := fromBytes(p, payload); err != nil {
160+
return fmt.Errorf("decode payload: %w", err)
161+
}
162+
}
163+
164+
if footer != nil {
165+
if err := fromBytes(f, footer); err != nil {
166+
return fmt.Errorf("decode footer: %w", err)
167+
}
168+
}
169+
return nil
170+
}
171+
172+
func v4locSplitKey(key, n []byte) (ek, n2, ak []byte, err error) {
173+
encKDF, err := blake2b.New(56, key)
174+
if err != nil {
175+
return nil, nil, nil, err
176+
}
177+
178+
encKDF.Write([]byte("paseto-encryption-key"))
179+
encKDF.Write(n)
180+
tmp := encKDF.Sum(nil)
181+
ek, n2 = tmp[:v4locKDF], tmp[v4locKDF:]
182+
183+
authKDF, err := blake2b.New(32, key)
184+
if err != nil {
185+
return nil, nil, nil, err
186+
}
187+
188+
authKDF.Write([]byte("paseto-auth-key-for-aead"))
189+
authKDF.Write(n)
190+
ak = authKDF.Sum(nil)
191+
192+
return ek, n2, ak, nil
193+
}

0 commit comments

Comments
 (0)