1// Copyright 2013 Miek Gieben. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package pkcs11
6
7/*
8#include <stdlib.h>
9#include <string.h>
10#include "pkcs11go.h"
11
12CK_ULONG Index(CK_ULONG_PTR array, CK_ULONG i)
13{
14	return array[i];
15}
16
17static inline void putAttributePval(CK_ATTRIBUTE_PTR a, CK_VOID_PTR pValue)
18{
19	a->pValue = pValue;
20}
21
22static inline void putMechanismParam(CK_MECHANISM_PTR m, CK_VOID_PTR pParameter)
23{
24	m->pParameter = pParameter;
25}
26*/
27import "C"
28
29import (
30	"fmt"
31	"time"
32	"unsafe"
33)
34
35type arena []unsafe.Pointer
36
37func (a *arena) Allocate(obj []byte) (C.CK_VOID_PTR, C.CK_ULONG) {
38	cobj := C.calloc(C.size_t(len(obj)), 1)
39	*a = append(*a, cobj)
40	C.memmove(cobj, unsafe.Pointer(&obj[0]), C.size_t(len(obj)))
41	return C.CK_VOID_PTR(cobj), C.CK_ULONG(len(obj))
42}
43
44func (a arena) Free() {
45	for _, p := range a {
46		C.free(p)
47	}
48}
49
50// toList converts from a C style array to a []uint.
51func toList(clist C.CK_ULONG_PTR, size C.CK_ULONG) []uint {
52	l := make([]uint, int(size))
53	for i := 0; i < len(l); i++ {
54		l[i] = uint(C.Index(clist, C.CK_ULONG(i)))
55	}
56	defer C.free(unsafe.Pointer(clist))
57	return l
58}
59
60// cBBool converts a bool to a CK_BBOOL.
61func cBBool(x bool) C.CK_BBOOL {
62	if x {
63		return C.CK_BBOOL(C.CK_TRUE)
64	}
65	return C.CK_BBOOL(C.CK_FALSE)
66}
67
68func uintToBytes(x uint64) []byte {
69	ul := C.CK_ULONG(x)
70	return C.GoBytes(unsafe.Pointer(&ul), C.int(unsafe.Sizeof(ul)))
71}
72
73// Error represents an PKCS#11 error.
74type Error uint
75
76func (e Error) Error() string {
77	return fmt.Sprintf("pkcs11: 0x%X: %s", uint(e), strerror[uint(e)])
78}
79
80func toError(e C.CK_RV) error {
81	if e == C.CKR_OK {
82		return nil
83	}
84	return Error(e)
85}
86
87// SessionHandle is a Cryptoki-assigned value that identifies a session.
88type SessionHandle uint
89
90// ObjectHandle is a token-specific identifier for an object.
91type ObjectHandle uint
92
93// Version represents any version information from the library.
94type Version struct {
95	Major byte
96	Minor byte
97}
98
99func toVersion(version C.CK_VERSION) Version {
100	return Version{byte(version.major), byte(version.minor)}
101}
102
103// SlotEvent holds the SlotID which for which an slot event (token insertion,
104// removal, etc.) occurred.
105type SlotEvent struct {
106	SlotID uint
107}
108
109// Info provides information about the library and hardware used.
110type Info struct {
111	CryptokiVersion    Version
112	ManufacturerID     string
113	Flags              uint
114	LibraryDescription string
115	LibraryVersion     Version
116}
117
118// SlotInfo provides information about a slot.
119type SlotInfo struct {
120	SlotDescription string // 64 bytes.
121	ManufacturerID  string // 32 bytes.
122	Flags           uint
123	HardwareVersion Version
124	FirmwareVersion Version
125}
126
127// TokenInfo provides information about a token.
128type TokenInfo struct {
129	Label              string
130	ManufacturerID     string
131	Model              string
132	SerialNumber       string
133	Flags              uint
134	MaxSessionCount    uint
135	SessionCount       uint
136	MaxRwSessionCount  uint
137	RwSessionCount     uint
138	MaxPinLen          uint
139	MinPinLen          uint
140	TotalPublicMemory  uint
141	FreePublicMemory   uint
142	TotalPrivateMemory uint
143	FreePrivateMemory  uint
144	HardwareVersion    Version
145	FirmwareVersion    Version
146	UTCTime            string
147}
148
149// SessionInfo provides information about a session.
150type SessionInfo struct {
151	SlotID      uint
152	State       uint
153	Flags       uint
154	DeviceError uint
155}
156
157// Attribute holds an attribute type/value combination.
158type Attribute struct {
159	Type  uint
160	Value []byte
161}
162
163// NewAttribute allocates a Attribute and returns a pointer to it.
164// Note that this is merely a convenience function, as values returned
165// from the HSM are not converted back to Go values, those are just raw
166// byte slices.
167func NewAttribute(typ uint, x interface{}) *Attribute {
168	// This function nicely transforms *to* an attribute, but there is
169	// no corresponding function that transform back *from* an attribute,
170	// which in PKCS#11 is just an byte array.
171	a := new(Attribute)
172	a.Type = typ
173	if x == nil {
174		return a
175	}
176	switch v := x.(type) {
177	case bool:
178		if v {
179			a.Value = []byte{1}
180		} else {
181			a.Value = []byte{0}
182		}
183	case int:
184		a.Value = uintToBytes(uint64(v))
185	case uint:
186		a.Value = uintToBytes(uint64(v))
187	case string:
188		a.Value = []byte(v)
189	case []byte:
190		a.Value = v
191	case time.Time: // for CKA_DATE
192		a.Value = cDate(v)
193	default:
194		panic("pkcs11: unhandled attribute type")
195	}
196	return a
197}
198
199// cAttribute returns the start address and the length of an attribute list.
200func cAttributeList(a []*Attribute) (arena, C.CK_ATTRIBUTE_PTR, C.CK_ULONG) {
201	var arena arena
202	if len(a) == 0 {
203		return nil, nil, 0
204	}
205	pa := make([]C.CK_ATTRIBUTE, len(a))
206	for i, attr := range a {
207		pa[i]._type = C.CK_ATTRIBUTE_TYPE(attr.Type)
208		if len(attr.Value) != 0 {
209			buf, len := arena.Allocate(attr.Value)
210			// field is unaligned on windows so this has to call into C
211			C.putAttributePval(&pa[i], buf)
212			pa[i].ulValueLen = len
213		}
214	}
215	return arena, &pa[0], C.CK_ULONG(len(a))
216}
217
218func cDate(t time.Time) []byte {
219	b := make([]byte, 8)
220	year, month, day := t.Date()
221	y := fmt.Sprintf("%4d", year)
222	m := fmt.Sprintf("%02d", month)
223	d1 := fmt.Sprintf("%02d", day)
224	b[0], b[1], b[2], b[3] = y[0], y[1], y[2], y[3]
225	b[4], b[5] = m[0], m[1]
226	b[6], b[7] = d1[0], d1[1]
227	return b
228}
229
230// Mechanism holds an mechanism type/value combination.
231type Mechanism struct {
232	Mechanism uint
233	Parameter []byte
234	generator interface{}
235}
236
237// NewMechanism returns a pointer to an initialized Mechanism.
238func NewMechanism(mech uint, x interface{}) *Mechanism {
239	m := new(Mechanism)
240	m.Mechanism = mech
241	if x == nil {
242		return m
243	}
244
245	switch p := x.(type) {
246	case *GCMParams, *OAEPParams, *ECDH1DeriveParams:
247		// contains pointers; defer serialization until cMechanism
248		m.generator = p
249	case []byte:
250		m.Parameter = p
251	default:
252		panic("parameter must be one of type: []byte, *GCMParams, *OAEPParams, *ECDH1DeriveParams")
253	}
254
255	return m
256}
257
258func cMechanism(mechList []*Mechanism) (arena, *C.CK_MECHANISM) {
259	if len(mechList) != 1 {
260		panic("expected exactly one mechanism")
261	}
262	mech := mechList[0]
263	cmech := &C.CK_MECHANISM{mechanism: C.CK_MECHANISM_TYPE(mech.Mechanism)}
264	// params that contain pointers are allocated here
265	param := mech.Parameter
266	var arena arena
267	switch p := mech.generator.(type) {
268	case *GCMParams:
269		// uses its own arena because it has to outlive this function call (yuck)
270		param = cGCMParams(p)
271	case *OAEPParams:
272		param, arena = cOAEPParams(p, arena)
273	case *ECDH1DeriveParams:
274		param, arena = cECDH1DeriveParams(p, arena)
275	}
276	if len(param) != 0 {
277		buf, len := arena.Allocate(param)
278		// field is unaligned on windows so this has to call into C
279		C.putMechanismParam(cmech, buf)
280		cmech.ulParameterLen = len
281	}
282	return arena, cmech
283}
284
285// MechanismInfo provides information about a particular mechanism.
286type MechanismInfo struct {
287	MinKeySize uint
288	MaxKeySize uint
289	Flags      uint
290}
291
292// stubData is a persistent nonempty byte array used by cMessage.
293var stubData = []byte{0}
294
295// cMessage returns the pointer/length pair corresponding to data.
296func cMessage(data []byte) (dataPtr C.CK_BYTE_PTR) {
297	l := len(data)
298	if l == 0 {
299		// &data[0] is forbidden in this case, so use a nontrivial array instead.
300		data = stubData
301	}
302	return C.CK_BYTE_PTR(unsafe.Pointer(&data[0]))
303}
304