master
  1const std = @import("std");
  2const base64 = std.base64;
  3const crypto = std.crypto;
  4const debug = std.debug;
  5const fmt = std.fmt;
  6const math = std.math;
  7const mem = std.mem;
  8const pwhash = crypto.pwhash;
  9const testing = std.testing;
 10const HmacSha512 = crypto.auth.hmac.sha2.HmacSha512;
 11const Sha512 = crypto.hash.sha2.Sha512;
 12
 13const phc_format = @import("phc_encoding.zig");
 14
 15const KdfError = pwhash.KdfError;
 16const HasherError = pwhash.HasherError;
 17const EncodingError = phc_format.Error;
 18const Error = pwhash.Error;
 19
 20const salt_length: usize = 16;
 21const salt_str_length: usize = 22;
 22const ct_str_length: usize = 31;
 23const ct_length: usize = 24;
 24const dk_length: usize = ct_length - 1;
 25
 26/// Length (in bytes) of a password hash in crypt encoding
 27pub const hash_length: usize = 60;
 28
 29pub const State = struct {
 30    sboxes: [4][256]u32 = [4][256]u32{
 31        .{
 32            0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
 33            0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
 34            0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
 35            0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
 36            0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
 37            0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
 38            0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
 39            0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
 40            0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
 41            0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
 42            0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
 43            0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
 44            0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
 45            0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
 46            0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
 47            0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
 48            0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
 49            0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
 50            0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
 51            0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
 52            0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
 53            0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
 54            0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
 55            0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
 56            0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
 57            0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
 58            0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
 59            0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
 60            0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
 61            0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
 62            0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
 63            0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
 64            0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
 65            0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
 66            0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
 67            0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
 68            0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
 69            0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
 70            0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
 71            0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
 72            0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
 73            0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
 74            0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
 75            0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
 76            0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
 77            0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
 78            0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
 79            0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
 80            0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
 81            0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
 82            0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
 83            0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
 84            0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
 85            0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
 86            0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
 87            0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
 88            0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
 89            0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
 90            0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
 91            0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
 92            0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
 93            0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
 94            0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
 95            0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
 96        },
 97        .{
 98            0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
 99            0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
100            0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
101            0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
102            0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
103            0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
104            0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
105            0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
106            0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
107            0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
108            0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
109            0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
110            0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
111            0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
112            0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
113            0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
114            0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
115            0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
116            0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
117            0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
118            0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
119            0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
120            0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
121            0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
122            0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
123            0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
124            0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
125            0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
126            0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
127            0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
128            0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
129            0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
130            0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
131            0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
132            0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
133            0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
134            0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
135            0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
136            0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
137            0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
138            0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
139            0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
140            0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
141            0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
142            0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
143            0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
144            0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
145            0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
146            0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
147            0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
148            0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
149            0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
150            0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
151            0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
152            0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
153            0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
154            0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
155            0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
156            0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
157            0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
158            0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
159            0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
160            0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
161            0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
162        },
163        .{
164            0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
165            0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
166            0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
167            0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
168            0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
169            0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
170            0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
171            0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
172            0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
173            0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
174            0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
175            0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
176            0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
177            0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
178            0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
179            0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
180            0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
181            0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
182            0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
183            0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
184            0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
185            0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
186            0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
187            0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
188            0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
189            0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
190            0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
191            0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
192            0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
193            0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
194            0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
195            0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
196            0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
197            0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
198            0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
199            0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
200            0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
201            0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
202            0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
203            0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
204            0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
205            0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
206            0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
207            0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
208            0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
209            0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
210            0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
211            0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
212            0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
213            0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
214            0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
215            0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
216            0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
217            0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
218            0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
219            0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
220            0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
221            0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
222            0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
223            0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
224            0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
225            0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
226            0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
227            0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
228        },
229        .{
230            0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
231            0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
232            0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
233            0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
234            0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
235            0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
236            0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
237            0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
238            0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
239            0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
240            0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
241            0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
242            0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
243            0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
244            0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
245            0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
246            0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
247            0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
248            0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
249            0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
250            0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
251            0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
252            0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
253            0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
254            0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
255            0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
256            0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
257            0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
258            0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
259            0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
260            0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
261            0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
262            0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
263            0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
264            0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
265            0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
266            0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
267            0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
268            0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
269            0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
270            0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
271            0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
272            0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
273            0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
274            0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
275            0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
276            0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
277            0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
278            0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
279            0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
280            0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
281            0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
282            0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
283            0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
284            0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
285            0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
286            0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
287            0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
288            0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
289            0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
290            0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
291            0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
292            0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
293            0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
294        },
295    },
296    subkeys: [18]u32 = [18]u32{
297        0x243f6a88, 0x85a308d3, 0x13198a2e,
298        0x03707344, 0xa4093822, 0x299f31d0,
299        0x082efa98, 0xec4e6c89, 0x452821e6,
300        0x38d01377, 0xbe5466cf, 0x34e90c6c,
301        0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,
302        0xb5470917, 0x9216d5d9, 0x8979fb1b,
303    },
304
305    fn toWord(data: []const u8, current: *usize) u32 {
306        var t: u32 = 0;
307        var j = current.*;
308        var i: usize = 0;
309        while (i < 4) : (i += 1) {
310            if (j >= data.len) j = 0;
311            t = (t << 8) | data[j];
312            j += 1;
313        }
314        current.* = j;
315        return t;
316    }
317
318    fn expand0(state: *State, key: []const u8) void {
319        var i: usize = 0;
320        var j: usize = 0;
321        while (i < state.subkeys.len) : (i += 1) {
322            state.subkeys[i] ^= toWord(key, &j);
323        }
324
325        var halves = Halves{ .l = 0, .r = 0 };
326        i = 0;
327        while (i < 18) : (i += 2) {
328            state.encipher(&halves);
329            state.subkeys[i] = halves.l;
330            state.subkeys[i + 1] = halves.r;
331        }
332
333        i = 0;
334        while (i < 4) : (i += 1) {
335            var k: usize = 0;
336            while (k < 256) : (k += 2) {
337                state.encipher(&halves);
338                state.sboxes[i][k] = halves.l;
339                state.sboxes[i][k + 1] = halves.r;
340            }
341        }
342    }
343
344    fn expand(state: *State, data: []const u8, key: []const u8) void {
345        var i: usize = 0;
346        var j: usize = 0;
347        while (i < state.subkeys.len) : (i += 1) {
348            state.subkeys[i] ^= toWord(key, &j);
349        }
350
351        var halves = Halves{ .l = 0, .r = 0 };
352        i = 0;
353        j = 0;
354        while (i < 18) : (i += 2) {
355            halves.l ^= toWord(data, &j);
356            halves.r ^= toWord(data, &j);
357            state.encipher(&halves);
358            state.subkeys[i] = halves.l;
359            state.subkeys[i + 1] = halves.r;
360        }
361
362        i = 0;
363        while (i < 4) : (i += 1) {
364            var k: usize = 0;
365            while (k < 256) : (k += 2) {
366                halves.l ^= toWord(data, &j);
367                halves.r ^= toWord(data, &j);
368                state.encipher(&halves);
369                state.sboxes[i][k] = halves.l;
370                state.sboxes[i][k + 1] = halves.r;
371            }
372        }
373    }
374
375    const Halves = struct { l: u32, r: u32 };
376
377    fn halfRound(state: *const State, i: u32, j: u32, n: usize) u32 {
378        var r = state.sboxes[0][@as(u8, @truncate(j >> 24))];
379        r +%= state.sboxes[1][@as(u8, @truncate(j >> 16))];
380        r ^= state.sboxes[2][@as(u8, @truncate(j >> 8))];
381        r +%= state.sboxes[3][@as(u8, @truncate(j))];
382        return i ^ r ^ state.subkeys[n];
383    }
384
385    fn encipher(state: *const State, halves: *Halves) void {
386        halves.l ^= state.subkeys[0];
387        comptime var i = 1;
388        inline while (i < 16) : (i += 2) {
389            halves.r = state.halfRound(halves.r, halves.l, i);
390            halves.l = state.halfRound(halves.l, halves.r, i + 1);
391        }
392        const halves_last = Halves{ .l = halves.r ^ state.subkeys[i], .r = halves.l };
393        halves.* = halves_last;
394    }
395
396    fn encrypt(state: *const State, data: []u32) void {
397        debug.assert(data.len % 2 == 0);
398        var i: usize = 0;
399        while (i < data.len) : (i += 2) {
400            var halves = Halves{ .l = data[i], .r = data[i + 1] };
401            state.encipher(&halves);
402            data[i] = halves.l;
403            data[i + 1] = halves.r;
404        }
405    }
406};
407
408/// bcrypt parameters
409pub const Params = struct {
410    const Self = @This();
411
412    /// log2 of the number of rounds
413    rounds_log: u6,
414
415    /// As originally defined, bcrypt silently truncates passwords to 72 bytes.
416    /// In order to overcome this limitation, if `silently_truncate_password` is set to `false`,
417    /// long passwords will be automatically pre-hashed using HMAC-SHA512 before being passed to bcrypt.
418    /// Only set `silently_truncate_password` to `true` for compatibility with traditional bcrypt implementations,
419    /// or if you want to handle the truncation yourself.
420    silently_truncate_password: bool,
421
422    /// Minimum recommended parameters according to the
423    /// [OWASP cheat sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html).
424    pub const owasp = Self{ .rounds_log = 10, .silently_truncate_password = false };
425};
426
427fn bcryptWithTruncation(
428    password: []const u8,
429    salt: [salt_length]u8,
430    params: Params,
431) [dk_length]u8 {
432    var state = State{};
433    var password_buf: [73]u8 = undefined;
434    const trimmed_len = @min(password.len, password_buf.len - 1);
435    @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);
436    password_buf[trimmed_len] = 0;
437    const passwordZ = password_buf[0 .. trimmed_len + 1];
438    state.expand(salt[0..], passwordZ);
439
440    const rounds: u64 = @as(u64, 1) << params.rounds_log;
441    var k: u64 = 0;
442    while (k < rounds) : (k += 1) {
443        state.expand0(passwordZ);
444        state.expand0(salt[0..]);
445    }
446    crypto.secureZero(u8, &password_buf);
447
448    var cdata = [6]u32{ 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; // "OrpheanBeholderScryDoubt"
449    k = 0;
450    while (k < 64) : (k += 1) {
451        state.encrypt(&cdata);
452    }
453
454    var ct: [ct_length]u8 = undefined;
455    for (cdata, 0..) |c, i| {
456        mem.writeInt(u32, ct[i * 4 ..][0..4], c, .big);
457    }
458    return ct[0..dk_length].*;
459}
460
461/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
462/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.
463///
464/// The function returns the hash as a `dk_length` byte array, that doesn't include anything besides the hash output.
465///
466/// This function was designed for password storage, not for key derivation.
467/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.
468pub fn bcrypt(
469    password: []const u8,
470    salt: [salt_length]u8,
471    params: Params,
472) [dk_length]u8 {
473    if (password.len <= 72 or params.silently_truncate_password) {
474        return bcryptWithTruncation(password, salt, params);
475    }
476
477    var pre_hash: [HmacSha512.mac_length]u8 = undefined;
478    HmacSha512.create(&pre_hash, password, &salt);
479
480    const Encoder = crypt_format.Codec.Encoder;
481    var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;
482    _ = Encoder.encode(&pre_hash_b64, &pre_hash);
483
484    return bcryptWithTruncation(&pre_hash_b64, salt, params);
485}
486
487const pbkdf_prf = struct {
488    const Self = @This();
489    pub const mac_length = 32;
490
491    hasher: Sha512,
492    sha2pass: [Sha512.digest_length]u8,
493
494    pub fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void {
495        var ctx = Self.init(key);
496        ctx.update(msg);
497        ctx.final(out);
498    }
499
500    pub fn init(key: []const u8) Self {
501        var self: Self = undefined;
502        self.hasher = Sha512.init(.{});
503        Sha512.hash(key, &self.sha2pass, .{});
504        return self;
505    }
506
507    pub fn update(self: *Self, msg: []const u8) void {
508        self.hasher.update(msg);
509    }
510
511    pub fn final(self: *Self, out: *[mac_length]u8) void {
512        var sha2salt: [Sha512.digest_length]u8 = undefined;
513        self.hasher.final(&sha2salt);
514        out.* = hash(self.sha2pass, sha2salt);
515    }
516
517    /// Matches OpenBSD function
518    /// https://github.com/openbsd/src/blob/6df1256b7792691e66c2ed9d86a8c103069f9e34/lib/libutil/bcrypt_pbkdf.c#L98
519    pub fn hash(sha2pass: [Sha512.digest_length]u8, sha2salt: [Sha512.digest_length]u8) [32]u8 {
520        var cdata: [8]u32 = undefined;
521        {
522            const ciphertext = "OxychromaticBlowfishSwatDynamite";
523            var j: usize = 0;
524            for (&cdata) |*v| {
525                v.* = State.toWord(ciphertext, &j);
526            }
527        }
528
529        var state = State{};
530
531        { // key expansion
532            state.expand(&sha2salt, &sha2pass);
533            var i: usize = 0;
534            while (i < 64) : (i += 1) {
535                state.expand0(&sha2salt);
536                state.expand0(&sha2pass);
537            }
538        }
539
540        { // encryption
541            var i: usize = 0;
542            while (i < 64) : (i += 1) {
543                state.encrypt(&cdata);
544            }
545        }
546
547        // copy out
548        var out: [32]u8 = undefined;
549        for (cdata, 0..) |v, i| {
550            std.mem.writeInt(u32, out[4 * i ..][0..4], v, .little);
551        }
552
553        // zap
554        crypto.secureZero(u32, &cdata);
555        crypto.secureZero(u32, &state.subkeys);
556
557        return out;
558    }
559};
560
561/// bcrypt-pbkdf is a key derivation function based on bcrypt.
562///
563/// Unlike the password hashing function `bcrypt`, this function doesn't silently truncate passwords longer than 72 bytes.
564pub fn pbkdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !void {
565    try crypto.pwhash.pbkdf2(key, pass, salt, rounds, pbkdf_prf);
566}
567
568/// The function used in OpenSSH to derive encryption keys from passphrases.
569///
570/// This implementation is compatible with the OpenBSD implementation (https://github.com/openbsd/src/blob/master/lib/libutil/bcrypt_pbkdf.c).
571pub fn opensshKdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !void {
572    var tmp: [32]u8 = undefined;
573    var tmp2: [32]u8 = undefined;
574    if (rounds < 1 or pass.len == 0 or salt.len == 0 or key.len == 0 or key.len > tmp.len * tmp.len) {
575        return error.InvalidInput;
576    }
577    var sha2pass: [Sha512.digest_length]u8 = undefined;
578    Sha512.hash(pass, &sha2pass, .{});
579    const stride = (key.len + tmp.len - 1) / tmp.len;
580    var amt = (key.len + stride - 1) / stride;
581    if (math.shr(usize, key.len, 32) >= amt) {
582        return error.InvalidInput;
583    }
584    var key_remainder = key.len;
585    var count: u32 = 1;
586    while (key_remainder > 0) : (count += 1) {
587        var count_salt: [4]u8 = undefined;
588        std.mem.writeInt(u32, count_salt[0..], count, .big);
589        var sha2salt: [Sha512.digest_length]u8 = undefined;
590        var h = Sha512.init(.{});
591        h.update(salt);
592        h.update(&count_salt);
593        h.final(&sha2salt);
594        tmp2 = pbkdf_prf.hash(sha2pass, sha2salt);
595        tmp = tmp2;
596        for (1..rounds) |_| {
597            Sha512.hash(&tmp2, &sha2salt, .{});
598            tmp2 = pbkdf_prf.hash(sha2pass, sha2salt);
599            for (&tmp, tmp2) |*o, t| o.* ^= t;
600        }
601        amt = @min(amt, key_remainder);
602        key_remainder -= for (0..amt) |i| {
603            const dest = i * stride + (count - 1);
604            if (dest >= key.len) break i;
605            key[dest] = tmp[i];
606        } else amt;
607    }
608    crypto.secureZero(u8, &tmp);
609    crypto.secureZero(u8, &tmp2);
610    crypto.secureZero(u8, &sha2pass);
611}
612
613const crypt_format = struct {
614    /// String prefix for bcrypt
615    pub const prefix = "$2";
616
617    // bcrypt has its own variant of base64, with its own alphabet and no padding
618    const bcrypt_alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".*;
619    const Codec = struct { Encoder: base64.Base64Encoder, Decoder: base64.Base64Decoder }{
620        .Encoder = base64.Base64Encoder.init(bcrypt_alphabet, null),
621        .Decoder = base64.Base64Decoder.init(bcrypt_alphabet, null),
622    };
623
624    fn strHashInternal(
625        password: []const u8,
626        salt: [salt_length]u8,
627        params: Params,
628    ) [hash_length]u8 {
629        var dk = bcrypt(password, salt, params);
630
631        var salt_str: [salt_str_length]u8 = undefined;
632        _ = Codec.Encoder.encode(salt_str[0..], salt[0..]);
633
634        var ct_str: [ct_str_length]u8 = undefined;
635        _ = Codec.Encoder.encode(ct_str[0..], dk[0..]);
636
637        var s_buf: [hash_length]u8 = undefined;
638        const s = fmt.bufPrint(
639            s_buf[0..],
640            "{s}b${d}{d}${s}{s}",
641            .{ prefix, params.rounds_log / 10, params.rounds_log % 10, salt_str, ct_str },
642        ) catch unreachable;
643        debug.assert(s.len == s_buf.len);
644        return s_buf;
645    }
646};
647
648/// Hash and verify passwords using the PHC format.
649const PhcFormatHasher = struct {
650    const alg_id = "bcrypt";
651    const BinValue = phc_format.BinValue;
652
653    const HashResult = struct {
654        alg_id: []const u8,
655        r: u6,
656        salt: BinValue(salt_length),
657        hash: BinValue(dk_length),
658    };
659
660    /// Return a non-deterministic hash of the password encoded as a PHC-format string
661    fn create(
662        password: []const u8,
663        params: Params,
664        buf: []u8,
665    ) HasherError![]const u8 {
666        var salt: [salt_length]u8 = undefined;
667        crypto.random.bytes(&salt);
668
669        const hash = bcrypt(password, salt, params);
670
671        return phc_format.serialize(HashResult{
672            .alg_id = alg_id,
673            .r = params.rounds_log,
674            .salt = try BinValue(salt_length).fromSlice(&salt),
675            .hash = try BinValue(dk_length).fromSlice(&hash),
676        }, buf);
677    }
678
679    /// Verify a password against a PHC-format encoded string
680    fn verify(
681        str: []const u8,
682        password: []const u8,
683        silently_truncate_password: bool,
684    ) HasherError!void {
685        const hash_result = try phc_format.deserialize(HashResult, str);
686
687        if (!mem.eql(u8, hash_result.alg_id, alg_id)) return HasherError.PasswordVerificationFailed;
688        if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
689            return HasherError.InvalidEncoding;
690
691        const params = Params{
692            .rounds_log = hash_result.r,
693            .silently_truncate_password = silently_truncate_password,
694        };
695        const hash = bcrypt(password, hash_result.salt.buf, params);
696        const expected_hash = hash_result.hash.constSlice();
697
698        if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
699    }
700};
701
702/// Hash and verify passwords using the modular crypt format.
703const CryptFormatHasher = struct {
704    /// Length of a string returned by the create() function
705    const pwhash_str_length: usize = hash_length;
706
707    /// Return a non-deterministic hash of the password encoded into the modular crypt format
708    fn create(
709        password: []const u8,
710        params: Params,
711        buf: []u8,
712    ) HasherError![]const u8 {
713        if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
714
715        var salt: [salt_length]u8 = undefined;
716        crypto.random.bytes(&salt);
717
718        const hash = crypt_format.strHashInternal(password, salt, params);
719        @memcpy(buf[0..hash.len], &hash);
720
721        return buf[0..pwhash_str_length];
722    }
723
724    /// Verify a password against a string in modular crypt format
725    fn verify(
726        str: []const u8,
727        password: []const u8,
728        silently_truncate_password: bool,
729    ) HasherError!void {
730        if (str.len != pwhash_str_length or str[3] != '$' or str[6] != '$')
731            return HasherError.InvalidEncoding;
732
733        const rounds_log_str = str[4..][0..2];
734        const rounds_log = fmt.parseInt(u6, rounds_log_str[0..], 10) catch
735            return HasherError.InvalidEncoding;
736
737        const salt_str = str[7..][0..salt_str_length];
738        var salt: [salt_length]u8 = undefined;
739        crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;
740
741        const wanted_s = crypt_format.strHashInternal(password, salt, .{
742            .rounds_log = rounds_log,
743            .silently_truncate_password = silently_truncate_password,
744        });
745        if (!mem.eql(u8, wanted_s[0..], str[0..])) return HasherError.PasswordVerificationFailed;
746    }
747};
748
749/// Options for hashing a password.
750pub const HashOptions = struct {
751    /// For `bcrypt`, that can be left to `null`.
752    allocator: ?mem.Allocator = null,
753    /// Internal bcrypt parameters.
754    params: Params,
755    /// Encoding to use for the output of the hash function.
756    encoding: pwhash.Encoding,
757};
758
759/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
760/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.
761///
762/// The function returns a string that includes all the parameters required for verification.
763///
764/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
765/// If this is an issue for your application, set the `silently_truncate_password` option to `false`.
766pub fn strHash(
767    password: []const u8,
768    options: HashOptions,
769    out: []u8,
770) Error![]const u8 {
771    switch (options.encoding) {
772        .phc => return PhcFormatHasher.create(password, options.params, out),
773        .crypt => return CryptFormatHasher.create(password, options.params, out),
774    }
775}
776
777/// Options for hash verification.
778pub const VerifyOptions = struct {
779    /// For `bcrypt`, that can be left to `null`.
780    allocator: ?mem.Allocator = null,
781    /// Whether to silently truncate the password to 72 bytes, or pre-hash the password when it is longer.
782    silently_truncate_password: bool,
783};
784
785/// Verify that a previously computed hash is valid for a given password.
786pub fn strVerify(
787    str: []const u8,
788    password: []const u8,
789    options: VerifyOptions,
790) Error!void {
791    if (mem.startsWith(u8, str, crypt_format.prefix)) {
792        return CryptFormatHasher.verify(str, password, options.silently_truncate_password);
793    } else {
794        return PhcFormatHasher.verify(str, password, options.silently_truncate_password);
795    }
796}
797
798test "bcrypt codec" {
799    var salt: [salt_length]u8 = undefined;
800    crypto.random.bytes(&salt);
801    var salt_str: [salt_str_length]u8 = undefined;
802    _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);
803    var salt2: [salt_length]u8 = undefined;
804    try crypt_format.Codec.Decoder.decode(salt2[0..], salt_str[0..]);
805    try testing.expectEqualSlices(u8, salt[0..], salt2[0..]);
806}
807
808test "bcrypt crypt format" {
809    var hash_options = HashOptions{
810        .params = .{ .rounds_log = 5, .silently_truncate_password = false },
811        .encoding = .crypt,
812    };
813    var verify_options = VerifyOptions{ .silently_truncate_password = false };
814
815    var buf: [hash_length]u8 = undefined;
816    const s = try strHash("password", hash_options, &buf);
817
818    try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
819    try strVerify(s, "password", verify_options);
820    try testing.expectError(
821        error.PasswordVerificationFailed,
822        strVerify(s, "invalid password", verify_options),
823    );
824
825    var long_buf: [hash_length]u8 = undefined;
826    var long_s = try strHash("password" ** 100, hash_options, &long_buf);
827
828    try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
829    try strVerify(long_s, "password" ** 100, verify_options);
830    try testing.expectError(
831        error.PasswordVerificationFailed,
832        strVerify(long_s, "password" ** 101, verify_options),
833    );
834
835    hash_options.params.silently_truncate_password = true;
836    verify_options.silently_truncate_password = true;
837    long_s = try strHash("password" ** 100, hash_options, &long_buf);
838    try strVerify(long_s, "password" ** 101, verify_options);
839
840    try strVerify(
841        "$2b$08$WUQKyBCaKpziCwUXHiMVvu40dYVjkTxtWJlftl0PpjY2BxWSvFIEe",
842        "The devil himself",
843        verify_options,
844    );
845}
846
847test "bcrypt phc format" {
848    var hash_options = HashOptions{
849        .params = .{ .rounds_log = 5, .silently_truncate_password = false },
850        .encoding = .phc,
851    };
852    var verify_options = VerifyOptions{ .silently_truncate_password = false };
853    const prefix = "$bcrypt$";
854
855    var buf: [hash_length * 2]u8 = undefined;
856    const s = try strHash("password", hash_options, &buf);
857
858    try testing.expect(mem.startsWith(u8, s, prefix));
859    try strVerify(s, "password", verify_options);
860    try testing.expectError(
861        error.PasswordVerificationFailed,
862        strVerify(s, "invalid password", verify_options),
863    );
864
865    var long_buf: [hash_length * 2]u8 = undefined;
866    var long_s = try strHash("password" ** 100, hash_options, &long_buf);
867
868    try testing.expect(mem.startsWith(u8, long_s, prefix));
869    try strVerify(long_s, "password" ** 100, verify_options);
870    try testing.expectError(
871        error.PasswordVerificationFailed,
872        strVerify(long_s, "password" ** 101, verify_options),
873    );
874
875    hash_options.params.silently_truncate_password = true;
876    verify_options.silently_truncate_password = true;
877    long_s = try strHash("password" ** 100, hash_options, &long_buf);
878    try strVerify(long_s, "password" ** 101, verify_options);
879
880    try strVerify(
881        "$bcrypt$r=5$2NopntlgE2lX3cTwr4qz8A$r3T7iKYQNnY4hAhGjk9RmuyvgrYJZwc",
882        "The devil himself",
883        verify_options,
884    );
885}
886
887test "openssh kdf" {
888    var key: [100]u8 = undefined;
889    const pass = "password";
890    const salt = "salt";
891    const rounds = 5;
892    try opensshKdf(pass, salt, &key, rounds);
893    const expected = [_]u8{ 65, 207, 68, 58, 55, 252, 114, 141, 255, 65, 216, 175, 5, 92, 235, 68, 220, 92, 118, 161, 40, 13, 241, 190, 56, 152, 69, 136, 41, 214, 51, 205, 37, 221, 101, 59, 105, 73, 133, 36, 14, 59, 94, 212, 111, 107, 109, 237, 213, 235, 246, 119, 59, 76, 45, 130, 142, 81, 178, 231, 161, 158, 138, 108, 18, 162, 26, 50, 218, 251, 23, 66, 2, 232, 20, 202, 216, 46, 12, 250, 247, 246, 252, 23, 155, 74, 77, 195, 120, 113, 57, 88, 126, 81, 9, 249, 72, 18, 208, 160 };
894    try testing.expectEqualSlices(u8, &key, &expected);
895}