func()

in server/server.go [401:533]


func (s *SecureSessionService) Finalize(ctx context.Context, req *sspb.FinalizeRequest) (*sspb.FinalizeResponse, error) {
	if err := s.verifyToken(ctx); err != nil {
		return nil, fmt.Errorf("failed to verify JWT: %w", err)
	}

	connID := base64.StdEncoding.EncodeToString(req.SessionContext)
	ch, found := s.channels[connID]

	if !found {
		return nil, fmt.Errorf("session with id: %v not found", connID)
	}

	if ch.state != ServerStateAttestationNegotiated {
		return nil, fmt.Errorf("session with id: %v in unexpected state: %d. Expecting: %d", connID, ch.state, ServerStateAttestationNegotiated)
	}

	// Unmarshal attestation evidence if included in request.
	var clientAttEvidence attpb.AttestationEvidence

	if len(req.GetAttestationEvidenceRecords()) > 0 {
		ch.shim.QueueReceiveBuf(req.AttestationEvidenceRecords)
		buf := make([]byte, len(req.AttestationEvidenceRecords))

		offset := 0
		priorChunkLen := 0

		/*
		 * Approach: for a large attestation (e.g., 11K TLS read returns the attestation
		 * in chunks.  The attestation size in total is smaller than the length
		 * of the req.AttestationEvidenceRecords buffer after its decrypted via
		 * ch.conn.Read. If ch.conn.Read is called beyond the total size of the
		 * decrypted attestion it will block and hang the connection.  Given that we
		 * do not know the attestation's exact size, the current strategy is to keep
		 * reading while the decrypted chunks returned by the ch.conn.Read are getting
		 * larger.  Once a decrease in size is detected, it is treated as the last chunk.
		 */

		for {
			chunkLen, err := ch.conn.Read(buf[offset:])

			if err != nil {
				ch.state = ServerStateFailed
				return nil, fmt.Errorf("failed to read client's AttestationEvidenceRecords message from TLS connection : %v", err)
			}

			offset += chunkLen

			// The multi-chunk approach described above only applies to large attestations (e.g., 11K).
			if priorChunkLen == 0 && chunkLen <= minUnchunkedAttestationSize {
				break
			}

			if chunkLen <= priorChunkLen {
				break
			} else {
				priorChunkLen = chunkLen
			}
		}

		if err := proto.Unmarshal(buf[:offset], &clientAttEvidence); err != nil {
			ch.state = ServerStateFailed
			return nil, fmt.Errorf("failed to unmarshal AttestationEvidence: %w", err)
		}
	}

	attestationExpected := false
	for _, tp := range ch.attestationEvidenceTypes {
		switch tp {
		case attpb.AttestationEvidenceType_TPM2_QUOTE, attpb.AttestationEvidenceType_TCG_EVENT_LOG:
			attestationExpected = true
		}
	}

	if attestationExpected {
		att := clientAttEvidence.GetAttestation()

		if att == nil {
			return nil, fmt.Errorf("negotiated vTPM attestation but payload did not contain attestation")
		}

		instanceInfo := att.GetInstanceInfo()
		if instanceInfo == nil {
			return nil, fmt.Errorf("instanceInfo is empty; can't look up shielded instance identity")
		}

		client, err := compute.NewService(ctx)
		if err != nil {
			return nil, fmt.Errorf("unable to create GCE client: %w", err)
		}

		instance, err := client.Instances.GetShieldedInstanceIdentity(
			instanceInfo.GetProjectId(), instanceInfo.GetZone(), instanceInfo.GetInstanceName()).Do()
		if err != nil {
			return nil, fmt.Errorf("couldn't retrieve shielded instance identity: %w", err)
		}

		// Verify quote using the signing key returned by GetShieldedInstanceIdentity.
		block, _ := pem.Decode([]byte(instance.SigningKey.EkPub))
		if block == nil || block.Type != "PUBLIC KEY" {
			return nil, fmt.Errorf("failed to decode PEM block containing public key")
		}

		pubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
		if err != nil {
			return nil, fmt.Errorf("unable to parse EK cert from GetShieldedInstanceIdentity: %w", err)
		}

		// Recreate the nonce generated on the client side to validate the attestation.
		tlsState := ch.conn.ConnectionState()
		material, err := tlsState.ExportKeyingMaterial(constants.ExportLabel, nil, 32)
		if err != nil {
			return nil, fmt.Errorf("error exporting key material: %w", err)
		}

		nonce := []byte(constants.AttestationPrefix)
		nonce = append(nonce, material...)

		ms, err := server.VerifyAttestation(att, server.VerifyOpts{Nonce: nonce, TrustedAKs: []crypto.PublicKey{pubKey}})
		if err != nil {
			return nil, fmt.Errorf("failed to verify quote: %w", err)
		}

		ch.ms = ms
		glog.Infof("Verified quote for instance: %v; machine state: %v", instanceInfo.String(), ms)
	} else {
		glog.Infof("Negotiated null attestation; skipping attestation verification")
	}

	rep := &sspb.FinalizeResponse{}

	ch.state = ServerStateAttestationAccepted
	return rep, nil
}