4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
30 #include <cryptoutil.h>
31 #include <security/cryptoki.h>
36 #define SET_VALUE(f, s) \
38 if (kmfrv != KMF_OK) { \
39 cryptoerror(LOG_STDERR, \
40 gettext("Failed to %s: 0x%02\n"), \
46 gencsr_pkcs11(KMF_HANDLE_T kmfhandle
,
47 char *token
, char *subject
, char *altname
,
48 KMF_GENERALNAMECHOICES alttype
, int altcrit
,
49 char *certlabel
, KMF_KEY_ALG keyAlg
,
50 int keylen
, uint16_t kubits
, int kucrit
,
51 KMF_ENCODE_FORMAT fmt
, char *csrfile
,
52 KMF_CREDENTIAL
*tokencred
, EKU_LIST
*ekulist
,
53 KMF_ALGORITHM_INDEX sigAlg
, KMF_OID
*curveoid
)
55 KMF_RETURN kmfrv
= KMF_OK
;
56 KMF_KEY_HANDLE pubk
, prik
;
57 KMF_X509_NAME csrSubject
;
59 KMF_DATA signedCsr
= { 0, NULL
};
61 KMF_KEYSTORE_TYPE kstype
= KMF_KEYSTORE_PK11TOKEN
;
63 KMF_ATTRIBUTE attrlist
[16];
65 (void) memset(&csr
, 0, sizeof (csr
));
66 (void) memset(&csrSubject
, 0, sizeof (csrSubject
));
68 /* If the subject name cannot be parsed, flag it now and exit */
69 if ((kmfrv
= kmf_dn_parser(subject
, &csrSubject
)) != KMF_OK
)
72 /* Select a PKCS11 token */
73 kmfrv
= select_token(kmfhandle
, token
, FALSE
);
77 * Share the "genkeypair" routine for creating the keypair.
79 kmfrv
= genkeypair_pkcs11(kmfhandle
, token
, certlabel
,
80 keyAlg
, keylen
, tokencred
, curveoid
, &prik
, &pubk
);
84 SET_VALUE(kmf_set_csr_pubkey(kmfhandle
, &pubk
, &csr
), "keypair");
86 SET_VALUE(kmf_set_csr_version(&csr
, 2), "version number");
88 SET_VALUE(kmf_set_csr_subject(&csr
, &csrSubject
), "subject name");
90 SET_VALUE(kmf_set_csr_sig_alg(&csr
, sigAlg
),
91 "SignatureAlgorithm");
93 if (altname
!= NULL
) {
94 SET_VALUE(kmf_set_csr_subject_altname(&csr
, altname
, altcrit
,
95 alttype
), "SetCSRSubjectAltName");
99 SET_VALUE(kmf_set_csr_ku(&csr
, kucrit
, kubits
),
102 if (ekulist
!= NULL
) {
104 for (i
= 0; kmfrv
== KMF_OK
&& i
< ekulist
->eku_count
; i
++) {
105 SET_VALUE(kmf_add_csr_eku(&csr
,
106 &ekulist
->ekulist
[i
],
107 ekulist
->critlist
[i
]),
108 "Extended Key Usage");
111 if ((kmfrv
= kmf_sign_csr(kmfhandle
, &csr
, &prik
, &signedCsr
)) ==
113 kmfrv
= kmf_create_csr_file(&signedCsr
, fmt
, csrfile
);
117 (void) kmf_free_data(&signedCsr
);
118 (void) kmf_free_signed_csr(&csr
);
120 /* delete the public key */
122 kmf_set_attr_at_index(attrlist
, numattr
, KMF_KEYSTORE_TYPE_ATTR
,
123 &kstype
, sizeof (kstype
));
126 kmf_set_attr_at_index(attrlist
, numattr
, KMF_PUBKEY_HANDLE_ATTR
,
127 &pubk
, sizeof (KMF_KEY_HANDLE
));
130 if (tokencred
!= NULL
&& tokencred
->cred
!= NULL
) {
131 kmf_set_attr_at_index(attrlist
, numattr
, KMF_CREDENTIAL_ATTR
,
132 tokencred
, sizeof (KMF_CREDENTIAL
));
136 (void) kmf_delete_key_from_keystore(kmfhandle
, numattr
, attrlist
);
139 * If there is an error, then we need to remove the private key
142 if (kmfrv
!= KMF_OK
) {
144 kmf_set_attr_at_index(attrlist
, numattr
,
145 KMF_KEYSTORE_TYPE_ATTR
, &kstype
, sizeof (kstype
));
148 kmf_set_attr_at_index(attrlist
, numattr
,
149 KMF_KEY_HANDLE_ATTR
, &prik
, sizeof (KMF_KEY_HANDLE
));
152 if (tokencred
!= NULL
&& tokencred
->cred
!= NULL
) {
153 kmf_set_attr_at_index(attrlist
, numattr
,
154 KMF_CREDENTIAL_ATTR
, tokencred
,
155 sizeof (KMF_CREDENTIAL
));
159 (void) kmf_delete_key_from_keystore(kmfhandle
, numattr
,
163 (void) kmf_free_kmf_key(kmfhandle
, &prik
);
168 gencsr_file(KMF_HANDLE_T kmfhandle
,
170 int keylen
, KMF_ENCODE_FORMAT fmt
,
171 char *subject
, char *altname
, KMF_GENERALNAMECHOICES alttype
,
172 int altcrit
, uint16_t kubits
, int kucrit
,
173 char *outcsr
, char *outkey
, EKU_LIST
*ekulist
,
174 KMF_ALGORITHM_INDEX sigAlg
)
177 KMF_KEY_HANDLE pubk
, prik
;
178 KMF_X509_NAME csrSubject
;
180 KMF_DATA signedCsr
= { 0, NULL
};
181 char *fullcsrpath
= NULL
;
182 char *fullkeypath
= NULL
;
185 (void) memset(&csr
, 0, sizeof (csr
));
186 (void) memset(&csrSubject
, 0, sizeof (csrSubject
));
188 if (EMPTYSTRING(outcsr
) || EMPTYSTRING(outkey
)) {
189 cryptoerror(LOG_STDERR
,
190 gettext("No output file was specified for "
191 "the csr or key\n"));
192 return (KMF_ERR_BAD_PARAMETER
);
194 fullcsrpath
= strdup(outcsr
);
195 if (verify_file(fullcsrpath
)) {
196 cryptoerror(LOG_STDERR
,
197 gettext("Cannot write the indicated output "
198 "certificate file (%s).\n"), fullcsrpath
);
200 return (PK_ERR_USAGE
);
203 /* If the subject name cannot be parsed, flag it now and exit */
204 if ((kmfrv
= kmf_dn_parser(subject
, &csrSubject
)) != KMF_OK
) {
208 * Share the "genkeypair" routine for creating the keypair.
210 kmfrv
= genkeypair_file(kmfhandle
, keyAlg
, keylen
,
211 fmt
, outkey
, &prik
, &pubk
);
215 SET_VALUE(kmf_set_csr_pubkey(kmfhandle
, &pubk
, &csr
),
218 SET_VALUE(kmf_set_csr_version(&csr
, 2), "SetCSRVersion");
220 SET_VALUE(kmf_set_csr_subject(&csr
, &csrSubject
),
221 "kmf_set_csr_subject");
223 SET_VALUE(kmf_set_csr_sig_alg(&csr
, sigAlg
), "kmf_set_csr_sig_alg");
225 if (altname
!= NULL
) {
226 SET_VALUE(kmf_set_csr_subject_altname(&csr
, altname
, altcrit
,
227 alttype
), "kmf_set_csr_subject_altname");
229 if (kubits
!= NULL
) {
230 SET_VALUE(kmf_set_csr_ku(&csr
, kucrit
, kubits
),
233 if (ekulist
!= NULL
) {
235 for (i
= 0; kmfrv
== KMF_OK
&& i
< ekulist
->eku_count
; i
++) {
236 SET_VALUE(kmf_add_csr_eku(&csr
,
237 &ekulist
->ekulist
[i
],
238 ekulist
->critlist
[i
]),
239 "Extended Key Usage");
242 if ((kmfrv
= kmf_sign_csr(kmfhandle
, &csr
, &prik
, &signedCsr
)) ==
244 kmfrv
= kmf_create_csr_file(&signedCsr
, fmt
, fullcsrpath
);
253 kmf_free_data(&signedCsr
);
254 kmf_free_kmf_key(kmfhandle
, &prik
);
255 kmf_free_signed_csr(&csr
);
261 gencsr_nss(KMF_HANDLE_T kmfhandle
,
262 char *token
, char *subject
, char *altname
,
263 KMF_GENERALNAMECHOICES alttype
, int altcrit
,
264 char *nickname
, char *dir
, char *prefix
,
265 KMF_KEY_ALG keyAlg
, int keylen
,
266 uint16_t kubits
, int kucrit
,
267 KMF_ENCODE_FORMAT fmt
, char *csrfile
,
268 KMF_CREDENTIAL
*tokencred
, EKU_LIST
*ekulist
,
269 KMF_ALGORITHM_INDEX sigAlg
, KMF_OID
*curveoid
)
272 KMF_KEY_HANDLE pubk
, prik
;
273 KMF_X509_NAME csrSubject
;
275 KMF_DATA signedCsr
= { 0, NULL
};
277 KMF_KEYSTORE_TYPE kstype
= KMF_KEYSTORE_NSS
;
279 KMF_ATTRIBUTE attrlist
[16];
282 token
= DEFAULT_NSS_TOKEN
;
284 kmfrv
= configure_nss(kmfhandle
, dir
, prefix
);
288 (void) memset(&csr
, 0, sizeof (csr
));
289 (void) memset(&csrSubject
, 0, sizeof (csrSubject
));
290 (void) memset(&pubk
, 0, sizeof (pubk
));
291 (void) memset(&prik
, 0, sizeof (prik
));
293 /* If the subject name cannot be parsed, flag it now and exit */
294 if ((kmfrv
= kmf_dn_parser(subject
, &csrSubject
)) != KMF_OK
) {
298 kmfrv
= genkeypair_nss(kmfhandle
, token
, nickname
, dir
,
299 prefix
, keyAlg
, keylen
, tokencred
, curveoid
,
304 SET_VALUE(kmf_set_csr_pubkey(kmfhandle
, &pubk
, &csr
),
305 "kmf_set_csr_pubkey");
306 SET_VALUE(kmf_set_csr_version(&csr
, 2), "kmf_set_csr_version");
307 SET_VALUE(kmf_set_csr_subject(&csr
, &csrSubject
),
308 "kmf_set_csr_subject");
309 SET_VALUE(kmf_set_csr_sig_alg(&csr
, sigAlg
), "kmf_set_csr_sig_alg");
311 if (altname
!= NULL
) {
312 SET_VALUE(kmf_set_csr_subject_altname(&csr
, altname
, altcrit
,
313 alttype
), "kmf_set_csr_subject_altname");
315 if (kubits
!= NULL
) {
316 SET_VALUE(kmf_set_csr_ku(&csr
, kucrit
, kubits
),
319 if (ekulist
!= NULL
) {
321 for (i
= 0; kmfrv
== KMF_OK
&& i
< ekulist
->eku_count
; i
++) {
322 SET_VALUE(kmf_add_csr_eku(&csr
,
323 &ekulist
->ekulist
[i
],
324 ekulist
->critlist
[i
]),
325 "Extended Key Usage");
328 if ((kmfrv
= kmf_sign_csr(kmfhandle
, &csr
, &prik
, &signedCsr
)) ==
330 kmfrv
= kmf_create_csr_file(&signedCsr
, fmt
, csrfile
);
334 (void) kmf_free_data(&signedCsr
);
335 (void) kmf_free_kmf_key(kmfhandle
, &prik
);
339 kmf_set_attr_at_index(attrlist
, numattr
, KMF_KEYSTORE_TYPE_ATTR
,
340 &kstype
, sizeof (kstype
));
343 kmf_set_attr_at_index(attrlist
, numattr
, KMF_PUBKEY_HANDLE_ATTR
,
344 &pubk
, sizeof (KMF_KEY_HANDLE
));
347 if (tokencred
!= NULL
&& tokencred
->credlen
> 0) {
348 kmf_set_attr_at_index(attrlist
, numattr
, KMF_CREDENTIAL_ATTR
,
349 tokencred
, sizeof (KMF_CREDENTIAL
));
353 if (token
&& strlen(token
)) {
354 kmf_set_attr_at_index(attrlist
, numattr
, KMF_TOKEN_LABEL_ATTR
,
355 token
, strlen(token
));
359 (void) kmf_delete_key_from_keystore(kmfhandle
, numattr
, attrlist
);
361 (void) kmf_free_signed_csr(&csr
);
367 pk_gencsr(int argc
, char *argv
[])
371 extern int optind_av
;
372 extern char *optarg_av
;
373 KMF_KEYSTORE_TYPE kstype
= 0;
374 char *subject
= NULL
;
375 char *tokenname
= NULL
;
378 int keylen
= PK_DEFAULT_KEYLENGTH
;
379 char *certlabel
= NULL
;
383 char *altname
= NULL
;
386 char *hashname
= NULL
;
388 char *keytype
= PK_DEFAULT_KEYTYPE
;
389 KMF_HANDLE_T kmfhandle
= NULL
;
390 KMF_ENCODE_FORMAT fmt
= KMF_FORMAT_ASN1
;
391 KMF_KEY_ALG keyAlg
= KMF_RSA
;
392 KMF_ALGORITHM_INDEX sigAlg
= KMF_ALGID_SHA1WithRSA
;
393 boolean_t interactive
= B_FALSE
;
394 char *subname
= NULL
;
395 KMF_CREDENTIAL tokencred
= { NULL
, 0 };
396 KMF_GENERALNAMECHOICES alttype
= 0;
397 int altcrit
= 0, kucrit
= 0;
398 EKU_LIST
*ekulist
= NULL
;
399 KMF_OID
*curveoid
= NULL
; /* ECC */
400 KMF_OID
*hashoid
= NULL
;
403 while ((opt
= getopt_av(argc
, argv
,
404 "ik:(keystore)s:(subject)n:(nickname)A:(altname)"
405 "u:(keyusage)T:(token)d:(dir)p:(prefix)t:(keytype)"
406 "y:(keylen)l:(label)c:(outcsr)e:(eku)C:(curve)"
407 "K:(outkey)F:(format)E(listcurves)h:(hash)")) != EOF
) {
415 return (PK_ERR_USAGE
);
417 cryptoerror(LOG_STDERR
,
418 gettext("Interactive (-i) and "
419 "subject options are mutually "
421 return (PK_ERR_USAGE
);
423 interactive
= B_TRUE
;
426 kstype
= KS2Int(optarg_av
);
428 return (PK_ERR_USAGE
);
432 return (PK_ERR_USAGE
);
433 else if (interactive
) {
434 cryptoerror(LOG_STDERR
,
435 gettext("Interactive (-i) and "
436 "subject options are mutually "
438 return (PK_ERR_USAGE
);
445 return (PK_ERR_USAGE
);
446 certlabel
= optarg_av
;
450 return (PK_ERR_USAGE
);
451 tokenname
= optarg_av
;
458 return (PK_ERR_USAGE
);
468 if (sscanf(optarg_av
, "%d",
470 cryptoerror(LOG_STDERR
,
471 gettext("Unrecognized "
472 "key length (%s)\n"), optarg_av
);
473 return (PK_ERR_USAGE
);
479 return (PK_ERR_USAGE
);
484 return (PK_ERR_USAGE
);
489 return (PK_ERR_USAGE
);
496 curveoid
= ecc_name_to_oid(optarg_av
);
497 if (curveoid
== NULL
) {
498 cryptoerror(LOG_STDERR
,
499 gettext("Unrecognized ECC "
501 return (PK_ERR_USAGE
);
506 * This argument is only to be used
507 * by itself, no other options should
511 cryptoerror(LOG_STDERR
,
512 gettext("listcurves has no other "
514 return (PK_ERR_USAGE
);
519 hashname
= optarg_av
;
520 hashoid
= ecc_name_to_oid(optarg_av
);
521 if (hashoid
== NULL
) {
522 cryptoerror(LOG_STDERR
,
523 gettext("Unrecognized hash.\n"));
524 return (PK_ERR_USAGE
);
528 cryptoerror(LOG_STDERR
, gettext(
529 "unrecognized gencsr option '%s'\n"),
531 return (PK_ERR_USAGE
);
534 /* No additional args allowed. */
538 return (PK_ERR_USAGE
);
541 /* Assume keystore = PKCS#11 if not specified. */
543 kstype
= KMF_KEYSTORE_PK11TOKEN
;
545 DIR_OPTION_CHECK(kstype
, dir
);
547 if (EMPTYSTRING(outcsr
) && interactive
) {
548 (void) get_filename("CSR", &outcsr
);
550 if (EMPTYSTRING(outcsr
)) {
551 (void) printf(gettext("A filename must be specified to hold"
552 "the final certificate request data.\n"));
553 return (PK_ERR_USAGE
);
556 * verify that the outcsr file does not already exist
557 * and that it can be created.
559 rv
= verify_file(outcsr
);
560 if (rv
== KMF_ERR_OPEN_FILE
) {
561 cryptoerror(LOG_STDERR
,
562 gettext("Warning: file \"%s\" exists, "
563 "will be overwritten."), outcsr
);
564 if (yesno(gettext("Continue with gencsr? "),
565 gettext("Respond with yes or no.\n"), B_FALSE
) == B_FALSE
) {
568 /* remove the file */
569 (void) unlink(outcsr
);
571 } else if (rv
!= KMF_OK
) {
572 cryptoerror(LOG_STDERR
,
573 gettext("Warning: error accessing \"%s\""), outcsr
);
577 if ((kstype
== KMF_KEYSTORE_NSS
|| kstype
== KMF_KEYSTORE_PK11TOKEN
)) {
578 if (EMPTYSTRING(certlabel
) && interactive
)
579 (void) get_certlabel(&certlabel
);
581 if (EMPTYSTRING(certlabel
)) {
582 cryptoerror(LOG_STDERR
, gettext("A label must be "
583 "specified to create a certificate request.\n"));
584 return (PK_ERR_USAGE
);
586 } else if (kstype
== KMF_KEYSTORE_OPENSSL
) {
587 if (EMPTYSTRING(outkey
) && interactive
)
588 (void) get_filename("private key", &outkey
);
590 if (EMPTYSTRING(outkey
)) {
591 cryptoerror(LOG_STDERR
, gettext("A key filename "
592 "must be specified to create a certificate "
594 return (PK_ERR_USAGE
);
598 if (format
&& (fmt
= Str2Format(format
)) == KMF_FORMAT_UNDEF
) {
599 cryptoerror(LOG_STDERR
,
600 gettext("Error parsing format string (%s).\n"), format
);
601 return (PK_ERR_USAGE
);
603 if (format
&& fmt
!= KMF_FORMAT_ASN1
&& fmt
!= KMF_FORMAT_PEM
) {
604 cryptoerror(LOG_STDERR
,
605 gettext("CSR must be DER or PEM format.\n"));
606 return (PK_ERR_USAGE
);
610 * Check the subject name.
611 * If interactive is true, get it now interactively.
614 if (get_subname(&subname
) != KMF_OK
) {
615 cryptoerror(LOG_STDERR
, gettext("Failed to get the "
616 "subject name interactively.\n"));
617 return (PK_ERR_USAGE
);
620 if (EMPTYSTRING(subject
)) {
621 cryptoerror(LOG_STDERR
, gettext("A subject name or "
622 "-i must be specified to create a certificate "
624 return (PK_ERR_USAGE
);
626 subname
= strdup(subject
);
627 if (subname
== NULL
) {
628 cryptoerror(LOG_STDERR
,
629 gettext("Out of memory.\n"));
630 return (PK_ERR_SYSTEM
);
634 if (altname
!= NULL
) {
635 rv
= verify_altname(altname
, &alttype
, &altcrit
);
637 cryptoerror(LOG_STDERR
, gettext("Subject AltName "
638 "must be specified as a name=value pair. "
639 "See the man page for details."));
642 /* advance the altname past the '=' sign */
643 char *p
= strchr(altname
, '=');
650 rv
= verify_keyusage(kustr
, &kubits
, &kucrit
);
652 cryptoerror(LOG_STDERR
, gettext("KeyUsage "
653 "must be specified as a comma-separated list. "
654 "See the man page for details."));
658 if (ekustr
!= NULL
) {
659 rv
= verify_ekunames(ekustr
, &ekulist
);
661 (void) fprintf(stderr
, gettext("EKUs must "
662 "be specified as a comma-separated list. "
663 "See the man page for details.\n"));
668 if ((rv
= Str2KeyType(keytype
, hashoid
, &keyAlg
, &sigAlg
)) != 0) {
669 cryptoerror(LOG_STDERR
,
670 gettext("Unsupported key/hash combination (%s/%s).\n"),
671 keytype
, (hashname
? hashname
: "none"));
674 if (curveoid
!= NULL
&& keyAlg
!= KMF_ECDSA
) {
675 cryptoerror(LOG_STDERR
, gettext("EC curves are only "
676 "valid for EC keytypes.\n"));
677 return (PK_ERR_USAGE
);
679 if (keyAlg
== KMF_ECDSA
&& curveoid
== NULL
) {
680 cryptoerror(LOG_STDERR
, gettext("A curve must be "
681 "specifed when using EC keys.\n"));
682 return (PK_ERR_USAGE
);
684 if (keyAlg
== KMF_ECDSA
&& kstype
== KMF_KEYSTORE_OPENSSL
) {
685 (void) fprintf(stderr
, gettext("ECC certificates are"
686 "only supported with the pkcs11 and nss keystores\n"));
691 /* Adjust default keylength for NSS and DSA */
692 if (keyAlg
== KMF_DSA
&& !y_flag
&& kstype
== KMF_KEYSTORE_NSS
)
695 if (kstype
== KMF_KEYSTORE_NSS
|| kstype
== KMF_KEYSTORE_PK11TOKEN
) {
696 if (tokenname
== NULL
|| !strlen(tokenname
)) {
697 if (kstype
== KMF_KEYSTORE_NSS
) {
698 tokenname
= "internal";
700 tokenname
= PK_DEFAULT_PK11TOKEN
;
704 (void) get_token_password(kstype
, tokenname
, &tokencred
);
707 if ((rv
= kmf_initialize(&kmfhandle
, NULL
, NULL
)) != KMF_OK
) {
708 cryptoerror(LOG_STDERR
, gettext("Error initializing KMF\n"));
709 return (PK_ERR_USAGE
);
713 if (kstype
== KMF_KEYSTORE_NSS
) {
715 dir
= PK_DEFAULT_DIRECTORY
;
717 rv
= gencsr_nss(kmfhandle
,
718 tokenname
, subname
, altname
, alttype
, altcrit
,
719 certlabel
, dir
, prefix
,
720 keyAlg
, keylen
, kubits
, kucrit
,
721 fmt
, outcsr
, &tokencred
, ekulist
,
724 } else if (kstype
== KMF_KEYSTORE_PK11TOKEN
) {
725 rv
= gencsr_pkcs11(kmfhandle
,
726 tokenname
, subname
, altname
, alttype
, altcrit
,
727 certlabel
, keyAlg
, keylen
,
728 kubits
, kucrit
, fmt
, outcsr
, &tokencred
,
729 ekulist
, sigAlg
, curveoid
);
731 } else if (kstype
== KMF_KEYSTORE_OPENSSL
) {
732 rv
= gencsr_file(kmfhandle
,
733 keyAlg
, keylen
, fmt
, subname
, altname
,
734 alttype
, altcrit
, kubits
, kucrit
,
735 outcsr
, outkey
, ekulist
, sigAlg
);
740 display_error(kmfhandle
, rv
,
741 gettext("Error creating CSR or keypair"));
743 if (rv
== KMF_ERR_RDN_PARSER
) {
744 cryptoerror(LOG_STDERR
, gettext("subject or "
745 "issuer name must be in proper DN format.\n"));
750 free_eku_list(ekulist
);
755 if (tokencred
.cred
!= NULL
)
756 free(tokencred
.cred
);
758 (void) kmf_finalize(kmfhandle
);
760 return (PK_ERR_USAGE
);