Remove support on both sides for *_fixed_(ec)dh client auth.

In the fixed_ecdh case, it wasn't even implemented, but there was stub code for
it. It complicates the ClientKeyExchange (the client parameters become implicit
in the certificate) and isn't used.

Change-Id: I3627a37042539c90e05e59cd0cb3cd6c56225561
Reviewed-on: https://boringssl-review.googlesource.com/1563
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin
2014-08-20 02:07:00 +00:00
committed by Adam Langley
parent d0639af978
commit bd30f8e34a
6 changed files with 85 additions and 345 deletions
-1
View File
@@ -341,7 +341,6 @@ typedef struct ssl3_buffer_st
#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001
#define SSL3_FLAGS_POP_BUFFER 0x0004
#define TLS1_FLAGS_TLS_PADDING_BUG 0x0008
#define TLS1_FLAGS_SKIP_CERT_VERIFY 0x0010
#define TLS1_FLAGS_KEEP_HANDSHAKE 0x0020
/* TODO(davidben): This flag can probably be merged into s3->change_cipher_spec
* to something tri-state. (Normal / Expect CCS / Between CCS and Finished). */
-3
View File
@@ -375,9 +375,6 @@ int dtls1_connect(SSL *s)
dtls1_start_timer(s);
ret=ssl3_send_client_key_exchange(s);
if (ret <= 0) goto end;
/* EAY EAY EAY need to check for DH fix cert
* sent back */
/* For TLS, cert_req is set to 2, so a cert chain
* of nothing is sent, but no verify packet is sent */
if (s->s3->tmp.cert_req == 1)
+3 -16
View File
@@ -492,22 +492,9 @@ int dtls1_accept(SSL *s)
case SSL3_ST_SR_KEY_EXCH_A:
case SSL3_ST_SR_KEY_EXCH_B:
ret=ssl3_get_client_key_exchange(s);
if (ret <= 0) goto end;
s->state=SSL3_ST_SR_CERT_VRFY_A;
s->init_num=0;
if (ret == 2)
{
/* For the ECDH ciphersuites when
* the client sends its ECDH pub key in
* a certificate, the CertificateVerify
* message is not sent.
*/
s->state=SSL3_ST_SR_FINISHED_A;
s->init_num = 0;
}
else if (SSL_USE_SIGALGS(s))
if (ret <= 0)
goto end;
if (SSL_USE_SIGALGS(s))
{
s->state=SSL3_ST_SR_CERT_VRFY_A;
s->init_num=0;
+56 -176
View File
@@ -362,17 +362,8 @@ int ssl3_connect(SSL *s)
case SSL3_ST_CW_KEY_EXCH_B:
ret=ssl3_send_client_key_exchange(s);
if (ret <= 0) goto end;
/* EAY EAY EAY need to check for DH fix cert
* sent back */
/* For TLS, cert_req is set to 2, so a cert chain
* of nothing is sent, but no verify packet is sent */
/* XXX: For now, we do not support client
* authentication in ECDH cipher suites with
* ECDH (rather than ECDSA) certificates.
* We need to skip the certificate verify
* message when client's ECDH public key is sent
* inside the client certificate.
*/
if (s->s3->tmp.cert_req == 1)
{
s->state=SSL3_ST_CW_CERT_VRFY_A;
@@ -382,11 +373,6 @@ int ssl3_connect(SSL *s)
s->state=SSL3_ST_CW_CHANGE_A;
s->s3->change_cipher_spec=0;
}
if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
{
s->state=SSL3_ST_CW_CHANGE_A;
s->s3->change_cipher_spec=0;
}
s->init_num=0;
break;
@@ -2122,33 +2108,17 @@ int ssl3_send_client_key_exchange(SSL *s)
goto err;
}
}
if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
/* generate a new random key */
if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL)
{
/* Use client certificate key */
EVP_PKEY *clkey = s->cert->key->privatekey;
dh_clnt = NULL;
if (clkey)
dh_clnt = EVP_PKEY_get1_DH(clkey);
if (dh_clnt == NULL)
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_INTERNAL_ERROR);
goto err;
}
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB);
goto err;
}
else
if (!DH_generate_key(dh_clnt))
{
/* generate a new random key */
if ((dh_clnt=DHparams_dup(dh_srvr)) == NULL)
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB);
goto err;
}
if (!DH_generate_key(dh_clnt))
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB);
DH_free(dh_clnt);
goto err;
}
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB);
DH_free(dh_clnt);
goto err;
}
/* use the 'p' output buffer for the DH key, but
@@ -2172,16 +2142,11 @@ int ssl3_send_client_key_exchange(SSL *s)
/* clean up */
memset(p,0,n);
if (s->s3->flags & TLS1_FLAGS_SKIP_CERT_VERIFY)
n = 0;
else
{
/* send off the data */
n=BN_num_bytes(dh_clnt->pub_key);
s2n(n,p);
BN_bn2bin(dh_clnt->pub_key,p);
n+=2;
}
/* send off the data */
n=BN_num_bytes(dh_clnt->pub_key);
s2n(n,p);
BN_bn2bin(dh_clnt->pub_key,p);
n+=2;
DH_free(dh_clnt);
@@ -2194,7 +2159,6 @@ int ssl3_send_client_key_exchange(SSL *s)
{
const EC_GROUP *srvr_group = NULL;
EC_KEY *tkey;
int ecdh_clnt_cert = 0;
int field_size = 0;
unsigned char *pre_ms;
unsigned char *t;
@@ -2208,34 +2172,6 @@ int ssl3_send_client_key_exchange(SSL *s)
goto err;
}
/* Did we send out the client's
* ECDH share for use in premaster
* computation as part of client certificate?
* If so, set ecdh_clnt_cert to 1.
*/
if ((alg_k & (SSL_kECDHr|SSL_kECDHe)) && (s->cert != NULL))
{
/* XXX: For now, we do not support client
* authentication using ECDH certificates.
* To add such support, one needs to add
* code that checks for appropriate
* conditions and sets ecdh_clnt_cert to 1.
* For example, the cert have an ECC
* key on the same curve as the server's
* and the key should be authorized for
* key agreement.
*
* One also needs to add code in ssl3_connect
* to skip sending the certificate verify
* message.
*
* if ((s->cert->key->privatekey != NULL) &&
* (s->cert->key->privatekey->type ==
* EVP_PKEY_EC) && ...)
* ecdh_clnt_cert = 1;
*/
}
if (s->session->sess_cert->peer_ecdh_tmp != NULL)
{
tkey = s->session->sess_cert->peer_ecdh_tmp;
@@ -2276,34 +2212,11 @@ int ssl3_send_client_key_exchange(SSL *s)
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_EC_LIB);
goto err;
}
if (ecdh_clnt_cert)
{
/* Reuse key info from our certificate
* We only need our private key to perform
* the ECDH computation.
*/
const BIGNUM *priv_key;
tkey = s->cert->key->privatekey->pkey.ec;
priv_key = EC_KEY_get0_private_key(tkey);
if (priv_key == NULL)
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
goto err;
}
if (!EC_KEY_set_private_key(clnt_ecdh, priv_key))
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_EC_LIB);
goto err;
}
}
else
/* Generate a new ECDH key pair */
if (!(EC_KEY_generate_key(clnt_ecdh)))
{
/* Generate a new ECDH key pair */
if (!(EC_KEY_generate_key(clnt_ecdh)))
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB);
goto err;
}
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB);
goto err;
}
/* use the 'p' output buffer for the ECDH key, but
@@ -2355,59 +2268,51 @@ int ssl3_send_client_key_exchange(SSL *s)
}
memset(p, 0, n); /* clean up */
if (ecdh_clnt_cert)
{
/* Send empty client key exch message */
n = 0;
}
else
{
/* First check the size of encoding and
* allocate memory accordingly.
*/
encoded_pt_len =
EC_POINT_point2oct(srvr_group,
/* First check the size of encoding and
* allocate memory accordingly.
*/
encoded_pt_len =
EC_POINT_point2oct(srvr_group,
EC_KEY_get0_public_key(clnt_ecdh),
POINT_CONVERSION_UNCOMPRESSED,
NULL, 0, NULL);
encodedPoint = (unsigned char *)
OPENSSL_malloc(encoded_pt_len *
encodedPoint = (unsigned char *)
OPENSSL_malloc(encoded_pt_len *
sizeof(unsigned char));
bn_ctx = BN_CTX_new();
if ((encodedPoint == NULL) ||
(bn_ctx == NULL))
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
goto err;
}
/* Encode the public key */
encoded_pt_len = EC_POINT_point2oct(srvr_group,
EC_KEY_get0_public_key(clnt_ecdh),
POINT_CONVERSION_UNCOMPRESSED,
encodedPoint, encoded_pt_len, bn_ctx);
n = 0;
if ((alg_a & SSL_aPSK) && psk_len != 0)
{
i = strlen(s->session->psk_identity);
s2n(i, p);
memcpy(p, s->session->psk_identity, i);
p += i;
n = i + 2;
}
*p = encoded_pt_len; /* length of encoded point */
/* Encoded point will be copied here */
p += 1;
n += 1;
/* copy the point */
memcpy((unsigned char *)p, encodedPoint, encoded_pt_len);
/* increment n to account for length field */
n += encoded_pt_len;
bn_ctx = BN_CTX_new();
if ((encodedPoint == NULL) ||
(bn_ctx == NULL))
{
OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
goto err;
}
/* Encode the public key */
encoded_pt_len = EC_POINT_point2oct(srvr_group,
EC_KEY_get0_public_key(clnt_ecdh),
POINT_CONVERSION_UNCOMPRESSED,
encodedPoint, encoded_pt_len, bn_ctx);
n = 0;
if ((alg_a & SSL_aPSK) && psk_len != 0)
{
i = strlen(s->session->psk_identity);
s2n(i, p);
memcpy(p, s->session->psk_identity, i);
p += i;
n = i + 2;
}
*p = encoded_pt_len; /* length of encoded point */
/* Encoded point will be copied here */
p += 1;
n += 1;
/* copy the point */
memcpy((unsigned char *)p, encodedPoint, encoded_pt_len);
/* increment n to account for length field */
n += encoded_pt_len;
/* Free allocated memory */
BN_CTX_free(bn_ctx);
if (encodedPoint != NULL) OPENSSL_free(encodedPoint);
@@ -2560,12 +2465,10 @@ err:
}
/* Check a certificate can be used for client authentication. Currently
* check cert exists, if we have a suitable digest for TLS 1.2 if
* static DH client certificates can be used.
* check the cert exists and if we have a suitable digest for TLS 1.2.
*/
static int ssl3_check_client_certificate(SSL *s)
{
unsigned long alg_k;
if (!s->cert || !s->cert->key->x509 || !s->cert->key->privatekey)
return 0;
/* If no suitable signature algorithm can't use certificate */
@@ -2576,29 +2479,6 @@ static int ssl3_check_client_certificate(SSL *s)
if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT &&
!tls1_check_chain(s, NULL, NULL, NULL, -2))
return 0;
alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
/* See if we can use client certificate for fixed DH */
if (alg_k & (SSL_kDHr|SSL_kDHd))
{
SESS_CERT *scert = s->session->sess_cert;
int i = scert->peer_cert_type;
EVP_PKEY *clkey = NULL, *spkey = NULL;
clkey = s->cert->key->privatekey;
/* If client key not DH assume it can be used */
if (EVP_PKEY_id(clkey) != EVP_PKEY_DH)
return 1;
if (i >= 0)
spkey = X509_get_pubkey(scert->peer_pkeys[i].x509);
if (spkey)
{
/* Compare server and client parameters */
i = EVP_PKEY_cmp_parameters(clkey, spkey);
EVP_PKEY_free(spkey);
if (i != 1)
return 0;
}
s->s3->flags |= TLS1_FLAGS_SKIP_CERT_VERIFY;
}
return 1;
}
-37
View File
@@ -2538,8 +2538,6 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
#ifndef OPENSSL_NO_ECDSA
int have_ecdsa_sign = 0;
#endif
int nostrict = 1;
unsigned long alg_k;
/* If we have custom certificate types set, use them */
if (s->cert->client_certificate_types)
@@ -2550,8 +2548,6 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
}
/* get configured sigalgs */
siglen = tls12_get_psigalgs(s, &sig);
if (s->cert->cert_flags & SSL_CERT_FLAGS_CHECK_TLS_STRICT)
nostrict = 0;
for (i = 0; i < siglen; i+=2, sig+=2)
{
switch(sig[1])
@@ -2571,45 +2567,12 @@ int ssl3_get_req_cert_type(SSL *s, unsigned char *p)
}
}
alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
#ifndef OPENSSL_NO_DH
if (alg_k & (SSL_kDHr|SSL_kEDH))
{
/* Since this refers to a certificate signed with an RSA
* algorithm, only check for rsa signing in strict mode.
*/
if (nostrict || have_rsa_sign)
p[ret++]=SSL3_CT_RSA_FIXED_DH;
# ifndef OPENSSL_NO_DSA
if (nostrict || have_dsa_sign)
p[ret++]=SSL3_CT_DSS_FIXED_DH;
# endif
}
if ((s->version == SSL3_VERSION) &&
(alg_k & (SSL_kEDH|SSL_kDHd|SSL_kDHr)))
{
p[ret++]=SSL3_CT_RSA_EPHEMERAL_DH;
# ifndef OPENSSL_NO_DSA
p[ret++]=SSL3_CT_DSS_EPHEMERAL_DH;
# endif
}
#endif /* !OPENSSL_NO_DH */
if (have_rsa_sign)
p[ret++]=SSL3_CT_RSA_SIGN;
#ifndef OPENSSL_NO_DSA
if (have_dsa_sign)
p[ret++]=SSL3_CT_DSS_SIGN;
#endif
#ifndef OPENSSL_NO_ECDH
if ((alg_k & (SSL_kECDHr|SSL_kECDHe)) && (s->version >= TLS1_VERSION))
{
if (nostrict || have_rsa_sign)
p[ret++]=TLS_CT_RSA_FIXED_ECDH;
if (nostrict || have_ecdsa_sign)
p[ret++]=TLS_CT_ECDSA_FIXED_ECDH;
}
#endif
#ifndef OPENSSL_NO_ECDSA
/* ECDSA certs can be used with RSA cipher suites as well
+26 -112
View File
@@ -259,7 +259,6 @@ int ssl3_accept(SSL *s)
s->init_num=0;
s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE;
s->s3->flags &= ~TLS1_FLAGS_SKIP_CERT_VERIFY;
if (s->state != SSL_ST_RENEGOTIATE)
{
@@ -493,17 +492,7 @@ int ssl3_accept(SSL *s)
ret=ssl3_get_client_key_exchange(s);
if (ret <= 0)
goto end;
if (ret == 2)
{
/* For the ECDH ciphersuites when
* the client sends its ECDH pub key in
* a certificate, the CertificateVerify
* message is not sent.
*/
s->init_num = 0;
s->state = SSL3_ST_SR_CHANGE;
}
else if (SSL_USE_SIGALGS(s))
if (SSL_USE_SIGALGS(s))
{
s->state=SSL3_ST_SR_CERT_VRFY_A;
s->init_num=0;
@@ -1885,13 +1874,12 @@ int ssl3_get_client_key_exchange(SSL *s)
unsigned long alg_a;
uint8_t *premaster_secret = NULL;
size_t premaster_secret_len = 0;
int skip_certificate_verify = 0;
RSA *rsa=NULL;
uint8_t *decrypt_buf = NULL;
EVP_PKEY *pkey=NULL;
#ifndef OPENSSL_NO_DH
BIGNUM *pub=NULL;
DH *dh_srvr, *dh_clnt = NULL;
DH *dh_srvr;
#endif
#ifndef OPENSSL_NO_ECDH
@@ -1972,8 +1960,7 @@ int ssl3_get_client_key_exchange(SSL *s)
}
/* Depending on the key exchange method, compute |premaster_secret| and
* |premaster_secret_len|. Also, for DH and ECDH, set
* |skip_certificate_verify| as appropriate. */
* |premaster_secret_len|. */
if (alg_k & SSL_kRSA)
{
CBS encrypted_premaster_secret;
@@ -2139,6 +2126,7 @@ int ssl3_get_client_key_exchange(SSL *s)
EVP_PKEY *skey = NULL;
if (!CBS_get_u16_length_prefixed(&client_key_exchange, &dh_Yc) ||
CBS_len(&dh_Yc) == 0 ||
CBS_len(&client_key_exchange) != 0)
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG);
@@ -2172,33 +2160,7 @@ int ssl3_get_client_key_exchange(SSL *s)
else
dh_srvr=s->s3->tmp.dh;
if (CBS_len(&dh_Yc) == 0)
{
/* Get pubkey from the client certificate. This is the
* 'implicit' case of ClientDiffieHellman.
*
* TODO(davidben): Either lose this code or fix a bug
* (or get the spec changed): if there is a fixed_dh
* client certificate, per spec, the 'implicit' mode
* MUST be used. This logic will still accept 'explicit'
* mode. */
EVP_PKEY *clkey=X509_get_pubkey(s->session->peer);
if (clkey)
{
if (EVP_PKEY_cmp_parameters(clkey, skey) == 1)
dh_clnt = EVP_PKEY_get1_DH(clkey);
}
if (dh_clnt == NULL)
{
al=SSL_AD_HANDSHAKE_FAILURE;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_MISSING_TMP_DH_KEY);
goto f_err;
}
EVP_PKEY_free(clkey);
pub = dh_clnt->pub_key;
}
else
pub = BN_bin2bn(CBS_data(&dh_Yc), CBS_len(&dh_Yc), NULL);
pub = BN_bin2bn(CBS_data(&dh_Yc), CBS_len(&dh_Yc), NULL);
if (pub == NULL)
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_BN_LIB);
@@ -2223,15 +2185,10 @@ int ssl3_get_client_key_exchange(SSL *s)
DH_free(s->s3->tmp.dh);
s->s3->tmp.dh=NULL;
if (dh_clnt)
DH_free(dh_clnt);
else
BN_clear_free(pub);
BN_clear_free(pub);
pub=NULL;
premaster_secret_len = dh_len;
if (dh_clnt)
skip_certificate_verify = 1;
}
#endif
@@ -2242,6 +2199,7 @@ int ssl3_get_client_key_exchange(SSL *s)
const EC_KEY *tkey;
const EC_GROUP *group;
const BIGNUM *priv_key;
CBS ecdh_Yc;
/* initialize structures for server's ECDH key pair */
if ((srvr_ecdh = EC_KEY_new()) == NULL)
@@ -2281,72 +2239,28 @@ int ssl3_get_client_key_exchange(SSL *s)
goto err;
}
if (CBS_len(&client_key_exchange) == 0)
/* Get client's public key from encoded point
* in the ClientKeyExchange message.
*/
if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ecdh_Yc) ||
CBS_len(&client_key_exchange) != 0)
{
/* Client Publickey was in Client Certificate */
if (alg_k & SSL_kEECDH)
{
al=SSL_AD_HANDSHAKE_FAILURE;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_MISSING_TMP_ECDH_KEY);
goto f_err;
}
if (((clnt_pub_pkey=X509_get_pubkey(s->session->peer))
== NULL) ||
(clnt_pub_pkey->type != EVP_PKEY_EC))
{
/* XXX: For now, we do not support client
* authentication using ECDH certificates
* so this branch (n == 0L) of the code is
* never executed. When that support is
* added, we ought to ensure the key
* received in the certificate is
* authorized for key agreement.
* ECDH_compute_key implicitly checks that
* the two ECDH shares are for the same
* group.
*/
al=SSL_AD_HANDSHAKE_FAILURE;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_UNABLE_TO_DECODE_ECDH_CERTS);
goto f_err;
}
if (EC_POINT_copy(clnt_ecpoint,
EC_KEY_get0_public_key(clnt_pub_pkey->pkey.ec)) == 0)
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB);
goto err;
}
/* Skip certificate verify processing */
skip_certificate_verify = 1;
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR);
goto f_err;
}
else
if ((bn_ctx = BN_CTX_new()) == NULL)
{
CBS ecdh_Yc;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_MALLOC_FAILURE);
goto err;
}
/* Get client's public key from encoded point
* in the ClientKeyExchange message.
*/
if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ecdh_Yc) ||
CBS_len(&client_key_exchange) != 0)
{
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR);
goto f_err;
}
if ((bn_ctx = BN_CTX_new()) == NULL)
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_MALLOC_FAILURE);
goto err;
}
if (!EC_POINT_oct2point(group, clnt_ecpoint,
CBS_data(&ecdh_Yc), CBS_len(&ecdh_Yc), bn_ctx))
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB);
goto err;
}
if (!EC_POINT_oct2point(group, clnt_ecpoint,
CBS_data(&ecdh_Yc), CBS_len(&ecdh_Yc), bn_ctx))
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB);
goto err;
}
/* Allocate a buffer for both the secret and the PSK. */
@@ -2441,7 +2355,7 @@ int ssl3_get_client_key_exchange(SSL *s)
OPENSSL_cleanse(premaster_secret, premaster_secret_len);
OPENSSL_free(premaster_secret);
return skip_certificate_verify ? 2 : 1;
return 1;
f_err:
ssl3_send_alert(s,SSL3_AL_FATAL,al);
err: