Cumulative patch from commit 7bf86e44161fe1bcadaf4ca2cc3b6358fb7f3bf9

7bf86e441 Define test config QCA vendor attribute for HE MU EDCA params
6cc77193c Define test config QCA vendor attribute for Tx beamformee NSTS
73d3f8841 HS 2.0: CoA-Request from Terms and Conditions server
abed6136a RADIUS: Add DAC implementation in hostapd(AS)
72aad113c RADIUS: Allow 0.0.0.0 to be used as wildard radius_das_client
2122fc83a RADIUS server: Add current_sessions SQLite table
d4e39c51f HS 2.0: Move Terms and Conditions Server URL generation from AP to AS
de3885fcc HS 2.0: Process Credential/UsernamePassword/EAPMethod nodes in PPS MO
3a8dd390c SAE: Allow authentication restart on AP
a10f5714c HS 2.0: Allow Hotspot 2.0 version number to be overridden for build
e8cf9fc33 P2P: Use more compact debug print of common group frequencies
26eac0a99 P2P: Improve common group channel selection if GO needs to be moved
616178a2a P2P/AP: More detailed debug prints on HT/VHT parameter selection
c6f01fbf2 nl80211: Add more command/event names to debug prints
4a0e01156 AP: Fix HT 20/40 co-ex transition timer cancellation on iface removal
b915f2cdf nl80211: Handle NL80211_CMD_WIPHY_REG_CHANGE event
c3e4f40cd FT: Derive PMKR0Name/PMKR1Name using SHA-384 with AKM 00-0F-AC:13
e8d08cf37 SAE: Do not drop STA entry on reauthentication in infrastructure BSS
ef13b33af FT: Support BIP-CMAC-256, BIP-GMAC-128, BIP-GMAC-256 in STA case
ff168f8c2 FT: Support 256-bit IGTK in AP case
2cf36d608 FT: Handle AssocResp generation failures as fatal errors
657b4c45a FILS: Fix KEK2 use in FT-FILS use cases
88bf44be4 FT: Fix potential NULL pointer dereference in MDE addition
661afb2ed FT: Fix RRB error path handling
df3bf6870 FT: Debug print of IE parsing element details
d8e8c992c FT: Add key management value FT-EAP-SHA384 for wpa_supplicant
c6f2eceb8 FT: Add key management value FT-EAP-SHA384 for hostapd
3d9cd0841 FT: Add support for variable length PMK-R1 receiving in RRB
a7968ea56 FT: FTE generation for SHA384-based AKM on AP
06f128660 FT: FTE generation for SHA384-based AKM on STA
9a33737a0 FT: FTE parsing for SHA384-based AKM
8c2715b35 FT: Connection settings for SHA384-based AKM
994eac7e6 FT: PMK-R0 derivation using SHA384-based AKM
1655e81c9 FT: PMKID derivation using SHA384-based AKM
40a2eb116 FT: PTK derivation using SHA384-based AKM
7880a6a2b FT: PMK-R1 derivation using SHA384-based AKM
b327026a7 FT: FTE MIC calculation using SHA384-based AKM
c49a9d6b9 FT: EAPOL-Key MIC calculation using SHA384-based AKM
9f12271b2 FT: XXKey derivation for SHA384-based AKM
a3e18dbb6 FT: Support variable length keys
c22bb5bba FT: SHA384-based AKM in RSNE processing
3a11c69e0 EAP-TLS: Derive Session-Id using TLS-Exporter when TLS v1.3 is used
2d26434ac EAP-TLS server: Fix EMSK derivation with TLS v1.3
f8aed720e EAP-TLS: Update key derivation label per draft-ietf-emu-eap-tls13-00
20c2ea412 wpa_supplicant: Fix parsing of max_oper_chwidth
8fd29a043 mesh: Fix crash with CONFIG_TAXONOMY enabled
8518326b2 Add some missing driver flags strings
b375b04b6 WNM: Fix time_zone advertisement without time_zone configuration
0928b6294 nl80211: Allow mesh interface to send channel switch request
f5a602168 HS 2.0: Allow OSEN connection to be used in an RSN BSS
8d660a4ba HS 2.0: Allow OSEN connection to be enabled in an RSN BSS
0fe3ede0a macsec_qca: Fix byte order of TX SCI port
22ac3dfeb EAP-pwd: Mask timing of PWE derivation
b8acd5011 EAP-pwd peer: Add SHA512 hash based salt mechanism
fb3675848 EAP-pwd peer: Add SHA256 hash based salt mechanism
7ef8adc7d EAP-pwd peer: Add SHA-1 hash based salt mechanism
7280723fb EAP-pwd peer: Check for unexpected state for ID exchange
44136f6ca EAP-pwd peer: Add support for salted password databases
0744e303c EAP-pwd peer: Move PWE derivation to commit exchange
d52ead3db EAP-pwd server: Add support for salted password databases
a8712ce5b EAP-pwd: Pre-processing method definitions from RFC 8146
2a5c29188 EAP-pwd: Move EC group initialization to earlier step
b829e4b69 Add a QCA vendor command attribute to enable/disable GTX
833bb2ab1 FT: Disable PMKSA caching with FT
91db940ff fst: Fix compile error in fst_ctrl_aux.h with C++ compilers
9be19d0b9 SAE: Add support for using the optional Password Identifier
d6a65a83f mesh: Register msg_ctx for hostapd/AP code
4e47eec5a Add QCA NAN vendor attributes to provide IPv6 information
997312964 wolfSSL: Fix crypto_bignum_rshift() wrapper
4b2e03c42 wolfSSL: DH initialization to call TEST_FAIL() for error tests
2b01270c8 wolfSSL: Fix ECDH set peer to use the index when importing point
ffff7f709 OWE: Mark connection failed in the unlikely no-bss-entry case
0cc41ee63 Move wpa_supplicant_event() EVENT_ASSOC_REJECT handling into a function
bda9c0859 hostapd: Fix CHAN_SWITCH command for VHT20 and VHT40
16d5c9637 OWE: Get the bss from bssid of assoc_reject to try for next group
c4dd3afa3 ACS: Avoid invalid interference factor when survey channel time is zero
6965705bf Fix style issues in qca-vendor.h
7d66e7c40 Define new QCA feature flag for self managed regulatory support
38f60142d Define test config vendor attribute for Tx beamformee configuration
d109aa6ca SAE: Flush PMKSA if an assoc reject without timeout is received
46053a4c9 wext: Avoid gcc warnings on restricted pointer aliasing
ce2a9644b Silence a gcc warning on switch statement fallthrough
d267bdf9b Silence new gcc warnings on switch statement fallthroughs
60a5737e8 FT: Clear SME FT data on disassoc
98b806b03 Add QCA vendor attribute for spectral hardware generation
d57e06170 wpa_supplicant: Make channel switch event available for non-AP builds
d7f038ded wpa_supplicant: Add ieee80211ac information in STATUS
ab35793ec wolfSSL: Fix EAP-FAST key derivation
71faf06cb wolfSSL: Do not free cert store after setting it
b7f5b0ec6 wolfSSL: Fix OCSP ifdefs
ca620a364 wolfSSL: Fix altSubjectName handling
7be462084 wolfSSL: Use defines from wolfssl/options.h
312212174 wolfSSL: Remove aes-omac1.o from hostapd build
385dd7189 wolfSSL: Use wolfSSL memory allocation in dh5_init()
6590d8466 wolfSSL: Load certificates using 'chain' APIs
fc5e88e3e wolfSSL: Changes for memory allocation failure testing
06657d316 wolfSSL: Fix crypto_hash_init() memory clearing
d39605710 wolfSSL: Fix crypto_ec_point_y_sqr()
e3501ac18 wolfSSL: Fix crypto_ec_point_solve_y_coord()
187ad3a30 wolfSSL: Add crypto_ecdh_*()
3d2f638d6 wolfSSL: Use new digest namespace
847665ebe wolfSSL: Fix conditional EAP-FAST compilation issue
d501c27cf EAP-TLS server: Disable TLS v1.3 by default
e8a7af9a3 EAP-TLS peer: Disable TLS v1.3 by default
bbbc7e801 EAP-TLS: Extend TLS version config to allow TLS v1.3 to be disabled
53b34578f OpenSSL: Move server vs. client information into connection data
0de820b33 EAP-TLS peer: MSK/EMSK derivation with TLS v1.3
1854981c7 EAP-TLS peer: Allow NewSessionTicket after Client Finished with TLS v1.3
6dd98483e EAP-TLS peer: Support fragmentation of last message
a80423b52 EAP-TLS server: MSK/EMSK derivation with TLS v1.3
bac1bdba3 EAP-TLS peer: Determine whether TLS v1.3 or newer is used
fe7b06c5e EAP-TLS server: Determine whether TLS v1.3 or newer is used
c26ac1895 Mark eap_server_tls_derive_key() label argument const
34c6949fb EAP-TLS server: Remove trailing whitespace
4a576c473 OpenSSL: Terminate TLS handshake if ClientHello cannot be generated
2439714f9 DPP: Fix testing code for invalid keys with OpenSSL 1.1.1
c456e6e3f HS 2.0: Terms and Conditions server and management
42f416916 HS 2.0: Update server SQL DB initialization to cover new fields
5bd5eb54d HS 2.0: Update server instructions for Ubuntu 16.04
04ee197f8 HS 2.0: Maintain a database of pending T&C acceptance sessions
625a8c86b wpa_cli: Indicate HS20-T-C-ACCEPTANCE to action scripts
6b21df0bb Add QCA vendor command/attr to filter frames from other BSSs
452603809 HS 2.0: Terms and Conditions testing feature in authentication server
f456940ef HS 2.0: CoA-Request processing for Terms and Conditions filtering
d239ab396 DFS: Mark channels required DFS based on reg-domain info from the driver
7fc6a024f HS 2.0: Process received Terms and Conditions Acceptance notification
8760b9848 HS 2.0: Send Terms and Conditions Acceptance notification
6cb8f4f38 HS 2.0: Terms and Conditions attributes in Access-Request messages
1952b626b hostapd: Add ctrl iface indications for WDS STA interface
2598e6930 FILS: Enable SHA256 KDF even without PMF/SAE in the build
1baa130bd nl80211: Print NL80211_CMD_SET_POWER_SAVE errors in debug log
72b6e5d1e Do not remove CCMP group cipher if any CCMP/GCMP cipher is enabled
c4315e662 AP: Handle AP initalization failure in async flow
86c998d37 FT: Add FT auth algorithm to connect params when roaming
3dc3afe29 FT: Add MDE to assoc request IEs in connect params
b55c623e4 Make CENTER_FRQ1 available independently in SIGNAL_POLL
4204669c6 HS 2.0: Add Roaming Consortium Selection element into AssocReq
6311547e7 HS 2.0: Add Roaming Consortium Selection network profile parameter
5b7695275 HS 2.0: Use roaming_consortiums list to match OIs for access
2e88032f1 HS 2.0: OSU client to send HomeSP/RoamingConsortiumOI to wpa_supplicant
909a948b0 HS 2.0: Add a new cred block parameter roaming_consortiums
9b6f93e4d HS 2.0: Document credential parameter required_roaming_consortium
67cca3464 HS 2.0: Copy Roaming Consortium OI from (Re)AssocReq to Access-Request
0e450db28 HS 2.0: Allow configuration of operator icons
6a8a04d74 HS 2.0: Add fetching of Operator Icon Metadata ANQP-element
37547ad63 wpa_supplicant: Increase authentication timeout if CAC is started
2dd5fbbff wpa_supplicant: Rename wpas_event_*() to wpas_ap_event_*()
f875da044 nl80211: Add MLME auth work-around for -EEXIST errno
4449efeb1 Fix building nt_password_hash with gnutls
5ecdf06c8 DPP: Fix build with LibreSSL v2.5
4b603f01d DPP: Fix X509_ALGOR_get0() use with LibreSSL
dee566d98 OpenSSL: Skip SSL_OP_NO_TLSv1_3 if not defined to fix LibreSSL build
ba3658cff Fix build with LibreSSL 2.7
a2ab37308 Fix sae_password documentation in wpa_supplicant to refer correct field
d5906fbb7 mesh: Properly handle sae_password
d6d7debb5 Fix wpa_supplicant build with CONFIG_NO_WPA
852b2f273 SAE: Only allow SAE AKMP for PMKSA caching attempts
06b1a1043 SAE: Fix default PMK configuration for PMKSA caching case
8e402d165 WPA: Fix a typo in a debug message
1bd131105 Clear pmk_len more consistently for extra protection
66dbc8d9c Add more debug prints for wpa_sm_set_pmk() calls
26e0ada47 Define new test config attribute for HE LTF configuration
4d6eb9f2e Fix hostapd wmm_enabled setup on config reload path
a6509e850 FT: Add session_timeout to push/resp
3cb286ca4 FT: Add set/get session_timeout callback functions
13f118dc5 Convert STA session_timeout to os_reltime
069b4e304 FT: Include identity and radius_cui in pull/resp frames
9fbe292ff FT: Move wpa_ft_rrb_build_r0() special case to caller
8c345234f FT: Add set/get identity/radius_cui callback functions
17a8a9893 Fix potential memory leak with identity/radius_cui
17010c38d FT: Add IEEE VLAN support (including tagged VLANs)
47a039b01 FT: Add set_vlan()/get_vlan() callback functions
3a3e28320 FT: Add expiration to PMK-R0 and PMK-R1 cache
09211c989 FT: Use dl_list for PMK-R0/R1 cache
89dea17e1 FT: Add helper function for FILS key storing
83fe4bd3b FT: Convert r0_key_lifetime to seconds
ee2c6bb5e Remove CONFIG_IEEE80211R_AP=y build option from wpa_supplicant
c6b5b9a33 hostapd: Add more authentication error case debugging
e2fc13d0a hostapd: Add logging around Michael MIC related failures
e7525a295 hostapd: Add send_auth_reply() caller info in debug logging
d89edb611 wpa_supplicant: Don't reply to EAPOL if pkt_type is PACKET_OTHERHOST
8fb2b3573 Clean up setting of iface->p2p_mgmt flag
d53401c54 dbus: Redirect signal processing to the management device if present
e48021276 dbus: Add FILS to global capabilities
208263c01 Add config information related to MACsec
e360010c3 dbus: Add mesh to global capabilities
9f917339c tests: Fix clearing of IGTK PN in handshake retransmit test functions
45f7574d3 Propagate the EAP method error code
77a020a11 wpa_supplicant: Fix auth failure when the MAC is updated externally
2ff9696d3 Add definitions for RADIUS attributes standardised in RFC 7055
c17915462 nl80211: Add DFS offload support using upstream nl80211 definitions
1841086ad Sync with mac80211-next.git include/uapi/linux/nl80211.h
7cfe2f001 Add SAR V2 power selection capability (QCA vendor attributes)
7e1d3ee96 Add hostapd.conf venue_url to set Venue URL ANQP-element
64624f31c OWE: Fix CONFIG_OWE=y build without CONFIG_IEEE80211R=y
d555c39c4 FILS: Fix CONFIG_FILS=y build without CONFIG_IEEE80211R=y
f6de72534 Add NOTE control interface command for hostapd
2f3738781 FILS: Add more complete support for FT-FILS use cases
4ddca8142 FT: Derive PMK-R1 locally if requested PMKR0Name is found
1dc0945cc FT: Do not send PMK-R1 pull request to own R0KH address
1778f1e9a SAE: Fix PTK derivation to use KDF-SHA256
9d94e4bb6 SAE: Fix PMKID in EAPOL-Key msg 1/4
a03f9d17e SAE: Fix FT-SAE key derivation for a case where PMKID in msg 1/4 matches
0fa669bca Fix a resource leak on hostapd maclist parsing error path
10949e248 Do not disable 40 MHz based on co-ex report with matching primary channel
8cbd9c3ed Support multiple 20/40 BSS Intolerant Channel Report elements
8127a0ac0 Ignore intra-BSS 20/40 BSS Coexistence Management from not-associated STA
8aa599d45 Add more debug prints for 20/40 BSS Coexistence Management frame Rx
d58c3bd8b hostapd: Ignore LOW_ACK event for co-operative steering clients
ec2b5173c Make STA opmode change event available to upper layers
e8ada1600 nl80211: Add support for STA opmode change events
72123a84c hostapd: Add last_ack_rssi into ctrl iface cmd STA
2df73f52e Add hostapd_cli poll_sta command
f5701cc66 OWE: Clean up pointer check in a testing code path
ebe61e564 Sync with mac80211-next.git include/uapi/linux/nl80211.h
8179ae3a2 DPP: Support retrieving of configurator's private key
4bc801ab4 SAE: Fix EAPOL-Key integrity and key-wrap algorithm selection
d74963d41 DPP: Extend dpp_test 89 functionality to transmit side
f8bfc9cbd Use correct WPA_ALG_* values to compare for enum wpa_alg
cce16e43f mka: Mark ieee802_1x_kay_create_mka() ckn and cak arguments const
22151b111 wpa_supplicant: Fix memory leaks in ieee802_1x_create_preshared_mka()
3a52f6b38 mka: Do not print contents of SAK to debug log
77977b3d5 mka: Detect duplicate MAC addresses during key server election
5762855ab mka: Loss of live peers to result in connect PENDING not AUTHENTICATED
8fb546d8e mka: Ignore MACsec SAK Use Old Key parameter if we don't have our old key
b54b53e64 mka: When matching CKNs ensure that lengths are identical
fec03f983 Add support for wolfSSL cryptographic library
92eb00aec Extend ACL check for Probe Request frames
01542e651 Add new WiFi test config attributes to configure BA params
ba6080441 Add new WiFi test config attribute to allow WEP/TKIP in HE
c05f96a38 FILS: Check kde more consistently to avoid static analyzer warnings
087474512 SAE: Debug print group support in the crypto library
f5e0a3324 SAE: Fix potential infinite loop in mismatching PMK case on AP
427729ee6 Reject eap_server_erp hostapd.conf parameter without CONFIG_ERP=y
c54cc8bb1 BoringSSL: Set appropriate sigalgs for Suite B RSA 3K cases
aa6de8e6b BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate sigalgs
7a47f34b1 BoringSSL: Map OpenSSL SUITEB192 cipher into appropriate parameters
355250234 OpenSSL: Replace SSL_set1_curves_list() with SSL_set1_curves()
4ab0f11b8 Allow HT40 on 5 GHz channels 165 and 169
299d21e8e nl80211: Use the new NL80211_MFP_OPTIONAL option
b8e88d357 wpa_supplicant: Handle port authorized event
a8c45d47d nl80211: Handle port authorized event
0a20bd7d9 driver: Add port authorized event
05fc7c68f nl80211: Add API to set the PMK to the driver
0ff08f963 nl80211: Check 4-way handshake offload support
730c5a1d0 nl80211: Support passing PSK on connect
14dcb22a5 wpa_passphrase: Include $(LIBS) for linking
4b07484c3 DPP: Do not include common/dpp.h without CONFIG_DPP=y
9ec0dfa31 Define host_to_le64() for Windows builds
6e3726c09 Fix a typo in disassoc_low_ack documentation
19e20c14f Add ap_isolate configuration option for wpa_supplicant AP mode
a4016163e Extend APF interface for read/write and enable/disable ops
b2e4074ca OpenSSL: Fix EAP-FAST with OpenSSL 1.1.1-pre1
a22e235fd OWE: Add testing RSNE for OWE assoc response with driver SME/MLME
aca4d84e3 DPP: Use wildcard BSSID in GAS query frames
0887215d9 nl80211: Do not try to add too large NL80211_ATTR_PMK for set/del PMKSA
e7f6e6ee1 nl80211: Print NL80211_CMD_{SET,DEL}_PMKSA failures in debug log
3988046de hostapd: Dynamic MAC ACL management over control interface
6a252ece2 DPP: Fix GAS query removal race condition on DPP_STOP_LISTEN
3b50f8a46 DPP: PKEX initiation on other bands
659ac96d7 ieee802_11_mgmt: Handle frame info more consistently
458d8984d SAE: Reject request with mismatching PMKID (no PMKSA cache entry)
5ac434642 Define new QCA vendor sub command for wifi test configuration
70e190137 DPP: Require use of PMF for DPP AKM
79ce2d519 OWE: Rename function to match use (driver-SME/MLME)
759da93a8 OWE: Check for ECDH availability before use (driver-SME/MLME)
af65ef28f OWE: Add RSNE when not using PMKSA caching (driver-SME/MLME)
5850cba38 OWE: Support DH groups 20 and 21 with driver-SME/MLME
f9854c183 hostapd: Fix wpa_psk_file support for FT-PSK
3bd35b681 wpa_supplicant: Fix parsing errors on additional config file
9c5fe742a wpa_supplicant: Free config only if it was allocated in same call
9f8d459d4 OWE: Fix association IEs for transition mode open AP connection
c6096c6ee wpa_cli: Enable add/remove/get vendor elements without P2P
d1e3d40d0 common: Fix the description of wpa_ctrl_request() function
82424732a Fix compiler issue with CONFIG_TESTING_OPTIONS
ebf404373 D-Bus: Report error on starting P2P find
66b9f3ca8 wpa_cli: Fix cred_fields[] declaration
5ff39c138 SAE: Support external authentication offload for driver-SME cases
40a68f338 nl80211: Create a netlink socket handle for the Connect interface
ba71cb821 nl80211: Introduce the interface for external authentication
8678b14fa Add new QCA vendor commands for thermal shutdown
99576f6f3 Sync with mac80211-next.git include/uapi/linux/nl80211.h
cd483be25 OWE: Use PMKSA caching if available with driver AP MLME
a4668c681 OWE: Handle unsupported finite cyclic group with driver MLME
f811cc83b atheros: Send correct SSID length to the driver
e005725a6 nl80211: Add DPP and OWE AKM selectors to CONNECT/ASSOC request
2cc2a0a7e Extend QCA vendor NDP params to support schedule update indication
a7769f6da Update QCA vendor commands to match ASOP
18e3e9c6e OWE: Transition mode with non-AP-MLME
92b6e0c58 hostapd: Send broadcast Public Action frame with wildcard BSSID address
fe3f81e6a DPP: Update PKEX role-specific points
b4cd8b7e1 Add QCA vendor command and attributes for MSDU queue depth threshold
703470bfa FILS: Fix extended capability bit setting for FILS in AP mode
9e834fc64 EAP-SIM/AKA: Separate identity for MK derivation
b6b5e3554 DPP: Get rid of compiler warnings on signed/unsigned comparison
630ea1334 DPP: Track M.x/N.x/L.x availability for ke derivation
c1790a5ff OWE: Allow station in transition mode to connect to an open BSS
c103c0b51 Extend NUD Stats to collect the data packet statistics
5d5ee699a Copy WLAN-Reason-Code value from Access-Reject to Deauthentication
f75ed556c RADIUS: Add WLAN-Reason-Code attribute to Access-Reject
727e9aacb HS 2.0: Set appropriate permission(s) for cert file/folders on Android
feba5848b Replace RSNE group key management mismatch status/reason codes
9cc8303d3 Add QCA vendor command to get the WLAN MAC information
fd9209549 DPP: Report reception of Config Request to upper layers
80f71b71d DPP: Authentication exchange retries and channel iteration in hostapd
c5cc7a59a Report offchannel RX frame frequency to hostapd
b7ed94834 GnuTLS: Add option to build with libnettle instead of libgcrypt
c36d82241 GnuTLS: Suite B validation
dd227e67a GnuTLS: Add support for disabling TLS versions
5d292fcfb GnuTLS: Implement tls_get_cipher()
5791d2117 GnuTLS: Make debug prints clearer for cert/key parsing
622d4d49e GnuTLS: Add TEST_FAIL() to crypto routines for testing purposes
f8697e810 GnuTLS: Implement tls_get_version()
133f8d09f GnuTLS: Implement HMAC functions using libgcrypt
85c12a62e GnuTLS: Implement sha{256,384,512}_vector() using libgcrypt
cc3e7bfc3 GnuTLS: Use a helper function for hash functions
2cb40e9f4 OWE: Try all supported DH groups automatically on STA
02b38d0ad Fix MFP-enabled test for disallowed TKIP
ba3d435fe SAE: Add option to require MFP for SAE associations
c4fc7e31c SAE: Set special Sc value when moving to Accepted state
abcbd0604 SAE: Add Rc variable and peer send-confirm validation
9249afc8e SAE: Print state changes in debug log
d8b841eba SAE: Make dot11RSNASAESync configurable
9596a7565 PAE: Remove OpenSSL header dependency
04b1bcc5f EAP-pwd: Use abstract crypto API
0c3d49afd EAP-EKE: Use abstract crypto API
c335507c8 Enhance crypto abstract API for DH
0e02f2a9f crypto: Implement new crypto API functions for DH
eac084cb3 OpenSSL: Implement new crypto API functions
23ff5d73d Enhance crypto abstract API
441e8bfbb Fix copy-paste errors in function comments
47e966aba DH: Remove trailing whitespace
3546ef510 EAP-pwd: Remove trailing empty line
61536b5f4 Add new QCA vendor attribute for getting preferred channel
1a2cb20d5 Fix couple of QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS comments
2fca2d214 Vendor parameter for forcing RSNE override
12bce14b1 wpa_cli: Add completion for get/set cred commands
6240424a7 dbus: Add FILS key mgmt values into BSS security properties
2d942ec4a Define a QCA vendor command to retrieve SAR Power limits
d98038bb0 FILS: Driver configuration to disable/enable FILS features
af832aa99 hostapd: Add average channel utilization in STATUS
802c0fd0c hostapd: Update BSS load update period dynamically
778d87054 Fix error handling in bss_load_update_period parser
dff5ab97e hostapd_cli: Add dpp_listen and dpp_stop_listen
5f30b69cd OWE: Allow DH Parameters element overriding with driver SME
04ded82ef OWE: Fix error case handling with drivers that implement AP SME
c23e87d0d OpenSSL: Replace EVP_PKEY_paramgen() with EC_KEY_new_by_curve_name()
7641d485d BoringSSL: Use EC_KEY_new_by_curve_name() to simplify implementation
a5da39607 Revert "BoringSSL: Add DPP special cases regardless of claimed version number"
0f3084472 Revert "BoringSSL: Define RSA_bits() helper"
a2c442be2 OpenSSL: Allow cipher list to be overridden for tls_suiteb=1 case
e5c7c2f28 Fix ap-mgmt-fuzzer build
63942cf0f OpenSSL: Avoid SSL*_use_default_passwd_cb()
149143e31 OpenSSL: Remove unnecessary os_strdup() from password callback
b65353a76 Android: Set CONFIG_NO_RANDOM_POOL=y
ff28df739 Android: Move hostapd to vendor partition
9b0de99fa Add new QCA vendor attribute for WLAN Latency Module (WLM)
055cd3978 tests: DPP P-256 test vectors
f55269753 DPP: Allow protocol key to be overridden for testing purposes
afd3cf14a WPS: Add GCMP-256 and CCMP-256 cipher options on Enrollee
5f481b6fd WPS: Check BSS table against current BSSID if credential does not match
402c8e00b WPS: Map GCMP-256 and CCMP-256 to AES encryption type
a2660890a WPS: Allow WPS to be enabled in CCMP-256 and GCMP-256 only cases
163fc3d56 DPP: Indicate to upper layers whether mutual authentication was used
34603767b DPP: Extend protocol testing to allow exchange termination
ed2c493e9 DPP: Change Authentication Response retry time to 1 second
963d3149a nl80211: Fix NL80211_MESHCONF_AUTO_OPEN_PLINKS encoding
a2426829c nl80211: Fix NL80211_ATTR_SMPS_MODE encoding
f0a383a98 DPP: Extend dpp_test with invalid E-Nonce in Config Req
55c6c8585 DPP: Extend dpp_test with invalid Transaction ID in Peer Disc Req
364039d22 Add QCA vendor command and attributes for RROP
0e19300d5 nl80211: Use consistent "0x" prefix for the cookie values
fa5c90749 DPP: Call wpas_dpp_stop() from wpas_dpp_deinit()
1866dfb52 DPP: Stop pending GAS client operation on DPP_STOP_LISTEN
f981ce729 DPP: Do not continue if public key hash derivation fails
8a37d54ea DPP: Fix memory leak on dpp_auth_build_conf() error paths
281f480c5 JSON: Fix a memory leak on an error path
622934128 DPP: Fix a memory leak on an error path
ed62d4011 DPP: Deinit PKEX instance on DPP_STOP_LISTEN
634a130a5 DPP: Clear authentication instance on configuration completion in AP
d3cb7ebe1 DPP: Do not process dpp_auth_ok_on_ack multiple times
dc2f24f1b DPP: Fix compilation without CONFIG_TESTING_OPTIONS=y
82feacced DPP: Ignore GAS server status callback for unknown response
a2588be82 DPP: Add DPP_CONFIGURATOR_SIGN support to hostapd
7eb6bfb45 DPP: Move hostapd Configurator/bootstrap data into global context
73f21929a DPP: Auto-generate Initiator bootstrapping info if needed
725a953ae nl80211: Filter global events based on wiphy
0bd7f104c hostapd: Add supported rate information into STATUS and STA
1489fcf87 FILS: Do not leave error value in left counter
beae255a7 WPA: Check wpa_eapol_key_mic() result on TX
acc555f9e DPP: Allow PKEX x/X and y/Y keypairs to be overridden
d722c50d0 DPP: Print more interim EC_POINT results into debug log
2bdc47a94 DPP: Allow PKEX own/peer MAC addresses to be overridden
af4103e5e DPP: Provide peer_mac to PKEX Initiator through function argument
918a2ac40 DPP: Work around missing EVP_PKEY_CTX_set_ec_param_enc()
3ca4be1ea DPP: Remove compiler warnings about signed/unsigned comparisons
116454f46 DPP: Fix error return value in dpp_auth_conf_rx()
63dc0f9c4 hostapd: Disassoc STA without WPA/RSN IE if AP proto is WPA/RSN
cc79e06f0 hostapd: Add wpa_msg_ctrl() to report Probe Request frames from STA
0db637ca8 DPP: Fix number of Authentication Request retry cases
921f5acd1 DPP: Take response wait time into account for init retries
248264c62 DPP: Stop Authentication Request attempts if no response after ACK
e3a5882b3 DPP: Add SAE credential support to Configurator
5dd745b73 DPP: Add akm=sae and akm=psk+sae support in Enrollee role
a44467395 DPP: Protocol testing capability to send invalid I-Nonce in Auth Req
00d2d13db DPP: Retry PKEX Exchange Request frame up to five times
ab0375508 TDLS: Add testing capability to send TPK M2 twice
4b8de0c92 DPP: Protocol testing for invalid Peer Discovery Req/Resp values
f9cf7d03f DPP: Protocol testing for invalid Config Attrib Object value
3f35ec2dc DPP: Protocol testing for invalid DPP Status value
9efa53149 DPP: Use helper functions to build Bootstrap Key Hash attributes
acdf703d5 DPP: Replace custom undefined attr with DPP Status in after-wrapped data
56f24d1da DPP: Use a helper function to build DPP Status attribute
65ecce87f DPP: Protocol testing for writing invalid I/R Bootstrap Key Hash
b6b4226bd DPP: Protocol testing capability to generate invalid Protocol Key
c6eb3e34b Stronger GTK derivation routine
94619905c DPP: Fix dpp_test_gen_invalid_key() with BoringSSL
746c1792a DPP: Build bootstrapping key DER encoding using custom routine
f2d27ef94 DPP: Use a helper function to DER encode bootstrapping key
c1564149a BoringSSL: Add AES support with 192-bit keys
edd72f55f OpenSSL: Debug message if requested AES key length is not supported
5548453a2 BoringSSL: Add DPP special cases regardless of claimed version number
f29761297 BoringSSL: Implement crypto_ecdh_init()
7721fe082 BoringSSL: Comment out SSL_set_default_passwd_cb*() calls
b9dc63c26 BoringSSL: Comment out SSL_set1_sigalgs_list() call
3cfbd3b0f BoringSSL: Define RSA_bits() helper
27781c0ab Allow group cipher selection to be overridden
af6614ca1 Fix block comment style in QCA vendor attribute definition
2115603a4 Add QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING
27987b67f Add new QCA vendor attribute for LL stats
41db74cf7 atheros: Process SAE authentication frames using EVENT_RX_MGMT
3d9dd4b77 atheros: Generate EVENT_TX_STATUS events for management frames
f5b74b966 common: Avoid conflict with __bitwise macro from linux/types.h
4109555ef DPP: Fix compiler warning of testing code
1d624a070 Reject PMK-to-PTK derivation with unsupported cipher
762fb4f06 DPP: Testing capability to send unexpected Authentication Response
03abb6b54 DPP: Reject unexpected Req/Resp message based on Auth/PKEX role
95b0104a3 DPP: Retransmit DPP Authentication Response frame if it is not ACKed
c1d377396 DPP: Stop authentication exchange of DPP_STOP_LISTEN
d1f082644 DPP: Allowed initiator to indicate either role
f97ace34c DPP: Support multiple channels for initiating DPP Authentication
de0298618 DPP: Share a helper function for PKEX final steps
a306ed5a5 DPP: Protocol testing to allow missing attributes in peer discovery
1fafdf112 DPP: Add DPP_LISTEN and DPP_STOP_LISTEN to hostapd
fbfceef3a Add QCA vendor commands for spectral scan
bb9808fa9 P2P: Continue P2P_WAIT_PEER_(IDLE/CONNECT) sequence on a listen cancel
37ed3254d P2P: ACS offload for the autonomous GO
962b8fcf4 Add new QCA vendor attributes for MAC counters
af7f10fcd DPP: Protocol testing for invalid Config Resp attribute values
8c99e6264 DPP: Report Config Request/Response failure reasons on control interface
f411ad1b8 DPP: Protocol testing to remove attributes from Config Req/Resp
7e0ebe21b DPP: Protocol testing - invalid I/R-Auth value in PKEX Commit-Reveal
89d0bf678 DPP: Protocol testing - invalid Bootstrap Key value in PKEX Commit-Reveal
f31ef96dc DPP: Protocol testing - invalid Status value in PKEX Exchange Response
d05c82c4d DPP: Move PKEX z derivation on Responder to earlier phase
578c9ea1a DPP: Fix a typo in a debug print
5f5fff436 DPP: Explicitly check that PKEX Qr is not the point-at-infinity
29ab69e4b DPP: PKEX counter t
039b8e736 DPP: Terminate PKEX exchange on detection of a mismatching code
fc0efa2a1 DPP: Use dpp_bn2bin_pad() helper to simplify code
e0247e798 DPP: PKEX and STATUS_BAD_GROUP
2265353a4 DPP: Remove obsolete TODO comment on discovery object
fe12ae777 Fix Status Code in TKIP countermeasures case
1cfcbd32a DPP: Testing capability to generate invalid PKEX encrypted key (M and N)
d7e7b7122 DPP: Report PKEX failure reasons over control interface
61f9f27f8 DPP: Extend protocol testing to cover missing attributes in PKEX
b3e4cc5cb DPP: Move PKEX Commit-Reveal Response building to a helper function
b0626c2a6 DPP: Move PKEX Commit-Reveal Request building to a helper function
a5c3b41b2 DPP: Move PKEX Exchange Response building to a helper function
60b9dd86f DPP: Fix couple of typos in debug messages
06f2df069 DPP: Fix hostapd control interface events for initiator case
219d4c9fc DPP: Report possible PKEX code mismatch in control interface
69d8d029f DPP: Enable PMF when adding wpa_supplicant network profile
0c3bc1be0 Fix test build breakage when not compiling with ieee80211w support
ea4ace9c7 hostapd: Add max_txpower into STATUS command
bf6c65afc hostapd: Add Beacon interval and DTIM period into STATUS command
c7ae2b310 hostapd: Add HT/VHT capability info into STATUS command
1f91a8bde hostapd: Add HT/VHT capability info into STA command
65f9db6bc hostapd: Add extended capabilities into STA command
d1f3a8144 hostapd: Add [HT] flag into STA command
ba72b4b12 hostapd: Add Min/Max Transmit Power Capability into STA command
33c8bbd8c OWE: Add AP mode handling of OWE with drivers that implement SME
28d126413 Check hostapd current_mode before dereferencing it in additional places
348c93847 AP-side workaround for WNM-Sleep Mode GTK/IGTK reinstallation issues
3f5a1860a wpa_auth: Deplete group rekey eloop handler for strict rekeying
92662fb28 Allow forcing group rekeying for testing purposes
d27092069 DPP: Negotiation channel change request from Initiator
b7dddab7b DPP: Allow testing override values to be cleared
e85b66012 DPP: Add DPP Status attribute into Peer Discovery Response
19ef4289c DPP: Process Authentication Confirm failure cases
7d917ab04 DPP: Send Authentication Confirm failure reports
978bc3f2a DPP: Auth Resp/Conf incorrect attribute values for protocol testing
9b5111203 DPP: Allow Responder to decide not to use mutual authentication
dcdaeab79 DPP: Report Auth Conf failures in control interface
f9c7d7702 DPP: Omission of Auth Conf attributes for protocol testing
26806abe8 DPP: Report invalid messages and failure conditions in control interface
ce9acce00 DPP: Omission of Auth Resp attributes for protocol testing
a03406dbe DPP: Move Authentication Response building into a separate function
0e7cb8c6c DPP: Omission of Auth Req attributes for protocol testing
606a8e8d5 DPP: Move Authentication Request building into a separate function
3749ad0e1 DPP: Explicitly check and reject 0x00 and 0x03 I/R-capab role
af48810ba DPP: Report transmitted messages as control interface events
a70739349 DPP: Report received messages as control interface events
27fefbbb7 DPP: Remove unnecessary Wrapped Data checks from callers
0c881807b DPP: Verify that Wrapped Data attribute is the last one in the message
60239f60a DPP: Protocol testing framework
7ed5337d8 Fix a typo in a debug message
5f7c9e50f EAP server: Add event messages for more EAP states
a68e0d869 Fix a typo in a comment (the variable is ptk, not pkt)
de41b960b wpa_supplicant: Increase UDP control interface RX buffer
74e55b658 Fix test build breakage when not compiling with mesh support
3d0fb9558 WNM: Ignore BSS Transition Management frames in bss_transition=0 case
114f2830d WNM: Ignore WNM-Sleep Mode Request in wnm_sleep_mode=0 case

Bug: 111131936
Test: Device boots up and connects to wifi networks.
Test: Able to turn on/off softap.
Test: Sent for regression tests (b/111209415).
Change-Id: I85cbeeee6a4ff52a5e23f8917b15633c896c0cf3
Signed-off-by: Roshan Pius <rpius@google.com>
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index a723201..507b7ca 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -417,6 +417,13 @@
 	struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
 	u8 *plain, size_t *plain_len);
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey);
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len);
+
 /**
  * crypto_global_init - Initialize crypto wrapper
  *
@@ -529,6 +536,14 @@
 			 u8 *buf, size_t buflen, size_t padlen);
 
 /**
+ * crypto_bignum_rand - Create a random number in range of modulus
+ * @r: Bignum; set to a random value
+ * @m: Bignum; modulus
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
+
+/**
  * crypto_bignum_add - c = a + b
  * @a: Bignum
  * @b: Bignum
@@ -610,6 +625,16 @@
 			 struct crypto_bignum *d);
 
 /**
+ * crypto_bignum_rshift - r = a >> n
+ * @a: Bignum
+ * @n: Number of bits
+ * @r: Bignum; used to store the result of a >> n
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r);
+
+/**
  * crypto_bignum_cmp - Compare two bignums
  * @a: Bignum
  * @b: Bignum
@@ -640,6 +665,13 @@
 int crypto_bignum_is_one(const struct crypto_bignum *a);
 
 /**
+ * crypto_bignum_is_odd - Is the given bignum odd
+ * @a: Bignum
+ * Returns: 1 if @a is odd or 0 if not
+ */
+int crypto_bignum_is_odd(const struct crypto_bignum *a);
+
+/**
  * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
  * @a: Bignum
  * @p: Bignum
@@ -671,6 +703,14 @@
 void crypto_ec_deinit(struct crypto_ec *e);
 
 /**
+ * crypto_ec_cofactor - Set the cofactor into the big number
+ * @e: EC context from crypto_ec_init()
+ * @cofactor: Cofactor of curve.
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor);
+
+/**
  * crypto_ec_prime_len - Get length of the prime in octets
  * @e: EC context from crypto_ec_init()
  * Returns: Length of the prime defining the group
@@ -685,6 +725,13 @@
 size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
 
 /**
+ * crypto_ec_order_len - Get length of the order in octets
+ * @e: EC context from crypto_ec_init()
+ * Returns: Length of the order defining the group
+ */
+size_t crypto_ec_order_len(struct crypto_ec *e);
+
+/**
  * crypto_ec_get_prime - Get prime defining an EC group
  * @e: EC context from crypto_ec_init()
  * Returns: Prime (bignum) defining the group
@@ -721,6 +768,16 @@
 void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
 
 /**
+ * crypto_ec_point_x - Copies the x-ordinate point into big number
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point data
+ * @x: Big number to set to the copy of x-ordinate
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x);
+
+/**
  * crypto_ec_point_to_bin - Write EC point value as binary data
  * @e: EC context from crypto_ec_init()
  * @p: EC point data from crypto_ec_point_init()
@@ -749,7 +806,7 @@
 						  const u8 *val);
 
 /**
- * crypto_bignum_add - c = a + b
+ * crypto_ec_point_add - c = a + b
  * @e: EC context from crypto_ec_init()
  * @a: Bignum
  * @b: Bignum
@@ -761,7 +818,7 @@
 			struct crypto_ec_point *c);
 
 /**
- * crypto_bignum_mul - res = b * p
+ * crypto_ec_point_mul - res = b * p
  * @e: EC context from crypto_ec_init()
  * @p: EC point
  * @b: Bignum
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 31a580e..7a797b5 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / wrapper functions for libgcrypt
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,26 +10,41 @@
 #include <gcrypt.h>
 
 #include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
 #include "crypto.h"
 
-int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+static int gnutls_digest_vector(int algo, size_t num_elem,
+				const u8 *addr[], const size_t *len, u8 *mac)
 {
 	gcry_md_hd_t hd;
 	unsigned char *p;
 	size_t i;
 
-	if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+	if (TEST_FAIL())
+		return -1;
+
+	if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR)
 		return -1;
 	for (i = 0; i < num_elem; i++)
 		gcry_md_write(hd, addr[i], len[i]);
-	p = gcry_md_read(hd, GCRY_MD_MD4);
+	p = gcry_md_read(hd, algo);
 	if (p)
-		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+		memcpy(mac, p, gcry_md_get_algo_dlen(algo));
 	gcry_md_close(hd);
 	return 0;
 }
 
 
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac);
+}
+
+
 int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 {
 	gcry_cipher_hd_t hd;
@@ -55,44 +70,155 @@
 
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
-	gcry_md_hd_t hd;
-	unsigned char *p;
-	size_t i;
-
-	if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
-		return -1;
-	for (i = 0; i < num_elem; i++)
-		gcry_md_write(hd, addr[i], len[i]);
-	p = gcry_md_read(hd, GCRY_MD_MD5);
-	if (p)
-		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
-	gcry_md_close(hd);
-	return 0;
+	return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac);
 }
 
 
 int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
+	return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac);
+}
+
+
+static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len,
+			      size_t num_elem, const u8 *addr[],
+			      const size_t *len, u8 *mac)
+{
 	gcry_md_hd_t hd;
 	unsigned char *p;
 	size_t i;
 
-	if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+	if (TEST_FAIL())
 		return -1;
+
+	if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+		return -1;
+	if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) {
+		gcry_md_close(hd);
+		return -1;
+	}
 	for (i = 0; i < num_elem; i++)
 		gcry_md_write(hd, addr[i], len[i]);
-	p = gcry_md_read(hd, GCRY_MD_SHA1);
+	p = gcry_md_read(hd, algo);
 	if (p)
-		memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+		memcpy(mac, p, gcry_md_get_algo_dlen(algo));
 	gcry_md_close(hd);
 	return 0;
 }
 
 
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr,
+				  len, mac);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
 void * aes_encrypt_init(const u8 *key, size_t len)
 {
 	gcry_cipher_hd_t hd;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
 	    GPG_ERR_NO_ERROR) {
 		printf("cipher open failed\n");
@@ -127,6 +253,9 @@
 {
 	gcry_cipher_hd_t hd;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
 	    GPG_ERR_NO_ERROR)
 		return NULL;
@@ -154,6 +283,42 @@
 }
 
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
 int crypto_mod_exp(const u8 *base, size_t base_len,
 		   const u8 *power, size_t power_len,
 		   const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
index 9dcabb9..92581ac 100644
--- a/src/crypto/crypto_internal-modexp.c
+++ b/src/crypto/crypto_internal-modexp.c
@@ -13,6 +13,42 @@
 #include "crypto.h"
 
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
 int crypto_mod_exp(const u8 *base, size_t base_len,
 		   const u8 *power, size_t power_len,
 		   const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index b80ad57..259f995 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -694,6 +694,42 @@
 
 #ifdef CONFIG_MODEXP
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
 int crypto_mod_exp(const u8 *base, size_t base_len,
 		   const u8 *power, size_t power_len,
 		   const u8 *modulus, size_t modulus_len,
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
new file mode 100644
index 0000000..4e31bc8
--- /dev/null
+++ b/src/crypto/crypto_nettle.c
@@ -0,0 +1,437 @@
+/*
+ * Wrapper functions for libnettle and libgmp
+ * Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <nettle/nettle-meta.h>
+#include <nettle/des.h>
+#undef des_encrypt
+#include <nettle/hmac.h>
+#include <nettle/aes.h>
+#undef aes_encrypt
+#undef aes_decrypt
+#include <nettle/arcfour.h>
+#include <nettle/bignum.h>
+
+#include "common.h"
+#include "md5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	struct des_ctx ctx;
+	u8 pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	nettle_des_set_key(&ctx, pkey);
+	nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem,
+				const u8 *addr[], const size_t *len, u8 *mac)
+{
+	void *ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = os_malloc(alg->context_size);
+	if (!ctx)
+		return -1;
+	alg->init(ctx);
+	for (i = 0; i < num_elem; i++)
+		alg->update(ctx, len[i], addr[i]);
+	alg->digest(ctx, alg->digest_size, mac);
+	bin_clear_free(ctx, alg->context_size);
+	return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac);
+}
+
+
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac);
+}
+
+
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac);
+}
+
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_md5_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_md5_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_md5_update(&ctx, len[i], addr[i]);
+	hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha1_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha1_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha1_update(&ctx, len[i], addr[i]);
+	hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	       u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha256_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha256_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha256_update(&ctx, len[i], addr[i]);
+	hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha384_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha384_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha384_update(&ctx, len[i], addr[i]);
+	hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	struct hmac_sha512_ctx ctx;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	hmac_sha512_set_key(&ctx, key_len, key);
+	for (i = 0; i < num_elem; i++)
+		hmac_sha512_update(&ctx, len[i], addr[i]);
+	hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac);
+	os_memset(&ctx, 0, sizeof(ctx));
+	return 0;
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	struct aes_ctx *ctx;
+
+	if (TEST_FAIL())
+		return NULL;
+	ctx = os_malloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	nettle_aes_set_encrypt_key(ctx, len, key);
+
+	return ctx;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	struct aes_ctx *actx = ctx;
+	nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain);
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	struct aes_ctx *actx = ctx;
+	bin_clear_free(actx, sizeof(*actx));
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	struct aes_ctx *ctx;
+
+	if (TEST_FAIL())
+		return NULL;
+	ctx = os_malloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	nettle_aes_set_decrypt_key(ctx, len, key);
+
+	return ctx;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	struct aes_ctx *actx = ctx;
+	nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt);
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	struct aes_ctx *actx = ctx;
+	bin_clear_free(actx, sizeof(*actx));
+}
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+		   const u8 *power, size_t power_len,
+		   const u8 *modulus, size_t modulus_len,
+		   u8 *result, size_t *result_len)
+{
+	mpz_t bn_base, bn_exp, bn_modulus, bn_result;
+	int ret = -1;
+	size_t len;
+
+	mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+	mpz_import(bn_base, base_len, 1, 1, 1, 0, base);
+	mpz_import(bn_exp, power_len, 1, 1, 1, 0, power);
+	mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus);
+
+	mpz_powm(bn_result, bn_base, bn_exp, bn_modulus);
+	len = mpz_sizeinbase(bn_result, 2);
+	len = (len + 7) / 8;
+	if (*result_len < len)
+		goto error;
+	mpz_export(result, result_len, 1, 1, 1, 0, bn_result);
+	ret = 0;
+
+error:
+	mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL);
+	return ret;
+}
+
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union {
+		struct arcfour_ctx arcfour_ctx;
+	} u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	ctx->alg = alg;
+
+	switch (alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key);
+		break;
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	switch (ctx->alg) {
+	case CRYPTO_CIPHER_ALG_RC4:
+		nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	bin_clear_free(ctx, sizeof(*ctx));
+}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 6bff202..f4cff43 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -33,7 +33,9 @@
 #include "aes_wrap.h"
 #include "crypto.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 /* Compatibility wrappers for older versions. */
 
 static HMAC_CTX * HMAC_CTX_new(void)
@@ -79,7 +81,9 @@
 
 static BIGNUM * get_group5_prime(void)
 {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+	!(defined(LIBRESSL_VERSION_NUMBER) && \
+	  LIBRESSL_VERSION_NUMBER < 0x20700000L)
 	return BN_get_rfc3526_prime_1536(NULL);
 #elif !defined(OPENSSL_IS_BORINGSSL)
 	return get_rfc3526_prime_1536(NULL);
@@ -270,10 +274,8 @@
 	switch (keylen) {
 	case 16:
 		return EVP_aes_128_ecb();
-#ifndef OPENSSL_IS_BORINGSSL
 	case 24:
 		return EVP_aes_192_ecb();
-#endif /* OPENSSL_IS_BORINGSSL */
 	case 32:
 		return EVP_aes_256_ecb();
 	}
@@ -291,8 +293,11 @@
 		return NULL;
 
 	type = aes_get_evp_cipher(len);
-	if (type == NULL)
+	if (!type) {
+		wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+			   __func__, (unsigned int) len);
 		return NULL;
+	}
 
 	ctx = EVP_CIPHER_CTX_new();
 	if (ctx == NULL)
@@ -345,8 +350,11 @@
 		return NULL;
 
 	type = aes_get_evp_cipher(len);
-	if (type == NULL)
+	if (!type) {
+		wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
+			   __func__, (unsigned int) len);
 		return NULL;
+	}
 
 	ctx = EVP_CIPHER_CTX_new();
 	if (ctx == NULL)
@@ -482,6 +490,42 @@
 }
 
 
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	size_t pubkey_len, pad;
+
+	if (os_get_random(privkey, prime_len) < 0)
+		return -1;
+	if (os_memcmp(privkey, prime, prime_len) > 0) {
+		/* Make sure private value is smaller than prime */
+		privkey[0] = 0;
+	}
+
+	pubkey_len = prime_len;
+	if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
+			   pubkey, &pubkey_len) < 0)
+		return -1;
+	if (pubkey_len < prime_len) {
+		pad = prime_len - pubkey_len;
+		os_memmove(pubkey + pad, pubkey, pubkey_len);
+		os_memset(pubkey, 0, pad);
+	}
+
+	return 0;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+			      prime, prime_len, secret, len);
+}
+
+
 int crypto_mod_exp(const u8 *base, size_t base_len,
 		   const u8 *power, size_t power_len,
 		   const u8 *modulus, size_t modulus_len,
@@ -641,7 +685,9 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	size_t publen, privlen;
@@ -742,7 +788,9 @@
 
 void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 	DH *dh;
 
 	dh = DH_new();
@@ -1199,6 +1247,12 @@
 }
 
 
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+	return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
+}
+
+
 int crypto_bignum_add(const struct crypto_bignum *a,
 		      const struct crypto_bignum *b,
 		      struct crypto_bignum *c)
@@ -1324,6 +1378,15 @@
 }
 
 
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r)
+{
+	/* Note: BN_rshift() does not modify the first argument even though it
+	 * has not been marked const. */
+	return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
+}
+
+
 int crypto_bignum_cmp(const struct crypto_bignum *a,
 		      const struct crypto_bignum *b)
 {
@@ -1349,6 +1412,12 @@
 }
 
 
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+	return BN_is_odd((const BIGNUM *) a);
+}
+
+
 int crypto_bignum_legendre(const struct crypto_bignum *a,
 			   const struct crypto_bignum *p)
 {
@@ -1483,6 +1552,13 @@
 }
 
 
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+	return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor,
+				     e->bnctx) == 0 ? -1 : 0;
+}
+
+
 struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
 {
 	if (TEST_FAIL())
@@ -1505,6 +1581,12 @@
 }
 
 
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+	return BN_num_bytes(e->order);
+}
+
+
 const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
 {
 	return (const struct crypto_bignum *) e->prime;
@@ -1526,6 +1608,16 @@
 }
 
 
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x)
+{
+	return EC_POINT_get_affine_coordinates_GFp(e->group,
+						   (const EC_POINT *) p,
+						   (BIGNUM *) x, NULL,
+						   e->bnctx) == 1 ? 0 : -1;
+}
+
+
 int crypto_ec_point_to_bin(struct crypto_ec *e,
 			   const struct crypto_ec_point *point, u8 *x, u8 *y)
 {
@@ -1701,7 +1793,7 @@
 {
 	struct crypto_ecdh *ecdh;
 	EVP_PKEY *params = NULL;
-	EVP_PKEY_CTX *pctx = NULL;
+	EC_KEY *ec_params;
 	EVP_PKEY_CTX *kctx = NULL;
 
 	ecdh = os_zalloc(sizeof(*ecdh));
@@ -1712,27 +1804,17 @@
 	if (!ecdh->ec)
 		goto fail;
 
-	pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
-	if (!pctx)
-		goto fail;
-
-	if (EVP_PKEY_paramgen_init(pctx) != 1) {
+	ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
+	if (!ec_params) {
 		wpa_printf(MSG_ERROR,
-			   "OpenSSL: EVP_PKEY_paramgen_init failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+			   "OpenSSL: Failed to generate EC_KEY parameters");
 		goto fail;
 	}
-
-	if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, ecdh->ec->nid) != 1) {
+	EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+	params = EVP_PKEY_new();
+	if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
 		wpa_printf(MSG_ERROR,
-			   "OpenSSL: EVP_PKEY_CTX_set_ec_paramgen_curve_nid failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
-		goto fail;
-	}
-
-	if (EVP_PKEY_paramgen(pctx, &params) != 1) {
-		wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_paramgen failed: %s",
-			   ERR_error_string(ERR_get_error(), NULL));
+			   "OpenSSL: Failed to generate EVP_PKEY parameters");
 		goto fail;
 	}
 
@@ -1755,7 +1837,6 @@
 
 done:
 	EVP_PKEY_free(params);
-	EVP_PKEY_CTX_free(pctx);
 	EVP_PKEY_CTX_free(kctx);
 
 	return ecdh;
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
new file mode 100644
index 0000000..b5a1e3f
--- /dev/null
+++ b/src/crypto/crypto_wolfssl.c
@@ -0,0 +1,1791 @@
+/*
+ * Wrapper functions for libwolfssl
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+/* wolfSSL headers */
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/md4.h>
+#include <wolfssl/wolfcrypt/md5.h>
+#include <wolfssl/wolfcrypt/sha.h>
+#include <wolfssl/wolfcrypt/sha256.h>
+#include <wolfssl/wolfcrypt/sha512.h>
+#include <wolfssl/wolfcrypt/hmac.h>
+#include <wolfssl/wolfcrypt/pwdbased.h>
+#include <wolfssl/wolfcrypt/arc4.h>
+#include <wolfssl/wolfcrypt/des3.h>
+#include <wolfssl/wolfcrypt/aes.h>
+#include <wolfssl/wolfcrypt/dh.h>
+#include <wolfssl/wolfcrypt/cmac.h>
+#include <wolfssl/wolfcrypt/ecc.h>
+#include <wolfssl/openssl/bn.h>
+
+
+#ifndef CONFIG_FIPS
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	Md4 md4;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitMd4(&md4);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Md4Update(&md4, addr[i], len[i]);
+
+	wc_Md4Final(&md4, mac);
+
+	return 0;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	wc_Md5 md5;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitMd5(&md5);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Md5Update(&md5, addr[i], len[i]);
+
+	wc_Md5Final(&md5, mac);
+
+	return 0;
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+	wc_Sha sha;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitSha(&sha);
+
+	for (i = 0; i < num_elem; i++)
+		wc_ShaUpdate(&sha, addr[i], len[i]);
+
+	wc_ShaFinal(&sha, mac);
+
+	return 0;
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha256 sha256;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitSha256(&sha256);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Sha256Update(&sha256, addr[i], len[i]);
+
+	wc_Sha256Final(&sha256, mac);
+
+	return 0;
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+#ifdef CONFIG_SHA384
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha384 sha384;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitSha384(&sha384);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Sha384Update(&sha384, addr[i], len[i]);
+
+	wc_Sha384Final(&sha384, mac);
+
+	return 0;
+}
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	wc_Sha512 sha512;
+	size_t i;
+
+	if (TEST_FAIL())
+		return -1;
+
+	wc_InitSha512(&sha512);
+
+	for (i = 0; i < num_elem; i++)
+		wc_Sha512Update(&sha512, addr[i], len[i]);
+
+	wc_Sha512Final(&sha512, mac);
+
+	return 0;
+}
+#endif /* CONFIG_SHA512 */
+
+
+static int wolfssl_hmac_vector(int type, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
+{
+	Hmac hmac;
+	size_t i;
+
+	(void) mdlen;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
+		return -1;
+	for (i = 0; i < num_elem; i++)
+		if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
+			return -1;
+	if (wc_HmacFinal(&hmac, mac) != 0)
+		return -1;
+	return 0;
+}
+
+
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
+				   mac, 20);
+}
+
+
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	      u8 *mac)
+{
+	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+#ifdef CONFIG_SHA256
+
+int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
+				   mac, 32);
+}
+
+
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA256 */
+
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
+				   mac, 48);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
+#ifdef CONFIG_SHA512
+
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
+				   mac, 64);
+}
+
+
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA512 */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
+		      ssid_len, iterations, buflen, WC_SHA) != 0)
+		return -1;
+	return 0;
+}
+
+
+#ifdef CONFIG_DES
+int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+	Des des;
+	u8  pkey[8], next, tmp;
+	int i;
+
+	/* Add parity bits to the key */
+	next = 0;
+	for (i = 0; i < 7; i++) {
+		tmp = key[i];
+		pkey[i] = (tmp >> i) | next | 1;
+		next = tmp << (7 - i);
+	}
+	pkey[i] = next | 1;
+
+	wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
+	wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
+
+	return 0;
+}
+#endif /* CONFIG_DES */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+	Aes *aes;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	aes = os_malloc(sizeof(Aes));
+	if (!aes)
+		return NULL;
+
+	if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
+		os_free(aes);
+		return NULL;
+	}
+
+	return aes;
+}
+
+
+int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+	wc_AesEncryptDirect(ctx, crypt, plain);
+	return 0;
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+	os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+	Aes *aes;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	aes = os_malloc(sizeof(Aes));
+	if (!aes)
+		return NULL;
+
+	if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
+		os_free(aes);
+		return NULL;
+	}
+
+	return aes;
+}
+
+
+int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+	wc_AesDecryptDirect(ctx, plain, crypt);
+	return 0;
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+	os_free(ctx);
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	Aes aes;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
+	if (ret != 0)
+		return -1;
+
+	ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
+	if (ret != 0)
+		return -1;
+	return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	Aes aes;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
+	if (ret != 0)
+		return -1;
+
+	ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
+	if (ret != 0)
+		return -1;
+	return 0;
+}
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
+			    NULL);
+	return ret != (n + 1) * 8 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
+			      NULL);
+	return ret != n * 8 ? -1 : 0;
+}
+
+
+#ifndef CONFIG_NO_RC4
+int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
+	     size_t data_len)
+{
+#ifndef NO_RC4
+	Arc4 arc4;
+	unsigned char skip_buf[16];
+
+	wc_Arc4SetKey(&arc4, key, keylen);
+
+	while (skip >= sizeof(skip_buf)) {
+		size_t len = skip;
+
+		if (len > sizeof(skip_buf))
+			len = sizeof(skip_buf);
+		wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
+		skip -= len;
+	}
+
+	wc_Arc4Process(&arc4, data, data, data_len);
+
+	return 0;
+#else /* NO_RC4 */
+	return -1;
+#endif /* NO_RC4 */
+}
+#endif /* CONFIG_NO_RC4 */
+
+
+#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
+		       || defined(EAP_SERVER_IKEV2)
+union wolfssl_cipher {
+	Aes aes;
+	Des3 des3;
+	Arc4 arc4;
+};
+
+struct crypto_cipher {
+	enum crypto_cipher_alg alg;
+	union wolfssl_cipher enc;
+	union wolfssl_cipher dec;
+};
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+					  const u8 *iv, const u8 *key,
+					  size_t key_len)
+{
+	struct crypto_cipher *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	switch (alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
+		wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
+		break;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		switch (key_len) {
+		case 16:
+		case 24:
+		case 32:
+			break;
+		default:
+			os_free(ctx);
+			return NULL;
+		}
+		if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
+				 AES_ENCRYPTION) ||
+		    wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
+				 AES_DECRYPTION)) {
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (key_len != DES3_KEYLEN ||
+		    wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
+		    wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
+			os_free(ctx);
+			return NULL;
+		}
+		break;
+#endif /* NO_DES3 */
+	case CRYPTO_CIPHER_ALG_RC2:
+	case CRYPTO_CIPHER_ALG_DES:
+	default:
+		os_free(ctx);
+		return NULL;
+	}
+
+	ctx->alg = alg;
+
+	return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+			  u8 *crypt, size_t len)
+{
+	switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
+		return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_DES3 */
+	default:
+		return -1;
+	}
+	return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+			  u8 *plain, size_t len)
+{
+	switch (ctx->alg) {
+#ifndef CONFIG_NO_RC4
+#ifndef NO_RC4
+	case CRYPTO_CIPHER_ALG_RC4:
+		wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
+		return 0;
+#endif /* NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
+#ifndef NO_AES
+	case CRYPTO_CIPHER_ALG_AES:
+		if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_AES */
+#ifndef NO_DES3
+	case CRYPTO_CIPHER_ALG_3DES:
+		if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
+			return -1;
+		return 0;
+#endif /* NO_DES3 */
+	default:
+		return -1;
+	}
+	return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+	os_free(ctx);
+}
+
+#endif
+
+
+#ifdef CONFIG_WPS_NFC
+
+static const unsigned char RFC3526_PRIME_1536[] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+	0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+	0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const unsigned char RFC3526_GENERATOR_1536[] = {
+	0x02
+};
+
+#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+	WC_RNG rng;
+	DhKey *ret = NULL;
+	DhKey *dh = NULL;
+	struct wpabuf *privkey = NULL;
+	struct wpabuf *pubkey = NULL;
+	word32 priv_sz, pub_sz;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!dh)
+		return NULL;
+	wc_InitDhKey(dh);
+
+	if (wc_InitRng(&rng) != 0) {
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+		return NULL;
+	}
+
+	privkey = wpabuf_alloc(RFC3526_LEN);
+	pubkey = wpabuf_alloc(RFC3526_LEN);
+	if (!privkey || !pubkey)
+		goto done;
+
+	if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+			RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+	    != 0)
+		goto done;
+
+	if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
+				 wpabuf_mhead(pubkey), &pub_sz) != 0)
+		goto done;
+
+	wpabuf_put(privkey, priv_sz);
+	wpabuf_put(pubkey, pub_sz);
+
+	ret = dh;
+	*priv = privkey;
+	*publ = pubkey;
+	dh = NULL;
+	privkey = NULL;
+	pubkey = NULL;
+done:
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	if (dh) {
+		wc_FreeDhKey(dh);
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	}
+	wc_FreeRng(&rng);
+	return ret;
+}
+
+
+void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
+{
+	DhKey *ret = NULL;
+	DhKey *dh;
+	byte *secret;
+	word32 secret_sz;
+
+	dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!dh)
+		return NULL;
+	wc_InitDhKey(dh);
+
+	secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	if (!secret)
+		goto done;
+
+	if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
+			RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
+	    != 0)
+		goto done;
+
+	if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
+		       wpabuf_len(priv), RFC3526_GENERATOR_1536,
+		       sizeof(RFC3526_GENERATOR_1536)) != 0)
+		goto done;
+
+	if (secret_sz != wpabuf_len(publ) ||
+	    os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
+		goto done;
+
+	ret = dh;
+	dh = NULL;
+done:
+	if (dh) {
+		wc_FreeDhKey(dh);
+		XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	}
+	XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+	return ret;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+				  const struct wpabuf *own_private)
+{
+	struct wpabuf *ret = NULL;
+	struct wpabuf *secret;
+	word32 secret_sz;
+
+	secret = wpabuf_alloc(RFC3526_LEN);
+	if (!secret)
+		goto done;
+
+	if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
+		       wpabuf_head(own_private), wpabuf_len(own_private),
+		       wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
+		goto done;
+
+	wpabuf_put(secret, secret_sz);
+
+	ret = secret;
+	secret = NULL;
+done:
+	wpabuf_clear_free(secret);
+	return ret;
+}
+
+
+void dh5_free(void *ctx)
+{
+	if (!ctx)
+		return;
+
+	wc_FreeDhKey(ctx);
+	XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
+		   u8 *pubkey)
+{
+	int ret = -1;
+	WC_RNG rng;
+	DhKey *dh = NULL;
+	word32 priv_sz, pub_sz;
+
+	if (TEST_FAIL())
+		return -1;
+
+	dh = os_malloc(sizeof(DhKey));
+	if (!dh)
+		return -1;
+	wc_InitDhKey(dh);
+
+	if (wc_InitRng(&rng) != 0) {
+		os_free(dh);
+		return -1;
+	}
+
+	if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+		goto done;
+
+	if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
+	    != 0)
+		goto done;
+
+	if (priv_sz < prime_len) {
+		size_t pad_sz = prime_len - priv_sz;
+
+		os_memmove(privkey + pad_sz, privkey, priv_sz);
+		os_memset(privkey, 0, pad_sz);
+	}
+
+	if (pub_sz < prime_len) {
+		size_t pad_sz = prime_len - pub_sz;
+
+		os_memmove(pubkey + pad_sz, pubkey, pub_sz);
+		os_memset(pubkey, 0, pad_sz);
+	}
+	ret = 0;
+done:
+	wc_FreeDhKey(dh);
+	os_free(dh);
+	wc_FreeRng(&rng);
+	return ret;
+}
+
+
+int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+			    const u8 *privkey, size_t privkey_len,
+			    const u8 *pubkey, size_t pubkey_len,
+			    u8 *secret, size_t *len)
+{
+	int ret = -1;
+	DhKey *dh;
+	word32 secret_sz;
+
+	dh = os_malloc(sizeof(DhKey));
+	if (!dh)
+		return -1;
+	wc_InitDhKey(dh);
+
+	if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
+		goto done;
+
+	if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
+		       pubkey_len) != 0)
+		goto done;
+
+	*len = secret_sz;
+	ret = 0;
+done:
+	wc_FreeDhKey(dh);
+	os_free(dh);
+	return ret;
+}
+
+
+#ifdef CONFIG_FIPS
+int crypto_get_random(void *buf, size_t len)
+{
+	int ret = 0;
+	WC_RNG rng;
+
+	if (wc_InitRng(&rng) != 0)
+		return -1;
+	if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
+		ret = -1;
+	wc_FreeRng(&rng);
+	return ret;
+}
+#endif /* CONFIG_FIPS */
+
+
+#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
+struct crypto_hash {
+	Hmac hmac;
+	int size;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+				      size_t key_len)
+{
+	struct crypto_hash *ret = NULL;
+	struct crypto_hash *hash;
+	int type;
+
+	hash = os_zalloc(sizeof(*hash));
+	if (!hash)
+		goto done;
+
+	switch (alg) {
+#ifndef NO_MD5
+	case CRYPTO_HASH_ALG_HMAC_MD5:
+		hash->size = 16;
+		type = WC_MD5;
+		break;
+#endif /* NO_MD5 */
+#ifndef NO_SHA
+	case CRYPTO_HASH_ALG_HMAC_SHA1:
+		type = WC_SHA;
+		hash->size = 20;
+		break;
+#endif /* NO_SHA */
+#ifdef CONFIG_SHA256
+#ifndef NO_SHA256
+	case CRYPTO_HASH_ALG_HMAC_SHA256:
+		type = WC_SHA256;
+		hash->size = 32;
+		break;
+#endif /* NO_SHA256 */
+#endif /* CONFIG_SHA256 */
+	default:
+		goto done;
+	}
+
+	if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
+		goto done;
+
+	ret = hash;
+	hash = NULL;
+done:
+	os_free(hash);
+	return ret;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+	if (!ctx)
+		return;
+	wc_HmacUpdate(&ctx->hmac, data, len);
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+	int ret = 0;
+
+	if (!ctx)
+		return -2;
+
+	if (!mac || !len)
+		goto done;
+
+	if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
+		ret = -1;
+		goto done;
+	}
+
+	*len = ctx->size;
+	ret = 0;
+done:
+	bin_clear_free(ctx, sizeof(*ctx));
+	return ret;
+}
+
+#endif
+
+
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	Cmac cmac;
+	size_t i;
+	word32 sz;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
+		return -1;
+
+	for (i = 0; i < num_elem; i++)
+		if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
+			return -1;
+
+	sz = AES_BLOCK_SIZE;
+	if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
+		return -1;
+
+	return 0;
+}
+
+
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
+
+
+struct crypto_bignum * crypto_bignum_init(void)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = os_malloc(sizeof(*a));
+	if (!a || mp_init(a) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
+struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
+{
+	mp_int *a;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	a = (mp_int *) crypto_bignum_init();
+	if (!a)
+		return NULL;
+
+	if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
+		os_free(a);
+		a = NULL;
+	}
+
+	return (struct crypto_bignum *) a;
+}
+
+
+void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
+{
+	if (!n)
+		return;
+
+	if (clear)
+		mp_forcezero((mp_int *) n);
+	mp_clear((mp_int *) n);
+	os_free((mp_int *) n);
+}
+
+
+int crypto_bignum_to_bin(const struct crypto_bignum *a,
+			 u8 *buf, size_t buflen, size_t padlen)
+{
+	int num_bytes, offset;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (padlen > buflen)
+		return -1;
+
+	num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
+	if ((size_t) num_bytes > buflen)
+		return -1;
+	if (padlen > (size_t) num_bytes)
+		offset = padlen - num_bytes;
+	else
+		offset = 0;
+
+	os_memset(buf, 0, offset);
+	mp_to_unsigned_bin((mp_int *) a, buf + offset);
+
+	return num_bytes + offset;
+}
+
+
+int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
+{
+	int ret = 0;
+	WC_RNG rng;
+
+	if (wc_InitRng(&rng) != 0)
+		return -1;
+	if (mp_rand_prime((mp_int *) r,
+			  (mp_count_bits((mp_int *) m) + 7) / 8 * 2,
+			  &rng, NULL) != 0)
+		ret = -1;
+	if (ret == 0 &&
+	    mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
+		ret = -1;
+	wc_FreeRng(&rng);
+	return ret;
+}
+
+
+int crypto_bignum_add(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *r)
+{
+	return mp_add((mp_int *) a, (mp_int *) b,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mod(const struct crypto_bignum *a,
+		      const struct crypto_bignum *m,
+		      struct crypto_bignum *r)
+{
+	return mp_mod((mp_int *) a, (mp_int *) m,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_exptmod(const struct crypto_bignum *b,
+			  const struct crypto_bignum *e,
+			  const struct crypto_bignum *m,
+			  struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
+			  (mp_int *) r) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_inverse(const struct crypto_bignum *a,
+			  const struct crypto_bignum *m,
+			  struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_invmod((mp_int *) a, (mp_int *) m,
+			 (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_sub(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *r)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_add((mp_int *) a, (mp_int *) b,
+		      (mp_int *) r) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_div(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b,
+		      struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
+		      NULL) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_bignum_mulmod(const struct crypto_bignum *a,
+			 const struct crypto_bignum *b,
+			 const struct crypto_bignum *m,
+			 struct crypto_bignum *d)
+{
+	if (TEST_FAIL())
+		return -1;
+
+	return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
+			 (mp_int *) d) == MP_OKAY ?  0 : -1;
+}
+
+
+int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
+			 struct crypto_bignum *r)
+{
+	if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
+		return -1;
+	mp_rshb((mp_int *) r, n);
+	return 0;
+}
+
+
+int crypto_bignum_cmp(const struct crypto_bignum *a,
+		      const struct crypto_bignum *b)
+{
+	return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+int crypto_bignum_bits(const struct crypto_bignum *a)
+{
+	return mp_count_bits((mp_int *) a);
+}
+
+
+int crypto_bignum_is_zero(const struct crypto_bignum *a)
+{
+	return mp_iszero((mp_int *) a);
+}
+
+
+int crypto_bignum_is_one(const struct crypto_bignum *a)
+{
+	return mp_isone((const mp_int *) a);
+}
+
+int crypto_bignum_is_odd(const struct crypto_bignum *a)
+{
+	return mp_isodd((mp_int *) a);
+}
+
+
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	mp_int t;
+	int ret;
+	int res = -2;
+
+	if (TEST_FAIL())
+		return -2;
+
+	if (mp_init(&t) != MP_OKAY)
+		return -2;
+
+	/* t = (p-1) / 2 */
+	ret = mp_sub_d((mp_int *) p, 1, &t);
+	if (ret == MP_OKAY)
+		mp_rshb(&t, 1);
+	if (ret == MP_OKAY)
+		ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
+	if (ret == MP_OKAY) {
+		if (mp_isone(&t))
+			res = 1;
+		else if (mp_iszero(&t))
+			res = 0;
+		else
+			res = -1;
+	}
+
+	mp_clear(&t);
+	return res;
+}
+
+
+#ifdef CONFIG_ECC
+
+int ecc_map(ecc_point *, mp_int *, mp_digit);
+int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
+			     mp_int *a, mp_int *modulus, mp_digit mp);
+
+struct crypto_ec {
+	ecc_key key;
+	mp_int a;
+	mp_int prime;
+	mp_int order;
+	mp_digit mont_b;
+	mp_int b;
+};
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+	int built = 0;
+	struct crypto_ec *e;
+	int curve_id;
+
+	/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
+	switch (group) {
+	case 19:
+		curve_id = ECC_SECP256R1;
+		break;
+	case 20:
+		curve_id = ECC_SECP384R1;
+		break;
+	case 21:
+		curve_id = ECC_SECP521R1;
+		break;
+	case 25:
+		curve_id = ECC_SECP192R1;
+		break;
+	case 26:
+		curve_id = ECC_SECP224R1;
+		break;
+#ifdef HAVE_ECC_BRAINPOOL
+	case 27:
+		curve_id = ECC_BRAINPOOLP224R1;
+		break;
+	case 28:
+		curve_id = ECC_BRAINPOOLP256R1;
+		break;
+	case 29:
+		curve_id = ECC_BRAINPOOLP384R1;
+		break;
+	case 30:
+		curve_id = ECC_BRAINPOOLP512R1;
+		break;
+#endif /* HAVE_ECC_BRAINPOOL */
+	default:
+		return NULL;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (!e)
+		return NULL;
+
+	if (wc_ecc_init(&e->key) != 0 ||
+	    wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
+	    mp_init(&e->a) != MP_OKAY ||
+	    mp_init(&e->prime) != MP_OKAY ||
+	    mp_init(&e->order) != MP_OKAY ||
+	    mp_init(&e->b) != MP_OKAY ||
+	    mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
+	    mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
+	    mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
+	    mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
+	    mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
+		goto done;
+
+	built = 1;
+done:
+	if (!built) {
+		crypto_ec_deinit(e);
+		e = NULL;
+	}
+	return e;
+}
+
+
+void crypto_ec_deinit(struct crypto_ec* e)
+{
+	if (!e)
+		return;
+
+	mp_clear(&e->b);
+	mp_clear(&e->order);
+	mp_clear(&e->prime);
+	mp_clear(&e->a);
+	wc_ecc_free(&e->key);
+	os_free(e);
+}
+
+
+int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
+{
+	if (!e || !cofactor)
+		return -1;
+
+	mp_set((mp_int *) cofactor, e->key.dp->cofactor);
+	return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
+{
+	if (TEST_FAIL())
+		return NULL;
+	if (!e)
+		return NULL;
+	return (struct crypto_ec_point *) wc_ecc_new_point();
+}
+
+
+size_t crypto_ec_prime_len(struct crypto_ec *e)
+{
+	return (mp_count_bits(&e->prime) + 7) / 8;
+}
+
+
+size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
+{
+	return mp_count_bits(&e->prime);
+}
+
+
+size_t crypto_ec_order_len(struct crypto_ec *e)
+{
+	return (mp_count_bits(&e->order) + 7) / 8;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->prime;
+}
+
+
+const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
+{
+	return (const struct crypto_bignum *) &e->order;
+}
+
+
+void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
+{
+	ecc_point *point = (ecc_point *) p;
+
+	if (!p)
+		return;
+
+	if (clear) {
+		mp_forcezero(point->x);
+		mp_forcezero(point->y);
+		mp_forcezero(point->z);
+	}
+	wc_ecc_del_point(point);
+}
+
+
+int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
+		      struct crypto_bignum *x)
+{
+	return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
+}
+
+
+int crypto_ec_point_to_bin(struct crypto_ec *e,
+			   const struct crypto_ec_point *point, u8 *x, u8 *y)
+{
+	ecc_point *p = (ecc_point *) point;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (!mp_isone(p->z)) {
+		if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
+			return -1;
+	}
+
+	if (x) {
+		if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
+					 e->key.dp->size,
+					 e->key.dp->size) <= 0)
+			return -1;
+	}
+
+	if (y) {
+		if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
+					 e->key.dp->size,
+					 e->key.dp->size) <= 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
+						  const u8 *val)
+{
+	ecc_point *point = NULL;
+	int loaded = 0;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	point = wc_ecc_new_point();
+	if (!point)
+		goto done;
+
+	if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
+		goto done;
+	val += e->key.dp->size;
+	if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
+		goto done;
+	mp_set(point->z, 1);
+
+	loaded = 1;
+done:
+	if (!loaded) {
+		wc_ecc_del_point(point);
+		point = NULL;
+	}
+	return (struct crypto_ec_point *) point;
+}
+
+
+int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b,
+			struct crypto_ec_point *c)
+{
+	mp_int mu;
+	ecc_point *ta = NULL, *tb = NULL;
+	ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
+	mp_int *modulus = &e->prime;
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = mp_init(&mu);
+	if (ret != MP_OKAY)
+		return -1;
+
+	ret = mp_montgomery_calc_normalization(&mu, modulus);
+	if (ret != MP_OKAY) {
+		mp_clear(&mu);
+		return -1;
+	}
+
+	if (!mp_isone(&mu)) {
+		ta = wc_ecc_new_point();
+		if (!ta) {
+			mp_clear(&mu);
+			return -1;
+		}
+		tb = wc_ecc_new_point();
+		if (!tb) {
+			wc_ecc_del_point(ta);
+			mp_clear(&mu);
+			return -1;
+		}
+
+		if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
+		    mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
+		    mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
+		    mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
+		    mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
+		    mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
+			ret = -1;
+			goto end;
+		}
+		pa = ta;
+		pb = tb;
+	}
+
+	ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
+				       &e->prime, e->mont_b);
+	if (ret != 0) {
+		ret = -1;
+		goto end;
+	}
+
+	if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
+		ret = -1;
+	else
+		ret = 0;
+end:
+	wc_ecc_del_point(tb);
+	wc_ecc_del_point(ta);
+	mp_clear(&mu);
+	return ret;
+}
+
+
+int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
+			const struct crypto_bignum *b,
+			struct crypto_ec_point *res)
+{
+	int ret;
+
+	if (TEST_FAIL())
+		return -1;
+
+	ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
+			    &e->a, &e->prime, 1);
+	return ret == 0 ? 0 : -1;
+}
+
+
+int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
+{
+	ecc_point *point = (ecc_point *) p;
+
+	if (TEST_FAIL())
+		return -1;
+
+	if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
+		return -1;
+
+	return 0;
+}
+
+
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+				  struct crypto_ec_point *p,
+				  const struct crypto_bignum *x, int y_bit)
+{
+	byte buf[1 + 2 * MAX_ECC_BYTES];
+	int ret;
+	int prime_len = crypto_ec_prime_len(e);
+
+	if (TEST_FAIL())
+		return -1;
+
+	buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
+	ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
+	if (ret <= 0)
+		return -1;
+	ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
+				      (ecc_point *) p);
+	if (ret != 0)
+		return -1;
+
+	return 0;
+}
+
+
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	mp_int *y2 = NULL;
+	mp_int t;
+	int calced = 0;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	if (mp_init(&t) != MP_OKAY)
+		return NULL;
+
+	y2 = (mp_int *) crypto_bignum_init();
+	if (!y2)
+		goto done;
+
+	if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
+	    mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
+	    mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
+	    mp_addmod(y2, &t, &e->prime, y2) != 0 ||
+	    mp_addmod(y2, &e->b, &e->prime, y2) != 0)
+		goto done;
+
+	calced = 1;
+done:
+	if (!calced) {
+		if (y2) {
+			mp_clear(y2);
+			os_free(y2);
+		}
+		mp_clear(&t);
+	}
+
+	return (struct crypto_bignum *) y2;
+}
+
+
+int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
+				   const struct crypto_ec_point *p)
+{
+	return wc_ecc_point_is_at_infinity((ecc_point *) p);
+}
+
+
+int crypto_ec_point_is_on_curve(struct crypto_ec *e,
+				const struct crypto_ec_point *p)
+{
+	return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
+		MP_OKAY;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
+}
+
+
+struct crypto_ecdh {
+	struct crypto_ec *ec;
+};
+
+struct crypto_ecdh * crypto_ecdh_init(int group)
+{
+	struct crypto_ecdh *ecdh = NULL;
+	WC_RNG rng;
+	int ret;
+
+	if (wc_InitRng(&rng) != 0)
+		goto fail;
+
+	ecdh = os_zalloc(sizeof(*ecdh));
+	if (!ecdh)
+		goto fail;
+
+	ecdh->ec = crypto_ec_init(group);
+	if (!ecdh->ec)
+		goto fail;
+
+	ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
+				 ecdh->ec->key.dp->id);
+	if (ret < 0)
+		goto fail;
+
+done:
+	wc_FreeRng(&rng);
+
+	return ecdh;
+fail:
+	crypto_ecdh_deinit(ecdh);
+	ecdh = NULL;
+	goto done;
+}
+
+
+void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
+{
+	if (ecdh) {
+		crypto_ec_deinit(ecdh->ec);
+		os_free(ecdh);
+	}
+}
+
+
+struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
+{
+	struct wpabuf *buf = NULL;
+	int ret;
+	int len = ecdh->ec->key.dp->size;
+
+	buf = wpabuf_alloc(inc_y ? 2 * len : len);
+	if (!buf)
+		goto fail;
+
+	ret = crypto_bignum_to_bin((struct crypto_bignum *)
+				   ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
+				   len, len);
+	if (ret < 0)
+		goto fail;
+	if (inc_y) {
+		ret = crypto_bignum_to_bin((struct crypto_bignum *)
+					   ecdh->ec->key.pubkey.y,
+					   wpabuf_put(buf, len), len, len);
+		if (ret < 0)
+			goto fail;
+	}
+
+done:
+	return buf;
+fail:
+	wpabuf_free(buf);
+	buf = NULL;
+	goto done;
+}
+
+
+struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
+					const u8 *key, size_t len)
+{
+	int ret;
+	struct wpabuf *pubkey = NULL;
+	struct wpabuf *secret = NULL;
+	word32 key_len = ecdh->ec->key.dp->size;
+	ecc_point *point = NULL;
+	size_t need_key_len = inc_y ? 2 * key_len : key_len;
+
+	if (len < need_key_len)
+		goto fail;
+	pubkey = wpabuf_alloc(1 + 2 * key_len);
+	if (!pubkey)
+		goto fail;
+	wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
+	wpabuf_put_data(pubkey, key, need_key_len);
+
+	point = wc_ecc_new_point();
+	if (!point)
+		goto fail;
+
+	ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
+				      ecdh->ec->key.idx, point);
+	if (ret != MP_OKAY)
+		goto fail;
+
+	secret = wpabuf_alloc(key_len);
+	if (!secret)
+		goto fail;
+
+	ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
+				      wpabuf_put(secret, key_len), &key_len);
+	if (ret != MP_OKAY)
+		goto fail;
+
+done:
+	wc_ecc_del_point(point);
+	wpabuf_free(pubkey);
+	return secret;
+fail:
+	wpabuf_free(secret);
+	secret = NULL;
+	goto done;
+}
+
+#endif /* CONFIG_ECC */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 7912361..a9b770e 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1151,7 +1151,7 @@
 { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
 dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
 dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
-		
+
 
 static const struct dh_group dh_groups[] = {
 	DH_GROUP(5, 1),
@@ -1203,19 +1203,6 @@
 	if (*priv == NULL)
 		return NULL;
 
-	if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
-	{
-		wpabuf_clear_free(*priv);
-		*priv = NULL;
-		return NULL;
-	}
-
-	if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
-		/* Make sure private value is smaller than prime */
-		*(wpabuf_mhead_u8(*priv)) = 0;
-	}
-	wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
-
 	pv_len = dh->prime_len;
 	pv = wpabuf_alloc(pv_len);
 	if (pv == NULL) {
@@ -1223,17 +1210,17 @@
 		*priv = NULL;
 		return NULL;
 	}
-	if (crypto_mod_exp(dh->generator, dh->generator_len,
-			   wpabuf_head(*priv), wpabuf_len(*priv),
-			   dh->prime, dh->prime_len, wpabuf_mhead(pv),
-			   &pv_len) < 0) {
+	if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len,
+			   wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) {
 		wpabuf_clear_free(pv);
-		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		wpa_printf(MSG_INFO, "DH: crypto_dh_init failed");
 		wpabuf_clear_free(*priv);
 		*priv = NULL;
 		return NULL;
 	}
-	wpabuf_put(pv, pv_len);
+	wpabuf_put(*priv, dh->prime_len);
+	wpabuf_put(pv, dh->prime_len);
+	wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
 	wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
 
 	return pv;
@@ -1261,12 +1248,14 @@
 	shared = wpabuf_alloc(shared_len);
 	if (shared == NULL)
 		return NULL;
-	if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
-			   wpabuf_head(own_private), wpabuf_len(own_private),
-			   dh->prime, dh->prime_len,
-			   wpabuf_mhead(shared), &shared_len) < 0) {
+	if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+				    wpabuf_head(own_private),
+				    wpabuf_len(own_private),
+				    wpabuf_head(peer_public),
+				    wpabuf_len(peer_public),
+				    wpabuf_mhead(shared), &shared_len) < 0) {
 		wpabuf_clear_free(shared);
-		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed");
 		return NULL;
 	}
 	wpabuf_put(shared, shared_len);
diff --git a/src/crypto/fips_prf_wolfssl.c b/src/crypto/fips_prf_wolfssl.c
new file mode 100644
index 0000000..feb39db
--- /dev/null
+++ b/src/crypto/fips_prf_wolfssl.c
@@ -0,0 +1,87 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <wolfssl/options.h>
+#include <wolfssl/wolfcrypt/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u32 *state, const u8 data[64])
+{
+	wc_Sha sha;
+
+	os_memset(&sha, 0, sizeof(sha));
+	sha.digest[0] = state[0];
+	sha.digest[1] = state[1];
+	sha.digest[2] = state[2];
+	sha.digest[3] = state[3];
+	sha.digest[4] = state[4];
+	wc_ShaUpdate(&sha, data, 64);
+	state[0] = sha.digest[0];
+	state[1] = sha.digest[1];
+	state[2] = sha.digest[2];
+	state[3] = sha.digest[3];
+	state[4] = sha.digest[4];
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+	u8 xkey[64];
+	u32 t[5], _t[5];
+	int i, j, m, k;
+	u8 *xpos = x;
+	u32 carry;
+
+	if (seed_len < sizeof(xkey))
+		os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
+	else
+		seed_len = sizeof(xkey);
+
+	/* FIPS 186-2 + change notice 1 */
+
+	os_memcpy(xkey, seed, seed_len);
+	t[0] = 0x67452301;
+	t[1] = 0xEFCDAB89;
+	t[2] = 0x98BADCFE;
+	t[3] = 0x10325476;
+	t[4] = 0xC3D2E1F0;
+
+	m = xlen / 40;
+	for (j = 0; j < m; j++) {
+		/* XSEED_j = 0 */
+		for (i = 0; i < 2; i++) {
+			/* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+			/* w_i = G(t, XVAL) */
+			os_memcpy(_t, t, 20);
+			sha1_transform(_t, xkey);
+			WPA_PUT_BE32(xpos, _t[0]);
+			WPA_PUT_BE32(xpos + 4, _t[1]);
+			WPA_PUT_BE32(xpos + 8, _t[2]);
+			WPA_PUT_BE32(xpos + 12, _t[3]);
+			WPA_PUT_BE32(xpos + 16, _t[4]);
+
+			/* XKEY = (1 + XKEY + w_i) mod 2^b */
+			carry = 1;
+			for (k = 19; k >= 0; k--) {
+				carry += xkey[k] + xpos[k];
+				xkey[k] = carry & 0xff;
+				carry >>= 8;
+			}
+
+			xpos += 20;
+		}
+		/* x_j = w_0|w_1 */
+	}
+
+	return 0;
+}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index dc4117c..585db8b 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -101,6 +101,7 @@
 #define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
 #define TLS_CONN_SUITEB BIT(11)
 #define TLS_CONN_SUITEB_NO_ECDH BIT(12)
+#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 8c76bfa..7ee3fa3 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for GnuTLS
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -346,6 +346,9 @@
 			      const struct tls_connection_params *params)
 {
 	int ret;
+	const char *err;
+	char prio_buf[100];
+	const char *prio = NULL;
 
 	if (conn == NULL || params == NULL)
 		return -1;
@@ -397,9 +400,57 @@
 
 	conn->flags = params->flags;
 
+	if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 |
+			     TLS_CONN_DISABLE_TLSv1_1 |
+			     TLS_CONN_DISABLE_TLSv1_2)) {
+		os_snprintf(prio_buf, sizeof(prio_buf),
+			    "NORMAL:-VERS-SSL3.0%s%s%s",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_0 ?
+			    ":-VERS-TLS1.0" : "",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_1 ?
+			    ":-VERS-TLS1.1" : "",
+			    params->flags & TLS_CONN_DISABLE_TLSv1_2 ?
+			    ":-VERS-TLS1.2" : "");
+		prio = prio_buf;
+	}
+
 	if (params->openssl_ciphers) {
-		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
-		return -1;
+		if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) {
+			prio = "SUITEB128";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "SUITEB192") == 0) {
+			prio = "SUITEB192";
+		} else if ((params->flags & TLS_CONN_SUITEB) &&
+			   os_strcmp(params->openssl_ciphers,
+				     "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "ECDHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "DHE-RSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+		} else if (os_strcmp(params->openssl_ciphers,
+				     "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) {
+			prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL";
+		} else {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: openssl_ciphers not supported");
+			return -1;
+		}
+	} else if (params->flags & TLS_CONN_SUITEB) {
+		prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH";
+	}
+
+	if (prio) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio);
+		ret = gnutls_priority_set_direct(conn->session, prio, &err);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR,
+				   "GnuTLS: Priority string failure at '%s'",
+				   err);
+			return -1;
+		}
 	}
 
 	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
@@ -425,6 +476,13 @@
 					   gnutls_strerror(ret));
 				return -1;
 			}
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read CA cert '%s' in PEM format",
+				   params->ca_cert);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read CA cert '%s' in DER format",
+				   params->ca_cert);
 		}
 	} else if (params->ca_cert_blob) {
 		gnutls_datum_t ca;
@@ -472,6 +530,9 @@
 	}
 
 	if (params->client_cert && params->private_key) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format",
+			   params->client_cert, params->private_key);
 #if GNUTLS_VERSION_NUMBER >= 0x03010b
 		ret = gnutls_certificate_set_x509_key_file2(
 			conn->xcred, params->client_cert, params->private_key,
@@ -483,8 +544,9 @@
 			GNUTLS_X509_FMT_DER);
 #endif
 		if (ret < 0) {
-			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-				   "in DER format: %s", gnutls_strerror(ret));
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format",
+				   gnutls_strerror(ret));
 #if GNUTLS_VERSION_NUMBER >= 0x03010b
 			ret = gnutls_certificate_set_x509_key_file2(
 				conn->xcred, params->client_cert,
@@ -501,11 +563,19 @@
 					   gnutls_strerror(ret));
 				return ret;
 			}
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read client cert/key in PEM format");
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Successfully read client cert/key in DER format");
 		}
 	} else if (params->private_key) {
 		int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
 		/* Try to load in PKCS#12 format */
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format",
+			   params->private_key);
 		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
 			params->private_key_passwd);
@@ -1333,6 +1403,7 @@
 	ret = gnutls_handshake(conn->session);
 	if (ret < 0) {
 		gnutls_alert_description_t alert;
+		union tls_event_data ev;
 
 		switch (ret) {
 		case GNUTLS_E_AGAIN:
@@ -1343,14 +1414,29 @@
 				conn->push_buf = wpabuf_alloc(0);
 			}
 			break;
+		case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
+			wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime");
+			if (conn->global->event_cb) {
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 1;
+				ev.alert.type = "fatal";
+				ev.alert.description = "insufficient security";
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
+			/*
+			 * Could send a TLS Alert to the server, but for now,
+			 * simply terminate handshake.
+			 */
+			conn->failed++;
+			conn->write_alerts++;
+			break;
 		case GNUTLS_E_FATAL_ALERT_RECEIVED:
 			alert = gnutls_alert_get(conn->session);
 			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
 				   __func__, gnutls_alert_get_name(alert));
 			conn->read_alerts++;
 			if (conn->global->event_cb != NULL) {
-				union tls_event_data ev;
-
 				os_memset(&ev, 0, sizeof(ev));
 				ev.alert.is_local = 0;
 				ev.alert.type = gnutls_alert_get_name(alert);
@@ -1501,16 +1587,53 @@
 int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
 		    char *buf, size_t buflen)
 {
-	/* TODO */
-	return -1;
+	gnutls_protocol_t ver;
+
+	ver = gnutls_protocol_get_version(conn->session);
+	if (ver == GNUTLS_TLS1_0)
+		os_strlcpy(buf, "TLSv1", buflen);
+	else if (ver == GNUTLS_TLS1_1)
+		os_strlcpy(buf, "TLSv1.1", buflen);
+	else if (ver == GNUTLS_TLS1_2)
+		os_strlcpy(buf, "TLSv1.2", buflen);
+	else
+		return -1;
+	return 0;
 }
 
 
 int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
-	/* TODO */
-	buf[0] = '\0';
+	gnutls_cipher_algorithm_t cipher;
+	gnutls_kx_algorithm_t kx;
+	gnutls_mac_algorithm_t mac;
+	const char *kx_str, *cipher_str, *mac_str;
+	int res;
+
+	cipher = gnutls_cipher_get(conn->session);
+	cipher_str = gnutls_cipher_get_name(cipher);
+	if (!cipher_str)
+		cipher_str = "";
+
+	kx = gnutls_kx_get(conn->session);
+	kx_str = gnutls_kx_get_name(kx);
+	if (!kx_str)
+		kx_str = "";
+
+	mac = gnutls_mac_get(conn->session);
+	mac_str = gnutls_mac_get_name(mac);
+	if (!mac_str)
+		mac_str = "";
+
+	if (kx == GNUTLS_KX_RSA)
+		res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str);
+	else
+		res = os_snprintf(buf, buflen, "%s-%s-%s",
+				  kx_str, cipher_str, mac_str);
+	if (os_snprintf_error(buflen, res))
+		return -1;
+
 	return 0;
 }
 
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 7243d9f..47ef642 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -59,7 +59,8 @@
 #endif /* SSL_set_tlsext_status_type */
 
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
-     defined(LIBRESSL_VERSION_NUMBER)) &&    \
+     (defined(LIBRESSL_VERSION_NUMBER) && \
+      LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
     !defined(BORINGSSL_API_VERSION)
 /*
  * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
@@ -245,6 +246,8 @@
 	unsigned int server_cert_only:1;
 	unsigned int invalid_hb_used:1;
 	unsigned int success_data:1;
+	unsigned int client_hello_generated:1;
+	unsigned int server:1;
 
 	u8 srv_cert_hash[32];
 
@@ -945,7 +948,9 @@
 		}
 #endif /* OPENSSL_FIPS */
 #endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 		SSL_load_error_strings();
 		SSL_library_init();
 #ifndef OPENSSL_NO_SHA256
@@ -1077,7 +1082,9 @@
 
 	tls_openssl_ref_count--;
 	if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
@@ -2451,7 +2458,8 @@
 #endif /* CONFIG_SUITEB */
 
 
-static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags)
+static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
+			      const char *openssl_ciphers)
 {
 	SSL *ssl = conn->ssl;
 
@@ -2480,11 +2488,27 @@
 	else
 		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
 #endif /* SSL_OP_NO_TLSv1_2 */
+#ifdef SSL_OP_NO_TLSv1_3
+	if (flags & TLS_CONN_DISABLE_TLSv1_3)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
+#endif /* SSL_OP_NO_TLSv1_3 */
 #ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+	/* Start with defaults from BoringSSL */
+	SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
+#endif /* OPENSSL_IS_BORINGSSL */
 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
 	if (flags & TLS_CONN_SUITEB_NO_ECDH) {
 		const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
 
+		if (openssl_ciphers) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Override ciphers for Suite B (no ECDH): %s",
+				   openssl_ciphers);
+			ciphers = openssl_ciphers;
+		}
 		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set Suite B ciphers");
@@ -2494,14 +2518,21 @@
 		EC_KEY *ecdh;
 		const char *ciphers =
 			"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
+		int nid[1] = { NID_secp384r1 };
 
+		if (openssl_ciphers) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Override ciphers for Suite B: %s",
+				   openssl_ciphers);
+			ciphers = openssl_ciphers;
+		}
 		if (SSL_set_cipher_list(ssl, ciphers) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set Suite B ciphers");
 			return -1;
 		}
 
-		if (SSL_set1_curves_list(ssl, "P-384") != 1) {
+		if (SSL_set1_curves(ssl, nid, 1) != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set Suite B curves");
 			return -1;
@@ -2517,12 +2548,23 @@
 		EC_KEY_free(ecdh);
 	}
 	if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
+#ifdef OPENSSL_IS_BORINGSSL
+		uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
+
+		if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+						       1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+#else /* OPENSSL_IS_BORINGSSL */
 		/* ECDSA+SHA384 if need to add EC support here */
 		if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
 			wpa_printf(MSG_INFO,
 				   "OpenSSL: Failed to set Suite B sigalgs");
 			return -1;
 		}
+#endif /* OPENSSL_IS_BORINGSSL */
 
 		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
 		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
@@ -2535,6 +2577,26 @@
 		return -1;
 	}
 #endif /* OPENSSL_VERSION_NUMBER */
+
+#ifdef OPENSSL_IS_BORINGSSL
+	if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
+		uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
+		int nid[1] = { NID_secp384r1 };
+
+		if (SSL_set1_curves(ssl, nid, 1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B curves");
+			return -1;
+		}
+
+		if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
+						       1) != 1) {
+			wpa_printf(MSG_INFO,
+				   "OpenSSL: Failed to set Suite B sigalgs");
+			return -1;
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
 #endif /* CONFIG_SUITEB */
 
 	return 0;
@@ -2561,7 +2623,7 @@
 		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 	}
 
-	if (tls_set_conn_flags(conn, flags) < 0)
+	if (tls_set_conn_flags(conn, flags, NULL) < 0)
 		return -1;
 	conn->flags = flags;
 
@@ -2702,16 +2764,6 @@
 }
 
 
-static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
-{
-	if (password == NULL) {
-		return 0;
-	}
-	os_strlcpy(buf, (char *) password, size);
-	return os_strlen(buf);
-}
-
-
 #ifdef PKCS12_FUNCS
 static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
 			    const char *passwd)
@@ -3030,16 +3082,61 @@
 }
 
 
-static void tls_clear_default_passwd_cb(SSL_CTX *ssl_ctx, SSL *ssl)
+#ifndef OPENSSL_NO_STDIO
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
 {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
-	if (ssl) {
-		SSL_set_default_passwd_cb(ssl, NULL);
-		SSL_set_default_passwd_cb_userdata(ssl, NULL);
+	if (!password)
+		return 0;
+	os_strlcpy(buf, (const char *) password, size);
+	return os_strlen(buf);
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
+				    const char *private_key,
+				    const char *private_key_passwd)
+{
+#ifndef OPENSSL_NO_STDIO
+	BIO *bio;
+	EVP_PKEY *pkey;
+	int ret;
+
+	/* First try ASN.1 (DER). */
+	bio = BIO_new_file(private_key, "r");
+	if (!bio)
+		return -1;
+	pkey = d2i_PrivateKey_bio(bio, NULL);
+	BIO_free(bio);
+
+	if (pkey) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
+	} else {
+		/* Try PEM with the provided password. */
+		bio = BIO_new_file(private_key, "r");
+		if (!bio)
+			return -1;
+		pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
+					       (void *) private_key_passwd);
+		BIO_free(bio);
+		if (!pkey)
+			return -1;
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
+		/* Clear errors from the previous failed load. */
+		ERR_clear_error();
 	}
-#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
-	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, NULL);
+
+	if (ssl)
+		ret = SSL_use_PrivateKey(ssl, pkey);
+	else
+		ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
+
+	EVP_PKEY_free(pkey);
+	return ret == 1 ? 0 : -1;
+#else /* OPENSSL_NO_STDIO */
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+	return -1;
+#endif /* OPENSSL_NO_STDIO */
 }
 
 
@@ -3050,32 +3147,11 @@
 				      const u8 *private_key_blob,
 				      size_t private_key_blob_len)
 {
-	SSL_CTX *ssl_ctx = data->ssl;
-	char *passwd;
 	int ok;
 
 	if (private_key == NULL && private_key_blob == NULL)
 		return 0;
 
-	if (private_key_passwd) {
-		passwd = os_strdup(private_key_passwd);
-		if (passwd == NULL)
-			return -1;
-	} else
-		passwd = NULL;
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
-	/*
-	 * In OpenSSL >= 1.1.0f SSL_use_PrivateKey_file() uses the callback
-	 * from the SSL object. See OpenSSL commit d61461a75253.
-	 */
-	SSL_set_default_passwd_cb(conn->ssl, tls_passwd_cb);
-	SSL_set_default_passwd_cb_userdata(conn->ssl, passwd);
-#endif /* >= 1.1.0f && !LibreSSL && !BoringSSL */
-	/* Keep these for OpenSSL < 1.1.0f */
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
-	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
-
 	ok = 0;
 	while (private_key_blob) {
 		if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
@@ -3106,7 +3182,8 @@
 		}
 
 		if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
-					 private_key_blob_len, passwd) == 0) {
+					 private_key_blob_len,
+					 private_key_passwd) == 0) {
 			wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
 				   "OK");
 			ok = 1;
@@ -3117,29 +3194,14 @@
 	}
 
 	while (!ok && private_key) {
-#ifndef OPENSSL_NO_STDIO
-		if (SSL_use_PrivateKey_file(conn->ssl, private_key,
-					    SSL_FILETYPE_ASN1) == 1) {
-			wpa_printf(MSG_DEBUG, "OpenSSL: "
-				   "SSL_use_PrivateKey_File (DER) --> OK");
+		if (tls_use_private_key_file(data, conn->ssl, private_key,
+					     private_key_passwd) == 0) {
 			ok = 1;
 			break;
 		}
 
-		if (SSL_use_PrivateKey_file(conn->ssl, private_key,
-					    SSL_FILETYPE_PEM) == 1) {
-			wpa_printf(MSG_DEBUG, "OpenSSL: "
-				   "SSL_use_PrivateKey_File (PEM) --> OK");
-			ok = 1;
-			break;
-		}
-#else /* OPENSSL_NO_STDIO */
-		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
-			   __func__);
-#endif /* OPENSSL_NO_STDIO */
-
-		if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
-		    == 0) {
+		if (tls_read_pkcs12(data, conn->ssl, private_key,
+				    private_key_passwd) == 0) {
 			wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
 				   "--> OK");
 			ok = 1;
@@ -3159,13 +3221,9 @@
 	if (!ok) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
-		tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
-		os_free(passwd);
 		return -1;
 	}
 	ERR_clear_error();
-	tls_clear_default_passwd_cb(ssl_ctx, conn->ssl);
-	os_free(passwd);
 
 	if (!SSL_check_private_key(conn->ssl)) {
 		tls_show_errors(MSG_INFO, __func__, "Private key failed "
@@ -3183,37 +3241,18 @@
 				  const char *private_key_passwd)
 {
 	SSL_CTX *ssl_ctx = data->ssl;
-	char *passwd;
 
 	if (private_key == NULL)
 		return 0;
 
-	if (private_key_passwd) {
-		passwd = os_strdup(private_key_passwd);
-		if (passwd == NULL)
-			return -1;
-	} else
-		passwd = NULL;
-
-	SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
-	SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
-	if (
-#ifndef OPENSSL_NO_STDIO
-	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
-					SSL_FILETYPE_ASN1) != 1 &&
-	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
-					SSL_FILETYPE_PEM) != 1 &&
-#endif /* OPENSSL_NO_STDIO */
-	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
+	if (tls_use_private_key_file(data, NULL, private_key,
+				     private_key_passwd) &&
+	    tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
-		tls_clear_default_passwd_cb(ssl_ctx, NULL);
-		os_free(passwd);
 		ERR_clear_error();
 		return -1;
 	}
-	tls_clear_default_passwd_cb(ssl_ctx, NULL);
-	os_free(passwd);
 	ERR_clear_error();
 
 	if (!SSL_CTX_check_private_key(ssl_ctx)) {
@@ -3401,7 +3440,9 @@
 #ifdef OPENSSL_NEED_EAP_FAST_PRF
 static int openssl_get_keyblock_size(SSL *ssl)
 {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
 	int md_size;
@@ -3548,8 +3589,7 @@
 
 
 static struct wpabuf *
-openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
-		  int server)
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
 {
 	int res;
 	struct wpabuf *out_data;
@@ -3567,7 +3607,7 @@
 	}
 
 	/* Initiate TLS handshake or continue the existing handshake */
-	if (server)
+	if (conn->server)
 		res = SSL_accept(conn->ssl);
 	else
 		res = SSL_connect(conn->ssl);
@@ -3582,11 +3622,25 @@
 		else {
 			tls_show_errors(MSG_INFO, __func__, "SSL_connect");
 			conn->failed++;
+			if (!conn->server && !conn->client_hello_generated) {
+				/* The server would not understand TLS Alert
+				 * before ClientHello, so simply terminate
+				 * handshake on this type of error case caused
+				 * by a likely internal error like no ciphers
+				 * available. */
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Could not generate ClientHello");
+				conn->write_alerts++;
+				return NULL;
+			}
 		}
 	}
 
+	if (!conn->server && !conn->failed)
+		conn->client_hello_generated = 1;
+
 #ifdef CONFIG_SUITEB
-	if ((conn->flags & TLS_CONN_SUITEB) && !server &&
+	if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
 	    os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
 	    conn->server_dh_prime_len < 3072) {
 		struct tls_context *context = conn->context;
@@ -3689,14 +3743,14 @@
 static struct wpabuf *
 openssl_connection_handshake(struct tls_connection *conn,
 			     const struct wpabuf *in_data,
-			     struct wpabuf **appl_data, int server)
+			     struct wpabuf **appl_data)
 {
 	struct wpabuf *out_data;
 
 	if (appl_data)
 		*appl_data = NULL;
 
-	out_data = openssl_handshake(conn, in_data, server);
+	out_data = openssl_handshake(conn, in_data);
 	if (out_data == NULL)
 		return NULL;
 	if (conn->invalid_hb_used) {
@@ -3733,7 +3787,7 @@
 			 const struct wpabuf *in_data,
 			 struct wpabuf **appl_data)
 {
-	return openssl_connection_handshake(conn, in_data, appl_data, 0);
+	return openssl_connection_handshake(conn, in_data, appl_data);
 }
 
 
@@ -3742,7 +3796,8 @@
 						const struct wpabuf *in_data,
 						struct wpabuf **appl_data)
 {
-	return openssl_connection_handshake(conn, in_data, appl_data, 1);
+	conn->server = 1;
+	return openssl_connection_handshake(conn, in_data, appl_data);
 }
 
 
@@ -4283,6 +4338,7 @@
 	const char *cert_id = params->cert_id;
 	const char *ca_cert_id = params->ca_cert_id;
 	const char *engine_id = params->engine ? params->engine_id : NULL;
+	const char *ciphers;
 
 	if (conn == NULL)
 		return -1;
@@ -4335,6 +4391,17 @@
 		}
 	}
 #endif
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+#ifdef SSL_OP_NO_TLSv1_3
+	if (params->flags & TLS_CONN_EAP_FAST) {
+		/* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
+		 * refuses to start the handshake with the modified ciphersuite
+		 * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
+		wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
+		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
+	}
+#endif /* SSL_OP_NO_TLSv1_3 */
+#endif
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 
 	while ((err = ERR_get_error())) {
@@ -4393,15 +4460,26 @@
 		return -1;
 	}
 
-	if (params->openssl_ciphers &&
-	    SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+	ciphers = params->openssl_ciphers;
+#ifdef CONFIG_SUITEB
+#ifdef OPENSSL_IS_BORINGSSL
+	if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
+		/* BoringSSL removed support for SUITEB192, so need to handle
+		 * this with hardcoded ciphersuite and additional checks for
+		 * other parameters. */
+		ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+#endif /* CONFIG_SUITEB */
+	if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
 		wpa_printf(MSG_INFO,
 			   "OpenSSL: Failed to set cipher string '%s'",
-			   params->openssl_ciphers);
+			   ciphers);
 		return -1;
 	}
 
-	if (tls_set_conn_flags(conn, params->flags) < 0)
+	if (tls_set_conn_flags(conn, params->flags,
+			       params->openssl_ciphers) < 0)
 		return -1;
 
 #ifdef OPENSSL_IS_BORINGSSL
@@ -4506,7 +4584,9 @@
 	struct tls_connection *conn = arg;
 	int ret;
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
 	if (conn == NULL || conn->session_ticket_cb == NULL)
 		return 0;
 
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
new file mode 100644
index 0000000..9544e2f
--- /dev/null
+++ b/src/crypto/tls_wolfssl.c
@@ -0,0 +1,2167 @@
+/*
+ * SSL/TLS interface functions for wolfSSL TLS case
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "tls.h"
+
+/* wolfSSL includes */
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/wolfcrypt/asn.h>
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#define HAVE_AESGCM
+#include <wolfssl/wolfcrypt/aes.h>
+#endif
+
+#if !defined(CONFIG_FIPS) &&                             \
+    (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) ||   \
+     defined(EAP_SERVER_FAST))
+#define WOLFSSL_NEED_EAP_FAST_PRF
+#endif
+
+#define SECRET_LEN          48
+#define RAN_LEN             32
+#define SESSION_TICKET_LEN  256
+
+static int tls_ref_count = 0;
+
+static int tls_ex_idx_session = 0;
+
+
+/* tls input data for wolfSSL Read Callback */
+struct tls_in_data {
+	const struct wpabuf *in_data;
+	size_t consumed; /* how many bytes have we used already */
+};
+
+/* tls output data for wolfSSL Write Callback */
+struct tls_out_data {
+	struct wpabuf *out_data;
+};
+
+struct tls_context {
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
+	char *ocsp_stapling_response;
+};
+
+static struct tls_context *tls_global = NULL;
+
+/* wolfssl tls_connection */
+struct tls_connection {
+	struct tls_context *context;
+	WOLFSSL *ssl;
+	int read_alerts;
+	int write_alerts;
+	int failed;
+	struct tls_in_data input;
+	struct tls_out_data output;
+	char *subject_match;
+	char *alt_subject_match;
+	char *suffix_match;
+	char *domain_match;
+
+	u8 srv_cert_hash[32];
+
+	unsigned char client_random[RAN_LEN];
+	unsigned char server_random[RAN_LEN];
+	unsigned int flags;
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	tls_session_ticket_cb session_ticket_cb;
+	void *session_ticket_cb_ctx;
+	byte session_ticket[SESSION_TICKET_LEN];
+#endif
+	unsigned int ca_cert_verify:1;
+	unsigned int cert_probe:1;
+	unsigned int server_cert_only:1;
+	unsigned int success_data:1;
+
+	WOLFSSL_X509 *peer_cert;
+	WOLFSSL_X509 *peer_issuer;
+	WOLFSSL_X509 *peer_issuer_issuer;
+};
+
+
+static struct tls_context * tls_context_new(const struct tls_config *conf)
+{
+	struct tls_context *context = os_zalloc(sizeof(*context));
+
+	if (!context)
+		return NULL;
+
+	if (conf) {
+		context->event_cb = conf->event_cb;
+		context->cb_ctx = conf->cb_ctx;
+		context->cert_in_cb = conf->cert_in_cb;
+	}
+
+	return context;
+}
+
+
+static void wolfssl_reset_in_data(struct tls_in_data *in,
+				  const struct wpabuf *buf)
+{
+	/* old one not owned by us so don't free */
+	in->in_data = buf;
+	in->consumed = 0;
+}
+
+
+static void wolfssl_reset_out_data(struct tls_out_data *out)
+{
+	/* old one not owned by us so don't free */
+	out->out_data = wpabuf_alloc_copy("", 0);
+}
+
+
+/* wolfSSL I/O Receive CallBack */
+static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+	size_t get = sz;
+	struct tls_in_data *data = ctx;
+
+	if (!data)
+		return -1;
+
+	if (get > (wpabuf_len(data->in_data) - data->consumed))
+		get = wpabuf_len(data->in_data) - data->consumed;
+
+	os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get);
+	data->consumed += get;
+
+	if (get == 0)
+		return -2; /* WANT_READ */
+
+	return (int) get;
+}
+
+
+/* wolfSSL I/O Send CallBack */
+static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx)
+{
+	struct wpabuf *tmp;
+	struct tls_out_data *data = ctx;
+
+	if (!data)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz);
+
+	tmp = wpabuf_alloc_copy(buf, sz);
+	if (!tmp)
+		return -1;
+	data->out_data = wpabuf_concat(data->out_data, tmp);
+	if (!data->out_data)
+		return -1;
+
+	return sz;
+}
+
+
+static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess)
+{
+	struct wpabuf *buf;
+
+	buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (!buf)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Free application session data %p (sess %p)",
+		   buf, sess);
+	wpabuf_free(buf);
+
+	wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+	WOLFSSL_CTX *ssl_ctx;
+	struct tls_context *context;
+	const char *ciphers;
+
+#ifdef DEBUG_WOLFSSL
+	wolfSSL_Debugging_ON();
+#endif /* DEBUG_WOLFSSL */
+
+	context = tls_context_new(conf);
+	if (!context)
+		return NULL;
+
+	if (tls_ref_count == 0) {
+		tls_global = context;
+
+		if (wolfSSL_Init() < 0)
+			return NULL;
+		/* wolfSSL_Debugging_ON(); */
+	}
+
+	tls_ref_count++;
+
+	/* start as client */
+	ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
+	if (!ssl_ctx) {
+		tls_ref_count--;
+		if (context != tls_global)
+			os_free(context);
+		if (tls_ref_count == 0) {
+			os_free(tls_global);
+			tls_global = NULL;
+		}
+	}
+	wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb);
+	wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb);
+	wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context);
+
+	if (conf->tls_session_lifetime > 0) {
+		wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1);
+		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+						   SSL_SESS_CACHE_SERVER);
+		wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime);
+		wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb);
+	} else {
+		wolfSSL_CTX_set_session_cache_mode(ssl_ctx,
+						   SSL_SESS_CACHE_CLIENT);
+	}
+
+	if (conf && conf->openssl_ciphers)
+		ciphers = conf->openssl_ciphers;
+	else
+		ciphers = "ALL";
+	if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		tls_deinit(ssl_ctx);
+		return NULL;
+	}
+
+	return ssl_ctx;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+	struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+	if (context != tls_global)
+		os_free(context);
+
+	wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx);
+
+	tls_ref_count--;
+	if (tls_ref_count == 0) {
+		wolfSSL_Cleanup();
+		os_free(tls_global);
+		tls_global = NULL;
+	}
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+#ifdef DEBUG_WOLFSSL
+#if 0
+	unsigned long err;
+
+	err = wolfSSL_ERR_peek_last_error_line(NULL, NULL);
+	if (err != 0) {
+		wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+			   wolfSSL_ERR_error_string(err, NULL));
+		return 1;
+	}
+#endif
+#endif /* DEBUG_WOLFSSL */
+	return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+	WOLFSSL_CTX *ssl_ctx = tls_ctx;
+	struct tls_connection *conn;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection init");
+
+	conn = os_zalloc(sizeof(*conn));
+	if (!conn)
+		return NULL;
+	conn->ssl = wolfSSL_new(ssl_ctx);
+	if (!conn->ssl) {
+		os_free(conn);
+		return NULL;
+	}
+
+	wolfSSL_SetIOReadCtx(conn->ssl,  &conn->input);
+	wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output);
+	wolfSSL_set_ex_data(conn->ssl, 0, conn);
+	conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0);
+
+	/* Need randoms post-hanshake for EAP-FAST, export key and deriving
+	 * session ID in EAP methods. */
+	wolfSSL_KeepArrays(conn->ssl);
+	wolfSSL_KeepHandshakeResources(conn->ssl);
+	wolfSSL_UseClientSuites(conn->ssl);
+
+	return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection deinit");
+
+	/* parts */
+	wolfSSL_free(conn->ssl);
+	os_free(conn->subject_match);
+	os_free(conn->alt_subject_match);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
+
+	/* self */
+	os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? wolfSSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *session;
+
+	if (!conn)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: connection shutdown");
+
+	/* Set quiet as OpenSSL does */
+	wolfSSL_set_quiet_shutdown(conn->ssl, 1);
+	wolfSSL_shutdown(conn->ssl);
+
+	session = wolfSSL_get_session(conn->ssl);
+	if (wolfSSL_clear(conn->ssl) != 1)
+		return -1;
+	wolfSSL_set_session(conn->ssl, session);
+
+	return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+					    const char *subject_match,
+					    const char *alt_subject_match,
+					    const char *suffix_match,
+					    const char *domain_match)
+{
+	os_free(conn->subject_match);
+	conn->subject_match = NULL;
+	if (subject_match) {
+		conn->subject_match = os_strdup(subject_match);
+		if (!conn->subject_match)
+			return -1;
+	}
+
+	os_free(conn->alt_subject_match);
+	conn->alt_subject_match = NULL;
+	if (alt_subject_match) {
+		conn->alt_subject_match = os_strdup(alt_subject_match);
+		if (!conn->alt_subject_match)
+			return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (suffix_match) {
+		conn->suffix_match = os_strdup(suffix_match);
+		if (!conn->suffix_match)
+			return -1;
+	}
+
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (!conn->domain_match)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file,
+			     const u8 *dh_blob, size_t blob_len)
+{
+	if (!dh_file && !dh_blob)
+		return 0;
+
+	wolfSSL_set_accept_state(conn->ssl);
+
+	if (dh_blob) {
+		if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len,
+					    SSL_FILETYPE_ASN1) < 0) {
+			wpa_printf(MSG_INFO, "SSL: use DH DER blob failed");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use DH blob OK");
+		return 0;
+	}
+
+	if (dh_file) {
+		wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file);
+		if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+					  SSL_FILETYPE_PEM) < 0) {
+			wpa_printf(MSG_INFO, "SSL: use DH PEM file failed");
+			if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file,
+						  SSL_FILETYPE_ASN1) < 0) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use DH DER file failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use DH file OK");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+				      const char *client_cert,
+				      const u8 *client_cert_blob,
+				      size_t blob_len)
+{
+	if (!client_cert && !client_cert_blob)
+		return 0;
+
+	if (client_cert_blob) {
+		if (wolfSSL_use_certificate_chain_buffer_format(
+			    conn->ssl, client_cert_blob, blob_len,
+			    SSL_FILETYPE_ASN1) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use client cert DER blob failed");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK");
+		return 0;
+	}
+
+	if (client_cert) {
+		if (wolfSSL_use_certificate_chain_file(conn->ssl,
+						       client_cert) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use client cert PEM file failed");
+			if (wolfSSL_use_certificate_chain_file_format(
+				    conn->ssl, client_cert,
+				    SSL_FILETYPE_ASN1) < 0) {
+				wpa_printf(MSG_INFO,
+					   "SSL: use client cert DER file failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use client cert file OK");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+	if (!password)
+		return 0;
+	os_strlcpy(buf, (char *) password, size);
+	return os_strlen(buf);
+}
+
+
+static int tls_connection_private_key(void *tls_ctx,
+				      struct tls_connection *conn,
+				      const char *private_key,
+				      const char *private_key_passwd,
+				      const u8 *private_key_blob,
+				      size_t blob_len)
+{
+	WOLFSSL_CTX *ctx = tls_ctx;
+	char *passwd = NULL;
+	int ok = 0;
+
+	if (!private_key && !private_key_blob)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (!passwd)
+			return -1;
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+	wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+	if (private_key_blob) {
+		if (wolfSSL_use_PrivateKey_buffer(conn->ssl,
+						  private_key_blob, blob_len,
+						  SSL_FILETYPE_ASN1) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use private DER blob failed");
+		} else {
+			wpa_printf(MSG_DEBUG, "SSL: use private key blob OK");
+			ok = 1;
+		}
+	}
+
+	if (!ok && private_key) {
+		if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+						SSL_FILETYPE_PEM) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: use private key PEM file failed");
+			if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key,
+							SSL_FILETYPE_ASN1) < 0)
+			{
+				wpa_printf(MSG_INFO,
+					   "SSL: use private key DER file failed");
+			} else {
+				ok = 1;
+			}
+		} else {
+			ok = 1;
+		}
+
+		if (ok)
+			wpa_printf(MSG_DEBUG, "SSL: use private key file OK");
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+	os_free(passwd);
+
+	if (!ok)
+		return -1;
+
+	return 0;
+}
+
+
+static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type,
+					   const char *value, size_t len)
+{
+	WOLFSSL_ASN1_OBJECT *gen;
+	void *ext;
+	int found = 0;
+	int i;
+
+	ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+	for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+		gen = wolfSSL_sk_value(ext, i);
+		if (gen->type != type)
+			continue;
+		if (os_strlen((char *) gen->obj) == len &&
+		    os_memcmp(value, gen->obj, len) == 0)
+			found++;
+	}
+
+	wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+	return found;
+}
+
+
+static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
+{
+	int type;
+	const char *pos, *end;
+	size_t len;
+
+	pos = match;
+	do {
+		if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+			type = GEN_EMAIL;
+			pos += 6;
+		} else if (os_strncmp(pos, "DNS:", 4) == 0) {
+			type = GEN_DNS;
+			pos += 4;
+		} else if (os_strncmp(pos, "URI:", 4) == 0) {
+			type = GEN_URI;
+			pos += 4;
+		} else {
+			wpa_printf(MSG_INFO,
+				   "TLS: Invalid altSubjectName match '%s'",
+				   pos);
+			return 0;
+		}
+		end = os_strchr(pos, ';');
+		while (end) {
+			if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+			    os_strncmp(end + 1, "DNS:", 4) == 0 ||
+			    os_strncmp(end + 1, "URI:", 4) == 0)
+				break;
+			end = os_strchr(end + 1, ';');
+		}
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		if (tls_match_alt_subject_component(cert, type, pos, len) > 0)
+			return 1;
+		pos = end + 1;
+	} while (end);
+
+	return 0;
+}
+
+
+static int domain_suffix_match(const char *val, size_t len, const char *match,
+			       int full)
+{
+	size_t i, match_len;
+
+	/* Check for embedded nuls that could mess up suffix matching */
+	for (i = 0; i < len; i++) {
+		if (val[i] == '\0') {
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Embedded null in a string - reject");
+			return 0;
+		}
+	}
+
+	match_len = os_strlen(match);
+	if (match_len > len || (full && match_len != len))
+		return 0;
+
+	if (os_strncasecmp(val + len - match_len, match, match_len) != 0)
+		return 0; /* no match */
+
+	if (match_len == len)
+		return 1; /* exact match */
+
+	if (val[len - match_len - 1] == '.')
+		return 1; /* full label match completes suffix match */
+
+	wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+	return 0;
+}
+
+
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+	WOLFSSL_ASN1_OBJECT *gen;
+	void *ext;
+	int i;
+	int j;
+	int dns_name = 0;
+	WOLFSSL_X509_NAME *name;
+
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "" : "suffix ", match);
+
+	ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL);
+
+	for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
+		gen = wolfSSL_sk_value(ext, j);
+		if (gen->type != ALT_NAMES_OID)
+			continue;
+		dns_name++;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+				  gen->obj, os_strlen((char *)gen->obj));
+		if (domain_suffix_match((const char *) gen->obj,
+					os_strlen((char *) gen->obj), match,
+					full) == 1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
+			wolfSSL_sk_ASN1_OBJECT_free(ext);
+			return 1;
+		}
+	}
+	wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+	if (dns_name) {
+		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+		return 0;
+	}
+
+	name = wolfSSL_X509_get_subject_name(cert);
+	i = -1;
+	for (;;) {
+		WOLFSSL_X509_NAME_ENTRY *e;
+		WOLFSSL_ASN1_STRING *cn;
+
+		i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME,
+						       i);
+		if (i == -1)
+			break;
+		e = wolfSSL_X509_NAME_get_entry(name, i);
+		if (!e)
+			continue;
+		cn = wolfSSL_X509_NAME_ENTRY_get_data(e);
+		if (!cn)
+			continue;
+		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+				  cn->data, cn->length);
+		if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+		{
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "" : "suffix ");
+	return 0;
+}
+
+
+static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
+{
+	switch (err) {
+	case X509_V_ERR_CERT_REVOKED:
+		return TLS_FAIL_REVOKED;
+	case ASN_BEFORE_DATE_E:
+	case X509_V_ERR_CERT_NOT_YET_VALID:
+	case X509_V_ERR_CRL_NOT_YET_VALID:
+		return TLS_FAIL_NOT_YET_VALID;
+	case ASN_AFTER_DATE_E:
+	case X509_V_ERR_CERT_HAS_EXPIRED:
+	case X509_V_ERR_CRL_HAS_EXPIRED:
+		return TLS_FAIL_EXPIRED;
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	case X509_V_ERR_UNABLE_TO_GET_CRL:
+	case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+	case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+	case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+	case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+	case X509_V_ERR_INVALID_CA:
+		return TLS_FAIL_UNTRUSTED;
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+	case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+	case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+	case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+	case X509_V_ERR_CERT_UNTRUSTED:
+	case X509_V_ERR_CERT_REJECTED:
+		return TLS_FAIL_BAD_CERTIFICATE;
+	default:
+		return TLS_FAIL_UNSPECIFIED;
+	}
+}
+
+
+static const char * wolfssl_tls_err_string(int err, const char *err_str)
+{
+	switch (err) {
+	case ASN_BEFORE_DATE_E:
+		return "certificate is not yet valid";
+	case ASN_AFTER_DATE_E:
+		return "certificate has expired";
+	default:
+		return err_str;
+	}
+}
+
+
+static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert)
+{
+	struct wpabuf *buf = NULL;
+	const u8 *data;
+	int cert_len;
+
+	data = wolfSSL_X509_get_der(cert, &cert_len);
+	if (!data)
+		buf = wpabuf_alloc_copy(data, cert_len);
+
+	return buf;
+}
+
+
+static void wolfssl_tls_fail_event(struct tls_connection *conn,
+				   WOLFSSL_X509 *err_cert, int err, int depth,
+				   const char *subject, const char *err_str,
+				   enum tls_fail_reason reason)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert = NULL;
+	struct tls_context *context = conn->context;
+
+	if (!context->event_cb)
+		return;
+
+	cert = get_x509_cert(err_cert);
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+		reason : wolfssl_tls_fail_reason(err);
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject;
+	ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str);
+	ev.cert_fail.cert = cert;
+	context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert);
+}
+
+
+static void wolfssl_tls_cert_event(struct tls_connection *conn,
+				   WOLFSSL_X509 *err_cert, int depth,
+				   const char *subject)
+{
+	struct wpabuf *cert = NULL;
+	union tls_event_data ev;
+	struct tls_context *context = conn->context;
+	char *alt_subject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_alt_subject = 0;
+	WOLFSSL_ASN1_OBJECT *gen;
+	void *ext;
+	int i;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+	if (!context->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+	    context->cert_in_cb) {
+		cert = get_x509_cert(err_cert);
+		ev.peer_cert.cert = cert;
+	}
+
+#ifdef CONFIG_SHA256
+	if (cert) {
+		const u8 *addr[1];
+		size_t len[1];
+
+		addr[0] = wpabuf_head(cert);
+		len[0] = wpabuf_len(cert);
+		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 = depth;
+	ev.peer_cert.subject = subject;
+
+	ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL);
+	for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) {
+		char *pos;
+
+		if (num_alt_subject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = wolfSSL_sk_value((void *) ext, i);
+		if (gen->type != GEN_EMAIL &&
+		    gen->type != GEN_DNS &&
+		    gen->type != GEN_URI)
+			continue;
+
+		pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1);
+		if (!pos)
+			break;
+		alt_subject[num_alt_subject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj));
+		pos += os_strlen((char *)gen->obj);
+		*pos = '\0';
+	}
+	wolfSSL_sk_ASN1_OBJECT_free(ext);
+
+	for (alt = 0; alt < num_alt_subject; alt++)
+		ev.peer_cert.altsubject[alt] = alt_subject[alt];
+	ev.peer_cert.num_altsubject = num_alt_subject;
+
+	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert);
+	for (alt = 0; alt < num_alt_subject; alt++)
+		os_free(alt_subject[alt]);
+}
+
+
+static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx)
+{
+	char buf[256];
+	WOLFSSL_X509 *err_cert;
+	int err, depth;
+	WOLFSSL *ssl;
+	struct tls_connection *conn;
+	struct tls_context *context;
+	char *match, *altmatch, *suffix_match, *domain_match;
+	const char *err_str;
+
+	err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx);
+	if (!err_cert) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: No Cert");
+		return 0;
+	}
+
+	err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx);
+	depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx);
+	ssl = wolfSSL_X509_STORE_CTX_get_ex_data(
+		x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx());
+	wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf,
+				  sizeof(buf));
+
+	conn = wolfSSL_get_ex_data(ssl, 0);
+	if (!conn) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data");
+		return 0;
+	}
+
+	if (depth == 0)
+		conn->peer_cert = err_cert;
+	else if (depth == 1)
+		conn->peer_issuer = err_cert;
+	else if (depth == 2)
+		conn->peer_issuer_issuer = err_cert;
+
+	context = conn->context;
+	match = conn->subject_match;
+	altmatch = conn->alt_subject_match;
+	suffix_match = conn->suffix_match;
+	domain_match = conn->domain_match;
+
+	if (!preverify_ok && !conn->ca_cert_verify)
+		preverify_ok = 1;
+	if (!preverify_ok && depth > 0 && conn->server_cert_only)
+		preverify_ok = 1;
+	if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
+	    (err == X509_V_ERR_CERT_HAS_EXPIRED ||
+	     err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E ||
+	     err == X509_V_ERR_CERT_NOT_YET_VALID)) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Ignore certificate validity time mismatch");
+		preverify_ok = 1;
+	}
+
+	err_str = wolfSSL_X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+	/*
+	 * Do not require preverify_ok so we can explicity allow otherwise
+	 * invalid pinned server certificates.
+	 */
+	if (depth == 0 && conn->server_cert_only) {
+		struct wpabuf *cert;
+
+		cert = get_x509_cert(err_cert);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Could not fetch server certificate data");
+			preverify_ok = 0;
+		} else {
+			u8 hash[32];
+			const u8 *addr[1];
+			size_t len[1];
+
+			addr[0] = wpabuf_head(cert);
+			len[0] = wpabuf_len(cert);
+			if (sha256_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+				err_str = "Server certificate mismatch";
+				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+				preverify_ok = 0;
+			} else if (!preverify_ok) {
+				/*
+				 * Certificate matches pinned certificate, allow
+				 * regardless of other problems.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "wolfSSL: Ignore validation issues for a pinned server certificate");
+				preverify_ok = 1;
+			}
+			wpabuf_free(cert);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	if (!preverify_ok) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'",
+			   err, err_str, depth, buf);
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       err_str, TLS_FAIL_UNSPECIFIED);
+		return preverify_ok;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+		   __func__, preverify_ok, err, err_str,
+		   conn->ca_cert_verify, depth, buf);
+	if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Subject '%s' did not match with '%s'",
+			   buf, match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Subject mismatch",
+				       TLS_FAIL_SUBJECT_MISMATCH);
+	} else if (depth == 0 && altmatch &&
+		   !tls_match_alt_subject(err_cert, altmatch)) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: altSubjectName match '%s' not found",
+			   altmatch);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "AltSubject mismatch",
+				       TLS_FAIL_ALTSUBJECT_MISMATCH);
+	} else if (depth == 0 && suffix_match &&
+		   !tls_match_suffix(err_cert, suffix_match, 0)) {
+		wpa_printf(MSG_WARNING,
+			   "TLS: Domain suffix match '%s' not found",
+			   suffix_match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain suffix mismatch",
+				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+	} else if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
+	} else {
+		wolfssl_tls_cert_event(conn, err_cert, depth, buf);
+	}
+
+	if (conn->cert_probe && preverify_ok && depth == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Reject server certificate on probe-only run");
+		preverify_ok = 0;
+		wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Server certificate chain probe",
+				       TLS_FAIL_SERVER_CHAIN_PROBE);
+	}
+
+#ifdef HAVE_OCSP_WOLFSSL
+	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+				      conn->peer_issuer,
+				      conn->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "certificate revoked",
+					       TLS_FAIL_REVOKED);
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD &&
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+			preverify_ok = 0;
+			wolfssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
+	}
+#endif /* HAVE_OCSP_WOLFSSL */
+	if (depth == 0 && preverify_ok && context->event_cb != NULL)
+		context->event_cb(context->cb_ctx,
+				  TLS_CERT_CHAIN_SUCCESS, NULL);
+
+	return preverify_ok;
+}
+
+
+static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn,
+				  const char *ca_cert,
+				  const u8 *ca_cert_blob, size_t blob_len,
+				  const char *ca_path)
+{
+	WOLFSSL_CTX *ctx = tls_ctx;
+
+	wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+	conn->ca_cert_verify = 1;
+
+	if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Probe for server certificate chain");
+		conn->cert_probe = 1;
+		conn->ca_cert_verify = 0;
+		return 0;
+	}
+
+	if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+		const char *pos = ca_cert + 7;
+
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Unsupported ca_cert hash value '%s'",
+				   ca_cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'",
+				   ca_cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'",
+				   ca_cert);
+			return -1;
+		}
+		conn->server_cert_only = 1;
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Checking only server certificate match");
+		return 0;
+#else /* CONFIG_SHA256 */
+		wpa_printf(MSG_INFO,
+			   "No SHA256 included in the build - cannot validate server certificate hash");
+		return -1;
+#endif /* CONFIG_SHA256 */
+	}
+
+	if (ca_cert_blob) {
+		if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len,
+						   SSL_FILETYPE_ASN1) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO, "SSL: failed to load CA blob");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK");
+		return 0;
+	}
+
+	if (ca_cert || ca_path) {
+		WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new();
+
+		if (!cm) {
+			wpa_printf(MSG_INFO,
+				   "SSL: failed to create certificate store");
+			return -1;
+		}
+		wolfSSL_CTX_set_cert_store(ctx, cm);
+
+		if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) !=
+		    SSL_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "SSL: failed to load ca_cert as PEM");
+
+			if (!ca_cert)
+				return -1;
+
+			if (wolfSSL_CTX_der_load_verify_locations(
+				    ctx, ca_cert, SSL_FILETYPE_ASN1) !=
+			    SSL_SUCCESS) {
+				wpa_printf(MSG_INFO,
+					   "SSL: failed to load ca_cert as DER");
+				return -1;
+			}
+		}
+		return 0;
+	}
+
+	conn->ca_cert_verify = 0;
+	return 0;
+}
+
+
+static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags)
+{
+#ifdef HAVE_SESSION_TICKET
+#if 0
+	if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET))
+		wolfSSL_UseSessionTicket(ssl);
+#endif
+#endif /* HAVE_SESSION_TICKET */
+
+	if (flags & TLS_CONN_DISABLE_TLSv1_0)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1);
+	if (flags & TLS_CONN_DISABLE_TLSv1_1)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+	if (flags & TLS_CONN_DISABLE_TLSv1_2)
+		wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+			      const struct tls_connection_params *params)
+{
+	wpa_printf(MSG_DEBUG, "SSL: set params");
+
+	if (tls_connection_set_subject_match(conn, params->subject_match,
+					     params->altsubject_match,
+					     params->suffix_match,
+					     params->domain_match) < 0) {
+		wpa_printf(MSG_INFO, "Error setting subject match");
+		return -1;
+	}
+
+	if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+				   params->ca_cert_blob,
+				   params->ca_cert_blob_len,
+				   params->ca_path) < 0) {
+		wpa_printf(MSG_INFO, "Error setting CA cert");
+		return -1;
+	}
+
+	if (tls_connection_client_cert(conn, params->client_cert,
+				       params->client_cert_blob,
+				       params->client_cert_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "Error setting client cert");
+		return -1;
+	}
+
+	if (tls_connection_private_key(tls_ctx, conn, params->private_key,
+				       params->private_key_passwd,
+				       params->private_key_blob,
+				       params->private_key_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "Error setting private key");
+		return -1;
+	}
+
+	if (tls_connection_dh(conn, params->dh_file, params->dh_blob,
+			      params->dh_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "Error setting DH");
+		return -1;
+	}
+
+	if (params->openssl_ciphers &&
+	    wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+	tls_set_conn_flags(conn->ssl, params->flags);
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP,
+					    WOLFSSL_CSR_OCSP_USE_NONCE) !=
+		    SSL_SUCCESS)
+			return -1;
+		wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+	}
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		if (wolfSSL_UseOCSPStaplingV2(conn->ssl,
+					      WOLFSSL_CSR2_OCSP_MULTI, 0) !=
+		    SSL_SUCCESS)
+			return -1;
+		wolfSSL_CTX_EnableOCSP(tls_ctx, 0);
+	}
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
+    !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
+#ifdef HAVE_OCSP
+	if (params->flags & TLS_CONN_REQUEST_OCSP)
+		wolfSSL_CTX_EnableOCSP(ctx, 0);
+#else /* HAVE_OCSP */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: No OCSP support included - reject configuration");
+		return -1;
+	}
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: No OCSP support included - allow optional OCSP case to continue");
+	}
+#endif /* HAVE_OCSP */
+#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST &&
+	* !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+
+	conn->flags = params->flags;
+
+	return 0;
+}
+
+
+static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (ca_cert) {
+		if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1)
+		{
+			wpa_printf(MSG_WARNING,
+				   "Failed to load root certificates");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "TLS: Trusted root certificate(s) loaded");
+	}
+
+	return 0;
+}
+
+
+static int tls_global_client_cert(void *ssl_ctx, const char *client_cert)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (!client_cert)
+		return 0;
+
+	if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert,
+							  SSL_FILETYPE_ASN1) !=
+	    SSL_SUCCESS &&
+	    wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) !=
+	    SSL_SUCCESS) {
+		wpa_printf(MSG_INFO, "Failed to load client certificate");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s",
+		   client_cert);
+
+	return 0;
+}
+
+
+static int tls_global_private_key(void *ssl_ctx, const char *private_key,
+				  const char *private_key_passwd)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+	char *passwd = NULL;
+	int ret = 0;
+
+	if (!private_key)
+		return 0;
+
+	if (private_key_passwd) {
+		passwd = os_strdup(private_key_passwd);
+		if (!passwd)
+			return -1;
+	}
+
+	wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb);
+	wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd);
+
+	if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+					    SSL_FILETYPE_ASN1) != 1 &&
+	    wolfSSL_CTX_use_PrivateKey_file(ctx, private_key,
+					    SSL_FILETYPE_PEM) != 1) {
+		wpa_printf(MSG_INFO, "Failed to load private key");
+		ret = -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "SSL: Loaded global private key");
+
+	os_free(passwd);
+	wolfSSL_CTX_set_default_passwd_cb(ctx, NULL);
+
+	return ret;
+}
+
+
+static int tls_global_dh(void *ssl_ctx, const char *dh_file,
+			 const u8 *dh_blob, size_t blob_len)
+{
+	WOLFSSL_CTX *ctx = ssl_ctx;
+
+	if (!dh_file && !dh_blob)
+		return 0;
+
+	if (dh_blob) {
+		if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len,
+						SSL_FILETYPE_ASN1) < 0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: global use DH DER blob failed");
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK");
+		return 0;
+	}
+
+	if (dh_file) {
+		if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) <
+		    0) {
+			wpa_printf(MSG_INFO,
+				   "SSL: global use DH PEM file failed");
+			if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file,
+						      SSL_FILETYPE_ASN1) < 0) {
+				wpa_printf(MSG_INFO,
+					   "SSL: global use DH DER file failed");
+				return -1;
+			}
+		}
+		wpa_printf(MSG_DEBUG, "SSL: global use DH file OK");
+		return 0;
+	}
+
+	return 0;
+}
+
+
+#ifdef HAVE_OCSP
+
+int ocsp_status_cb(void *unused, const char *url, int url_sz,
+		   unsigned char *request, int request_sz,
+		   unsigned char **response)
+{
+	size_t len;
+
+	(void) unused;
+
+	if (!url) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: OCSP status callback - no response configured");
+		*response = NULL;
+		return 0;
+	}
+
+	*response = (unsigned char *) os_readfile(url, &len);
+	if (!*response) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: OCSP status callback - could not read response file");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: OCSP status callback - send cached response");
+	return len;
+}
+
+
+void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response)
+{
+	os_free(response);
+}
+
+#endif /* HAVE_OCSP */
+
+
+int tls_global_set_params(void *tls_ctx,
+			  const struct tls_connection_params *params)
+{
+	wpa_printf(MSG_DEBUG, "SSL: global set params");
+
+	if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
+		wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
+			   params->ca_cert);
+		return -1;
+	}
+
+	if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SSL: Failed to load client cert file '%s'",
+			   params->client_cert);
+		return -1;
+	}
+
+	if (tls_global_private_key(tls_ctx, params->private_key,
+				   params->private_key_passwd) < 0) {
+		wpa_printf(MSG_INFO,
+			   "SSL: Failed to load private key file '%s'",
+			   params->private_key);
+		return -1;
+	}
+
+	if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob,
+			  params->dh_blob_len) < 0) {
+		wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'",
+			   params->dh_file);
+		return -1;
+	}
+
+	if (params->openssl_ciphers &&
+	    wolfSSL_CTX_set_cipher_list(tls_ctx,
+					params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "wolfSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
+#ifdef HAVE_SESSION_TICKET
+	/* Session ticket is off by default - can't disable once on. */
+	if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
+		wolfSSL_CTX_UseSessionTicket(tls_ctx);
+#endif /* HAVE_SESSION_TICKET */
+
+#ifdef HAVE_OCSP
+	if (params->ocsp_stapling_response) {
+		wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx,
+						params->ocsp_stapling_response);
+		wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb,
+				       ocsp_resp_free_cb, NULL);
+	}
+#endif /* HAVE_OCSP */
+
+	return 0;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+	wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
+
+	if (check_crl) {
+		/* Hack to Enable CRLs. */
+		wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM);
+	}
+
+	return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
+{
+	if (!conn)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer);
+
+	if (verify_peer) {
+		conn->ca_cert_verify = 1;
+		wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+				   SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+				   tls_verify_cb);
+	} else {
+		conn->ca_cert_verify = 0;
+		wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+	}
+
+	wolfSSL_set_accept_state(conn->ssl);
+
+	/* TODO: do we need to fake a session like OpenSSL does here? */
+
+	return 0;
+}
+
+
+static struct wpabuf * wolfssl_handshake(struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 int server)
+{
+	int res;
+
+	wolfssl_reset_out_data(&conn->output);
+
+	/* Initiate TLS handshake or continue the existing handshake */
+	if (server) {
+		wolfSSL_set_accept_state(conn->ssl);
+		res = wolfSSL_accept(conn->ssl);
+		wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res);
+	} else {
+		wolfSSL_set_connect_state(conn->ssl);
+		res = wolfSSL_connect(conn->ssl);
+		wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res);
+	}
+
+	if (res != 1) {
+		int err = wolfSSL_get_error(conn->ssl, res);
+
+		if (err == SSL_ERROR_WANT_READ) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: wolfSSL_connect - want more data");
+		} else if (err == SSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: wolfSSL_connect - want to write");
+		} else {
+			char msg[80];
+
+			wpa_printf(MSG_DEBUG,
+				   "SSL: wolfSSL_connect - failed %s",
+				   wolfSSL_ERR_error_string(err, msg));
+			conn->failed++;
+		}
+	}
+
+	return conn->output.out_data;
+}
+
+
+static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn,
+					     size_t max_len)
+{
+	int res;
+	struct wpabuf *appl_data = wpabuf_alloc(max_len + 100);
+
+	if (!appl_data)
+		return NULL;
+
+	res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data),
+			   wpabuf_size(appl_data));
+	if (res < 0) {
+		int err = wolfSSL_get_error(conn->ssl, res);
+
+		if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+			wpa_printf(MSG_DEBUG,
+				   "SSL: No Application Data included");
+		} else {
+			char msg[80];
+
+			wpa_printf(MSG_DEBUG,
+				   "Failed to read possible Application Data %s",
+				   wolfSSL_ERR_error_string(err, msg));
+		}
+
+		wpabuf_free(appl_data);
+		return NULL;
+	}
+
+	wpabuf_put(appl_data, res);
+	wpa_hexdump_buf_key(MSG_MSGDUMP,
+			    "SSL: Application Data in Finished message",
+			    appl_data);
+	return appl_data;
+}
+
+
+static struct wpabuf *
+wolfssl_connection_handshake(struct tls_connection *conn,
+			     const struct wpabuf *in_data,
+			     struct wpabuf **appl_data, int server)
+{
+	struct wpabuf *out_data;
+
+	wolfssl_reset_in_data(&conn->input, in_data);
+
+	if (appl_data)
+		*appl_data = NULL;
+
+	out_data = wolfssl_handshake(conn, in_data, server);
+	if (!out_data)
+		return NULL;
+
+	if (wolfSSL_is_init_finished(conn->ssl)) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: Handshake finished - resumed=%d",
+			   tls_connection_resumed(NULL, conn));
+		if (appl_data && in_data)
+			*appl_data = wolfssl_get_appl_data(conn,
+							   wpabuf_len(in_data));
+	}
+
+	return out_data;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+					 struct tls_connection *conn,
+					 const struct wpabuf *in_data,
+					 struct wpabuf **appl_data)
+{
+	return wolfssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+						struct tls_connection *conn,
+						const struct wpabuf *in_data,
+						struct wpabuf **appl_data)
+{
+	return wolfssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+
+	if (!conn)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data));
+
+	wolfssl_reset_out_data(&conn->output);
+
+	res = wolfSSL_write(conn->ssl, wpabuf_head(in_data),
+			    wpabuf_len(in_data));
+	if (res < 0) {
+		int  err = wolfSSL_get_error(conn->ssl, res);
+		char msg[80];
+
+		wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s",
+			   wolfSSL_ERR_error_string(err, msg));
+		return NULL;
+	}
+
+	return conn->output.out_data;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+				       struct tls_connection *conn,
+				       const struct wpabuf *in_data)
+{
+	int res;
+	struct wpabuf *buf;
+
+	if (!conn)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "SSL: decrypt");
+
+	wolfssl_reset_in_data(&conn->input, in_data);
+
+	/* Read decrypted data for further processing */
+	/*
+	 * Even though we try to disable TLS compression, it is possible that
+	 * this cannot be done with all TLS libraries. Add extra buffer space
+	 * to handle the possibility of the decrypted data being longer than
+	 * input data.
+	 */
+	buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+	if (!buf)
+		return NULL;
+	res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "Decryption failed - SSL_read");
+		wpabuf_free(buf);
+		return NULL;
+	}
+	wpabuf_put(buf, res);
+
+	wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf));
+
+	return buf;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+	return conn ? wolfSSL_session_reused(conn->ssl) : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+				   u8 *ciphers)
+{
+	char buf[128], *pos, *end;
+	u8 *c;
+	int ret;
+
+	if (!conn || !conn->ssl || !ciphers)
+		return -1;
+
+	buf[0] = '\0';
+	pos = buf;
+	end = pos + sizeof(buf);
+
+	c = ciphers;
+	while (*c != TLS_CIPHER_NONE) {
+		const char *suite;
+
+		switch (*c) {
+		case TLS_CIPHER_RC4_SHA:
+			suite = "RC4-SHA";
+			break;
+		case TLS_CIPHER_AES128_SHA:
+			suite = "AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES128_SHA:
+			suite = "DHE-RSA-AES128-SHA";
+			break;
+		case TLS_CIPHER_ANON_DH_AES128_SHA:
+			suite = "ADH-AES128-SHA";
+			break;
+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
+			suite = "DHE-RSA-AES256-SHA";
+			break;
+		case TLS_CIPHER_AES256_SHA:
+			suite = "AES256-SHA";
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Unsupported cipher selection: %d", *c);
+			return -1;
+		}
+		ret = os_snprintf(pos, end - pos, ":%s", suite);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+
+		c++;
+	}
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1);
+
+	if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+		wpa_printf(MSG_DEBUG, "Cipher suite configuration failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+		   char *buf, size_t buflen)
+{
+	WOLFSSL_CIPHER *cipher;
+	const char *name;
+
+	if (!conn || !conn->ssl)
+		return -1;
+
+	cipher = wolfSSL_get_current_cipher(conn->ssl);
+	if (!cipher)
+		return -1;
+
+	name = wolfSSL_CIPHER_get_name(cipher);
+	if (!name)
+		return -1;
+
+	if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0)
+		os_strlcpy(buf, "RC4-SHA", buflen);
+	else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0)
+		os_strlcpy(buf, "ADH-AES128-SHA", buflen);
+	else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0)
+		os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen);
+	else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0)
+		os_strlcpy(buf, "AES256-SHA", buflen);
+	else
+		os_strlcpy(buf, name, buflen);
+
+	return 0;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+				     struct tls_connection *conn)
+{
+	/* no empty fragments in wolfSSL for now */
+	return 0;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	/* TODO: this is not incremented anywhere */
+	return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+				    struct tls_connection *conn)
+{
+	if (!conn)
+		return -1;
+
+	/* TODO: this is not incremented anywhere */
+	return conn->write_alerts;
+}
+
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s",
+			   WOLFSSL_VERSION, wolfSSL_lib_version());
+}
+
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	const char *name;
+
+	if (!conn || !conn->ssl)
+		return -1;
+
+	name = wolfSSL_get_version(conn->ssl);
+	if (!name)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
+{
+	WOLFSSL *ssl;
+
+	if (!conn || !keys)
+		return -1;
+	ssl = conn->ssl;
+	if (!ssl)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = conn->client_random;
+	keys->client_random_len = wolfSSL_get_client_random(
+		ssl, conn->client_random, sizeof(conn->client_random));
+	keys->server_random = conn->server_random;
+	keys->server_random_len = wolfSSL_get_server_random(
+		ssl, conn->server_random, sizeof(conn->server_random));
+
+	return 0;
+}
+
+
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, u8 *out, size_t out_len)
+{
+	if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
+		return -1;
+	return 0;
+}
+
+
+#define SEED_LEN	(RAN_LEN + RAN_LEN)
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	byte seed[SEED_LEN];
+	int ret = -1;
+	WOLFSSL *ssl;
+	byte *tmp_out;
+	byte *_out;
+	int skip = 0;
+	byte *master_key;
+	unsigned int master_key_len;
+	byte *server_random;
+	unsigned int server_len;
+	byte *client_random;
+	unsigned int client_len;
+
+	if (!conn || !conn->ssl)
+		return -1;
+	ssl = conn->ssl;
+
+	skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) +
+		    wolfSSL_GetIVSize(ssl));
+
+	tmp_out = os_malloc(skip + out_len);
+	if (!tmp_out)
+		return -1;
+	_out = tmp_out;
+
+	wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random,
+			 &server_len, &client_random, &client_len);
+	os_memcpy(seed, server_random, RAN_LEN);
+	os_memcpy(seed + RAN_LEN, client_random, RAN_LEN);
+
+	if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) {
+		tls_prf_sha256(master_key, master_key_len,
+			       "key expansion", seed, sizeof(seed),
+			       _out, skip + out_len);
+		ret = 0;
+	} else {
+		ret = tls_prf_sha1_md5(master_key, master_key_len,
+				       "key expansion", seed, sizeof(seed),
+				       _out, skip + out_len);
+	}
+
+	os_memset(master_key, 0, master_key_len);
+	if (ret == 0)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip + out_len);
+
+	return ret;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+				    int ext_type, const u8 *data,
+				    size_t data_len)
+{
+	(void) ssl_ctx;
+
+	if (!conn || !conn->ssl || ext_type != 35)
+		return -1;
+
+	if (wolfSSL_set_SessionTicket(conn->ssl, data,
+				      (unsigned int) data_len) != 1)
+		return -1;
+
+	return 0;
+}
+
+
+static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg)
+{
+	struct tls_connection *conn = arg;
+	int ret;
+	unsigned char client_random[RAN_LEN];
+	unsigned char server_random[RAN_LEN];
+	word32 ticket_len = sizeof(conn->session_ticket);
+
+	if (!conn || !conn->session_ticket_cb)
+		return 1;
+
+	if (wolfSSL_get_client_random(s, client_random,
+				      sizeof(client_random)) == 0 ||
+	    wolfSSL_get_server_random(s, server_random,
+				      sizeof(server_random)) == 0 ||
+	    wolfSSL_get_SessionTicket(s, conn->session_ticket,
+				      &ticket_len) != 1)
+		return 1;
+
+	if (ticket_len == 0)
+		return 0;
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket, ticket_len,
+				      client_random, server_random, secret);
+	if (ret <= 0)
+		return 1;
+
+	*secret_len = SECRET_LEN;
+	return 0;
+}
+
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+					 struct tls_connection *conn,
+					 tls_session_ticket_cb cb,
+					 void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	conn->session_ticket_cb = cb;
+	conn->session_ticket_cb_ctx = ctx;
+
+	if (cb) {
+		if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+						  conn) != 1)
+			return -1;
+	} else {
+		if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+			return -1;
+	}
+
+	return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+	return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Success data accepted for resumed session");
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *sess;
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess)
+		return;
+
+	wolfSSL_SSL_SESSION_set_timeout(sess, 0);
+	wpa_printf(MSG_DEBUG,
+		   "wolfSSL: Removed cached session to disable session resumption");
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	WOLFSSL_SESSION *sess;
+	struct wpabuf *old;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Set success data");
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess) {
+		wpa_printf(MSG_DEBUG,
+			   "wolfSSL: No session found for success data");
+		goto fail;
+	}
+
+	old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (old) {
+		wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p",
+			   old);
+		wpabuf_free(old);
+	}
+	if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data);
+	conn->success_data = 1;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data");
+	wpabuf_free(data);
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	WOLFSSL_SESSION *sess;
+
+	wpa_printf(MSG_DEBUG, "wolfSSL: Get success data");
+
+	sess = wolfSSL_get_session(conn->ssl);
+	if (!sess)
+		return NULL;
+	return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}