mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
2d8fcd9bf4
Co-authored-by: Malte Poll <mp@edgeless.systems> Co-authored-by: katexochen <katexochen@users.noreply.github.com> Co-authored-by: Daniel Weiße <dw@edgeless.systems> Co-authored-by: Thomas Tendyck <tt@edgeless.systems> Co-authored-by: Benedict Schlueter <bs@edgeless.systems> Co-authored-by: leongross <leon.gross@rub.de> Co-authored-by: Moritz Eckert <m1gh7ym0@gmail.com>
159 lines
4.5 KiB
Go
159 lines
4.5 KiB
Go
package atls
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/base64"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/edgelesssys/constellation/coordinator/config"
|
|
"github.com/edgelesssys/constellation/coordinator/oid"
|
|
"github.com/edgelesssys/constellation/coordinator/util"
|
|
)
|
|
|
|
// CreateAttestationServerTLSConfig creates a tls.Config object with a self-signed certificate and an embedded attestation document.
|
|
func CreateAttestationServerTLSConfig(issuer Issuer) (*tls.Config, error) {
|
|
// generate and hash key
|
|
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hash, err := hashPublicKey(&priv.PublicKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
getCertificate := func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
|
serialNumber, err := util.GenerateCertificateSerialNumber()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// abuse ServerName as a channel to receive the nonce
|
|
nonce, err := base64.StdEncoding.DecodeString(chi.ServerName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
attDoc, err := issuer.Issue(hash, nonce)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// create certficate that includes the attestation document as extension
|
|
now := time.Now()
|
|
template := &x509.Certificate{
|
|
SerialNumber: serialNumber,
|
|
Subject: pkix.Name{CommonName: "Constellation"},
|
|
NotBefore: now.Add(-2 * time.Hour),
|
|
NotAfter: now.Add(2 * time.Hour),
|
|
ExtraExtensions: []pkix.Extension{{Id: issuer.OID(), Value: attDoc}},
|
|
}
|
|
cert, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &tls.Certificate{Certificate: [][]byte{cert}, PrivateKey: priv}, nil
|
|
}
|
|
|
|
return &tls.Config{GetCertificate: getCertificate, MinVersion: tls.VersionTLS12}, nil
|
|
}
|
|
|
|
// CreateAttestationClientTLSConfig creates a tls.Config object that verifies a certificate with an embedded attestation document.
|
|
func CreateAttestationClientTLSConfig(validators []Validator) (*tls.Config, error) {
|
|
nonce, err := util.GenerateRandomBytes(config.RNGLengthDefault)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
verify := func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
|
// parse certificate
|
|
if len(rawCerts) == 0 {
|
|
return errors.New("rawCerts is empty")
|
|
}
|
|
cert, err := x509.ParseCertificate(rawCerts[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// verify self-signed certificate
|
|
roots := x509.NewCertPool()
|
|
roots.AddCert(cert)
|
|
_, err = cert.Verify(x509.VerifyOptions{Roots: roots})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
hash, err := hashPublicKey(cert.PublicKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// verify embedded report
|
|
for _, ex := range cert.Extensions {
|
|
for _, validator := range validators {
|
|
if ex.Id.Equal(validator.OID()) {
|
|
userData, err := validator.Validate(ex.Value, nonce)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(userData, hash) {
|
|
return errors.New("certificate hash does not match user data")
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return errors.New("certificate does not contain attestation document")
|
|
}
|
|
|
|
return &tls.Config{
|
|
VerifyPeerCertificate: verify,
|
|
InsecureSkipVerify: true, // disable default verification because we use our own verify func
|
|
ServerName: base64.StdEncoding.EncodeToString(nonce), // abuse ServerName as a channel to transmit the nonce
|
|
MinVersion: tls.VersionTLS12,
|
|
}, nil
|
|
}
|
|
|
|
// CreateUnverifiedClientTLSConfig creates a tls.Config object that skips verification of a certificate with an embedded attestation document.
|
|
func CreateUnverifiedClientTLSConfig() (*tls.Config, error) {
|
|
nonce, err := util.GenerateRandomBytes(config.RNGLengthDefault)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &tls.Config{
|
|
InsecureSkipVerify: true, // disable certificate verification
|
|
ServerName: base64.StdEncoding.EncodeToString(nonce), // abuse ServerName as a channel to transmit the nonce
|
|
MinVersion: tls.VersionTLS12,
|
|
}, nil
|
|
}
|
|
|
|
type Issuer interface {
|
|
oid.Getter
|
|
Issue(userData []byte, nonce []byte) (quote []byte, err error)
|
|
}
|
|
|
|
type Validator interface {
|
|
oid.Getter
|
|
Validate(attDoc []byte, nonce []byte) ([]byte, error)
|
|
}
|
|
|
|
func hashPublicKey(pub interface{}) ([]byte, error) {
|
|
pubBytes, err := x509.MarshalPKIXPublicKey(pub)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := sha256.Sum256(pubBytes)
|
|
return result[:], nil
|
|
}
|