static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, size_t psk_len, const u8 *data /* Z */, size_t data_len, u8 *buf, size_t len /* X */) { u8 *opos; size_t i, n, hashlen, left, clen; u8 ibuf[2], hash[SHA256_MAC_LEN]; const u8 *addr[3]; size_t vlen[3]; hashlen = SHA256_MAC_LEN; /* M_i = Hash-Function (i || Y || Z); */ addr[0] = ibuf; vlen[0] = sizeof(ibuf); addr[1] = psk; vlen[1] = psk_len; addr[2] = data; vlen[2] = data_len; opos = buf; left = len; n = (len + hashlen - 1) / hashlen; for (i = 1; i <= n; i++) { WPA_PUT_BE16(ibuf, i); sha256_vector(3, addr, vlen, hash); clen = left > hashlen ? hashlen : left; os_memcpy(opos, hash, clen); opos += clen; left -= clen; } return 0; }
static int tdls_derive_tpk(struct wlantest_tdls *tdls, const u8 *bssid, const u8 *ftie, u8 ftie_len) { const struct rsn_ftie *f; u8 key_input[SHA256_MAC_LEN]; const u8 *nonce[2]; size_t len[2]; u8 data[3 * ETH_ALEN]; if (ftie == NULL || ftie_len < sizeof(struct rsn_ftie)) return 0; f = (const struct rsn_ftie *) ftie; wpa_hexdump(MSG_DEBUG, "TDLS ANonce", f->anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "TDLS SNonce", f->snonce, WPA_NONCE_LEN); /* * IEEE Std 802.11z-2010 8.5.9.1: * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) */ len[0] = WPA_NONCE_LEN; len[1] = WPA_NONCE_LEN; if (os_memcmp(f->anonce, f->snonce, WPA_NONCE_LEN) < 0) { nonce[0] = f->anonce; nonce[1] = f->snonce; } else { nonce[0] = f->snonce; nonce[1] = f->anonce; } sha256_vector(2, nonce, len, key_input); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input", key_input, SHA256_MAC_LEN); /* * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) * TODO: is N_KEY really included in KDF Context and if so, in which * presentation format (little endian 16-bit?) is it used? It gets * added by the KDF anyway.. */ if (os_memcmp(tdls->init->addr, tdls->resp->addr, ETH_ALEN) < 0) { os_memcpy(data, tdls->init->addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, tdls->resp->addr, ETH_ALEN); } else { os_memcpy(data, tdls->resp->addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, tdls->init->addr, ETH_ALEN); } os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN); wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data)); sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data), (u8 *) &tdls->tpk, sizeof(tdls->tpk)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK", tdls->tpk.kck, sizeof(tdls->tpk.kck)); wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK", tdls->tpk.tk, sizeof(tdls->tpk.tk)); return 1; }
static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, size_t pk_len) { if (pk == NULL || pk_len == 0) { wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); return -1; } #ifdef CONFIG_WPS_OOB if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->oob_conf.pubkey_hash) { const u8 *addr[1]; u8 hash[WPS_HASH_LEN]; addr[0] = pk; sha256_vector(1, addr, &pk_len, hash); if (os_memcmp(hash, wpabuf_head(wps->wps->oob_conf.pubkey_hash), WPS_OOB_PUBKEY_HASH_LEN) != 0) { wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); return -1; } } #endif /* CONFIG_WPS_OOB */ wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) return -1; if (wps_derive_keys(wps) < 0) return -1; return 0; }
static void eap_aka_add_checkcode(struct eap_aka_data *data, struct eap_sim_msg *msg) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); if (data->id_msgs == NULL) { /* * No EAP-AKA/Identity packets were exchanged - send empty * checkcode. */ eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); return; } /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else #endif /* EAP_AKA_PRIME */ sha1_vector(1, &addr, &len, hash); eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); }
/** * @brief Wrapper function for SHA256 hash * * @param data Pointers to the data areas * @param data_len Lengths of the data blocks * @param digest Buffer for the hash * @return 0 on success, -1 on failure */ int Mrv_SHA256(const u8 * data, u32 data_len, u8 * digest) { const u8 *addr[2]; size_t len[2]; addr[0] = (u8 *) data; len[0] = (size_t) data_len; sha256_vector(1, addr, len, digest); return 0; }
/** * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name * * IEEE Std 802.11r-2008 - 8.5.1.5.3 */ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, const u8 *ssid, size_t ssid_len, const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) { u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; u8 *pos, r0_key_data[48], hash[32]; const u8 *addr[2]; size_t len[2]; /* * R0-Key-Data = KDF-384(XXKey, "FT-R0", * SSIDlength || SSID || MDID || R0KHlength || * R0KH-ID || S0KH-ID) * XXKey is either the second 256 bits of MSK or PSK. * PMK-R0 = L(R0-Key-Data, 0, 256) * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) */ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) return; pos = buf; *pos++ = ssid_len; os_memcpy(pos, ssid, ssid_len); pos += ssid_len; os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN); pos += MOBILITY_DOMAIN_ID_LEN; *pos++ = r0kh_id_len; os_memcpy(pos, r0kh_id, r0kh_id_len); pos += r0kh_id_len; os_memcpy(pos, s0kh_id, ETH_ALEN); pos += ETH_ALEN; sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, r0_key_data, sizeof(r0_key_data)); os_memcpy(pmk_r0, r0_key_data, PMK_LEN); /* * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) */ addr[0] = (const u8 *) "FT-R0N"; len[0] = 6; addr[1] = r0_key_data + PMK_LEN; len[1] = 16; sha256_vector(2, addr, len, hash); os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); }
static int eap_aka_verify_checkcode(struct eap_aka_data *data, const u8 *checkcode, size_t checkcode_len) { const u8 *addr; size_t len; u8 hash[SHA256_MAC_LEN]; size_t hash_len; if (checkcode == NULL) return -1; if (data->id_msgs == NULL) { if (checkcode_len != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " "indicates that AKA/Identity messages were " "used, but they were not"); return -1; } return 0; } hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; if (checkcode_len != hash_len) { wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " "indicates that AKA/Identity message were not " "used, but they were"); return -1; } /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ addr = wpabuf_head(data->id_msgs); len = wpabuf_len(data->id_msgs); #ifdef EAP_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) sha256_vector(1, &addr, &len, hash); else #endif /* EAP_AKA_PRIME */ sha1_vector(1, &addr, &len, hash); if (os_memcmp(hash, checkcode, hash_len) != 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); return -1; } return 0; }
/** * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1 * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, u8 *ptk, size_t ptk_len, u8 *ptk_name) { u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ pos = buf; os_memcpy(pos, snonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, anonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, bssid, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || * ANonce || BSSID || STA-ADDR)) */ addr[0] = pmk_r1_name; len[0] = WPA_PMK_NAME_LEN; addr[1] = (const u8 *) "FT-PTKN"; len[1] = 7; addr[2] = snonce; len[2] = WPA_NONCE_LEN; addr[3] = anonce; len[3] = WPA_NONCE_LEN; addr[4] = bssid; len[4] = ETH_ALEN; addr[5] = sta_addr; len[5] = ETH_ALEN; sha256_vector(6, addr, len, hash); os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); }
int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, const struct wpabuf *pubkey, const u8 *dev_pw, size_t dev_pw_len) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; addr[0] = wpabuf_head(pubkey); hash_len = wpabuf_len(pubkey); sha256_vector(1, addr, &hash_len, pubkey_hash); wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpabuf_put_be16(msg, dev_pw_id); wpabuf_put_data(msg, dev_pw, dev_pw_len); return 0; }
int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); addr[0] = wpabuf_head(wps->dh_pubkey); hash_len = wpabuf_len(wps->dh_pubkey); sha256_vector(1, addr, &hash_len, pubkey_hash); if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { wpa_printf(MSG_ERROR, "WPS: device password id " "generation error"); return -1; } wps->oob_dev_pw_id |= 0x0010; if (random_get_bytes(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) { wpa_printf(MSG_ERROR, "WPS: OOB device password " "generation error"); return -1; } wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpabuf_put_be16(msg, wps->oob_dev_pw_id); wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); wpa_snprintf_hex_uppercase( wpabuf_put(wps->oob_conf.dev_password, wpabuf_size(wps->oob_conf.dev_password)), wpabuf_size(wps->oob_conf.dev_password), dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); return 0; }
/** * wpa_derive_pmk_r1_name - Derive PMKR1Name * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, const u8 *s1kh_id, u8 *pmk_r1_name) { u8 hash[32]; const u8 *addr[4]; size_t len[4]; /* * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || * R1KH-ID || S1KH-ID)) */ addr[0] = (const u8 *) "FT-R1N"; len[0] = 6; addr[1] = pmk_r0_name; len[1] = WPA_PMK_NAME_LEN; addr[2] = r1kh_id; len[2] = FT_R1KH_ID_LEN; addr[3] = s1kh_id; len[3] = ETH_ALEN; sha256_vector(4, addr, len, hash); os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); }
int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, const struct wpabuf *pubkey, const u8 *dev_pw, size_t dev_pw_len) { size_t hash_len; const u8 *addr[1]; u8 pubkey_hash[WPS_HASH_LEN]; wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password (dev_pw_id=%u)", dev_pw_id); addr[0] = wpabuf_head(pubkey); hash_len = wpabuf_len(pubkey); if (sha256_vector(1, addr, &hash_len, pubkey_hash) < 0) return -1; #ifdef CONFIG_WPS_TESTING if (wps_corrupt_pkhash) { wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash", pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash"); pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++; } #endif /* CONFIG_WPS_TESTING */ wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash", pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wpabuf_put_be16(msg, dev_pw_id); if (dev_pw) { wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password", dev_pw, dev_pw_len); wpabuf_put_data(msg, dev_pw, dev_pw_len); } return 0; }
static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, size_t pk_len) { if (pk == NULL || pk_len == 0) { wpa_printf(MSG_DEBUG, "WPS: No Public Key received"); return -1; } if (wps->peer_pubkey_hash_set) { u8 hash[WPS_HASH_LEN]; sha256_vector(1, &pk, &pk_len, hash); if (os_memcmp(hash, wps->peer_pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN) != 0) { wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch"); wpa_hexdump(MSG_DEBUG, "WPS: Received public key", pk, pk_len); wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key " "hash", hash, WPS_OOB_PUBKEY_HASH_LEN); wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash", wps->peer_pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH; return -1; } } wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) return -1; if (wps_derive_keys(wps) < 0) return -1; return 0; }
/** * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @num_elem: Number of elements in the data vector * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (32 bytes) */ void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[32]; const u8 *_addr[6]; size_t _len[6], i; if (num_elem > 5) { /* * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ return; } /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { sha256_vector(1, &key, &key_len, tk); key = tk; key_len = 32; } /* the HMAC_SHA256 transform looks like: * * SHA256(K XOR opad, SHA256(K XOR ipad, text)) * * where K is an n byte key * ipad is the byte 0x36 repeated 64 times * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ /* start out by storing key in ipad */ os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with ipad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x36; /* perform inner SHA256 */ _addr[0] = k_pad; _len[0] = 64; for (i = 0; i < num_elem; i++) { _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } sha256_vector(1 + num_elem, _addr, _len, mac); os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); /* XOR key with opad values */ for (i = 0; i < 64; i++) k_pad[i] ^= 0x5c; /* perform outer SHA256 */ _addr[0] = k_pad; _len[0] = 64; _addr[1] = mac; _len[1] = SHA256_MAC_LEN; sha256_vector(2, _addr, _len, mac); }
static int tls_connection_verify_peer(gnutls_session_t session) { struct tls_connection *conn; unsigned int status, num_certs, i; struct os_time now; const gnutls_datum_t *certs; gnutls_x509_crt_t cert; gnutls_alert_description_t err; int res; conn = gnutls_session_get_ptr(session); if (!conn->verify_peer) { wpa_printf(MSG_DEBUG, "GnuTLS: No peer certificate verification enabled"); return 0; } wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate"); #if GNUTLS_VERSION_NUMBER >= 0x030300 { gnutls_typed_vdata_st data[1]; unsigned int elements = 0; os_memset(data, 0, sizeof(data)); if (!conn->global->server) { data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID; data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER; elements++; } res = gnutls_certificate_verify_peers(session, data, 1, &status); } #else /* < 3.3.0 */ res = gnutls_certificate_verify_peers2(session, &status); #endif if (res < 0) { wpa_printf(MSG_INFO, "TLS: Failed to verify peer " "certificate chain"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030104 { gnutls_datum_t info; int ret, type; type = gnutls_certificate_type_get(session); ret = gnutls_certificate_verification_status_print(status, type, &info, 0); if (ret < 0) { wpa_printf(MSG_DEBUG, "GnuTLS: Failed to print verification status"); err = GNUTLS_A_INTERNAL_ERROR; goto out; } wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data); gnutls_free(info.data); } #endif /* GnuTLS 3.1.4 or newer */ certs = gnutls_certificate_get_peers(session, &num_certs); if (certs == NULL || num_certs == 0) { wpa_printf(MSG_INFO, "TLS: No peer certificate chain received"); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { wpa_printf(MSG_INFO, "TLS: Certificate uses insecure " "algorithm"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate uses insecure algorithm", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_INSUFFICIENT_SECURITY; goto out; } if (status & GNUTLS_CERT_NOT_ACTIVATED) { wpa_printf(MSG_INFO, "TLS: Certificate not yet " "activated"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate not yet valid", TLS_FAIL_NOT_YET_VALID); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } if (status & GNUTLS_CERT_EXPIRED) { wpa_printf(MSG_INFO, "TLS: Certificate expired"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate has expired", TLS_FAIL_EXPIRED); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } gnutls_tls_fail_event(conn, NULL, 0, NULL, "untrusted certificate", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " "known issuer"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found", TLS_FAIL_UNTRUSTED); err = GNUTLS_A_UNKNOWN_CA; goto out; } if (status & GNUTLS_CERT_REVOKED) { wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); gnutls_tls_fail_event(conn, NULL, 0, NULL, "certificate revoked", TLS_FAIL_REVOKED); err = GNUTLS_A_CERTIFICATE_REVOKED; goto out; } if (status != 0) { wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d", status); err = GNUTLS_A_INTERNAL_ERROR; goto out; } if (check_ocsp(conn, session, &err)) goto out; os_get_time(&now); for (i = 0; i < num_certs; i++) { char *buf; size_t len; if (gnutls_x509_crt_init(&cert) < 0) { wpa_printf(MSG_INFO, "TLS: Certificate initialization " "failed"); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { wpa_printf(MSG_INFO, "TLS: Could not parse peer " "certificate %d/%d", i + 1, num_certs); gnutls_x509_crt_deinit(cert); err = GNUTLS_A_BAD_CERTIFICATE; goto out; } gnutls_x509_crt_get_dn(cert, NULL, &len); len++; buf = os_malloc(len + 1); if (buf) { buf[0] = buf[len] = '\0'; gnutls_x509_crt_get_dn(cert, buf, &len); } wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", i + 1, num_certs, buf); if (conn->global->event_cb) { struct wpabuf *cert_buf = NULL; union tls_event_data ev; #ifdef CONFIG_SHA256 u8 hash[32]; const u8 *_addr[1]; size_t _len[1]; #endif /* CONFIG_SHA256 */ os_memset(&ev, 0, sizeof(ev)); if (conn->global->cert_in_cb) { cert_buf = wpabuf_alloc_copy(certs[i].data, certs[i].size); ev.peer_cert.cert = cert_buf; } #ifdef CONFIG_SHA256 _addr[0] = certs[i].data; _len[0] = certs[i].size; if (sha256_vector(1, _addr, _len, hash) == 0) { ev.peer_cert.hash = hash; ev.peer_cert.hash_len = sizeof(hash); } #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = i; ev.peer_cert.subject = buf; conn->global->event_cb(conn->global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert_buf); } if (i == 0) { if (conn->suffix_match && !gnutls_x509_crt_check_hostname( cert, conn->suffix_match)) { wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found", conn->suffix_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain suffix mismatch", TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #if GNUTLS_VERSION_NUMBER >= 0x030300 if (conn->domain_match && !gnutls_x509_crt_check_hostname2( cert, conn->domain_match, GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) { wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", conn->domain_match); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Domain mismatch", TLS_FAIL_DOMAIN_MISMATCH); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* >= 3.3.0 */ /* TODO: validate altsubject_match. * For now, any such configuration is rejected in * tls_connection_set_params() */ #if GNUTLS_VERSION_NUMBER < 0x030300 /* * gnutls_certificate_verify_peers() not available, so * need to check EKU separately. */ if (!conn->global->server && !server_eku_purpose(cert)) { wpa_printf(MSG_WARNING, "GnuTLS: No server EKU"); gnutls_tls_fail_event( conn, &certs[i], i, buf, "No server EKU", TLS_FAIL_BAD_CERTIFICATE); err = GNUTLS_A_BAD_CERTIFICATE; gnutls_x509_crt_deinit(cert); os_free(buf); goto out; } #endif /* < 3.3.0 */ } if (!conn->disable_time_checks && (gnutls_x509_crt_get_expiration_time(cert) < now.sec || gnutls_x509_crt_get_activation_time(cert) > now.sec)) { wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " "not valid at this time", i + 1, num_certs); gnutls_tls_fail_event( conn, &certs[i], i, buf, "Certificate is not valid at this time", TLS_FAIL_EXPIRED); gnutls_x509_crt_deinit(cert); os_free(buf); err = GNUTLS_A_CERTIFICATE_EXPIRED; goto out; } os_free(buf); gnutls_x509_crt_deinit(cert); } if (conn->global->event_cb != NULL) conn->global->event_cb(conn->global->cb_ctx, TLS_CERT_CHAIN_SUCCESS, NULL); return 0; out: conn->failed++; gnutls_alert_send(session, GNUTLS_AL_FATAL, err); return GNUTLS_E_CERTIFICATE_ERROR; }
/** * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1 * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher) { u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN]; u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; size_t ptk_len; /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ pos = buf; os_memcpy(pos, snonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, anonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, bssid, ETH_ALEN); pos += ETH_ALEN; os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; ptk->kck_len = wpa_kck_len(akmp); ptk->kek_len = wpa_kek_len(akmp); ptk->tk_len = wpa_cipher_key_len(cipher); ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || * ANonce || BSSID || STA-ADDR)) */ addr[0] = pmk_r1_name; len[0] = WPA_PMK_NAME_LEN; addr[1] = (const u8 *) "FT-PTKN"; len[1] = 7; addr[2] = snonce; len[2] = WPA_NONCE_LEN; addr[3] = anonce; len[3] = WPA_NONCE_LEN; addr[4] = bssid; len[4] = ETH_ALEN; addr[5] = sta_addr; len[5] = ETH_ALEN; sha256_vector(6, addr, len, hash); os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); os_memcpy(ptk->kck, tmp, ptk->kck_len); os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len); wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); os_memset(tmp, 0, sizeof(tmp)); return 0; }
static int cavp_rsa_sig_ver(const char *fname) { FILE *f; int ret = 0; char buf[15000], *pos, *pos2; u8 msg[200], n[512], s[512], em[512], e[512]; size_t msg_len = 0, n_len = 0, s_len = 0, em_len, e_len = 0; size_t tmp_len; char sha_alg[20]; int ok = 0; printf("CAVP RSA SigVer test vectors from %s\n", fname); f = fopen(fname, "r"); if (f == NULL) { printf("%s does not exist - cannot validate CAVP RSA SigVer test vectors\n", fname); return 0; } while (fgets(buf, sizeof(buf), f)) { pos = os_strchr(buf, '='); if (pos == NULL) continue; pos2 = pos - 1; while (pos2 >= buf && *pos2 == ' ') *pos2-- = '\0'; *pos++ = '\0'; while (*pos == ' ') *pos++ = '\0'; pos2 = os_strchr(pos, '\r'); if (!pos2) pos2 = os_strchr(pos, '\n'); if (pos2) *pos2 = '\0'; else pos2 = pos + os_strlen(pos); if (os_strcmp(buf, "SHAAlg") == 0) { os_strlcpy(sha_alg, pos, sizeof(sha_alg)); } else if (os_strcmp(buf, "Msg") == 0) { tmp_len = os_strlen(pos); if (tmp_len > sizeof(msg) * 2) { printf("Too long Msg\n"); return -1; } msg_len = tmp_len / 2; if (hexstr2bin(pos, msg, msg_len) < 0) { printf("Invalid hex string '%s'\n", pos); ret++; break; } } else if (os_strcmp(buf, "n") == 0) { tmp_len = os_strlen(pos); if (tmp_len > sizeof(n) * 2) { printf("Too long n\n"); return -1; } n_len = tmp_len / 2; if (hexstr2bin(pos, n, n_len) < 0) { printf("Invalid hex string '%s'\n", pos); ret++; break; } } else if (os_strcmp(buf, "e") == 0) { tmp_len = os_strlen(pos); if (tmp_len > sizeof(e) * 2) { printf("Too long e\n"); return -1; } e_len = tmp_len / 2; if (hexstr2bin(pos, e, e_len) < 0) { printf("Invalid hex string '%s'\n", pos); ret++; break; } } else if (os_strcmp(buf, "S") == 0) { tmp_len = os_strlen(pos); if (tmp_len > sizeof(s) * 2) { printf("Too long S\n"); return -1; } s_len = tmp_len / 2; if (hexstr2bin(pos, s, s_len) < 0) { printf("Invalid hex string '%s'\n", pos); ret++; break; } } else if (os_strncmp(buf, "EM", 2) == 0) { tmp_len = os_strlen(pos); if (tmp_len > sizeof(em) * 2) return -1; em_len = tmp_len / 2; if (hexstr2bin(pos, em, em_len) < 0) { printf("Invalid hex string '%s'\n", pos); ret++; break; } } else if (os_strcmp(buf, "Result") == 0) { const u8 *addr[1]; size_t len[1]; struct crypto_public_key *pk; int res; u8 hash[32]; size_t hash_len; const struct asn1_oid *alg; addr[0] = msg; len[0] = msg_len; if (os_strcmp(sha_alg, "SHA1") == 0) { if (sha1_vector(1, addr, len, hash) < 0) return -1; hash_len = 20; alg = &asn1_sha1_oid; } else if (os_strcmp(sha_alg, "SHA256") == 0) { if (sha256_vector(1, addr, len, hash) < 0) return -1; hash_len = 32; alg = &asn1_sha256_oid; } else { continue; } printf("\nExpected result: %s\n", pos); wpa_hexdump(MSG_INFO, "Hash(Msg)", hash, hash_len); pk = crypto_public_key_import_parts(n, n_len, e, e_len); if (pk == NULL) { printf("Failed to import public key\n"); ret++; continue; } res = pkcs1_v15_sig_ver(pk, s, s_len, alg, hash, hash_len); crypto_public_key_free(pk); if ((*pos == 'F' && !res) || (*pos != 'F' && res)) { printf("FAIL\n"); ret++; continue; } printf("PASS\n"); ok++; } } fclose(f); if (ret) printf("Test case failed\n"); else printf("%d test vectors OK\n", ok); return ret; }
int main(int argc, char *argv[]) { unsigned int i; u8 hash[32]; const u8 *addr[2]; size_t len[2]; int errors = 0; for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { printf("SHA256 test case %d:", i + 1); addr[0] = (u8 *) tests[i].data; len[0] = strlen(tests[i].data); sha256_vector(1, addr, len, hash); if (memcmp(hash, tests[i].hash, 32) != 0) { printf(" FAIL"); errors++; } else printf(" OK"); if (len[0]) { addr[0] = (u8 *) tests[i].data; len[0] = 1; addr[1] = (u8 *) tests[i].data + 1; len[1] = strlen(tests[i].data) - 1; sha256_vector(2, addr, len, hash); if (memcmp(hash, tests[i].hash, 32) != 0) { printf(" FAIL"); errors++; } else printf(" OK"); } printf("\n"); } for (i = 0; i < sizeof(hmac_tests) / sizeof(hmac_tests[0]); i++) { struct hmac_test *t = &hmac_tests[i]; printf("HMAC-SHA256 test case %d:", i + 1); hmac_sha256(t->key, t->key_len, t->data, t->data_len, hash); if (memcmp(hash, t->hash, 32) != 0) { printf(" FAIL"); errors++; } else printf(" OK"); addr[0] = t->data; len[0] = t->data_len; hmac_sha256_vector(t->key, t->key_len, 1, addr, len, hash); if (memcmp(hash, t->hash, 32) != 0) { printf(" FAIL"); errors++; } else printf(" OK"); if (len[0]) { addr[0] = t->data; len[0] = 1; addr[1] = t->data + 1; len[1] = t->data_len - 1; hmac_sha256_vector(t->key, t->key_len, 2, addr, len, hash); if (memcmp(hash, t->hash, 32) != 0) { printf(" FAIL"); errors++; } else printf(" OK"); } printf("\n"); } printf("Test IEEE 802.11r KDF\n"); sha256_prf("abc", 3, "KDF test", "data", 4, hash, sizeof(hash)); /* TODO: add proper test case for this */ return errors; }
/** * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) * @key: Key for HMAC operations * @key_len: Length of the key in bytes * @num_elem: Number of elements in the data vector; including [0] spare * @addr: Pointers to the data areas, [0] element must be left as spare * @len: Lengths of the data blocks, [0] element must be left as spare * @mac: Buffer for the hash (32 bytes) * @pScratchMem: Scratch Memory; At least a 492 byte buffer. */ void hmac_sha256_vector(void *priv, UINT8 *key, size_t key_len, size_t num_elem, UINT8 *addr[], size_t * len, UINT8 *mac, UINT8 *pScratchMem) { phostsa_private psapriv = (phostsa_private)priv; hostsa_util_fns *util_fns = &psapriv->util_fns; size_t i; UINT8 *pKpad; /* was UINT8 k_pad[64], padding - key XORd with ipad/opad */ UINT8 *pTk; /* was UINT8 tk[32] */ UINT8 *pTmpBuf; UINT32 *ptrU32; pKpad = pScratchMem; /* kpad = 64 bytes */ pTk = pKpad + 64; /* tk = 32 bytes */ pTmpBuf = pTk + 32; /* offset into the scratch buf = +96 bytes */ /* if key is longer than 64 bytes reset it to key = SHA256(key) */ if (key_len > 64) { /* pTmpBuf = At least 396 bytes */ sha256_vector(priv, 1, &key, &key_len, pTk, pTmpBuf); key = pTk; key_len = 32; } /* the HMAC_SHA256 transform looks like: SHA256(K XOR opad, SHA256(K XOR ipad, text)) where K is an n byte key ipad is the byte 0x36 repeated 64 times opad is the byte 0x5c repeated 64 times and text is the data being protected */ /* start out by storing key in ipad */ memset(util_fns, pKpad, 0x00, 64); memcpy(util_fns, pKpad, key, key_len); /* XOR key with ipad values */ ptrU32 = (UINT32 *)pKpad; for (i = 16; i > 0; i--) { *ptrU32++ ^= 0x36363636; } /* perform inner SHA256 */ addr[0] = pKpad; len[0] = 64; /* pTmpBuf = At least 396 bytes */ sha256_vector((void *)priv, num_elem, addr, len, mac, pTmpBuf); memset(util_fns, pKpad, 0x00, 64); memcpy(util_fns, pKpad, key, key_len); /* XOR key with opad values */ ptrU32 = (UINT32 *)pKpad; for (i = 16; i > 0; i--) { *ptrU32++ ^= 0x5C5C5C5C; } /* perform outer SHA256 */ addr[0] = pKpad; len[0] = 64; addr[1] = mac; len[1] = SHA256_MAC_LEN; /* pTmpBuf = At least 396 bytes */ sha256_vector((void *)priv, 2, addr, len, mac, pTmpBuf); }
int wps_derive_keys(struct wps_data *wps) { struct wpabuf *pubkey, *dh_shared; u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN]; const u8 *addr[3]; size_t len[3]; u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN]; if (wps->dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available"); return -1; } pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r; if (pubkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available"); return -1; } wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey); dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); dh5_free(wps->dh_ctx); wps->dh_ctx = NULL; dh_shared = wpabuf_zeropad(dh_shared, 192); if (dh_shared == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key"); return -1; } /* Own DH private key is not needed anymore */ wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); /* DHKey = SHA-256(g^AB mod p) */ addr[0] = wpabuf_head(dh_shared); len[0] = wpabuf_len(dh_shared); sha256_vector(1, addr, len, dhkey); wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); wpabuf_free(dh_shared); /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ addr[0] = wps->nonce_e; len[0] = WPS_NONCE_LEN; addr[1] = wps->mac_addr_e; len[1] = ETH_ALEN; addr[2] = wps->nonce_r; len[2] = WPS_NONCE_LEN; hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk); wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk)); wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation", keys, sizeof(keys)); os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN); os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN); os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN, WPS_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey", wps->authkey, WPS_AUTHKEY_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey", wps->keywrapkey, WPS_KEYWRAPKEY_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN); return 0; }
int wps_derive_keys(struct wps_data *wps) { struct wpabuf *pubkey, *dh_shared; u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN]; const u8 *addr[3]; size_t len[3]; u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN]; if (wps->dh_privkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available"); return -1; } pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r; if (pubkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available"); return -1; } dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); dh5_free(wps->dh_ctx); wps->dh_ctx = NULL; dh_shared = wpabuf_zeropad(dh_shared, 192); if (dh_shared == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key"); return -1; } /* Own DH private key is not needed anymore */ wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared); /* DHKey = SHA-256(g^AB mod p) */ addr[0] = wpabuf_head(dh_shared); len[0] = wpabuf_len(dh_shared); sha256_vector(1, addr, len, dhkey); wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey)); wpabuf_free(dh_shared); /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */ addr[0] = wps->nonce_e; len[0] = WPS_NONCE_LEN; addr[1] = wps->mac_addr_e; len[1] = ETH_ALEN; addr[2] = wps->nonce_r; len[2] = WPS_NONCE_LEN; hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk); wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk)); wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation", keys, sizeof(keys)); os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN); os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN); os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN, WPS_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey", wps->authkey, WPS_AUTHKEY_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey", wps->keywrapkey, WPS_KEYWRAPKEY_LEN); wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN); /****** ADD THIS PART ******/ memset(pixie_authkey,0,sizeof(pixie_authkey)); if ( get_debug()==4 ) { //verbose (-vvv) printf("[P] AuthKey: "); } int pixiecnt = 0; for (; pixiecnt < WPS_AUTHKEY_LEN; pixiecnt++) { if ( get_debug()==4 ) { //verbose (-vvv) printf("%02x", wps->authkey[pixiecnt]); } sprintf(cmd_pixie_aux, "%02x", wps->authkey[pixiecnt]); strcat(pixie_authkey, cmd_pixie_aux); if (pixiecnt != WPS_AUTHKEY_LEN - 1) { if ( get_debug()==4 ) { //verbose (-vvv) printf(":"); } strcat(pixie_authkey,":"); } } if ( get_debug()==4 ) { //verbose (-vvv) printf("\n"); } /******/ return 0; }