4 * Portions Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Portions Copyright (C) 1999-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
12 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
14 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
17 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
21 * Permission to use, copy, modify, and/or distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the above
23 * copyright notice and this permission notice appear in all copies.
25 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
26 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
27 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
28 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
29 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
30 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
31 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 * Principal Author: Brian Wellington
36 * Id: dst_parse.c,v 1.23 2009/10/26 21:18:24 each Exp
41 #include <isc/base64.h>
43 #include <isc/fsaccess.h>
46 #include <isc/stdtime.h>
47 #include <isc/string.h>
52 #include "dst_internal.h"
53 #include "dst_parse.h"
54 #include "dst/result.h"
56 #define DST_AS_STR(t) ((t).value.as_textregion.base)
58 #define PRIVATE_KEY_STR "Private-key-format:"
59 #define ALGORITHM_STR "Algorithm:"
61 #define TIMING_NTAGS (DST_MAX_TIMES + 1)
62 static const char *timetags
[TIMING_NTAGS
] = {
72 #define NUMERIC_NTAGS (DST_MAX_NUMERIC + 1)
73 static const char *numerictags
[NUMERIC_NTAGS
] = {
85 static struct parse_map map
[] = {
86 {TAG_RSA_MODULUS
, "Modulus:"},
87 {TAG_RSA_PUBLICEXPONENT
, "PublicExponent:"},
88 {TAG_RSA_PRIVATEEXPONENT
, "PrivateExponent:"},
89 {TAG_RSA_PRIME1
, "Prime1:"},
90 {TAG_RSA_PRIME2
, "Prime2:"},
91 {TAG_RSA_EXPONENT1
, "Exponent1:"},
92 {TAG_RSA_EXPONENT2
, "Exponent2:"},
93 {TAG_RSA_COEFFICIENT
, "Coefficient:"},
94 {TAG_RSA_ENGINE
, "Engine:" },
95 {TAG_RSA_LABEL
, "Label:" },
96 {TAG_RSA_PIN
, "PIN:" },
98 {TAG_DH_PRIME
, "Prime(p):"},
99 {TAG_DH_GENERATOR
, "Generator(g):"},
100 {TAG_DH_PRIVATE
, "Private_value(x):"},
101 {TAG_DH_PUBLIC
, "Public_value(y):"},
103 {TAG_DSA_PRIME
, "Prime(p):"},
104 {TAG_DSA_SUBPRIME
, "Subprime(q):"},
105 {TAG_DSA_BASE
, "Base(g):"},
106 {TAG_DSA_PRIVATE
, "Private_value(x):"},
107 {TAG_DSA_PUBLIC
, "Public_value(y):"},
109 {TAG_HMACMD5_KEY
, "Key:"},
110 {TAG_HMACMD5_BITS
, "Bits:"},
112 {TAG_HMACSHA1_KEY
, "Key:"},
113 {TAG_HMACSHA1_BITS
, "Bits:"},
115 {TAG_HMACSHA224_KEY
, "Key:"},
116 {TAG_HMACSHA224_BITS
, "Bits:"},
118 {TAG_HMACSHA256_KEY
, "Key:"},
119 {TAG_HMACSHA256_BITS
, "Bits:"},
121 {TAG_HMACSHA384_KEY
, "Key:"},
122 {TAG_HMACSHA384_BITS
, "Bits:"},
124 {TAG_HMACSHA512_KEY
, "Key:"},
125 {TAG_HMACSHA512_BITS
, "Bits:"},
131 find_value(const char *s
, const unsigned int alg
) {
134 for (i
= 0; map
[i
].tag
!= NULL
; i
++) {
135 if (strcasecmp(s
, map
[i
].tag
) == 0 &&
136 (TAG_ALG(map
[i
].value
) == alg
))
137 return (map
[i
].value
);
143 find_tag(const int value
) {
147 if (map
[i
].tag
== NULL
)
149 else if (value
== map
[i
].value
)
155 find_metadata(const char *s
, const char *tags
[], int ntags
) {
158 for (i
= 0; i
< ntags
; i
++) {
159 if (strcasecmp(s
, tags
[i
]) == 0)
167 find_timedata(const char *s
) {
168 return (find_metadata(s
, timetags
, TIMING_NTAGS
));
172 find_numericdata(const char *s
) {
173 return (find_metadata(s
, numerictags
, NUMERIC_NTAGS
));
177 check_rsa(const dst_private_t
*priv
) {
179 isc_boolean_t have
[RSA_NTAGS
];
183 for (i
= 0; i
< RSA_NTAGS
; i
++)
185 for (j
= 0; j
< priv
->nelements
; j
++) {
186 for (i
= 0; i
< RSA_NTAGS
; i
++)
187 if (priv
->elements
[j
].tag
== TAG(DST_ALG_RSAMD5
, i
))
195 mask
<<= sizeof(mask
) * 8 - TAG_SHIFT
;
196 mask
>>= sizeof(mask
) * 8 - TAG_SHIFT
;
198 if (have
[TAG_RSA_ENGINE
& mask
])
199 ok
= have
[TAG_RSA_MODULUS
& mask
] &&
200 have
[TAG_RSA_PUBLICEXPONENT
& mask
] &&
201 have
[TAG_RSA_LABEL
& mask
];
203 ok
= have
[TAG_RSA_MODULUS
& mask
] &&
204 have
[TAG_RSA_PUBLICEXPONENT
& mask
] &&
205 have
[TAG_RSA_PRIVATEEXPONENT
& mask
] &&
206 have
[TAG_RSA_PRIME1
& mask
] &&
207 have
[TAG_RSA_PRIME2
& mask
] &&
208 have
[TAG_RSA_EXPONENT1
& mask
] &&
209 have
[TAG_RSA_EXPONENT2
& mask
] &&
210 have
[TAG_RSA_COEFFICIENT
& mask
];
211 return (ok
? 0 : -1 );
215 check_dh(const dst_private_t
*priv
) {
217 if (priv
->nelements
!= DH_NTAGS
)
219 for (i
= 0; i
< DH_NTAGS
; i
++) {
220 for (j
= 0; j
< priv
->nelements
; j
++)
221 if (priv
->elements
[j
].tag
== TAG(DST_ALG_DH
, i
))
223 if (j
== priv
->nelements
)
230 check_dsa(const dst_private_t
*priv
) {
232 if (priv
->nelements
!= DSA_NTAGS
)
234 for (i
= 0; i
< DSA_NTAGS
; i
++) {
235 for (j
= 0; j
< priv
->nelements
; j
++)
236 if (priv
->elements
[j
].tag
== TAG(DST_ALG_DSA
, i
))
238 if (j
== priv
->nelements
)
245 check_hmac_md5(const dst_private_t
*priv
, isc_boolean_t old
) {
248 if (priv
->nelements
!= HMACMD5_NTAGS
) {
250 * If this is a good old format and we are accepting
251 * the old format return success.
253 if (old
&& priv
->nelements
== OLD_HMACMD5_NTAGS
&&
254 priv
->elements
[0].tag
== TAG_HMACMD5_KEY
)
259 * We must be new format at this point.
261 for (i
= 0; i
< HMACMD5_NTAGS
; i
++) {
262 for (j
= 0; j
< priv
->nelements
; j
++)
263 if (priv
->elements
[j
].tag
== TAG(DST_ALG_HMACMD5
, i
))
265 if (j
== priv
->nelements
)
272 check_hmac_sha(const dst_private_t
*priv
, unsigned int ntags
,
276 if (priv
->nelements
!= ntags
)
278 for (i
= 0; i
< ntags
; i
++) {
279 for (j
= 0; j
< priv
->nelements
; j
++)
280 if (priv
->elements
[j
].tag
== TAG(alg
, i
))
282 if (j
== priv
->nelements
)
289 check_data(const dst_private_t
*priv
, const unsigned int alg
,
292 /* XXXVIX this switch statement is too sparse to gen a jump table. */
295 case DST_ALG_RSASHA1
:
296 return (check_rsa(priv
));
298 return (check_dh(priv
));
300 return (check_dsa(priv
));
301 case DST_ALG_HMACMD5
:
302 return (check_hmac_md5(priv
, old
));
303 case DST_ALG_HMACSHA1
:
304 return (check_hmac_sha(priv
, HMACSHA1_NTAGS
, alg
));
305 case DST_ALG_HMACSHA224
:
306 return (check_hmac_sha(priv
, HMACSHA224_NTAGS
, alg
));
307 case DST_ALG_HMACSHA256
:
308 return (check_hmac_sha(priv
, HMACSHA256_NTAGS
, alg
));
309 case DST_ALG_HMACSHA384
:
310 return (check_hmac_sha(priv
, HMACSHA384_NTAGS
, alg
));
311 case DST_ALG_HMACSHA512
:
312 return (check_hmac_sha(priv
, HMACSHA512_NTAGS
, alg
));
314 return (DST_R_UNSUPPORTEDALG
);
319 dst__privstruct_free(dst_private_t
*priv
, isc_mem_t
*mctx
) {
324 for (i
= 0; i
< priv
->nelements
; i
++) {
325 if (priv
->elements
[i
].data
== NULL
)
327 memset(priv
->elements
[i
].data
, 0, MAXFIELDSIZE
);
328 isc_mem_put(mctx
, priv
->elements
[i
].data
, MAXFIELDSIZE
);
334 dst__privstruct_parse(dst_key_t
*key
, unsigned int alg
, isc_lex_t
*lex
,
335 isc_mem_t
*mctx
, dst_private_t
*priv
)
337 int n
= 0, major
, minor
;
340 unsigned char *data
= NULL
;
341 unsigned int opt
= ISC_LEXOPT_EOL
;
345 REQUIRE(priv
!= NULL
);
348 memset(priv
->elements
, 0, sizeof(priv
->elements
));
350 #define NEXTTOKEN(lex, opt, token) \
352 ret = isc_lex_gettoken(lex, opt, token); \
353 if (ret != ISC_R_SUCCESS) \
357 #define READLINE(lex, opt, token) \
359 ret = isc_lex_gettoken(lex, opt, token); \
360 if (ret == ISC_R_EOF) \
362 else if (ret != ISC_R_SUCCESS) \
364 } while ((*token).type != isc_tokentype_eol)
367 * Read the description line.
369 NEXTTOKEN(lex
, opt
, &token
);
370 if (token
.type
!= isc_tokentype_string
||
371 strcmp(DST_AS_STR(token
), PRIVATE_KEY_STR
) != 0)
373 ret
= DST_R_INVALIDPRIVATEKEY
;
377 NEXTTOKEN(lex
, opt
, &token
);
378 if (token
.type
!= isc_tokentype_string
||
379 (DST_AS_STR(token
))[0] != 'v')
381 ret
= DST_R_INVALIDPRIVATEKEY
;
384 if (sscanf(DST_AS_STR(token
), "v%d.%d", &major
, &minor
) != 2)
386 ret
= DST_R_INVALIDPRIVATEKEY
;
390 if (major
> DST_MAJOR_VERSION
) {
391 ret
= DST_R_INVALIDPRIVATEKEY
;
396 * Store the private key format version number
398 dst_key_setprivateformat(key
, major
, minor
);
400 READLINE(lex
, opt
, &token
);
403 * Read the algorithm line.
405 NEXTTOKEN(lex
, opt
, &token
);
406 if (token
.type
!= isc_tokentype_string
||
407 strcmp(DST_AS_STR(token
), ALGORITHM_STR
) != 0)
409 ret
= DST_R_INVALIDPRIVATEKEY
;
413 NEXTTOKEN(lex
, opt
| ISC_LEXOPT_NUMBER
, &token
);
414 if (token
.type
!= isc_tokentype_number
||
415 token
.value
.as_ulong
!= (unsigned long) dst_key_alg(key
))
417 ret
= DST_R_INVALIDPRIVATEKEY
;
421 READLINE(lex
, opt
, &token
);
426 for (n
= 0; n
< MAXFIELDS
; n
++) {
430 ret
= isc_lex_gettoken(lex
, opt
, &token
);
431 if (ret
== ISC_R_EOF
)
433 if (ret
!= ISC_R_SUCCESS
)
435 } while (token
.type
== isc_tokentype_eol
);
437 if (token
.type
!= isc_tokentype_string
) {
438 ret
= DST_R_INVALIDPRIVATEKEY
;
442 /* Numeric metadata */
443 tag
= find_numericdata(DST_AS_STR(token
));
445 INSIST(tag
< NUMERIC_NTAGS
);
447 NEXTTOKEN(lex
, opt
| ISC_LEXOPT_NUMBER
, &token
);
448 if (token
.type
!= isc_tokentype_number
) {
449 ret
= DST_R_INVALIDPRIVATEKEY
;
453 dst_key_setnum(key
, tag
, token
.value
.as_ulong
);
457 /* Timing metadata */
458 tag
= find_timedata(DST_AS_STR(token
));
460 INSIST(tag
< TIMING_NTAGS
);
462 NEXTTOKEN(lex
, opt
, &token
);
463 if (token
.type
!= isc_tokentype_string
) {
464 ret
= DST_R_INVALIDPRIVATEKEY
;
468 ret
= dns_time32_fromtext(DST_AS_STR(token
), &when
);
469 if (ret
!= ISC_R_SUCCESS
)
472 dst_key_settime(key
, tag
, when
);
478 tag
= find_value(DST_AS_STR(token
), alg
);
479 if (tag
< 0 && minor
> DST_MINOR_VERSION
)
482 ret
= DST_R_INVALIDPRIVATEKEY
;
486 priv
->elements
[n
].tag
= tag
;
488 data
= (unsigned char *) isc_mem_get(mctx
, MAXFIELDSIZE
);
492 isc_buffer_init(&b
, data
, MAXFIELDSIZE
);
493 ret
= isc_base64_tobuffer(lex
, &b
, -1);
494 if (ret
!= ISC_R_SUCCESS
)
497 isc_buffer_usedregion(&b
, &r
);
498 priv
->elements
[n
].length
= r
.length
;
499 priv
->elements
[n
].data
= r
.base
;
503 READLINE(lex
, opt
, &token
);
507 if (check_data(priv
, alg
, ISC_TRUE
) < 0)
510 return (ISC_R_SUCCESS
);
513 dst__privstruct_free(priv
, mctx
);
515 isc_mem_put(mctx
, data
, MAXFIELDSIZE
);
521 dst__privstruct_writefile(const dst_key_t
*key
, const dst_private_t
*priv
,
522 const char *directory
)
527 char filename
[ISC_DIR_NAMEMAX
];
528 char buffer
[MAXFIELDSIZE
* 2];
529 isc_fsaccess_t access
;
536 REQUIRE(priv
!= NULL
);
538 if (check_data(priv
, dst_key_alg(key
), ISC_FALSE
) < 0)
539 return (DST_R_INVALIDPRIVATEKEY
);
541 isc_buffer_init(&b
, filename
, sizeof(filename
));
542 ret
= dst_key_buildfilename(key
, DST_TYPE_PRIVATE
, directory
, &b
);
543 if (ret
!= ISC_R_SUCCESS
)
546 if ((fp
= fopen(filename
, "w")) == NULL
)
547 return (DST_R_WRITEERROR
);
550 isc_fsaccess_add(ISC_FSACCESS_OWNER
,
551 ISC_FSACCESS_READ
| ISC_FSACCESS_WRITE
,
553 (void)isc_fsaccess_set(filename
, access
);
555 dst_key_getprivateformat(key
, &major
, &minor
);
556 if (major
== 0 && minor
== 0) {
557 major
= DST_MAJOR_VERSION
;
558 minor
= DST_MINOR_VERSION
;
561 /* XXXDCL return value should be checked for full filesystem */
562 fprintf(fp
, "%s v%d.%d\n", PRIVATE_KEY_STR
, major
, minor
);
564 fprintf(fp
, "%s %d ", ALGORITHM_STR
, dst_key_alg(key
));
566 /* XXXVIX this switch statement is too sparse to gen a jump table. */
567 switch (dst_key_alg(key
)) {
569 fprintf(fp
, "(RSA)\n");
572 fprintf(fp
, "(DH)\n");
575 fprintf(fp
, "(DSA)\n");
577 case DST_ALG_RSASHA1
:
578 fprintf(fp
, "(RSASHA1)\n");
580 case DST_ALG_NSEC3RSASHA1
:
581 fprintf(fp
, "(NSEC3RSASHA1)\n");
583 case DST_ALG_NSEC3DSA
:
584 fprintf(fp
, "(NSEC3DSA)\n");
586 case DST_ALG_RSASHA256
:
587 fprintf(fp
, "(RSASHA256)\n");
589 case DST_ALG_RSASHA512
:
590 fprintf(fp
, "(RSASHA512)\n");
592 case DST_ALG_HMACMD5
:
593 fprintf(fp
, "(HMAC_MD5)\n");
595 case DST_ALG_HMACSHA1
:
596 fprintf(fp
, "(HMAC_SHA1)\n");
598 case DST_ALG_HMACSHA224
:
599 fprintf(fp
, "(HMAC_SHA224)\n");
601 case DST_ALG_HMACSHA256
:
602 fprintf(fp
, "(HMAC_SHA256)\n");
604 case DST_ALG_HMACSHA384
:
605 fprintf(fp
, "(HMAC_SHA384)\n");
607 case DST_ALG_HMACSHA512
:
608 fprintf(fp
, "(HMAC_SHA512)\n");
611 fprintf(fp
, "(?)\n");
615 for (i
= 0; i
< priv
->nelements
; i
++) {
618 s
= find_tag(priv
->elements
[i
].tag
);
620 r
.base
= priv
->elements
[i
].data
;
621 r
.length
= priv
->elements
[i
].length
;
622 isc_buffer_init(&b
, buffer
, sizeof(buffer
));
623 result
= isc_base64_totext(&r
, sizeof(buffer
), "", &b
);
624 if (result
!= ISC_R_SUCCESS
) {
626 return (DST_R_INVALIDPRIVATEKEY
);
628 isc_buffer_usedregion(&b
, &r
);
630 fprintf(fp
, "%s ", s
);
631 fwrite(r
.base
, 1, r
.length
, fp
);
635 /* Add the metadata tags */
636 if (major
> 1 || (major
== 1 && minor
>= 3)) {
637 for (i
= 0; i
< NUMERIC_NTAGS
; i
++) {
638 result
= dst_key_getnum(key
, i
, &value
);
639 if (result
!= ISC_R_SUCCESS
)
641 fprintf(fp
, "%s %u\n", numerictags
[i
], value
);
643 for (i
= 0; i
< TIMING_NTAGS
; i
++) {
644 result
= dst_key_gettime(key
, i
, &when
);
645 if (result
!= ISC_R_SUCCESS
)
648 isc_buffer_init(&b
, buffer
, sizeof(buffer
));
649 result
= dns_time32_totext(when
, &b
);
650 if (result
!= ISC_R_SUCCESS
)
653 isc_buffer_usedregion(&b
, &r
);
655 fprintf(fp
, "%s ", timetags
[i
]);
656 fwrite(r
.base
, 1, r
.length
, fp
);
662 result
= ferror(fp
) ? DST_R_WRITEERROR
: ISC_R_SUCCESS
;