Skip to content

Commit

Permalink
attestedtls: send attestation report asynchronously
Browse files Browse the repository at this point in the history
If the attestation report is too large, both the
TLS dialer and listener block, as the attestation reports
are exchanged at the same time. Send them asynchronously
to avoid this.

Signed-off-by: Simon Ott <[email protected]>
  • Loading branch information
smo4201 committed Mar 18, 2024
1 parent 43c873a commit 531fc5f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 40 deletions.
2 changes: 1 addition & 1 deletion attestationreport/validationreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ func (r *VerificationResult) PrintErr() {
if !a.Success {
details := ""
if a.Pcr != nil {
details = "PCR%v"
details = fmt.Sprintf("PCR%v", *a.Pcr)
}
log.Warnf("%v Measurement %v: %v verification failed", details, a.Name, a.Digest)
}
Expand Down
76 changes: 53 additions & 23 deletions attestedtls/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var id = "0000"
var log = logrus.WithField("service", "atls")

func attestDialer(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
ch := make(chan error)

//optional: attest Client
if cc.Attest == Attest_Mutual || cc.Attest == Attest_Client {
Expand All @@ -38,13 +39,17 @@ func attestDialer(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
}

// Send created attestation report to listener
log.Tracef("Sending attestation report length %v to listener", len(resp))

err = Write(append([]byte{byte(cc.Attest)}, resp...), conn)
if err != nil {
return fmt.Errorf("failed to send AR: %w", err)
}
log.Trace("Sent AR")
log.Tracef("Dialer: sending attestation report length %v to listener %v",
len(resp), conn.RemoteAddr().String())

go func() {
err = Write(append([]byte{byte(cc.Attest)}, resp...), conn)
if err != nil {
ch <- fmt.Errorf("failed to send AR to listener: %w", err)
}
log.Trace("Finished asynchronous sending of attestation report to listener")
ch <- nil
}()
} else {
//if not sending attestation report, send the attestation mode
err := Write([]byte{byte(cc.Attest)}, conn)
Expand All @@ -54,14 +59,14 @@ func attestDialer(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
log.Debug("Skipping client-side attestation")
}

readvalue, err := readValue(conn, cc.Attest, true)
// Fetch attestation report from listener
report, err := readValue(conn, cc.Attest)
if err != nil {
return err
}

//optional: Wait for attestation report from Server
if cc.Attest == Attest_Mutual || cc.Attest == Attest_Server {
report := readvalue
// Verify AR from listener with own channel bindings
log.Trace("Verifying attestation report from listener")
err = cc.CmcApi.verifyAR(chbindings, report, cc)
Expand All @@ -72,12 +77,22 @@ func attestDialer(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
log.Debug("Skipping client-side verification")
}

// Finally check if asynchronous sending succeeded
if cc.Attest == Attest_Mutual || cc.Attest == Attest_Client {
err = <-ch
if err != nil {
return fmt.Errorf("failed to write asynchronously: %w", err)
}
}

log.Trace("Attestation successful")

return nil
}

func attestListener(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
ch := make(chan error)

// optional: attest server
if cc.Attest == Attest_Mutual || cc.Attest == Attest_Server {
// Obtain own attestation report from local cmcd
Expand All @@ -87,12 +102,19 @@ func attestListener(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
return fmt.Errorf("could not obtain AR of Listener : %w", err)
}

// Send own attestation report to dialer
log.Trace("Sending own attestation report")
err = Write(append([]byte{byte(cc.Attest)}, resp...), conn)
if err != nil {
return fmt.Errorf("failed to send AR: %w", err)
}
// Send own attestation report to dialer. This is done asynchronously to
// avoid blocking if each side sends a large report at the same time
log.Tracef("Listener: Sending attestation report length %v to dialer %v",
len(resp), conn.RemoteAddr().String())

go func() {
err = Write(append([]byte{byte(cc.Attest)}, resp...), conn)
if err != nil {
ch <- fmt.Errorf("failed to send AR to dialer: %w", err)
}
ch <- nil
log.Trace("Finished asynchronous sending of attestation report to dialer")
}()
} else {
//if not sending attestation report, send the attestation mode
err := Write([]byte{byte(cc.Attest)}, conn)
Expand All @@ -102,7 +124,7 @@ func attestListener(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
log.Debug("Skipping server-side attestation")
}

report, err := readValue(conn, cc.Attest, false)
report, err := readValue(conn, cc.Attest)
if err != nil {
return err
}
Expand All @@ -119,30 +141,38 @@ func attestListener(conn *tls.Conn, chbindings []byte, cc CmcConfig) error {
log.Debug("Skipping server-side verification")
}

// Finally check if asynchronous sending succeeded
if cc.Attest == Attest_Mutual || cc.Attest == Attest_Server {
err = <-ch
if err != nil {
return fmt.Errorf("failed to write asynchronously: %w", err)
}
}

log.Trace("Attestation successful")

return nil
}

func readValue(conn *tls.Conn, selection AttestSelect, dialer bool) ([]byte, error) {
func readValue(conn *tls.Conn, selection AttestSelect) ([]byte, error) {
readvalue, err := Read(conn)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}

selectionStr, errS1 := selectionString(byte(selection))
if errS1 != nil {
return nil, errS1
selectionStr, err := selectionString(byte(selection))
if err != nil {
return nil, err
}

// the first byte should always be the attestation mode
if readvalue[0] == byte(selection) {
log.Debugf("Matching attestation mode: [%v]", selectionStr)
} else {
reportByte := readvalue[0]
reportStr, errS1 := selectionString(reportByte)
if errS1 != nil {
return nil, errS1
reportStr, err := selectionString(reportByte)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("mismatching attestation mode, local set to: [%v], while remote is set to: [%v]", selectionStr, reportStr)
}
Expand Down
49 changes: 33 additions & 16 deletions attestedtls/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,44 @@
package attestedtls

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"time"
)

// Writes byte array to provided channel by first sending length information, then data
// Writes byte array to provided channel by first sending length information, then data.
// Used for transmitting the attestation reports between peers
func Write(msg []byte, c net.Conn) error {

length := len(msg)
lenbuf := make([]byte, 4)
binary.BigEndian.PutUint32(lenbuf, uint32(len(msg)))
binary.BigEndian.PutUint32(lenbuf, uint32(length))

buf := append(lenbuf, msg...)
n, err := c.Write(lenbuf)
if err != nil {
return fmt.Errorf("failed to write length to %v: %w", c.RemoteAddr().String(), err)
}
if n != len(lenbuf) {
return fmt.Errorf("could only send %v of %v bytes to %v", n, len(lenbuf),
c.RemoteAddr().String())
}

_, err := c.Write(buf)
n, err = c.Write(msg)
if err != nil {
return fmt.Errorf("failed to write payload to %v: %w", c.RemoteAddr().String(), err)
}
if n != len(msg) {
return fmt.Errorf("could only send %v of %v bytes to %v", n, len(msg),
c.RemoteAddr().String())
}

return err
}

// Receives byte array from provided channel by first receiving length information, then data
// Receives byte array from provided channel by first receiving length information, then data.
// Used for transmitting the attestation reports between peers
func Read(c net.Conn) ([]byte, error) {
start := time.Now()
Expand All @@ -48,30 +65,30 @@ func Read(c net.Conn) ([]byte, error) {
return nil, fmt.Errorf("failed to receive message: no length: %v", err)
}

len := binary.BigEndian.Uint32(lenbuf) // Max size of 4GB
log.Trace("TCP Message Length: ", len)
len := int(binary.BigEndian.Uint32(lenbuf)) // Max size of 4GB
log.Tracef("TCP Message to be received: %v", len)

if len == 0 {
return nil, errors.New("message length is zero")
}

// Receive data in chunks of 1024 bytes as the Read function receives a maxium of 65536 bytes
// Receive data in chunks of 1024 bytes as the Read function receives a maxium of 64K bytes
// and the buffer must be longer, then append it to the final buffer
tmpbuf := make([]byte, 1024)
buf := make([]byte, 0)
rcvlen := uint32(0)
buf := bytes.NewBuffer(nil)
received := 0

for {
n, err := c.Read(tmpbuf)
rcvlen += uint32(n)
chunk := make([]byte, 64*1024)
n, err := c.Read(chunk)
received += n
if err != nil {
return nil, fmt.Errorf("failed to receive message: %w", err)
}
buf = append(buf, tmpbuf[:n]...)
buf.Write(chunk[:n])

// Abort as soon as we have read the expected data as signaled in the first 4 bytes
// of the message
if rcvlen == len {
if received == len {
log.Trace("Received message")
break
}
Expand All @@ -81,5 +98,5 @@ func Read(c net.Conn) ([]byte, error) {
break
}
}
return buf, nil
return buf.Bytes(), nil
}

0 comments on commit 531fc5f

Please sign in to comment.