xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/hcrypto/test_rsa.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
1*d3273b5bSchristos /*	$NetBSD: test_rsa.c,v 1.2 2017/01/28 21:31:47 christos Exp $	*/
2ca1c9b0cSelric 
3ca1c9b0cSelric /*
4ca1c9b0cSelric  * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
5ca1c9b0cSelric  * (Royal Institute of Technology, Stockholm, Sweden).
6ca1c9b0cSelric  * All rights reserved.
7ca1c9b0cSelric  *
8ca1c9b0cSelric  * Redistribution and use in source and binary forms, with or without
9ca1c9b0cSelric  * modification, are permitted provided that the following conditions
10ca1c9b0cSelric  * are met:
11ca1c9b0cSelric  *
12ca1c9b0cSelric  * 1. Redistributions of source code must retain the above copyright
13ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer.
14ca1c9b0cSelric  *
15ca1c9b0cSelric  * 2. Redistributions in binary form must reproduce the above copyright
16ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer in the
17ca1c9b0cSelric  *    documentation and/or other materials provided with the distribution.
18ca1c9b0cSelric  *
19ca1c9b0cSelric  * 3. Neither the name of the Institute nor the names of its contributors
20ca1c9b0cSelric  *    may be used to endorse or promote products derived from this software
21ca1c9b0cSelric  *    without specific prior written permission.
22ca1c9b0cSelric  *
23ca1c9b0cSelric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24ca1c9b0cSelric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25ca1c9b0cSelric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ca1c9b0cSelric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27ca1c9b0cSelric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28ca1c9b0cSelric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29ca1c9b0cSelric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30ca1c9b0cSelric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31ca1c9b0cSelric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32ca1c9b0cSelric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33ca1c9b0cSelric  * SUCH DAMAGE.
34ca1c9b0cSelric  */
35ca1c9b0cSelric 
36ca1c9b0cSelric #include <config.h>
37ca1c9b0cSelric #include <krb5/roken.h>
38ca1c9b0cSelric #include <krb5/getarg.h>
39ca1c9b0cSelric 
40ca1c9b0cSelric #include <engine.h>
41ca1c9b0cSelric #include <evp.h>
42ca1c9b0cSelric 
43ca1c9b0cSelric /*
44ca1c9b0cSelric  *
45ca1c9b0cSelric  */
46ca1c9b0cSelric 
47ca1c9b0cSelric static int version_flag;
48ca1c9b0cSelric static int help_flag;
49ca1c9b0cSelric static int time_keygen;
50ca1c9b0cSelric static char *time_key;
51ca1c9b0cSelric static int key_blinding = 1;
52ca1c9b0cSelric static char *rsa_key;
53ca1c9b0cSelric static char *id_flag;
54ca1c9b0cSelric static int loops = 1;
55ca1c9b0cSelric 
56ca1c9b0cSelric static struct getargs args[] = {
57ca1c9b0cSelric     { "loops",		0,	arg_integer,	&loops,
58ca1c9b0cSelric       "number of loops", 	"loops" },
59ca1c9b0cSelric     { "id",		0,	arg_string,	&id_flag,
60ca1c9b0cSelric       "selects the engine id", 	"engine-id" },
61ca1c9b0cSelric     { "time-keygen",	0,	arg_flag,	&time_keygen,
62ca1c9b0cSelric       "time rsa generation", NULL },
63ca1c9b0cSelric     { "time-key",	0,	arg_string,	&time_key,
64ca1c9b0cSelric       "rsa key file", NULL },
65ca1c9b0cSelric     { "key-blinding",	0,	arg_negative_flag, &key_blinding,
66ca1c9b0cSelric       "key blinding", NULL },
67ca1c9b0cSelric     { "key",	0,	arg_string,	&rsa_key,
68ca1c9b0cSelric       "rsa key file", NULL },
69ca1c9b0cSelric     { "version",	0,	arg_flag,	&version_flag,
70ca1c9b0cSelric       "print version", NULL },
71ca1c9b0cSelric     { "help",		0,	arg_flag,	&help_flag,
72ca1c9b0cSelric       NULL, 	NULL }
73ca1c9b0cSelric };
74ca1c9b0cSelric 
75ca1c9b0cSelric /*
76ca1c9b0cSelric  *
77ca1c9b0cSelric  */
78ca1c9b0cSelric 
79ca1c9b0cSelric static void
check_rsa(const unsigned char * in,size_t len,RSA * rsa,int padding)80ca1c9b0cSelric check_rsa(const unsigned char *in, size_t len, RSA *rsa, int padding)
81ca1c9b0cSelric {
82ca1c9b0cSelric     unsigned char *res, *res2;
83ca1c9b0cSelric     unsigned int len2;
84ca1c9b0cSelric     int keylen;
85ca1c9b0cSelric 
86ca1c9b0cSelric     res = malloc(RSA_size(rsa));
87ca1c9b0cSelric     if (res == NULL)
88ca1c9b0cSelric 	errx(1, "res: ENOMEM");
89ca1c9b0cSelric 
90ca1c9b0cSelric     res2 = malloc(RSA_size(rsa));
91ca1c9b0cSelric     if (res2 == NULL)
92ca1c9b0cSelric 	errx(1, "res2: ENOMEM");
93ca1c9b0cSelric 
94ca1c9b0cSelric     /* signing */
95ca1c9b0cSelric 
96ca1c9b0cSelric     keylen = RSA_private_encrypt(len, in, res, rsa, padding);
97ca1c9b0cSelric     if (keylen <= 0)
98ca1c9b0cSelric 	errx(1, "failed to private encrypt: %d %d", (int)len, (int)keylen);
99ca1c9b0cSelric 
100ca1c9b0cSelric     if (keylen > RSA_size(rsa))
101ca1c9b0cSelric 	errx(1, "keylen > RSA_size(rsa)");
102ca1c9b0cSelric 
103ca1c9b0cSelric     keylen = RSA_public_decrypt(keylen, res, res2, rsa, padding);
104ca1c9b0cSelric     if (keylen <= 0)
105ca1c9b0cSelric 	errx(1, "failed to public decrypt: %d", (int)keylen);
106ca1c9b0cSelric 
107ca1c9b0cSelric     if (keylen != len)
108ca1c9b0cSelric 	errx(1, "output buffer not same length: %d", (int)keylen);
109ca1c9b0cSelric 
110ca1c9b0cSelric     if (memcmp(res2, in, len) != 0)
111ca1c9b0cSelric 	errx(1, "string not the same after decryption");
112ca1c9b0cSelric 
113ca1c9b0cSelric     /* encryption */
114ca1c9b0cSelric 
115ca1c9b0cSelric     keylen = RSA_public_encrypt(len, in, res, rsa, padding);
116ca1c9b0cSelric     if (keylen <= 0)
117ca1c9b0cSelric 	errx(1, "failed to public encrypt: %d", (int)keylen);
118ca1c9b0cSelric 
119ca1c9b0cSelric     if (keylen > RSA_size(rsa))
120ca1c9b0cSelric 	errx(1, "keylen > RSA_size(rsa)");
121ca1c9b0cSelric 
122ca1c9b0cSelric     keylen = RSA_private_decrypt(keylen, res, res2, rsa, padding);
123ca1c9b0cSelric     if (keylen <= 0)
124ca1c9b0cSelric 	errx(1, "failed to private decrypt: %d", (int)keylen);
125ca1c9b0cSelric 
126ca1c9b0cSelric     if (keylen != len)
127ca1c9b0cSelric 	errx(1, "output buffer not same length: %d", (int)keylen);
128ca1c9b0cSelric 
129ca1c9b0cSelric     if (memcmp(res2, in, len) != 0)
130ca1c9b0cSelric 	errx(1, "string not the same after decryption");
131ca1c9b0cSelric 
132ca1c9b0cSelric     len2 = keylen;
133ca1c9b0cSelric 
134ca1c9b0cSelric     if (RSA_sign(NID_sha1, in, len, res, &len2, rsa) != 1)
135ca1c9b0cSelric 	errx(1, "RSA_sign failed");
136ca1c9b0cSelric 
137ca1c9b0cSelric     if (RSA_verify(NID_sha1, in, len, res, len2, rsa) != 1)
138ca1c9b0cSelric 	errx(1, "RSA_verify failed");
139ca1c9b0cSelric 
140ca1c9b0cSelric     free(res);
141ca1c9b0cSelric     free(res2);
142ca1c9b0cSelric }
143ca1c9b0cSelric 
144ca1c9b0cSelric static int
cb_func(int a,int b,BN_GENCB * c)145ca1c9b0cSelric cb_func(int a, int b, BN_GENCB *c)
146ca1c9b0cSelric {
147ca1c9b0cSelric     return 1;
148ca1c9b0cSelric }
149ca1c9b0cSelric 
150ca1c9b0cSelric static RSA *
read_key(ENGINE * engine,const char * keyfile)151b9d004c6Schristos read_key(ENGINE *engine, const char *keyfile)
152ca1c9b0cSelric {
153ca1c9b0cSelric     unsigned char buf[1024 * 4];
154ca1c9b0cSelric     const unsigned char *p;
155ca1c9b0cSelric     size_t size;
156ca1c9b0cSelric     RSA *rsa;
157ca1c9b0cSelric     FILE *f;
158ca1c9b0cSelric 
159b9d004c6Schristos     f = fopen(keyfile, "rb");
160ca1c9b0cSelric     if (f == NULL)
161b9d004c6Schristos 	err(1, "could not open file %s", keyfile);
162ca1c9b0cSelric     rk_cloexec_file(f);
163ca1c9b0cSelric 
164ca1c9b0cSelric     size = fread(buf, 1, sizeof(buf), f);
165ca1c9b0cSelric     fclose(f);
166ca1c9b0cSelric     if (size == 0)
167b9d004c6Schristos 	err(1, "failed to read file %s", keyfile);
168ca1c9b0cSelric     if (size == sizeof(buf))
169b9d004c6Schristos 	err(1, "key too long in file %s!", keyfile);
170ca1c9b0cSelric 
171ca1c9b0cSelric     p = buf;
172ca1c9b0cSelric     rsa = d2i_RSAPrivateKey(NULL, &p, size);
173ca1c9b0cSelric     if (rsa == NULL)
174b9d004c6Schristos 	err(1, "failed to parse key in file %s", keyfile);
175ca1c9b0cSelric 
176ca1c9b0cSelric     RSA_set_method(rsa, ENGINE_get_RSA(engine));
177ca1c9b0cSelric 
178ca1c9b0cSelric     if (!key_blinding)
179ca1c9b0cSelric 	rsa->flags |= RSA_FLAG_NO_BLINDING;
180ca1c9b0cSelric 
181ca1c9b0cSelric     return rsa;
182ca1c9b0cSelric }
183ca1c9b0cSelric 
184ca1c9b0cSelric /*
185ca1c9b0cSelric  *
186ca1c9b0cSelric  */
187ca1c9b0cSelric 
188ca1c9b0cSelric static void
usage(int ret)189ca1c9b0cSelric usage (int ret)
190ca1c9b0cSelric {
191ca1c9b0cSelric     arg_printusage (args,
192ca1c9b0cSelric 		    sizeof(args)/sizeof(*args),
193ca1c9b0cSelric 		    NULL,
194ca1c9b0cSelric 		    "filename.so");
195ca1c9b0cSelric     exit (ret);
196ca1c9b0cSelric }
197ca1c9b0cSelric 
198ca1c9b0cSelric int
main(int argc,char ** argv)199ca1c9b0cSelric main(int argc, char **argv)
200ca1c9b0cSelric {
201ca1c9b0cSelric     ENGINE *engine = NULL;
202ca1c9b0cSelric     int i, j, idx = 0;
203ca1c9b0cSelric     RSA *rsa;
204ca1c9b0cSelric 
205ca1c9b0cSelric     setprogname(argv[0]);
206ca1c9b0cSelric 
207ca1c9b0cSelric     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &idx))
208ca1c9b0cSelric 	usage(1);
209ca1c9b0cSelric 
210ca1c9b0cSelric     if (help_flag)
211ca1c9b0cSelric 	usage(0);
212ca1c9b0cSelric 
213ca1c9b0cSelric     if(version_flag){
214ca1c9b0cSelric 	print_version(NULL);
215ca1c9b0cSelric 	exit(0);
216ca1c9b0cSelric     }
217ca1c9b0cSelric 
218ca1c9b0cSelric     argc -= idx;
219ca1c9b0cSelric     argv += idx;
220ca1c9b0cSelric 
221ca1c9b0cSelric     OpenSSL_add_all_algorithms();
222ca1c9b0cSelric #ifdef OPENSSL
223ca1c9b0cSelric     ENGINE_load_openssl();
224ca1c9b0cSelric #endif
225ca1c9b0cSelric     ENGINE_load_builtin_engines();
226ca1c9b0cSelric 
227ca1c9b0cSelric     if (argc == 0) {
228ca1c9b0cSelric 	engine = ENGINE_by_id("builtin");
229ca1c9b0cSelric     } else {
230ca1c9b0cSelric 	engine = ENGINE_by_id(argv[0]);
231ca1c9b0cSelric 	if (engine == NULL)
232ca1c9b0cSelric 	    engine = ENGINE_by_dso(argv[0], id_flag);
233ca1c9b0cSelric     }
234ca1c9b0cSelric     if (engine == NULL)
235ca1c9b0cSelric 	errx(1, "ENGINE_by_dso failed");
236ca1c9b0cSelric 
237ca1c9b0cSelric     if (ENGINE_get_RSA(engine) == NULL)
238ca1c9b0cSelric 	return 77;
239ca1c9b0cSelric 
240ca1c9b0cSelric     printf("rsa %s\n", ENGINE_get_RSA(engine)->name);
241ca1c9b0cSelric 
242ca1c9b0cSelric     if (RAND_status() != 1)
243ca1c9b0cSelric 	errx(77, "no functional random device, refusing to run tests");
244ca1c9b0cSelric 
245ca1c9b0cSelric     if (time_keygen) {
246ca1c9b0cSelric 	struct timeval tv1, tv2;
247ca1c9b0cSelric 	BIGNUM *e;
248ca1c9b0cSelric 
249ca1c9b0cSelric 	rsa = RSA_new_method(engine);
250ca1c9b0cSelric 	if (!key_blinding)
251ca1c9b0cSelric 	    rsa->flags |= RSA_FLAG_NO_BLINDING;
252ca1c9b0cSelric 
253ca1c9b0cSelric 	e = BN_new();
254ca1c9b0cSelric 	BN_set_word(e, 0x10001);
255ca1c9b0cSelric 
256ca1c9b0cSelric 	printf("running keygen with %d loops\n", loops);
257ca1c9b0cSelric 
258ca1c9b0cSelric 	gettimeofday(&tv1, NULL);
259ca1c9b0cSelric 
260ca1c9b0cSelric 	for (i = 0; i < loops; i++) {
261ca1c9b0cSelric 	    rsa = RSA_new_method(engine);
262ca1c9b0cSelric 	    if (RSA_generate_key_ex(rsa, 1024, e, NULL) != 1)
263ca1c9b0cSelric 		errx(1, "RSA_generate_key_ex");
264ca1c9b0cSelric 	    RSA_free(rsa);
265ca1c9b0cSelric 	}
266ca1c9b0cSelric 
267ca1c9b0cSelric 	gettimeofday(&tv2, NULL);
268ca1c9b0cSelric 	timevalsub(&tv2, &tv1);
269ca1c9b0cSelric 
270ca1c9b0cSelric 	printf("time %lu.%06lu\n",
271ca1c9b0cSelric 	       (unsigned long)tv2.tv_sec,
272ca1c9b0cSelric 	       (unsigned long)tv2.tv_usec);
273ca1c9b0cSelric 
274ca1c9b0cSelric 	BN_free(e);
275ca1c9b0cSelric 	ENGINE_finish(engine);
276ca1c9b0cSelric 
277ca1c9b0cSelric 	return 0;
278ca1c9b0cSelric     }
279ca1c9b0cSelric 
280ca1c9b0cSelric     if (time_key) {
281ca1c9b0cSelric 	const int size = 20;
282ca1c9b0cSelric 	struct timeval tv1, tv2;
283ca1c9b0cSelric 	unsigned char *p;
284ca1c9b0cSelric 
285ca1c9b0cSelric 	if (strcmp(time_key, "generate") == 0) {
286ca1c9b0cSelric 	    BIGNUM *e;
287ca1c9b0cSelric 
288ca1c9b0cSelric 	    rsa = RSA_new_method(engine);
289ca1c9b0cSelric 	    if (!key_blinding)
290ca1c9b0cSelric 		rsa->flags |= RSA_FLAG_NO_BLINDING;
291ca1c9b0cSelric 
292ca1c9b0cSelric 	    e = BN_new();
293ca1c9b0cSelric 	    BN_set_word(e, 0x10001);
294ca1c9b0cSelric 
295ca1c9b0cSelric 	    if (RSA_generate_key_ex(rsa, 1024, e, NULL) != 1)
296ca1c9b0cSelric 		errx(1, "RSA_generate_key_ex");
297ca1c9b0cSelric 	} else {
298ca1c9b0cSelric 	    rsa = read_key(engine, time_key);
299ca1c9b0cSelric 	}
300ca1c9b0cSelric 
301ca1c9b0cSelric 	p = emalloc(loops * size);
302ca1c9b0cSelric 
303ca1c9b0cSelric 	RAND_bytes(p, loops * size);
304ca1c9b0cSelric 
305ca1c9b0cSelric 	gettimeofday(&tv1, NULL);
306ca1c9b0cSelric 	for (i = 0; i < loops; i++)
307ca1c9b0cSelric 	    check_rsa(p + (i * size), size, rsa, RSA_PKCS1_PADDING);
308ca1c9b0cSelric 	gettimeofday(&tv2, NULL);
309ca1c9b0cSelric 
310ca1c9b0cSelric 	timevalsub(&tv2, &tv1);
311ca1c9b0cSelric 
312ca1c9b0cSelric 	printf("time %lu.%06lu\n",
313ca1c9b0cSelric 	       (unsigned long)tv2.tv_sec,
314ca1c9b0cSelric 	       (unsigned long)tv2.tv_usec);
315ca1c9b0cSelric 
316ca1c9b0cSelric 	RSA_free(rsa);
317ca1c9b0cSelric 	ENGINE_finish(engine);
318ca1c9b0cSelric 
319ca1c9b0cSelric 	return 0;
320ca1c9b0cSelric     }
321ca1c9b0cSelric 
322ca1c9b0cSelric     if (rsa_key) {
323ca1c9b0cSelric 	rsa = read_key(engine, rsa_key);
324ca1c9b0cSelric 
325ca1c9b0cSelric 	/*
326ca1c9b0cSelric 	 * Assuming that you use the RSA key in the distribution, this
327ca1c9b0cSelric 	 * test will generate a signature have a starting zero and thus
328ca1c9b0cSelric 	 * will generate a checksum that is 127 byte instead of the
329ca1c9b0cSelric 	 * checksum that is 128 byte (like the key).
330ca1c9b0cSelric 	 */
331ca1c9b0cSelric 	{
332ca1c9b0cSelric 	    const unsigned char sha1[20] = {
333ca1c9b0cSelric 		0x6d, 0x33, 0xf9, 0x40, 0x75, 0x5b, 0x4e, 0xc5, 0x90, 0x35,
334ca1c9b0cSelric 		0x48, 0xab, 0x75, 0x02, 0x09, 0x76, 0x9a, 0xb4, 0x7d, 0x6b
335ca1c9b0cSelric 	    };
336ca1c9b0cSelric 
337ca1c9b0cSelric 	    check_rsa(sha1, sizeof(sha1), rsa, RSA_PKCS1_PADDING);
338ca1c9b0cSelric 	}
339ca1c9b0cSelric 
340ca1c9b0cSelric 	for (i = 0; i < 128; i++) {
341ca1c9b0cSelric 	    unsigned char sha1[20];
342ca1c9b0cSelric 
343ca1c9b0cSelric 	    RAND_bytes(sha1, sizeof(sha1));
344ca1c9b0cSelric 	    check_rsa(sha1, sizeof(sha1), rsa, RSA_PKCS1_PADDING);
345ca1c9b0cSelric 	}
346ca1c9b0cSelric 	for (i = 0; i < 128; i++) {
347ca1c9b0cSelric 	    unsigned char des3[21];
348ca1c9b0cSelric 
349ca1c9b0cSelric 	    RAND_bytes(des3, sizeof(des3));
350ca1c9b0cSelric 	    check_rsa(des3, sizeof(des3), rsa, RSA_PKCS1_PADDING);
351ca1c9b0cSelric 	}
352ca1c9b0cSelric 	for (i = 0; i < 128; i++) {
353ca1c9b0cSelric 	    unsigned char aes[32];
354ca1c9b0cSelric 
355ca1c9b0cSelric 	    RAND_bytes(aes, sizeof(aes));
356ca1c9b0cSelric 	    check_rsa(aes, sizeof(aes), rsa, RSA_PKCS1_PADDING);
357ca1c9b0cSelric 	}
358ca1c9b0cSelric 
359ca1c9b0cSelric 	RSA_free(rsa);
360ca1c9b0cSelric     }
361ca1c9b0cSelric 
362ca1c9b0cSelric     for (i = 0; i < loops; i++) {
363ca1c9b0cSelric 	BN_GENCB cb;
364ca1c9b0cSelric 	BIGNUM *e;
365ca1c9b0cSelric 	unsigned int n;
366ca1c9b0cSelric 
367ca1c9b0cSelric 	rsa = RSA_new_method(engine);
368ca1c9b0cSelric 	if (!key_blinding)
369ca1c9b0cSelric 	    rsa->flags |= RSA_FLAG_NO_BLINDING;
370ca1c9b0cSelric 
371ca1c9b0cSelric 	e = BN_new();
372ca1c9b0cSelric 	BN_set_word(e, 0x10001);
373ca1c9b0cSelric 
374ca1c9b0cSelric 	BN_GENCB_set(&cb, cb_func, NULL);
375ca1c9b0cSelric 
376ca1c9b0cSelric 	RAND_bytes(&n, sizeof(n));
377ca1c9b0cSelric 	n &= 0x1ff;
378ca1c9b0cSelric 	n += 1024;
379ca1c9b0cSelric 
380ca1c9b0cSelric 	if (RSA_generate_key_ex(rsa, n, e, &cb) != 1)
381ca1c9b0cSelric 	    errx(1, "RSA_generate_key_ex");
382ca1c9b0cSelric 
383ca1c9b0cSelric 	BN_free(e);
384ca1c9b0cSelric 
385ca1c9b0cSelric 	for (j = 0; j < 8; j++) {
386ca1c9b0cSelric 	    unsigned char sha1[20];
387ca1c9b0cSelric 	    RAND_bytes(sha1, sizeof(sha1));
388ca1c9b0cSelric 	    check_rsa(sha1, sizeof(sha1), rsa, RSA_PKCS1_PADDING);
389ca1c9b0cSelric 	}
390ca1c9b0cSelric 
391ca1c9b0cSelric 	RSA_free(rsa);
392ca1c9b0cSelric     }
393ca1c9b0cSelric 
394ca1c9b0cSelric     ENGINE_finish(engine);
395ca1c9b0cSelric 
396ca1c9b0cSelric     return 0;
397ca1c9b0cSelric }
398