Skip to content
This repository has been archived by the owner on Feb 8, 2023. It is now read-only.

Verifying HTTPS page in smart contract | Bounty: USD $500 in NEAR #15

Closed
2 tasks
ilblackdragon opened this issue Jun 27, 2020 · 19 comments
Closed
2 tasks
Labels
development rust tribe Cross-post bounty to tribe

Comments

@ilblackdragon
Copy link
Member

ilblackdragon commented Jun 27, 2020

Description

HTTPS pages are signed with SSL certificates.
It would be great to have a Rust tool that will be able to verify that some payload was signed by certificate and decrypt it to be able to parse the content of a page.

Acceptance Criteria

Bounty

USD $500 in NEAR

@ilblackdragon ilblackdragon added tribe Cross-post bounty to tribe development rust labels Jun 27, 2020
@jakestutzman jakestutzman changed the title Verifying HTTPS page in smart contract Verifying HTTPS page in smart contract | Bounty: USD $500 in NEAR Jul 24, 2020
@wjw12
Copy link

wjw12 commented Nov 14, 2020

I'm interested in this bounty issue. Are there any specific requirements of the contract? What interface functions do I need to implement?

@alexeyneu
Copy link

just google "HTTPS pages are signed with SSL certificates"

https://www.ssl.com/article/ssl-tls-handshake-overview/

picture , shared secret key stuff
no chances

@benjamingreenberg
Copy link

If I am understanding correctly, then I'm not sure if what you are asking for is possible.

coinbase.com's certificate is used to verify that the server the client is communicating with is really coinbase.com, but it is not used to encrypt the content of the page. The content is encrypted using a symmetric encryption method, and a shared key. During the initial stages of communication, the server sends its certificate to the client which includes a public key. The client uses the public key to encrypt a "pre-key" which it sends back to the server. The server decrypts the pre-key, and both the client and server use the pre-key to generate the same symmetric encryption key. The symmetric encryption key is used to encrypt/decrypt all further communication, including the client's request for the page and the content of the page. Because of this, I can't see how the smart contract would be able to verify that the content given to it from the command-line tool came from coinbase.com.

Perhaps the smart-contract could verify the certificate by doing something like this (not necessarily in this order):

  • Receive the certificate from the command-line tool, along with the string "coinbase.com"
  • Check that the certificate is for the domain coinbase.com
  • Verify the signature on the certificate
  • Verify that the certificate is still valid (e.g. has not expired, been revoked, etc)
  • Check that the certificate path contains a certificate signed by a certificate authority that the smart-contract trusts ("trust anchor")
  • Verify that, starting from the certificate signed by the trust anchor, each certificate's private key was used to issue the next certificate in the path, all the way down to the coinbase.com certificate.
  • Reply back to the command-line tool with the result, which would include the reason why a certificate failed validation, if applicable

References:
Browsers and Certificate Validation
The SSL/TLS Handshake: an Overview

@ilblackdragon
Copy link
Member Author

ilblackdragon commented Mar 16, 2021 via email

@benjamingreenberg
Copy link

I think there are still some issues.

The server does not send the master/session key back to the client. The server and client each use the pre-master key to generate the session key independently.

Another issue is that by making the pre-master key visible to everyone, you introduce the possibility of a man-in-the-middle attack;

  1. Attacker gets pre-master key, uses it to generate the session key
  2. Attacker intercepts the data from the server to the client
  3. Attacker decrypts the data and changes it
  4. Attacker encrypts the manipulated version, and sends it to the client

By the way, it's possible that you introduce a vulnerability by using info from the certificate chain to generate the pre-master key. By limiting how the pre-master key is generated in this way, it may reduce the number of possible keys so that a computer could try them all fast enough to do a man-in-the-middle attack.

If you want multiple clients to communicate with the server using the same session key, then you will need to store additional data on the smart contract, like the encryption protocol the client/server agreed to use. It also needs to store whatever the server requires the client to use to identify itself, like a session-id, access token, cookie, etc. That's assuming the server even keeps/manages sessions. If the server doesn't, then every connection to the server would require negotiating and generating a new pre-master key and master key.

I think it might help if you explained what your ultimate goal is, and/or what a real-world use-case looks like, and/or what problem you are trying to solve.

@ilblackdragon
Copy link
Member Author

ilblackdragon commented Mar 16, 2021 via email

@ilblackdragon
Copy link
Member Author

ilblackdragon commented Mar 16, 2021 via email

@benjamingreenberg
Copy link

I'm brand new to blockchains, smart contracts, etc, so I am not sure if something like this is possible, and if it is possible, if it can be done using one smart-contract or you would need multiple smart-contracts for the different tasks.

  1. Rust app (App) initiates an https connection to coinbase.com like it was a regular browser (including list of encryption protocols)
  2. coinbase.com responds with their certificate, and their choice of encryption protocol
  3. Rust app sends to the smart-contract the url, the certificate, and the encryption protocol.
  4. Smart-contract verifies the certificate
  5. Smart-contract generates pre-master key
  6. Smart-contract generates session key using pre-master key
  7. Smart-contract creates the response to coinbase.com, which includes the pre-master key, and encrypts the response using the public key in the certificate
  8. Smart-contract creates a hash of the session key
  9. Smart-contract stores the pre-master key and session key in a secure/private location (they are not public yet)
  10. Smart-contract publishes the hash of the session key, encrypted package with pre-master key, along with any other relevant details (url, encryption protocol, certificate, etc?)
  11. App gets the encrypted response with the pre-master key and sends it to coinbase.com
  12. coinbase.com generates the session key using the pre-master key
  13. coinbase.com creates the "I got it" message, encrypts it using the session key and sends it to the App
  14. App forwards the response from coinbase.com to the smart-contract, along with the actual request it wants to send to coinbase.com (i.e. GET https://coinbase.com)
  15. Smart-contract retrieves the session key from storage and verifies that the key hasn't been altered using the published hash
  16. Smart-contract decrypts the message from coinbase.com and publishes the encrypted and decrypted versions, along with relevant info
  17. Smart-contract encrypts the request that the App wants to send to coinbase.com, and publishes the encrypted and decrypted versions, along with relevant info
  18. App gets the encrypted request and sends it to coinbase.com
  19. coinbase.com sends encrypted content to the App
  20. App forwards the encrypted content to the Smart-contract
  21. Smart-contract gets the session key, and verifies it with the hash
  22. Smart-contract decrypts the content, and publishes the encrypted and decrypted versions
  23. App parses the content and, if necessary, sends more requests to be encrypted by the smart-contract, forwarding them to coinbase.com, and forwarding the response to the smart-contract
  24. Process repeats until App has gotten all the content it requires from coinbase.com
  25. App instructs the smart-contract to publish the session key and the pre-master key so that anyone can
    a: Use the hash to verify the session key is the same as the one the smart-contract generated at step 6
    b: Use the pre-master key to verify that it was used to create the session key
    c: Use the session key to verify that the published encrypted messages contain the same content found in the corresponding published decrypted messages

It's important that the pre-master key and session key are kept private/secret/secure until the end of the session, because this helps ensure that the only entities that can encrypt messages are the smart-contract(s) and coinbase.com. Well, technically anyone with access to the private key whose public key the smart-contract used to encrypt the pre-master key can encrypt messages. Also, anyone with access to wherever the smart-contract stores the pre-master key and session-key will be able to encrypt messages, so this is a potential weak spot that needs to be addressed (a way to be reasonably certain that only the smart-contract can read the pre-master key and session key before the session ends and they are published).

There is one other major issue to overcome, and which is alluded to in steps 23 & 24. Content on a webpage is often added dynamically after the initial page load. A good example of this is https://coinbase.com/price/bitcoin

image

The left part of the screenshot is the fully rendered page in chrome. The part on the right is the content received after the initial request for the page. As you can see, the most important info for that page is not included in the initial content received. The browser needs to request more content from the server (and often other servers too) to fully render the page. If your goal is to eventually publish or store a fully rendered page, or at least a part that needs to be rendered dynamically, then the App will need to make a series of requests, not just one. This makes things more complicated, but not impossible.

@cameron-NEAR
Copy link

Hey @ilblackdragon, did you get a chance to read @benjamingreenberg's approach to completing the bounty? If it makes sense, i'm happy to assign the bounty to them.

@benjamingreenberg - are you still interested in completing this bounty?

@abacabadabacaba
Copy link
Collaborator

@cameron-NEAR This approach won't work, because there is no way a smart contract can store or work with secret data. In general, AFAIK there is currently no feasible way to verifiably get a HTTPS page from a smart contract (unless the server supports SXG, which is a tiny minority of the servers). For this reason, I think that this bounty is misguided.

@mfornet
Copy link
Member

mfornet commented Sep 17, 2021

@abacabadabacaba if I understand correctly we don't need secret data here. We can establish a private connection with coinbase.com, but ultimately we can publish all the secret data generated publicly to be verified on-chain, given that none of this secret data actually compromises the client (i.e it is not logged in or any personal information is used).

The reason I think this is doable disregarding how the protocol works is the following: I can connect with a clean browser (or python script) to coinbase and fetch some information securely that I can trust (without providing any credentials). All the validation done in my end by the browser/script can be as well done in the blockchain.

@benjamingreenberg
Copy link

fetch some information securely that I can trust (without providing any credentials). All the validation done in my end by the browser/script can be as well done in the blockchain.

How can the smart contract independently validate that the data you gave it actually came from coinbase.com?

@mfornet
Copy link
Member

mfornet commented Sep 17, 2021

How can the smart contract independently validate that the data you gave it actually came from coinbase.com?

I haven't look in all technical details of the HTTPS protocol, but the idea is that the browser is only aware of the public key from the root authority, so this key is as well added to the blockchain (this step is only required once). Other public information that is known ahead of time needs to be added on chain. I expect this information doesn't change often so it only needs to be submitted once.

When we connect to coinbase and fetch some information we record all data that is sent and received, and all data that is generated locally (even private keys or random numbers used). Once the communication ended we close the channel, and all the recorded information is submitted on-chain. This information should be enough to validate and deserialise the final data.

The previous approach is described in an inefficient way (regarding the amount of information and computation required on-chain), since I'm only trying to say that it is possible. With more in-depth knowledge about how HTTPS actually work, we can optimise which information needs to be recorded and submitted on-chain.

@benjamingreenberg
Copy link

The previous approach is described in an inefficient way (regarding the amount of information and computation required on-chain), since I'm only trying to say that it is possible. With more in-depth knowledge about how HTTPS actually work, we can optimise which information needs to be recorded and submitted on-chain.

I completely agree that the previous approach is inefficient. That was my way of saying it wasn't something I thought I could do :)

I hope you do come up with a solution! The only advice I would give you, is to work under the assumption that the system that is running your script has been compromised, and a malicious 3rd party can make any changes it wants to incoming data before your script sees it, and to outgoing data your script sends to the smart contract.

@abacabadabacaba
Copy link
Collaborator

@mfornet You probably assume that the data coming over HTTPS is signed. It is not. Instead, its authenticity is protected using a scheme called Message Authentication Code (MAC). MAC is more efficient to compute and verify. However, unlike digital signatures, it uses the same key for generation and verification. Thus, anyone who can verify a MAC can also forge it. When HTTPS is used normally, only the client and the server know the key, so the data is secure. However, the client can not prove this to anyone, because the client can generate a valid MAC for any data.

@boris-kolar
Copy link

It's possible in theory (by submitting the whole raw TLS encrypted bytes exchanged to the contract), but it's exceptionally difficult. The solution would require a customized web browser. More in the $500k price range in my opinion.

@ilblackdragon
Copy link
Member Author

Hi @boris-kolar, the customized browser doesn't really make sense as far as I understand, as the requirement here is to run on public blockchain and allow smart contracts to verify the content of retrieved information.

As @abacabadabacaba mentioned, the actual session encryption used is established after a handshake, which means that wouldn't work with public blockchain.

If there was a standard where web browsers sign with public/private cryptography the outgoing message - this would allow for a public blockchain to verify that indeed information received was signed via one of such agents.

Which means, we should close this bounty at this point.

The alternative approach to this problem is to extend NEAR Protocol to have off-consensus work by validators: the smart contract can request validators to do some work, which they will run outside of consensus and publish back on-chain. Similar to Substrate off-chain workers.

@mfornet
Copy link
Member

mfornet commented Oct 31, 2021

Another option suggested by @abacabadabacaba (also complex), is to use secure multi-party computation to run the exchange protocol in such a way that several nodes runs as a client, and none of them has access to the secret key. If this is possible, then only the server will be able to sign the messages and it can be used as a proof that can be verified on-chain.

Some problems with this idea:

  • Secure multi-party protocols are potentially too slow for running TLS (which potentially has some timeouts)
  • Who is going to run the node working as clients? If these nodes collude they can recover the private key and sign any other message.
  • It is complex to implement

@frol
Copy link
Collaborator

frol commented Dec 16, 2021

Just for extra reference, another discussion arrived to the same conclusions that it is impossible to do by design.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
development rust tribe Cross-post bounty to tribe
Projects
None yet
Development

No branches or pull requests

9 participants