You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
What do SSL_get_finished/SSL_get_peer_finished return? #5509
Hi! I’m opening an issue because there is a pull request to expose these two functions, which are public OpenSSL API (afaict), in Node.js.
There is almost no documentation on what the contents of the returned data are, or what guarantees could be made about them. They seem to be used in real-world applications, though.
Could anybody help with explaining? Even hints for where to start looking would be appreciated a lot.
*) Clean up 'Finished' handling, and add functions SSL_get_finished and
SSL_get_peer_finished to allow applications to obtain the latest
Finished messages sent to the peer or expected from the peer,
respectively. (SSL_get_peer_finished is usually the Finished message
actually received from the peer, otherwise the protocol will be aborted.)
As the Finished message are message digests of the complete handshake
(with a total of 192 bits for TLS 1.0 and more for SSL 3.0), they can
be used for external authentication procedures when the authentication
provided by SSL/TLS is not desired or is not enough.
[Bodo Moeller]
There are probably betters ways to do channel-binding, although I forget which PR has it. Look for secret-exporter maybe? @addaleax you should push back on your requestor and ask what they want to do. There might be a better API for it.
@richsalz, I know this is a somewhat ancient thread and I hate to resurrect the dead, but in the interest of improving our code and cleaning up the "mess" I am going to give it a shot.
I am curious to know how you'd go about ensuring that two endpoints A and B can determine that they are talking directly to each other through one SSL session and not across two SSL connections that are intermediated by a proxy.
You have several restrictions, but of particular interest are that:
You don't know the certificate of the other endpoint; and
You can't rely on a user "authorizing" unknown certificates (like in a web browser).
In our case, each endpoint has a public/private keypair (the node identity). For our purposes, what we want is to know the identity of the other endpoint and to know that we are talking directly to that endpoint.
Our solution: In the initial phases of a connection, each side independently calculates a "cookie" shared with the other side (derived by independently hashing both finished messages with SHA512, and then XORing the two values together) and signs the resulting hash with its node identity.
The result: each endpoint knows the node identity of the other endpoint and is guaranteed that the SSL link is direct and not intermediated across two sessions through a proxy. Proxies that simply shuffle around encrypted packets aren't a concern in this scenario.
Would your recommendation be to skip the complicated "cookie" calculation and use SSL_get_tls_unique instead? Or something else entirely?
Thanks for the reply, @richsalz. I guess I'm not following because, in essence, that is exaclty what we do.
Once the TLS connection is up, one of the two endpoints issues an HTTP GET with a request to Upgrade to our own protocol. The headers of that request (and the corresponding response) each include two fields: a public identity and a signature over a piece of data both endpoints have access to with the private key corresponding to the claimed identity.
I assume you take exception with the data that we sign which we derive from the finished messages by "reaching inside" the SSL session and pulling that data out. There's a reason for that: we need something that isn't under the sole control of either of the endpoints, since it's essential that the value being signed be random and not under the control of either party (coercing someone to sign an arbitrary value is bad).
We could achieve this by having each side pick a random 256-bit value and then passing it to the other end; each endpoint would, then, combine the two values using XOR in a deterministic way and sign the result. But that would require the addition of at least two additional messages after the HTTP upgrade is negotiated.
I'll grant you that that's not a huge burden. But even if we were willing to do that, there's still something else to consider: this solution would be vulnerable to an attack: an attacker could convince us both we're talking directly to each other when we are, in reality, talking through him over two independent TLS connections: it's an almost classic "triple handshake" attack.
Using the TLS connection state effectively mitigates this scenario, strongly binds the TLS connection to its and eliminates the need for sending additional messages and complicating an already complex codebase.
I don't mean to be difficult and I'm happy to redesign this scheme, but I'll need a little more than just "wow, what a mess."
Sorry if I offended you, but reaching inside the protocol to extra data is not a good idea unless you own the protocol implementation. And I did give more suggestions, as did Kurt. Here's more details: make the node identity an X25519 keypair, and do a X25519 key exchange at the application layer. Look at channel bindings.
You have, I think, designed yourself into a box. The only way to avoid an active MitM attack is to have authentication at least on the server side, and your requirements reject the common way (X509 certs and WebPKI) of doing that.
I wasn't offended. I genuinely want to understand your concerns and to come up with a way to address them and I do appreciate your responses. I'm not arrogant or self-conceited enough to believe I have all the answers, and I do very much welcome feedback, especially from a subject matter expert, such as yourself.
[...] reaching inside the protocol to extra data is not a good idea unless you own the protocol implementation
I generally agree with you, although I guess what one does with the data matters. If I was, say, parsing the finished messages myself or somesuch, that would be bad.
[...] make the node identity an X25519 keypair, and do a X25519 key exchange at the application layer.
The node identity already is a secp256k1 keypair. The whole point of this scheme is to strongly "bind" the SSL session to the secp256k1 keypairs each endpoint claims to have, which, in turn ensures that given two endpoints A and B, both A and B can be sure that the TLS link is directly between each other and not via a third party C with independent TLS sessions A <-> C <-> B.
I agree that this scheme doesn't prevent a MITM attack in the general and broadest sense, but it does mean that each endpoints knows what the node identity of the other side is. This means that C can't convince A he's talking directly to B, or vice versa. And I hope you'll agree that that is, in itself, useful.
I can't think of a way to do this purely at the "application layer" (i.e. without reaching into the TLS session and getting some data out of it) especially given the restrictions we have (can't mandate certificates, for example).
If there's a "standard" way for us to get the benefits of the current scheme considering the restrictions we're operating under, I'm all over it and I'll code it up today. If the answer is tls-unique (at least as outlined in RFC-5929) then I have some concerns, but maybe that's not the latest-and-greatest.
Activity
kroeckx commentedon Mar 4, 2018
This is what is currently in ssl.h:
addaleax commentedon Mar 4, 2018
@kroeckx Is there any information on what a “Finished” message contains and/or how a program could meaningfully use it?
kroeckx commentedon Mar 4, 2018
The CHANGES files mentions:
addaleax commentedon Mar 4, 2018
Great, thank you a lot! This sounds like something we could almost directly carry over into our own documentation. (/cc @codedot)
(I guess I could have come up with the idea of looking up why these methods were added in the first place myself… well, next time.)
kroeckx commentedon Mar 4, 2018
I guess we would also like to properly document that function, so a pull request is always welcome.
kroeckx commentedon Mar 4, 2018
As clear from the pull requests you pointed to, this can be used for tls-unique channel binding from rfc5929
richsalz commentedon Mar 4, 2018
There are probably betters ways to do channel-binding, although I forget which PR has it. Look for secret-exporter maybe? @addaleax you should push back on your requestor and ask what they want to do. There might be a better API for it.
addaleax commentedon Mar 4, 2018
@richsalz The requester wants this for Ripple, where apparently usage of the Finished messages is built into the protocol. :(
richsalz commentedon Mar 4, 2018
nbougalis commentedon Jul 2, 2019
@richsalz, I know this is a somewhat ancient thread and I hate to resurrect the dead, but in the interest of improving our code and cleaning up the "mess" I am going to give it a shot.
I am curious to know how you'd go about ensuring that two endpoints A and B can determine that they are talking directly to each other through one SSL session and not across two SSL connections that are intermediated by a proxy.
You have several restrictions, but of particular interest are that:
In our case, each endpoint has a public/private keypair (the node identity). For our purposes, what we want is to know the identity of the other endpoint and to know that we are talking directly to that endpoint.
Our solution: In the initial phases of a connection, each side independently calculates a "cookie" shared with the other side (derived by independently hashing both finished messages with SHA512, and then XORing the two values together) and signs the resulting hash with its node identity.
The result: each endpoint knows the node identity of the other endpoint and is guaranteed that the SSL link is direct and not intermediated across two sessions through a proxy. Proxies that simply shuffle around encrypted packets aren't a concern in this scenario.
Would your recommendation be to skip the complicated "cookie" calculation and use
SSL_get_tls_uniqueinstead? Or something else entirely?richsalz commentedon Jul 2, 2019
Treat the TLS connection as an authenticated, DH-style connection. Then do the key exchange as your first application-level messages.
nbougalis commentedon Jul 9, 2019
Thanks for the reply, @richsalz. I guess I'm not following because, in essence, that is exaclty what we do.
Once the TLS connection is up, one of the two endpoints issues an
HTTP GETwith a request toUpgradeto our own protocol. The headers of that request (and the corresponding response) each include two fields: a public identity and a signature over a piece of data both endpoints have access to with the private key corresponding to the claimed identity.For example:
I assume you take exception with the data that we sign which we derive from the finished messages by "reaching inside" the SSL session and pulling that data out. There's a reason for that: we need something that isn't under the sole control of either of the endpoints, since it's essential that the value being signed be random and not under the control of either party (coercing someone to sign an arbitrary value is bad).
We could achieve this by having each side pick a random 256-bit value and then passing it to the other end; each endpoint would, then, combine the two values using XOR in a deterministic way and sign the result. But that would require the addition of at least two additional messages after the HTTP upgrade is negotiated.
I'll grant you that that's not a huge burden. But even if we were willing to do that, there's still something else to consider: this solution would be vulnerable to an attack: an attacker could convince us both we're talking directly to each other when we are, in reality, talking through him over two independent TLS connections: it's an almost classic "triple handshake" attack.
Using the TLS connection state effectively mitigates this scenario, strongly binds the TLS connection to its and eliminates the need for sending additional messages and complicating an already complex codebase.
I don't mean to be difficult and I'm happy to redesign this scheme, but I'll need a little more than just "wow, what a mess."
richsalz commentedon Jul 9, 2019
Sorry if I offended you, but reaching inside the protocol to extra data is not a good idea unless you own the protocol implementation. And I did give more suggestions, as did Kurt. Here's more details: make the node identity an X25519 keypair, and do a X25519 key exchange at the application layer. Look at channel bindings.
You have, I think, designed yourself into a box. The only way to avoid an active MitM attack is to have authentication at least on the server side, and your requirements reject the common way (X509 certs and WebPKI) of doing that.
nbougalis commentedon Jul 9, 2019
I wasn't offended. I genuinely want to understand your concerns and to come up with a way to address them and I do appreciate your responses. I'm not arrogant or self-conceited enough to believe I have all the answers, and I do very much welcome feedback, especially from a subject matter expert, such as yourself.
I generally agree with you, although I guess what one does with the data matters. If I was, say, parsing the finished messages myself or somesuch, that would be bad.
The node identity already is a secp256k1 keypair. The whole point of this scheme is to strongly "bind" the SSL session to the secp256k1 keypairs each endpoint claims to have, which, in turn ensures that given two endpoints A and B, both A and B can be sure that the TLS link is directly between each other and not via a third party C with independent TLS sessions A <-> C <-> B.
I agree that this scheme doesn't prevent a MITM attack in the general and broadest sense, but it does mean that each endpoints knows what the node identity of the other side is. This means that C can't convince A he's talking directly to B, or vice versa. And I hope you'll agree that that is, in itself, useful.
I can't think of a way to do this purely at the "application layer" (i.e. without reaching into the TLS session and getting some data out of it) especially given the restrictions we have (can't mandate certificates, for example).
If there's a "standard" way for us to get the benefits of the current scheme considering the restrictions we're operating under, I'm all over it and I'll code it up today. If the answer is tls-unique (at least as outlined in RFC-5929) then I have some concerns, but maybe that's not the latest-and-greatest.
richsalz commentedon Jul 11, 2019
A couple of thoughts:
good luck.
Harden MITM detection during session establishment:
Harden MITM detection during session establishment:
Harden MITM detection during session establishment: