blob: b0b55e10e012759ddc58398e5c2fd48cd31c1299 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package hwsec
import (
"context"
"encoding/hex"
apb "chromiumos/system_api/attestation_proto"
"chromiumos/tast/errors"
)
// AttestationError wraps the attestation error status.
type AttestationError struct {
*errors.E
apb.AttestationStatus
}
// AttestationClient wraps and the functions of AttestationDBus.
type AttestationClient struct {
ac AttestationDBus
}
// NewAttestationClient creates a new AttestationClient.
func NewAttestationClient(ac AttestationDBus) *AttestationClient {
return &AttestationClient{ac}
}
// hexEncode encode the []byte into hex-encoded []byte; also returns encountered error if any
func hexEncode(src []byte) []byte {
dst := make([]byte, hex.EncodedLen(len(src)))
hex.Encode(dst, src)
return dst
}
// IsPreparedForEnrollment checks if prepared for enrollment.
func (u *AttestationClient) IsPreparedForEnrollment(ctx context.Context) (bool, error) {
status, err := u.ac.GetStatus(ctx, &apb.GetStatusRequest{})
if err != nil {
return false, errors.Wrap(err, "failed to call |GetStatus|")
}
return status.GetPreparedForEnrollment(), nil
}
// IsEnrolled checks if DUT is enrolled.
func (u *AttestationClient) IsEnrolled(ctx context.Context) (bool, error) {
status, err := u.ac.GetStatus(ctx, &apb.GetStatusRequest{})
if err != nil {
return false, errors.Wrap(err, "failed to call |GetStatus|")
}
return status.GetEnrolled(), nil
}
// CreateEnrollRequest creates enroll request.
func (u *AttestationClient) CreateEnrollRequest(ctx context.Context, pcaType PCAType) (string, error) {
acaType := apb.ACAType(ACAType(pcaType))
reply, err := u.ac.CreateEnrollRequest(ctx, &apb.CreateEnrollRequestRequest{AcaType: &acaType})
if err != nil {
return "", errors.Wrap(err, "failed to call |CreateEnrollRequest|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |CreateEnrollRequest|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(reply.GetPcaRequest()), nil
}
// FinishEnroll handles enroll response.
func (u *AttestationClient) FinishEnroll(ctx context.Context, pcaType PCAType, resp string) error {
acaType := apb.ACAType(ACAType(pcaType))
reply, err := u.ac.FinishEnroll(ctx, &apb.FinishEnrollRequest{
PcaResponse: []byte(resp),
AcaType: &acaType,
})
if err != nil {
return errors.Wrap(err, "failed to call |FinishEnroll|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return &AttestationError{
errors.Errorf("failed |FinishEnroll|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return nil
}
// CreateCertRequest creates a cert request.
func (u *AttestationClient) CreateCertRequest(
ctx context.Context,
pcaType PCAType,
profile apb.CertificateProfile,
username,
origin string) (string, error) {
acaType := apb.ACAType(ACAType(pcaType))
reply, err := u.ac.CreateCertificateRequest(ctx, &apb.CreateCertificateRequestRequest{
AcaType: &acaType,
CertificateProfile: &profile,
Username: &username,
RequestOrigin: &origin,
})
if err != nil {
return "", errors.Wrap(err, "failed to call |CreateCertificateRequest|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |CreateCertificateRequest|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(reply.GetPcaRequest()), nil
}
// FinishCertRequest handles cert response.
func (u *AttestationClient) FinishCertRequest(ctx context.Context, resp, username, label string) error {
reply, err := u.ac.FinishCertificateRequest(ctx, &apb.FinishCertificateRequestRequest{
PcaResponse: []byte(resp),
KeyLabel: &label,
Username: &username,
})
if err != nil {
return errors.Wrap(err, "failed to call |FinishCertificateRequest|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return &AttestationError{
errors.Errorf("failed |FinishCertificateRequest|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return nil
}
// SignEnterpriseVAChallenge performs SPKAC for the challenge.
func (u *AttestationClient) SignEnterpriseVAChallenge(
ctx context.Context,
vaType VAType,
username,
label,
domain,
deviceID string,
includeSignedPublicKey bool,
challenge []byte) (string, error) {
apbVAType := apb.VAType(vaType)
reply, err := u.ac.SignEnterpriseChallenge(ctx, &apb.SignEnterpriseChallengeRequest{
KeyLabel: &label,
Username: &username,
Domain: &domain,
DeviceId: []byte(deviceID),
IncludeSignedPublicKey: &includeSignedPublicKey,
Challenge: challenge,
VaType: &apbVAType,
})
if err != nil {
return "", errors.Wrap(err, "failed to call |SignEnterpriseChallenge|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |SignEnterpriseChallenge|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(reply.GetChallengeResponse()), nil
}
// SignSimpleChallenge signs the challenge with the specified key.
func (u *AttestationClient) SignSimpleChallenge(
ctx context.Context,
username,
label string,
challenge []byte) (string, error) {
reply, err := u.ac.SignSimpleChallenge(ctx, &apb.SignSimpleChallengeRequest{
KeyLabel: &label,
Username: &username,
Challenge: challenge,
})
if err != nil {
return "", errors.Wrap(err, "failed to call |SignSimpleChallenge|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |SignSimpleChallenge|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(reply.GetChallengeResponse()), nil
}
// GetPublicKey gets the public part of the key.
func (u *AttestationClient) GetPublicKey(
ctx context.Context,
username,
label string) (string, error) {
reply, err := u.ac.GetKeyInfo(ctx, &apb.GetKeyInfoRequest{
KeyLabel: &label,
Username: &username,
})
if err != nil {
return "", errors.Wrap(err, "failed to call |GetKeyInfo|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |GetKeyInfo|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(hexEncode(reply.GetPublicKey())), nil
}
// GetEnrollmentID gets the enrollment ID.
func (u *AttestationClient) GetEnrollmentID(ctx context.Context) (string, error) {
reply, err := u.ac.GetEnrollmentID(ctx, &apb.GetEnrollmentIdRequest{})
if err != nil {
return "", errors.Wrap(err, "failed to call |GetEnrollmentID|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |GetEnrollmentID|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(hexEncode([]byte(reply.GetEnrollmentId()))), nil
}
// GetKeyPayload gets the payload associated with the specified key.
func (u *AttestationClient) GetKeyPayload(
ctx context.Context,
username,
label string) (string, error) {
reply, err := u.ac.GetKeyInfo(ctx, &apb.GetKeyInfoRequest{
KeyLabel: &label,
Username: &username,
})
if err != nil {
return "", errors.Wrap(err, "failed to call |GetKeyInfo|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return "", &AttestationError{
errors.Errorf("failed |GetKeyInfo|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return string(reply.GetPayload()), nil
}
// SetKeyPayload sets the payload associated with the specified key.
func (u *AttestationClient) SetKeyPayload(
ctx context.Context,
username,
label,
payload string) (bool, error) {
reply, err := u.ac.SetKeyPayload(ctx, &apb.SetKeyPayloadRequest{
KeyLabel: &label,
Username: &username,
Payload: []byte(payload),
})
if err != nil {
return false, errors.Wrap(err, "failed to call |SetKeyPayload|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return false, &AttestationError{
errors.Errorf("failed |SetKeyPayload|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return true, nil
}
// RegisterKeyWithChapsToken registers the key into chaps.
func (u *AttestationClient) RegisterKeyWithChapsToken(
ctx context.Context,
username,
label string) (bool, error) {
reply, err := u.ac.RegisterKeyWithChapsToken(ctx, &apb.RegisterKeyWithChapsTokenRequest{
KeyLabel: &label,
Username: &username,
})
if err != nil {
return false, errors.Wrap(err, "failed to call |RegisterKeyWithChapsToken|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return false, &AttestationError{
errors.Errorf("failed |RegisterKeyWithChapsToken|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return true, nil
}
// DeleteKeys delete all the |usernames|'s keys with label having prefix.
func (u *AttestationClient) DeleteKeys(ctx context.Context, username, prefix string) error {
bahavior := apb.DeleteKeysRequest_MATCH_BEHAVIOR_PREFIX
reply, err := u.ac.DeleteKeys(ctx, &apb.DeleteKeysRequest{
Username: &username,
KeyLabelMatch: &prefix,
MatchBehavior: &bahavior,
})
if err != nil {
return errors.Wrap(err, "failed to call |DeleteKeys|")
}
if reply.GetStatus() != apb.AttestationStatus_STATUS_SUCCESS {
return &AttestationError{
errors.Errorf("failed |DeleteKeys|: %s", reply.GetStatus().String()),
reply.GetStatus(),
}
}
return nil
}