v / vlib / x / crypto / slhdsa / base.v
405 lines · 363 sloc · 12.02 KB · a8a45072371795b72d2aaa145b8195cde71cb9db
Raw
1// Copyright (c) blackshirt. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module slhdsa
5
6// Configuration options used in SLH-DSA key generation.
7@[params]
8pub struct KeyOpts {
9pub mut:
10 // An opaque represents the kind of SLH-DSA keys want to built.
11 // See `enum Kind` for available options.
12 kind Kind = .sha2_128s
13
14 // flag, 0=random (default), 1= use seed bytes, 2 = use priv bytes, otherwise error
15 flag int
16 // when you set flag=1, builder will use seed bytes as a params,
17 // so, make sure to pass seed bytes length != 0
18 seed []u8
19 // when flag=2, you should ensure private bytes length != 0
20 priv []u8
21
22 // This option below was not supported yet.
23 //
24 // Sets properties to be used when fetching algorithm implementations
25 // used for SLH-DSA hashing operations.
26 propq string
27}
28
29// Configurations parameters used for signing (and or verifying)
30@[params]
31pub struct SignerOpts {
32pub mut:
33 // optional context string up to 255 length, used in signing (verifying)
34 context string
35
36 // "message-encoding"
37 // The default value of 1 uses 'Pure SLH-DSA Signature Generation'.
38 // Setting it to 0 does not encode the message, which is used for testing,
39 // but can also be used for 'Pre Hash SLH-DSA Signature Generation'.
40 // If you set encoding to 0, you should provide the entropy.
41 encoding int = 1
42
43 // "test-entropy" used for testing to pass a optional random value.
44 entropy []u8 // octet-string
45
46 // "deterministic" integer option.
47 // The default value of 0 generates a random value (using a DRBG) this is used when
48 // processing the message. Setting this to 1 causes the private key seed to be used instead.
49 // This value is ignored if "test-entropy" is set.
50 deterministic int
51}
52
53// from_seed creates a new SLH-DSA PrivateKey from seed bytes and kind options.
54// If the seed length was zero, it will create a key based on randomly generated seed.
55// You should make sure, the seed bytes comes from trusted cryptographic secure source.
56// The seed size was 3 times of `𝑛` parameter defined in the standard.
57// The `𝑛` size maybe 16, 24 or 32 bytes length, depend on the chosen type.
58fn PrivateKey.from_seed(seed []u8, kind Kind) !PrivateKey {
59 // when seed bytes length was zero, build the key based on the random ones
60 if seed.len == 0 {
61 return PrivateKey.new(kind: kind)!
62 }
63 // Get the n size parameter set from the options
64 nsize := kind.nsize()
65 // The length of the seed bytes supplied must be 3 * nsize.
66 if seed.len != 3 * nsize {
67 return error('Unmatching seed length with kind supplied, need ${3 * nsize} bytes')
68 }
69
70 param_bld := C.OSSL_PARAM_BLD_new()
71 assert param_bld != 0
72
73 m := C.OSSL_PARAM_BLD_push_octet_string(param_bld, c'seed', seed.data, seed.len)
74 if m <= 0 {
75 C.OSSL_PARAM_BLD_free(param_bld)
76 return error('OSSL_PARAM_BLD_push failed')
77 }
78
79 // build params
80 params := C.OSSL_PARAM_BLD_to_param(param_bld)
81 // create a desired context
82 pctx := C.EVP_PKEY_CTX_new_from_name(0, kind.long_name(), 0)
83 if params == 0 || pctx == 0 {
84 C.OSSL_PARAM_BLD_free(param_bld)
85 C.OSSL_PARAM_free(params)
86 C.EVP_PKEY_CTX_free(pctx)
87 return error('EVP_PKEY_CTX_new or OSSL_PARAM_BLD_to_param failed')
88 }
89 pkey := C.EVP_PKEY_new()
90 assert pkey != 0
91 ke := C.EVP_PKEY_keygen_init(pctx)
92 if ke <= 0 {
93 C.OSSL_PARAM_BLD_free(param_bld)
94 C.OSSL_PARAM_free(params)
95 C.EVP_PKEY_CTX_free(pctx)
96 C.EVP_PKEY_free(pkey)
97 return error('EVP_PKEY_keygen_init failed')
98 }
99 // Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init().
100 s := C.EVP_PKEY_CTX_set_params(pctx, params)
101 if s != 1 {
102 C.OSSL_PARAM_BLD_free(param_bld)
103 C.OSSL_PARAM_free(params)
104 C.EVP_PKEY_CTX_free(pctx)
105 C.EVP_PKEY_free(pkey)
106 return error('EVP_PKEY_CTX_set_params failed')
107 }
108
109 ss := C.EVP_PKEY_keygen(pctx, &pkey)
110 if ss <= 0 {
111 C.OSSL_PARAM_BLD_free(param_bld)
112 C.OSSL_PARAM_free(params)
113 C.EVP_PKEY_CTX_free(pctx)
114 C.EVP_PKEY_free(pkey)
115 return error('EVP_PKEY_keygen failed')
116 }
117 // TODO: right way to check the key
118 pvkey := PrivateKey{
119 key: pkey
120 }
121 // Cleans up
122 C.OSSL_PARAM_BLD_free(param_bld)
123 C.OSSL_PARAM_free(params)
124 C.EVP_PKEY_CTX_free(pctx)
125
126 return pvkey
127}
128
129fn PrivateKey.from_bytes(bytes []u8, kind Kind) !PrivateKey {
130 // when bytes length was zero, build the key based on the random ones
131 if bytes.len == 0 {
132 return PrivateKey.new(kind: kind)!
133 }
134 // Get the n size parameter set from the options
135 nsize := kind.nsize()
136 // The private key has a size of 4 * n bytes.
137 if bytes.len != 4 * nsize {
138 return error('Unmatching private bytes length with kind supplied, need ${4 * nsize} bytes')
139 }
140
141 param_bld := C.OSSL_PARAM_BLD_new()
142 assert param_bld != 0
143
144 m := C.OSSL_PARAM_BLD_push_octet_string(param_bld, c'priv', bytes.data, bytes.len)
145 if m <= 0 {
146 C.OSSL_PARAM_BLD_free(param_bld)
147 return error('OSSL_PARAM_BLD_push FAILED')
148 }
149
150 // build params
151 params := C.OSSL_PARAM_BLD_to_param(param_bld)
152 // create a desired context
153 pctx := C.EVP_PKEY_CTX_new_from_name(0, kind.long_name(), 0)
154 if params == 0 || pctx == 0 {
155 C.OSSL_PARAM_BLD_free(param_bld)
156 C.OSSL_PARAM_free(params)
157 C.EVP_PKEY_CTX_free(pctx)
158 return error('EVP_PKEY_CTX_new or OSSL_PARAM_BLD_to_param failed')
159 }
160
161 pkey := C.EVP_PKEY_new()
162 assert pkey != 0
163
164 s := C.EVP_PKEY_fromdata_init(pctx)
165 if s <= 0 {
166 C.OSSL_PARAM_BLD_free(param_bld)
167 C.OSSL_PARAM_free(params)
168 C.EVP_PKEY_CTX_free(pctx)
169 C.EVP_PKEY_free(pkey)
170 return error('EVP_PKEY_fromdata_init failed')
171 }
172
173 ss := C.EVP_PKEY_fromdata(pctx, &pkey, evp_pkey_keypair, params)
174 if ss <= 0 {
175 C.OSSL_PARAM_BLD_free(param_bld)
176 C.OSSL_PARAM_free(params)
177 C.EVP_PKEY_CTX_free(pctx)
178 C.EVP_PKEY_free(pkey)
179 return error('EVP_PKEY_fromdata failed')
180 }
181
182 pvkey := PrivateKey{
183 key: pkey
184 }
185 // Cleans up
186 C.OSSL_PARAM_BLD_free(param_bld)
187 C.OSSL_PARAM_free(params)
188 C.EVP_PKEY_CTX_free(pctx)
189
190 return pvkey
191}
192
193// from_bytes creates a new PublicKey with type of supported key and bytes array.
194// If you dont provide the bytes, ie, supplied with zero-length bytes,
195// it will be generated with random bytes for you.
196pub fn PublicKey.from_bytes(bytes []u8, kind Kind) !PublicKey {
197 // when bytes length was zero, build the key based on the random ones
198 if bytes.len == 0 {
199 pv := PrivateKey.new(kind: kind)!
200 pbk := pv.public_key()!
201 return pbk
202 }
203 // Get the n size parameter set from the options
204 nsize := kind.nsize()
205 // The public key has a size of 2 * n bytes.
206 if bytes.len != 2 * nsize {
207 return error('Unmatching public bytes length with kind supplied, need ${2 * nsize} bytes')
208 }
209
210 param_bld := C.OSSL_PARAM_BLD_new()
211 assert param_bld != 0
212
213 m := C.OSSL_PARAM_BLD_push_octet_string(param_bld, c'pub', bytes.data, bytes.len)
214 if m <= 0 {
215 C.OSSL_PARAM_BLD_free(param_bld)
216 return error('OSSL_PARAM_BLD_push FAILED')
217 }
218
219 // build params
220 params := C.OSSL_PARAM_BLD_to_param(param_bld)
221 // create a desired context
222 pctx := C.EVP_PKEY_CTX_new_from_name(0, kind.long_name(), 0)
223 if params == 0 || pctx == 0 {
224 C.OSSL_PARAM_BLD_free(param_bld)
225 C.OSSL_PARAM_free(params)
226 C.EVP_PKEY_CTX_free(pctx)
227 return error('EVP_PKEY_CTX_new or OSSL_PARAM_BLD_to_param failed')
228 }
229
230 pkey := C.EVP_PKEY_new()
231 assert pkey != 0
232
233 s := C.EVP_PKEY_fromdata_init(pctx)
234 if s <= 0 {
235 C.OSSL_PARAM_BLD_free(param_bld)
236 C.OSSL_PARAM_free(params)
237 C.EVP_PKEY_CTX_free(pctx)
238 C.EVP_PKEY_free(pkey)
239 return error('EVP_PKEY_fromdata failed')
240 }
241
242 ss := C.EVP_PKEY_fromdata(pctx, &pkey, evp_pkey_public_key, params)
243 if ss <= 0 {
244 C.OSSL_PARAM_BLD_free(param_bld)
245 C.OSSL_PARAM_free(params)
246 C.EVP_PKEY_CTX_free(pctx)
247 C.EVP_PKEY_free(pkey)
248 return error('EVP_PKEY_fromdata failed')
249 }
250
251 pbkey := PublicKey{
252 key: pkey
253 }
254 // Cleans up
255 C.OSSL_PARAM_BLD_free(param_bld)
256 C.OSSL_PARAM_free(params)
257 C.EVP_PKEY_CTX_free(pctx)
258
259 return pbkey
260}
261
262// The enumeration of NID of SLHDSA parameters set. <br>
263// See Table 2. SLH-DSA parameter sets of the Chapter 11. Parameter Sets<br>
264// Each sets name indicates:
265//
266// - the hash function family (SHA2 or SHAKE) that is used to instantiate the hash functions.
267// - the length in bits of the security parameter, in the 128, 192, and 256 respectives number.
268// - the mnemonic name indicates parameter to create relatively small signatures (`s`)
269// or to have relatively fast signature generation (`f`).
270pub enum Kind {
271 // SHA2-based family
272 sha2_128s = C.NID_SLH_DSA_SHA2_128s
273 sha2_128f = C.NID_SLH_DSA_SHA2_128f
274 sha2_192s = C.NID_SLH_DSA_SHA2_192s
275 sha2_192f = C.NID_SLH_DSA_SHA2_192f
276 sha2_256s = C.NID_SLH_DSA_SHA2_256s
277 sha2_256f = C.NID_SLH_DSA_SHA2_256f
278 // SHAKE-based family
279 shake_128s = C.NID_SLH_DSA_SHAKE_128s
280 shake_128f = C.NID_SLH_DSA_SHAKE_128f
281 shake_192s = C.NID_SLH_DSA_SHAKE_192s
282 shake_192f = C.NID_SLH_DSA_SHAKE_192f
283 shake_256s = C.NID_SLH_DSA_SHAKE_256s
284 shake_256f = C.NID_SLH_DSA_SHAKE_256f
285}
286
287// nsize returns the size of underlying n parameter from current type.
288@[inline]
289fn (n Kind) nsize() int {
290 match n {
291 .sha2_128s, .sha2_128f, .shake_128s, .shake_128f { return 16 }
292 .sha2_192s, .sha2_192f, .shake_192s, .shake_192f { return 24 }
293 .sha2_256s, .sha2_256f, .shake_256s, .shake_256f { return 32 }
294 }
295}
296
297fn (n Kind) str() string {
298 match n {
299 // vfmt off
300 // SHA2-based family
301 .sha2_128s { return "sha2_128s" }
302 .sha2_128f { return "sha2_128f" }
303 .sha2_192s { return "sha2_192s" }
304 .sha2_192f { return "sha2_192f" }
305 .sha2_256s { return "sha2_256s" }
306 .sha2_256f { return "sha2_256f" }
307 // SHAKE-based family
308 .shake_128s { return "shake_128s" }
309 .shake_128f { return "shake_128f" }
310 .shake_192s { return "shake_192s" }
311 .shake_192f { return "shake_192f" }
312 .shake_256s { return "shake_256s" }
313 .shake_256f { return "shake_256f" }
314 // vfmt on
315 }
316}
317
318// Kind long name as v string
319@[inline]
320fn (n Kind) ln_to_vstr() string {
321 out := unsafe { n.long_name().vstring() }
322 return out
323}
324
325// Kind long name as c-style string
326@[inline]
327fn (n Kind) long_name() &char {
328 match n {
329 // vfmt off
330 // SHA2-based family
331 .sha2_128s { return ln_slhdsa_sha2_128s }
332 .sha2_128f { return ln_slhdsa_sha2_128f }
333 .sha2_192s { return ln_slhdsa_sha2_192s }
334 .sha2_192f { return ln_slhdsa_sha2_192f }
335 .sha2_256s { return ln_slhdsa_sha2_256s }
336 .sha2_256f { return ln_slhdsa_sha2_256f }
337 // SHAKE-based family
338 .shake_128s { return ln_slhdsa_shake_128s }
339 .shake_128f { return ln_slhdsa_shake_128f }
340 .shake_192s { return ln_slhdsa_shake_192s }
341 .shake_192f { return ln_slhdsa_shake_192f }
342 .shake_256s { return ln_slhdsa_shake_256s }
343 .shake_256f { return ln_slhdsa_shake_256f }
344 // vfmt on
345 }
346}
347
348// Chapter 11. Parameters Set
349struct ParamSet {
350 // Algorithm name
351 id Kind
352 n int
353 h int
354 d int
355 hp int
356 a int
357 k int
358 lgw int = 4
359 m int
360 sc int
361 pkb int
362 sig int
363}
364
365// Table 2. SLH-DSA parameter sets
366const paramset = {
367 // id 𝑛 ℎ 𝑑 ℎ′ 𝑎 𝑘 𝑙𝑔𝑤 𝑚 sc pkb sig
368 'sha2_128s': ParamSet{.sha2_128s, 16, 63, 7, 9, 12, 14, 4, 30, 1, 32, 7856}
369 'sha2_128f': ParamSet{.sha2_128f, 16, 66, 22, 3, 6, 33, 4, 34, 1, 32, 17088}
370 'sha2_192s': ParamSet{.sha2_192s, 24, 63, 7, 9, 14, 17, 4, 39, 3, 48, 16224}
371 'sha2_192f': ParamSet{.sha2_192f, 24, 66, 22, 3, 8, 33, 4, 42, 3, 48, 35664}
372 'sha2_256s': ParamSet{.sha2_256s, 32, 64, 8, 8, 14, 22, 4, 47, 5, 64, 29792}
373 'sha2_256f': ParamSet{.sha2_256f, 32, 68, 17, 4, 9, 35, 4, 49, 5, 64, 49856}
374 // SHAKE family
375 'shake_128s': ParamSet{.shake_128s, 16, 63, 7, 9, 12, 14, 4, 30, 1, 32, 7856}
376 'shake_128f': ParamSet{.shake_128f, 16, 66, 22, 3, 6, 33, 4, 34, 1, 32, 17088}
377 'shake_192s': ParamSet{.shake_192s, 24, 63, 7, 9, 14, 17, 4, 39, 3, 48, 16224}
378 'shake_192f': ParamSet{.shake_192f, 24, 66, 22, 3, 8, 33, 4, 42, 3, 48, 35664}
379 'shake_256s': ParamSet{.shake_256s, 32, 64, 8, 8, 14, 22, 4, 47, 5, 64, 29792}
380 'shake_256f': ParamSet{.shake_256f, 32, 68, 17, 4, 9, 35, 4, 49, 5, 64, 49856}
381}
382
383fn ParamSet.from_kind(k Kind) ParamSet {
384 return paramset[k.str()]
385}
386
387// Some helpers
388//
389fn key_algo_name(key &C.EVP_PKEY) &char {
390 name := voidptr(C.EVP_PKEY_get0_type_name(key))
391 assert name != 0
392 return name
393}
394
395fn key_type_name(key &C.EVP_PKEY) &char {
396 kn := voidptr(C.EVP_PKEY_get0_type_name(key))
397 assert kn != 0
398 return kn
399}
400
401fn key_description(key &C.EVP_PKEY) &char {
402 kd := voidptr(C.EVP_PKEY_get0_description(key))
403 assert kd != 0
404 return kd
405}
406