File: tbs_windows.go

package info (click to toggle)
golang-github-google-go-tpm 0.9.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,932 kB
  • sloc: makefile: 13
file content (267 lines) | stat: -rw-r--r-- 10,424 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
// Copyright (c) 2018, Google LLC All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package tbs provides an low-level interface directly mapping to Windows
// Tbs.dll system library commands:
// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal
// Public field descriptions contain links to the high-level Windows documentation.
package tbs

import (
	"fmt"
	"syscall"
	"unsafe"
)

// Context references the current TPM context
type Context uintptr

// Version of TPM being used by the application.
type Version uint32

// Flag indicates TPM versions that are supported by the application.
type Flag uint32

// CommandPriority is used to determine which pending command to submit whenever the TPM is free.
type CommandPriority uint32

// Command parameters:
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.10240.0/shared/tbs.h
const (
	// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/ns-tbs-tdtbs_context_params2
	// OR flags to use multiple.
	RequestRaw   Flag = 1 << iota // Add flag to request raw context
	IncludeTPM12                  // Add flag to support TPM 1.2
	IncludeTPM20                  // Add flag to support TPM 2

	TPMVersion12 Version = 1 // For TPM 1.2 applications
	TPMVersion20 Version = 2 // For TPM 2 applications or applications using multiple TPM versions

	// https://docs.microsoft.com/en-us/windows/desktop/tbs/command-scheduling
	// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command#parameters
	LowPriority    CommandPriority = 100 // For low priority application use
	NormalPriority CommandPriority = 200 // For normal priority application use
	HighPriority   CommandPriority = 300 // For high priority application use
	SystemPriority CommandPriority = 400 // For system tasks that access the TPM

	commandLocalityZero uint32 = 0 // Windows currently only supports TBS_COMMAND_LOCALITY_ZERO.
)

// Error is the return type of all functions in this package.
type Error uint32

func (err Error) Error() string {
	if description, ok := errorDescriptions[err]; ok {
		return fmt.Sprintf("TBS Error 0x%X: %s", uint32(err), description)
	}
	return fmt.Sprintf("Unrecognized TBS Error 0x%X", uint32(err))
}

func getError(err uintptr) error {
	// tbs.dll uses 0x0 as the return value for success.
	if err == 0 {
		return nil
	}
	return Error(err)
}

// TBS Return Codes:
// https://docs.microsoft.com/en-us/windows/desktop/TBS/tbs-return-codes
const (
	ErrInternalError          Error = 0x80284001
	ErrBadParameter           Error = 0x80284002
	ErrInvalidOutputPointer   Error = 0x80284003
	ErrInvalidContext         Error = 0x80284004
	ErrInsufficientBuffer     Error = 0x80284005
	ErrIOError                Error = 0x80284006
	ErrInvalidContextParam    Error = 0x80284007
	ErrServiceNotRunning      Error = 0x80284008
	ErrTooManyTBSContexts     Error = 0x80284009
	ErrTooManyResources       Error = 0x8028400A
	ErrServiceStartPending    Error = 0x8028400B
	ErrPPINotSupported        Error = 0x8028400C
	ErrCommandCanceled        Error = 0x8028400D
	ErrBufferTooLarge         Error = 0x8028400E
	ErrTPMNotFound            Error = 0x8028400F
	ErrServiceDisabled        Error = 0x80284010
	ErrNoEventLog             Error = 0x80284011
	ErrAccessDenied           Error = 0x80284012
	ErrProvisioningNotAllowed Error = 0x80284013
	ErrPPIFunctionUnsupported Error = 0x80284014
	ErrOwnerauthNotFound      Error = 0x80284015
)

var errorDescriptions = map[Error]string{
	ErrInternalError:          "An internal software error occurred.",
	ErrBadParameter:           "One or more parameter values are not valid.",
	ErrInvalidOutputPointer:   "A specified output pointer is bad.",
	ErrInvalidContext:         "The specified context handle does not refer to a valid context.",
	ErrInsufficientBuffer:     "The specified output buffer is too small.",
	ErrIOError:                "An error occurred while communicating with the TPM.",
	ErrInvalidContextParam:    "A context parameter that is not valid was passed when attempting to create a TBS context.",
	ErrServiceNotRunning:      "The TBS service is not running and could not be started.",
	ErrTooManyTBSContexts:     "A new context could not be created because there are too many open contexts.",
	ErrTooManyResources:       "A new virtual resource could not be created because there are too many open virtual resources.",
	ErrServiceStartPending:    "The TBS service has been started but is not yet running.",
	ErrPPINotSupported:        "The physical presence interface is not supported.",
	ErrCommandCanceled:        "The command was canceled.",
	ErrBufferTooLarge:         "The input or output buffer is too large.",
	ErrTPMNotFound:            "A compatible Trusted Platform Module (TPM) Security Device cannot be found on this computer.",
	ErrServiceDisabled:        "The TBS service has been disabled.",
	ErrNoEventLog:             "The TBS event log is not available.",
	ErrAccessDenied:           "The caller does not have the appropriate rights to perform the requested operation.",
	ErrProvisioningNotAllowed: "The TPM provisioning action is not allowed by the specified flags.",
	ErrPPIFunctionUnsupported: "The Physical Presence Interface of this firmware does not support the requested method.",
	ErrOwnerauthNotFound:      "The requested TPM OwnerAuth value was not found.",
}

// Tbs.dll provides an API for making calls to the TPM:
// https://docs.microsoft.com/en-us/windows/desktop/TBS/tpm-base-services-portal
var (
	tbsDLL           = syscall.NewLazyDLL("Tbs.dll")
	tbsGetDeviceInfo = tbsDLL.NewProc("Tbsi_GetDeviceInfo")
	tbsCreateContext = tbsDLL.NewProc("Tbsi_Context_Create")
	tbsContextClose  = tbsDLL.NewProc("Tbsip_Context_Close")
	tbsSubmitCommand = tbsDLL.NewProc("Tbsip_Submit_Command")
	tbsGetTCGLog     = tbsDLL.NewProc("Tbsi_Get_TCG_Log")
)

// Returns the address of the beginning of a slice or 0 for a nil slice.
func sliceAddress(s []byte) uintptr {
	if len(s) == 0 {
		return 0
	}
	return uintptr(unsafe.Pointer(&(s[0])))
}

// DeviceInfo is TPM_DEVICE_INFO from tbs.h
type DeviceInfo struct {
	StructVersion    uint32
	TPMVersion       Version
	TPMInterfaceType uint32
	TPMImpRevision   uint32
}

// GetDeviceInfo gets the DeviceInfo of the current TPM:
// https://docs.microsoft.com/en-us/windows/win32/api/tbs/nf-tbs-tbsi_getdeviceinfo
func GetDeviceInfo() (*DeviceInfo, error) {
	info := DeviceInfo{}
	// TBS_RESULT Tbsi_GetDeviceInfo(
	//   UINT32 Size,
	//   PVOID  Info
	// );
	if err := tbsGetDeviceInfo.Find(); err != nil {
		return nil, err
	}
	result, _, _ := tbsGetDeviceInfo.Call(
		unsafe.Sizeof(info),
		uintptr(unsafe.Pointer(&info)),
	)
	return &info, getError(result)
}

// CreateContext creates a new TPM context:
// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_context_create
func CreateContext(version Version, flag Flag) (Context, error) {
	var context Context
	params := struct {
		Version
		Flag
	}{version, flag}
	// TBS_RESULT Tbsi_Context_Create(
	//   _In_  PCTBS_CONTEXT_PARAMS pContextParams,
	//   _Out_ PTBS_HCONTEXT        *phContext
	// );
	if err := tbsCreateContext.Find(); err != nil {
		return context, err
	}
	result, _, _ := tbsCreateContext.Call(
		uintptr(unsafe.Pointer(&params)),
		uintptr(unsafe.Pointer(&context)),
	)
	return context, getError(result)
}

// Close closes an existing TPM context:
// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_context_close
func (context Context) Close() error {
	// TBS_RESULT Tbsip_Context_Close(
	//   _In_ TBS_HCONTEXT hContext
	// );
	if err := tbsContextClose.Find(); err != nil {
		return err
	}
	result, _, _ := tbsContextClose.Call(uintptr(context))
	return getError(result)
}

// SubmitCommand sends commandBuffer to the TPM, returning the number of bytes
// written to responseBuffer. ErrInsufficientBuffer is returned if the
// responseBuffer is too short. ErrInvalidOutputPointer is returned if the
// responseBuffer is nil. On failure, the returned length is unspecified.
// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsip_submit_command
func (context Context) SubmitCommand(
	priority CommandPriority,
	commandBuffer []byte,
	responseBuffer []byte,
) (uint32, error) {
	responseBufferLen := uint32(len(responseBuffer))

	// TBS_RESULT Tbsip_Submit_Command(
	//   _In_          TBS_HCONTEXT         hContext,
	//   _In_          TBS_COMMAND_LOCALITY Locality,
	//   _In_          TBS_COMMAND_PRIORITY Priority,
	//   _In_    const PCBYTE               *pabCommand,
	//   _In_          UINT32               cbCommand,
	//   _Out_         PBYTE                *pabResult,
	//   _Inout_       UINT32               *pcbOutput
	// );
	if err := tbsSubmitCommand.Find(); err != nil {
		return 0, err
	}
	result, _, _ := tbsSubmitCommand.Call(
		uintptr(context),
		uintptr(commandLocalityZero),
		uintptr(priority),
		sliceAddress(commandBuffer),
		uintptr(len(commandBuffer)),
		sliceAddress(responseBuffer),
		uintptr(unsafe.Pointer(&responseBufferLen)),
	)
	return responseBufferLen, getError(result)
}

// GetTCGLog gets the system event log, returning the number of bytes written
// to logBuffer. If logBuffer is nil, the size of the TCG log is returned.
// ErrInsufficientBuffer is returned if the logBuffer is too short. On failure,
// the returned length is unspecified.
// https://docs.microsoft.com/en-us/windows/desktop/api/Tbs/nf-tbs-tbsi_get_tcg_log
func (context Context) GetTCGLog(logBuffer []byte) (uint32, error) {
	logBufferLen := uint32(len(logBuffer))

	// TBS_RESULT Tbsi_Get_TCG_Log(
	//   TBS_HCONTEXT hContext,
	//   PBYTE        pOutputBuf,
	//   PUINT32      pOutputBufLen
	// );
	if err := tbsGetTCGLog.Find(); err != nil {
		return 0, err
	}
	result, _, _ := tbsGetTCGLog.Call(
		uintptr(context),
		sliceAddress(logBuffer),
		uintptr(unsafe.Pointer(&logBufferLen)),
	)
	return logBufferLen, getError(result)
}