I am building a EAP-TLS authentication client (802.1X EAPOL). The requirement thus far is just EAP-TLS. I am using the FreeRadius server to test against, and it is using TLS 1.1, so that is the transport version I'm developing to.
Because this supplicant is using a network stack that is custom, and on a small embedded device, I cannot use the OpenSSL libs, since they do all the handshaking as blackbox socket level for communication. Also, the supplicants I found all contain code that is strongly intertwined with the AAA and Authenticator. I don't have that much space to add all that source (in addition to making support more difficult)
It's good to learn while rolling your own anyway.
So, as I dig in, I am seeing things that are not consistent with the RFC's or simply not defined.
Before asking the WPA-Supplicant mailing questions about trying to "roll my own" I first wanted to politely ask simply "Is this a good place to ask technical questions, or is there another resource". I was politely ignored. So I am posting here.
Consulting RFC 3579, 3748, 4346, 5216 and others, I have performed MD5 challenge authentication to the server. Success with understanding EAP, Ethernet packets, fragments, etc.
On to TLS, I have successfully received, assembled and parsed the TLS Server Hello handshake. (RFC 5216 only defines a TLS header over EAP, while RFC 4346 explains the full TLS handshake, but EAP uses a subset of it.) Since I have access to the test server cert and key, I have also verified ciphering a premaster secret with the public key, and it deciphers correctly with the private key.
Now I am trying to build the full Client handshake, piece by piece, adding blocks to the message. And finding things that I cannot resolve.
Below, I am referring to RFC 4346 for the following TLS 1.1 messages.
In Section 4.3, vectors are defined with the specific "Presentation language". Using [] for fixed known lengths, and <..> for variables lengths that must contain a leading value indicating the size.
Section 7.4.7 defines the Client Key Exchange. In my case, it is simply an RSA, therefore is a "EncryptedPreMasterSecret". Section 7.4.7.1 defines the EncryptedPreMasterSecret for RSA, which is the version and random numbers, totaling 48 bytes in length.
The definition does not make any claim about this being a variable vector. And yet, the debug information from FreeRadius rejects it if it does not have a two byte host order value of the length.
(27) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(27) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(27) eap_tls: <<< recv TLS 1.0 Handshake [length 0104], ClientKeyExchange
(27) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decode_error
(27) eap_tls: ERROR: TLS Alert write:fatal:decode error
tls: TLS_accept: Error in error
(27) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read): error:1419F09F:SSL routines:tls_process_cke_rsa:length mismatch
(27) eap_tls: ERROR: System call (I/O) error (-1)
(27) eap_tls: ERROR: TLS receive handshake failed during operation
(27) eap_tls: ERROR: [eaptls process] = fail
Interestingly enough, Wireshark doesn't seem to mind if it's missing.
By adding the two byte length, I am past this failure. However, I don't like that it's not following the specification I read.
Is this described someplace else that I am missing?
So I seem to have gotten past the PremasterSecret, and have moved on to the Certificate Verify message. As for it, section 7.4.8 defines the certificate verify which contains the MD5 and SHA hashes, referring back to section 7.4.3. The definition in 7.4.3 defines what a "Signature" is, and does not make any claim about this being a variable vector.
In fact, section 7.4.3 very clearly indicates that is it a known length vector (i.e. uses fixed lengths [16] and [20]). And yet, Wireshark expects a two byte header here too and reports an error if it is not present.
So I added it the two byte header, Wireshark is happy.
But that is still not following the specification. The known max length is 36 bytes, which fits in one 8 bit number. So requiring two bytes violates the specification which says in section 4.3:
The length will be in the form of a number consuming as many bytes as required to hold the vector’s specified maximum (ceiling) length.
However even with that change, the server is still complaining.
(13) eap_tls: TLS-Client-Cert-X509v3-Basic-Constraints += "CA:FALSE"
(13) eap_tls: TLS_accept: SSLv3/TLS read client certificate
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 0106], ClientKeyExchange
(13) eap_tls: TLS_accept: SSLv3/TLS read client key exchange
(13) eap_tls: <<< recv TLS 1.0 Handshake [length 002a], CertificateVerify
(13) eap_tls: >>> send TLS 1.0 Alert [length 0002], fatal decrypt_error
(13) eap_tls: ERROR: TLS Alert write:fatal:decrypt error
tls: TLS_accept: Error in error
(13) eap_tls: ERROR: Failed in __FUNCTION__ (SSL_read)
(13) eap_tls: ERROR: error:04091077:rsa routines:int_rsa_verify:wrong signature length
(13) eap_tls: ERROR: error:1417B07B:SSL routines:tls_process_cert_verify:bad signature
The server says "decrypt_error". Is this verification message supposed to be encrypted? The spec doesn't say so. Grepping the server source, I cannot find that text message anywhere. It's been hidden very well, making it difficult to find the function that is rejecting it.
And if it is supposed to be encrypted, what key is used? The client private key or the server public key?
Again, is this described someplace else that I am missing? It's not following the specification on two fronts (using a variable length, and two bytes where one is sufficient).
In section 7.4.9 the finished message is defined using the Presentation language containing "[0..11]", which description is not defined anywhere in section 4. Is it a typo meant to be a variable length vector <0..11>? Or what does [0..11] mean here?
Next major question:
Am I making this too hard?
Are there OpenSSL calls which will simply take the reassembled TLS handshake, and create the client handshake reply, populating it into a supplied buffer? Again, because the supplicant client on an embedded device uses it's own network stack, I can't use OpenSSL's internal socket call for the handshake.
The OpenSSL documentation is lacking in many areas, and if such a API exists I have not stumbled across it.
Thanks for any answers and advice.
-Scott