1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/ossrv/ssl/tsrc/BC/libcrypto/topenssl/src/s_cb.c Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,578 @@
1.4 +/* apps/s_cb.c - callback functions used by s_client, s_server, and s_time */
1.5 +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
1.6 + * All rights reserved.
1.7 + *
1.8 + * This package is an SSL implementation written
1.9 + * by Eric Young (eay@cryptsoft.com).
1.10 + * The implementation was written so as to conform with Netscapes SSL.
1.11 + *
1.12 + * This library is free for commercial and non-commercial use as long as
1.13 + * the following conditions are aheared to. The following conditions
1.14 + * apply to all code found in this distribution, be it the RC4, RSA,
1.15 + * lhash, DES, etc., code; not just the SSL code. The SSL documentation
1.16 + * included with this distribution is covered by the same copyright terms
1.17 + * except that the holder is Tim Hudson (tjh@cryptsoft.com).
1.18 + *
1.19 + * Copyright remains Eric Young's, and as such any Copyright notices in
1.20 + * the code are not to be removed.
1.21 + * If this package is used in a product, Eric Young should be given attribution
1.22 + * as the author of the parts of the library used.
1.23 + * This can be in the form of a textual message at program startup or
1.24 + * in documentation (online or textual) provided with the package.
1.25 + *
1.26 + * Redistribution and use in source and binary forms, with or without
1.27 + * modification, are permitted provided that the following conditions
1.28 + * are met:
1.29 + * 1. Redistributions of source code must retain the copyright
1.30 + * notice, this list of conditions and the following disclaimer.
1.31 + * 2. Redistributions in binary form must reproduce the above copyright
1.32 + * notice, this list of conditions and the following disclaimer in the
1.33 + * documentation and/or other materials provided with the distribution.
1.34 + * 3. All advertising materials mentioning features or use of this software
1.35 + * must display the following acknowledgement:
1.36 + * "This product includes cryptographic software written by
1.37 + * Eric Young (eay@cryptsoft.com)"
1.38 + * The word 'cryptographic' can be left out if the rouines from the library
1.39 + * being used are not cryptographic related :-).
1.40 + * 4. If you include any Windows specific code (or a derivative thereof) from
1.41 + * the apps directory (application code) you must include an acknowledgement:
1.42 + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
1.43 + *
1.44 + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
1.45 + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1.46 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1.47 + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1.48 + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1.49 + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1.50 + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1.51 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1.52 + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1.53 + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1.54 + * SUCH DAMAGE.
1.55 + *
1.56 + * The licence and distribution terms for any publically available version or
1.57 + * derivative of this code cannot be changed. i.e. this code cannot simply be
1.58 + * copied and put under another distribution licence
1.59 + * [including the GNU Public Licence.]
1.60 + */
1.61 +/* ====================================================================
1.62 + * Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved.
1.63 + *
1.64 + * Redistribution and use in source and binary forms, with or without
1.65 + * modification, are permitted provided that the following conditions
1.66 + * are met:
1.67 + *
1.68 + * 1. Redistributions of source code must retain the above copyright
1.69 + * notice, this list of conditions and the following disclaimer.
1.70 + *
1.71 + * 2. Redistributions in binary form must reproduce the above copyright
1.72 + * notice, this list of conditions and the following disclaimer in
1.73 + * the documentation and/or other materials provided with the
1.74 + * distribution.
1.75 + *
1.76 + * 3. All advertising materials mentioning features or use of this
1.77 + * software must display the following acknowledgment:
1.78 + * "This product includes software developed by the OpenSSL Project
1.79 + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
1.80 + *
1.81 + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
1.82 + * endorse or promote products derived from this software without
1.83 + * prior written permission. For written permission, please contact
1.84 + * openssl-core@openssl.org.
1.85 + *
1.86 + * 5. Products derived from this software may not be called "OpenSSL"
1.87 + * nor may "OpenSSL" appear in their names without prior written
1.88 + * permission of the OpenSSL Project.
1.89 + *
1.90 + * 6. Redistributions of any form whatsoever must retain the following
1.91 + * acknowledgment:
1.92 + * "This product includes software developed by the OpenSSL Project
1.93 + * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
1.94 + *
1.95 + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
1.96 + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1.97 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1.98 + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
1.99 + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1.100 + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1.101 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
1.102 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1.103 + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
1.104 + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1.105 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
1.106 + * OF THE POSSIBILITY OF SUCH DAMAGE.
1.107 + * ====================================================================
1.108 + *
1.109 + * This product includes cryptographic software written by Eric Young
1.110 + * (eay@cryptsoft.com). This product includes software written by Tim
1.111 + * Hudson (tjh@cryptsoft.com).
1.112 + *
1.113 + */
1.114 +
1.115 +#include <stdio.h>
1.116 +#include <stdlib.h>
1.117 +#define USE_SOCKETS
1.118 +#define NON_MAIN
1.119 +#include "apps.h"
1.120 +#undef NON_MAIN
1.121 +#undef USE_SOCKETS
1.122 +#include <openssl/err.h>
1.123 +#include <openssl/x509.h>
1.124 +#include <openssl/ssl.h>
1.125 +#include "s_apps.h"
1.126 +
1.127 +int verify_depth=0;
1.128 +int verify_error=X509_V_OK;
1.129 +
1.130 +
1.131 +int MS_CALLBACK verify_callback(int ok, X509_STORE_CTX *ctx)
1.132 + {
1.133 + char buf[256];
1.134 + X509 *err_cert;
1.135 + int err,depth;
1.136 +
1.137 + err_cert=X509_STORE_CTX_get_current_cert(ctx);
1.138 + err= X509_STORE_CTX_get_error(ctx);
1.139 + depth= X509_STORE_CTX_get_error_depth(ctx);
1.140 +
1.141 + X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof buf);
1.142 + BIO_printf(bio_err,"depth=%d %s\n",depth,buf);
1.143 + if (!ok)
1.144 + {
1.145 + BIO_printf(bio_err,"verify error:num=%d:%s\n",err,
1.146 + X509_verify_cert_error_string(err));
1.147 + if (verify_depth >= depth)
1.148 + {
1.149 + ok=1;
1.150 + verify_error=X509_V_OK;
1.151 + }
1.152 + else
1.153 + {
1.154 + ok=0;
1.155 + verify_error=X509_V_ERR_CERT_CHAIN_TOO_LONG;
1.156 + }
1.157 + }
1.158 + switch (ctx->error)
1.159 + {
1.160 + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
1.161 + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,sizeof buf);
1.162 + BIO_printf(bio_err,"issuer= %s\n",buf);
1.163 + break;
1.164 + case X509_V_ERR_CERT_NOT_YET_VALID:
1.165 + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
1.166 + BIO_printf(bio_err,"notBefore=");
1.167 + ASN1_TIME_print(bio_err,X509_get_notBefore(ctx->current_cert));
1.168 + BIO_printf(bio_err,"\n");
1.169 + break;
1.170 + case X509_V_ERR_CERT_HAS_EXPIRED:
1.171 + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
1.172 + BIO_printf(bio_err,"notAfter=");
1.173 + ASN1_TIME_print(bio_err,X509_get_notAfter(ctx->current_cert));
1.174 + BIO_printf(bio_err,"\n");
1.175 + break;
1.176 + }
1.177 + BIO_printf(bio_err,"verify return:%d\n",ok);
1.178 + return(ok);
1.179 + }
1.180 +
1.181 +int set_cert_stuff(SSL_CTX *ctx, char *cert_file, char *key_file)
1.182 + {
1.183 + if (cert_file != NULL)
1.184 + {
1.185 + /*
1.186 + SSL *ssl;
1.187 + X509 *x509;
1.188 + */
1.189 +
1.190 + if (SSL_CTX_use_certificate_file(ctx,cert_file,
1.191 + SSL_FILETYPE_PEM) <= 0)
1.192 + {
1.193 + BIO_printf(bio_err,"unable to get certificate from '%s'\n",cert_file);
1.194 + ERR_print_errors(bio_err);
1.195 + return(0);
1.196 + }
1.197 + if (key_file == NULL) key_file=cert_file;
1.198 + if (SSL_CTX_use_PrivateKey_file(ctx,key_file,
1.199 + SSL_FILETYPE_PEM) <= 0)
1.200 + {
1.201 + BIO_printf(bio_err,"unable to get private key from '%s'\n",key_file);
1.202 + ERR_print_errors(bio_err);
1.203 + return(0);
1.204 + }
1.205 +
1.206 + /*
1.207 + In theory this is no longer needed
1.208 + ssl=SSL_new(ctx);
1.209 + x509=SSL_get_certificate(ssl);
1.210 +
1.211 + if (x509 != NULL) {
1.212 + EVP_PKEY *pktmp;
1.213 + pktmp = X509_get_pubkey(x509);
1.214 + EVP_PKEY_copy_parameters(pktmp,
1.215 + SSL_get_privatekey(ssl));
1.216 + EVP_PKEY_free(pktmp);
1.217 + }
1.218 + SSL_free(ssl);
1.219 + */
1.220 +
1.221 + /* If we are using DSA, we can copy the parameters from
1.222 + * the private key */
1.223 +
1.224 +
1.225 + /* Now we know that a key and cert have been set against
1.226 + * the SSL context */
1.227 + if (!SSL_CTX_check_private_key(ctx))
1.228 + {
1.229 + BIO_printf(bio_err,"Private key does not match the certificate public key\n");
1.230 + return(0);
1.231 + }
1.232 + }
1.233 + return(1);
1.234 + }
1.235 +
1.236 +int set_cert_key_stuff(SSL_CTX *ctx, X509 *cert, EVP_PKEY *key)
1.237 + {
1.238 + if (cert == NULL)
1.239 + return 1;
1.240 + if (SSL_CTX_use_certificate(ctx,cert) <= 0)
1.241 + {
1.242 + BIO_printf(bio_err,"error setting certificate\n");
1.243 + ERR_print_errors(bio_err);
1.244 + return 0;
1.245 + }
1.246 + if (SSL_CTX_use_PrivateKey(ctx,key) <= 0)
1.247 + {
1.248 + BIO_printf(bio_err,"error setting private key\n");
1.249 + ERR_print_errors(bio_err);
1.250 + return 0;
1.251 + }
1.252 +
1.253 +
1.254 + /* Now we know that a key and cert have been set against
1.255 + * the SSL context */
1.256 + if (!SSL_CTX_check_private_key(ctx))
1.257 + {
1.258 + BIO_printf(bio_err,"Private key does not match the certificate public key\n");
1.259 + return 0;
1.260 + }
1.261 + return 1;
1.262 + }
1.263 +
1.264 +long MS_CALLBACK bio_dump_callback(BIO *bio, int cmd, const char *argp,
1.265 + int argi, long argl, long ret)
1.266 + {
1.267 + BIO *out;
1.268 +
1.269 + out=(BIO *)BIO_get_callback_arg(bio);
1.270 + if (out == NULL) return(ret);
1.271 +
1.272 + if (cmd == (BIO_CB_READ|BIO_CB_RETURN))
1.273 + {
1.274 + BIO_printf(out,"read from %p [%p] (%d bytes => %ld (0x%lX))\n",
1.275 + (void *)bio,argp,argi,ret,ret);
1.276 + BIO_dump(out,argp,(int)ret);
1.277 + return(ret);
1.278 + }
1.279 + else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN))
1.280 + {
1.281 + BIO_printf(out,"write to %p [%p] (%d bytes => %ld (0x%lX))\n",
1.282 + (void *)bio,argp,argi,ret,ret);
1.283 + BIO_dump(out,argp,(int)ret);
1.284 + }
1.285 + return(ret);
1.286 + }
1.287 +
1.288 +void MS_CALLBACK apps_ssl_info_callback(const SSL *s, int where, int ret)
1.289 + {
1.290 + const char *str;
1.291 + int w;
1.292 +
1.293 + w=where& ~SSL_ST_MASK;
1.294 +
1.295 + if (w & SSL_ST_CONNECT) str="SSL_connect";
1.296 + else if (w & SSL_ST_ACCEPT) str="SSL_accept";
1.297 + else str="undefined";
1.298 +
1.299 + if (where & SSL_CB_LOOP)
1.300 + {
1.301 + BIO_printf(bio_err,"%s:%s\n",str,SSL_state_string_long(s));
1.302 + }
1.303 + else if (where & SSL_CB_ALERT)
1.304 + {
1.305 + str=(where & SSL_CB_READ)?"read":"write";
1.306 + BIO_printf(bio_err,"SSL3 alert %s:%s:%s\n",
1.307 + str,
1.308 + SSL_alert_type_string_long(ret),
1.309 + SSL_alert_desc_string_long(ret));
1.310 + }
1.311 + else if (where & SSL_CB_EXIT)
1.312 + {
1.313 + if (ret == 0)
1.314 + BIO_printf(bio_err,"%s:failed in %s\n",
1.315 + str,SSL_state_string_long(s));
1.316 + else if (ret < 0)
1.317 + {
1.318 + BIO_printf(bio_err,"%s:error in %s\n",
1.319 + str,SSL_state_string_long(s));
1.320 + }
1.321 + }
1.322 + }
1.323 +
1.324 +
1.325 +void MS_CALLBACK msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)
1.326 + {
1.327 + BIO *bio = arg;
1.328 + const char *str_write_p, *str_version, *str_content_type = "", *str_details1 = "", *str_details2= "";
1.329 +
1.330 + str_write_p = write_p ? ">>>" : "<<<";
1.331 +
1.332 + switch (version)
1.333 + {
1.334 + case SSL2_VERSION:
1.335 + str_version = "SSL 2.0";
1.336 + break;
1.337 + case SSL3_VERSION:
1.338 + str_version = "SSL 3.0 ";
1.339 + break;
1.340 + case TLS1_VERSION:
1.341 + str_version = "TLS 1.0 ";
1.342 + break;
1.343 + default:
1.344 + str_version = "???";
1.345 + }
1.346 +
1.347 + if (version == SSL2_VERSION)
1.348 + {
1.349 + str_details1 = "???";
1.350 +
1.351 + if (len > 0)
1.352 + {
1.353 + switch (((const unsigned char*)buf)[0])
1.354 + {
1.355 + case 0:
1.356 + str_details1 = ", ERROR:";
1.357 + str_details2 = " ???";
1.358 + if (len >= 3)
1.359 + {
1.360 + unsigned err = (((const unsigned char*)buf)[1]<<8) + ((const unsigned char*)buf)[2];
1.361 +
1.362 + switch (err)
1.363 + {
1.364 + case 0x0001:
1.365 + str_details2 = " NO-CIPHER-ERROR";
1.366 + break;
1.367 + case 0x0002:
1.368 + str_details2 = " NO-CERTIFICATE-ERROR";
1.369 + break;
1.370 + case 0x0004:
1.371 + str_details2 = " BAD-CERTIFICATE-ERROR";
1.372 + break;
1.373 + case 0x0006:
1.374 + str_details2 = " UNSUPPORTED-CERTIFICATE-TYPE-ERROR";
1.375 + break;
1.376 + }
1.377 + }
1.378 +
1.379 + break;
1.380 + case 1:
1.381 + str_details1 = ", CLIENT-HELLO";
1.382 + break;
1.383 + case 2:
1.384 + str_details1 = ", CLIENT-MASTER-KEY";
1.385 + break;
1.386 + case 3:
1.387 + str_details1 = ", CLIENT-FINISHED";
1.388 + break;
1.389 + case 4:
1.390 + str_details1 = ", SERVER-HELLO";
1.391 + break;
1.392 + case 5:
1.393 + str_details1 = ", SERVER-VERIFY";
1.394 + break;
1.395 + case 6:
1.396 + str_details1 = ", SERVER-FINISHED";
1.397 + break;
1.398 + case 7:
1.399 + str_details1 = ", REQUEST-CERTIFICATE";
1.400 + break;
1.401 + case 8:
1.402 + str_details1 = ", CLIENT-CERTIFICATE";
1.403 + break;
1.404 + }
1.405 + }
1.406 + }
1.407 +
1.408 + if (version == SSL3_VERSION || version == TLS1_VERSION)
1.409 + {
1.410 + switch (content_type)
1.411 + {
1.412 + case 20:
1.413 + str_content_type = "ChangeCipherSpec";
1.414 + break;
1.415 + case 21:
1.416 + str_content_type = "Alert";
1.417 + break;
1.418 + case 22:
1.419 + str_content_type = "Handshake";
1.420 + break;
1.421 + }
1.422 +
1.423 + if (content_type == 21) /* Alert */
1.424 + {
1.425 + str_details1 = ", ???";
1.426 +
1.427 + if (len == 2)
1.428 + {
1.429 + switch (((const unsigned char*)buf)[0])
1.430 + {
1.431 + case 1:
1.432 + str_details1 = ", warning";
1.433 + break;
1.434 + case 2:
1.435 + str_details1 = ", fatal";
1.436 + break;
1.437 + }
1.438 +
1.439 + str_details2 = " ???";
1.440 + switch (((const unsigned char*)buf)[1])
1.441 + {
1.442 + case 0:
1.443 + str_details2 = " close_notify";
1.444 + break;
1.445 + case 10:
1.446 + str_details2 = " unexpected_message";
1.447 + break;
1.448 + case 20:
1.449 + str_details2 = " bad_record_mac";
1.450 + break;
1.451 + case 21:
1.452 + str_details2 = " decryption_failed";
1.453 + break;
1.454 + case 22:
1.455 + str_details2 = " record_overflow";
1.456 + break;
1.457 + case 30:
1.458 + str_details2 = " decompression_failure";
1.459 + break;
1.460 + case 40:
1.461 + str_details2 = " handshake_failure";
1.462 + break;
1.463 + case 42:
1.464 + str_details2 = " bad_certificate";
1.465 + break;
1.466 + case 43:
1.467 + str_details2 = " unsupported_certificate";
1.468 + break;
1.469 + case 44:
1.470 + str_details2 = " certificate_revoked";
1.471 + break;
1.472 + case 45:
1.473 + str_details2 = " certificate_expired";
1.474 + break;
1.475 + case 46:
1.476 + str_details2 = " certificate_unknown";
1.477 + break;
1.478 + case 47:
1.479 + str_details2 = " illegal_parameter";
1.480 + break;
1.481 + case 48:
1.482 + str_details2 = " unknown_ca";
1.483 + break;
1.484 + case 49:
1.485 + str_details2 = " access_denied";
1.486 + break;
1.487 + case 50:
1.488 + str_details2 = " decode_error";
1.489 + break;
1.490 + case 51:
1.491 + str_details2 = " decrypt_error";
1.492 + break;
1.493 + case 60:
1.494 + str_details2 = " export_restriction";
1.495 + break;
1.496 + case 70:
1.497 + str_details2 = " protocol_version";
1.498 + break;
1.499 + case 71:
1.500 + str_details2 = " insufficient_security";
1.501 + break;
1.502 + case 80:
1.503 + str_details2 = " internal_error";
1.504 + break;
1.505 + case 90:
1.506 + str_details2 = " user_canceled";
1.507 + break;
1.508 + case 100:
1.509 + str_details2 = " no_renegotiation";
1.510 + break;
1.511 + }
1.512 + }
1.513 + }
1.514 +
1.515 + if (content_type == 22) /* Handshake */
1.516 + {
1.517 + str_details1 = "???";
1.518 +
1.519 + if (len > 0)
1.520 + {
1.521 + switch (((const unsigned char*)buf)[0])
1.522 + {
1.523 + case 0:
1.524 + str_details1 = ", HelloRequest";
1.525 + break;
1.526 + case 1:
1.527 + str_details1 = ", ClientHello";
1.528 + break;
1.529 + case 2:
1.530 + str_details1 = ", ServerHello";
1.531 + break;
1.532 + case 11:
1.533 + str_details1 = ", Certificate";
1.534 + break;
1.535 + case 12:
1.536 + str_details1 = ", ServerKeyExchange";
1.537 + break;
1.538 + case 13:
1.539 + str_details1 = ", CertificateRequest";
1.540 + break;
1.541 + case 14:
1.542 + str_details1 = ", ServerHelloDone";
1.543 + break;
1.544 + case 15:
1.545 + str_details1 = ", CertificateVerify";
1.546 + break;
1.547 + case 16:
1.548 + str_details1 = ", ClientKeyExchange";
1.549 + break;
1.550 + case 20:
1.551 + str_details1 = ", Finished";
1.552 + break;
1.553 + }
1.554 + }
1.555 + }
1.556 + }
1.557 +
1.558 + BIO_printf(bio, "%s %s%s [length %04lx]%s%s\n", str_write_p, str_version, str_content_type, (unsigned long)len, str_details1, str_details2);
1.559 +
1.560 + if (len > 0)
1.561 + {
1.562 + size_t num, i;
1.563 +
1.564 + BIO_printf(bio, " ");
1.565 + num = len;
1.566 +#if 0
1.567 + if (num > 16)
1.568 + num = 16;
1.569 +#endif
1.570 + for (i = 0; i < num; i++)
1.571 + {
1.572 + if (i % 16 == 0 && i > 0)
1.573 + BIO_printf(bio, "\n ");
1.574 + BIO_printf(bio, " %02x", ((const unsigned char*)buf)[i]);
1.575 + }
1.576 + if (i < len)
1.577 + BIO_printf(bio, " ...");
1.578 + BIO_printf(bio, "\n");
1.579 + }
1.580 + BIO_flush(bio);
1.581 + }