livepeer / go-livepeer

Official Go implementation of the Livepeer protocol
http://livepeer.org
MIT License
547 stars 173 forks source link

TLS with Ethereum Keys #437

Open j0sh opened 6 years ago

j0sh commented 6 years ago

(This post assumes some familiarity with the mechanics of TLS because it was getting long enough, but I've added some links around important concepts. Happy to elaborate on anything.)

Justification

The proposal for Broadcaster-Transcoder Networking v2 does not address authenticity and integrity of the protocol messages. Both the v2 proposal and basicnet are vulnerable to MITM-style attacks (injection, tampering, withholding, etc), and do not guarantee message confidentiality in transit.

While the smart-contract protocol has its own defenses against fraudulent activity, there are still concerns external to the smart contract. For example: the broadcaster may have confidentiality concerns, and may want to be reassured they are talking to the expected transcoder. TLS provides both confidentiality and authentication.

Orchestrators might want to ensure that a broadcaster's query for a job is legitimate. For example, they may want to avoid distributing transcode credentials unnecessarily. Over an unauthenticated connection, replay attacks are possible. Hence, mutual authentication is a nice property to have.

Transcoders, especially those part of a public pool, may want to ensure that broadcasters are communicating with the correct transcoder and eg, not being spoofed by a competing transcoder in the pool. Going further with mutual authentication, the transcoder could ensure a broadcaster's transcode request is legitimate by looking at a broadcaster certificate which has been issued by the orchestrator.

Right now, we sign certain protocol messages on an ad-hoc basis[1][2]. This leads to uneven coverage and more difficulty reasoning about the security properties of the network protocol. By using TLS, we avoid the need to manually sign messages, except for those signatures intended for the blockchain. This substantially reduces the uncertainty and error surface within the v2 protocol.

Livepeer

The fundamental challenge in using TLS with Livepeer is in key management and the absence of a truly decentralized root CA. However, the blockchain gives us the essential ingredients to establish a PKI.

We take the Ethereum keys and generate "root" CAs by self-signing certificates. Since the broadcaster and orchestrator keys are already known, the keys can be used to verify signatures on incoming certificates during the TLS handshake phase. Thereafter, TLS gives us authentication, integrity and confidentiality for the duration of the message exchange.

Discovering the Root

The problem here is that most TLS implementations expect a pre-installed list of root certificates. We don't have such a list for all our transcoders (or broadcasters). However, all we need to do is verify that a cert's root is signed with the keys that we expect. This requires some trickery, but I think we can get away with it.

An alternative is to use ENS to store the certificates and retrieve them on-demand, but those may be too large to store on-chain, several KB. Or the client could simply request a copy of the certificate from the server via plaintext HTTP, prior to the actual HTTPS connection.

While we can sign root certificates using Ethereum keys, we also want to take advantage of X.509's chain-of-trust property, for two reasons: to keep the root keys in cold storage, and to delegate transcoding authority. The result being that each transcoder pool ends up with its own mini-PKI that can be traced back to the on-chain orchestrator address.

Orchestrator Intermediate Certificates

Transcoder Certificates

Transcoders generate a CSR, have cert issued by the orchestrator's intermediate cert.

Transcoders may be issued multiple certs if they are part of several pools (one per orchestrator).

Broadcaster Certificates

Once we have transcoders acting independently of the orchestrator, mutual auth can then be used for the transcoder to authenticate the broadcaster.

Certificate Revocation

As of yet undefined, especially for revoking orchestrator certs.

Mutual (Broadcaster) Authentication

Not strictly necessary, but certainly doable with TLS as described earlier. The alternatives (eg, transcoder authentication tokens) would work just as well. The concern about replay attacks on the Job request is minor, since any data messages would still be sent encrypted.

[1] Job request from the broadcaster to the orchestrator [2] TranscodeSub request in basicnet, MsgSubReq has signing as a todo

yondonfu commented 6 years ago

Generally, I like the property described of keeping any Ethereum keys registered on-chain in cold storage with other authorized keys being used for signing networking protocol messages. In theory, I think the parties could also discard keys used in a connection once its closed to reduce key management overhead.

The transcoder certificates seem like they would work with a trusted pool operated by an orchestrator, but just noting that we might not want to rely on the orchestrator as a CA in a untrusted pool.

Perhaps something to keep in mind is that in TLS 1.3 the curves allowed for the ECDSA signature scheme are limited to secp256r1, secp384r1 and secp521r1[1], but Ethereum uses the secp256k1 curve[2]. Looks like TLS 1.2 does not contain any restrictions on the curves for ECDSA so this wouldn't be a problem at the moment since TLS 1.3 isn't in golang yet, but might be if TLS 1.3 is used later on.

[1] https://timtaubert.de/blog/2016/07/the-evolution-of-signatures-in-tls/ [2] https://ethereum.github.io/yellowpaper/paper.pdf

j0sh commented 6 years ago

The transcoder certificates seem like they would work with a trusted pool operated by an orchestrator, but just noting that we might not want to rely on the orchestrator as a CA in a untrusted pool.

Right now, orchestrators need to be trusted, so even for a public pool composed of transcoders that don't trust one another, they all need to trust the orchestrator. Some pools might devise a "provably fair" sharing system where transcoders can be assured that they're receiving an equal slice of the work/rewards from the orchestrator, but the transcoder-orchestrator flow is rather decoupled from the broadcaster-related networking protocol. Otherwise, I think we may be looking at changes to the smart contract / economic protocol.

Perhaps something to keep in mind is that in TLS 1.3 the curves allowed for the ECDSA signature scheme are limited to secp256r1, secp384r1 and secp521r1[1], but Ethereum uses the secp256k1 curve[2]. Looks like TLS 1.2 does not contain any restrictions on the curves for ECDSA so this wouldn't be a problem at the moment since TLS 1.3 isn't in golang yet, but might be if TLS 1.3 is used later on.

You are correct. While we can generate and validate such Ethereum-signed certificates through OpenSSL, golang doesn't (easily) expose the ability to use such certs with its native TLS.

How's this for a workaround: in the SubjectAltName dNSName extension, we append the ENS path. By default, this could be <broadcaster-addr>.transcoder.eth . Then we can store the hash of the certificate within ENS (or a hash of the public key). Upon connection with the orchestrator, the broadcaster ensures the cert's root CA hash matches what's in ENS. That also allows to use the same cert for multiple blockchains, and segues nicely into the broadcaster's use of SNI.

This also serves as a revocation mechanism; revoking a cert is a matter of replacing the hash in ENS. While this may become inconvenient later on (eg, revoking a leaf transcoder's cert means replacing all certs), it should be enough to get us started.

Another way to save on costs and avoid putting anything in ENS, is to put an Ethereum signature from the orchestrator somewhere else in the cert (eg, a URI or custom SubjectAltName), but that doesn't really lend itself to revocation.

iameli-streams commented 3 months ago

I love that this is the top Google result for "TLS Cert with Ethereum keys."