Of course I should talk with akash, which is my plan, but I'm posting here at the moment. Akash is a blockchain hosting provider. They recently added support for GPU compute. I'm writing code to verify connections to akash nodes by renters. Provider certificates are registered on-chain and used for client communications, but I don't understand how the protocol produces security. The code clients use for verifying a provider certificate when executing administrative actions is here: https://github.com/akash-network/provider/blob/44c85af39a56a43830efbdcbe7a2f... I don't understand how the verification provides security. -> I am not very familiar with TLS and X509 and am very, very, very confused. <- The first and primary authentication is that the common name is the account address in bech32 format. It reports a hijacked certificate if this is not the case:
// validation var prov sdk.Address if prov, err = sdk.AccAddressFromBech32(cert.Subject.CommonName); err != nil { return errors.Wrap(err, "tls: invalid certificate's subject common name") }
// 1. CommonName in issuer and Subject must be the same if cert.Subject.CommonName != cert.Issuer.CommonName { return errors.Wrap(err, "tls: invalid certificate's issuer common name") }
if !c.addr.Equals(prov) { return errors.Errorf("tls: hijacked certificate") }
I don't understand how this helps prove the provider owns the certificate. Couldn't anybody set the common name of a certificate to an account address? Wouldn't it be important here to sign material with the private key of the account, rather than using the address? Then it verifies the certificate is active on-chain, but it compares only the serial number rather than a fingerprint:
// 2. serial number must be in if cert.SerialNumber == nil { return errors.Wrap(err, "tls: invalid certificate serial number") }
// 3. look up certificate on chain. it must not be revoked var resp *ctypes.QueryCertificatesResponse resp, err = c.cclient.Certificates( context.Background(), &ctypes.QueryCertificatesRequest{ Filter: ctypes.CertificateFilter{ Owner: prov.String(), Serial: cert.SerialNumber.String(), State: "valid", }, }, ) if err != nil { return errors.Wrap(err, "tls: unable to fetch certificate from chain") } if (len(resp.Certificates) != 1) || !resp.Certificates[0].Certificate.IsState(ctypes.CertificateValid) { return errors.New("tls: attempt to use non-existing or revoked certificate") }
I don't understand how this helps prove the provider owns the certificate. Couldn't anybody set the serial number of a certificate to the value active on-chain? Wouldn't it be necessary to use a digest of the certificate to verify it is the same one? Finally, it places the certificate in a pool of its own, and uses that same pool to verify that same certificate:
certPool := x509.NewCertPool() certPool.AddCert(cert)
opts := x509.VerifyOptions{ DNSName: c.host.Hostname(), Roots: certPool, CurrentTime: time.Now(), KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, MaxConstraintComparisions: 0, }
if _, err = cert.Verify(opts); err != nil { return errors.Wrap(err, "tls: unable to verify certificate") }
I don't understand how this helps prove the provider owns the certificate. Couldn't anybody craft a certificate that verifies itself? Shouldn't there be some cryptographic path between the on-chain material and the off-chain material? Note: every provider and every provider's hostname can be enumerated on-chain, so an attacker can easily identify if there are providers that are easy to intercept, or enumerate all providers to intercept them all. (Might be this cli command, haven't verified: https://github.com/akash-network/docs/blob/master/cli/provider-services_quer... ) To me it looks like this system could have the function of making it very easy to intercept and mutate traffic between all akash providers and clients, optionally presenting a fake provider to clients that pretends to be the real one.