File: signer.go

package info (click to toggle)
golang-github-smallstep-crypto 0.57.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,284 kB
  • sloc: sh: 53; makefile: 36
file content (131 lines) | stat: -rw-r--r-- 3,486 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package tpm

import (
	"context"
	"crypto"
	"errors"
	"fmt"
	"io"

	"go.step.sm/crypto/tpm/storage"
	"go.step.sm/crypto/tpm/tss2"
)

// signer implements crypto.Signer backed by a TPM key.
type signer struct {
	tpm    *TPM
	key    Key
	public crypto.PublicKey
}

// Public returns the signers public key.
func (s *signer) Public() crypto.PublicKey {
	return s.public
}

// Sign implements crypto.Signer. It is backed by a TPM key.
// The TPM key is loaded lazily, meaning that every call to Sign()
// will reload the TPM key to be used.
func (s *signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
	ctx := context.Background()
	if err = s.tpm.open(ctx); err != nil {
		return nil, fmt.Errorf("failed opening TPM: %w", err)
	}
	defer closeTPM(ctx, s.tpm, &err)

	loadedKey, err := s.tpm.attestTPM.LoadKey(s.key.data)
	if err != nil {
		return nil, err
	}
	defer loadedKey.Close()

	priv, err := loadedKey.Private(s.public)
	if err != nil {
		return nil, fmt.Errorf("failed getting TPM private key %q: %w", s.key.name, err)
	}

	var signer crypto.Signer
	var ok bool
	if signer, ok = priv.(crypto.Signer); !ok {
		return nil, fmt.Errorf("failed getting TPM private key %q as crypto.Signer", s.key.name)
	}

	return signer.Sign(rand, digest, opts)
}

// GetSigner returns a crypto.Signer for a TPM Key identified by `name`.
func (t *TPM) GetSigner(ctx context.Context, name string) (csigner crypto.Signer, err error) {
	if err = t.open(ctx); err != nil {
		return nil, fmt.Errorf("failed opening TPM: %w", err)
	}
	defer closeTPM(ctx, t, &err)

	key, err := t.store.GetKey(name)
	if err != nil {
		if errors.Is(err, storage.ErrNotFound) {
			return nil, fmt.Errorf("failed getting signer for key %q: %w", name, ErrNotFound)
		}
		return nil, fmt.Errorf("failed getting signer for key %q: %w", name, err)
	}

	loadedKey, err := t.attestTPM.LoadKey(key.Data)
	if err != nil {
		return nil, err
	}
	defer loadedKey.Close()

	priv, err := loadedKey.Private(loadedKey.Public())
	if err != nil {
		return nil, fmt.Errorf("failed getting TPM private key %q: %w", name, err)
	}

	if _, ok := priv.(crypto.Signer); !ok {
		return nil, fmt.Errorf("failed getting TPM private key %q as crypto.Signer", name)
	}

	csigner = &signer{
		tpm:    t,
		key:    Key{name: name, data: key.Data, attestedBy: key.AttestedBy, createdAt: key.CreatedAt, tpm: t},
		public: loadedKey.Public(),
	}

	return
}

// tss2Signer is a wrapper on top of [*tss2.Signer] that opens and closes the
// tpm on each sign call.
type tss2Signer struct {
	*tss2.Signer
	tpm *TPM
}

func (s *tss2Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
	ctx := context.Background()
	if err = s.tpm.open(goTPMCall(ctx)); err != nil {
		return nil, fmt.Errorf("failed opening TPM: %w", err)
	}
	defer closeTPM(ctx, s.tpm, &err)
	s.SetCommandChannel(s.tpm.rwc)
	signature, err = s.Signer.Sign(rand, digest, opts)
	return
}

// CreateTSS2Signer returns a crypto.Signer using the given [TPM] and [tss2.TPMKey].
func CreateTSS2Signer(ctx context.Context, t *TPM, key *tss2.TPMKey) (csigner crypto.Signer, err error) {
	if err := t.open(goTPMCall(ctx)); err != nil {
		return nil, fmt.Errorf("failed opening TPM: %w", err)
	}
	defer closeTPM(ctx, t, &err)

	s, err := tss2.CreateSigner(t.rwc, key)
	if err != nil {
		return nil, fmt.Errorf("failed creating TSS2 signer: %w", err)
	}

	csigner = &tss2Signer{
		Signer: s,
		tpm:    t,
	}

	return
}