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
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
// SPDX-License-Identifier: GPL-2.0
/*
* pkey uv specific code
*
* Copyright IBM Corp. 2024
*/
#define KMSG_COMPONENT "pkey"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/cpufeature.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uv.h>
#include "zcrypt_ccamisc.h"
#include "pkey_base.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key UV handler");
/*
* UV secret token struct and defines.
*/
#define TOKVER_UV_SECRET 0x09
struct uvsecrettoken {
u8 type; /* 0x00 = TOKTYPE_NON_CCA */
u8 res0[3];
u8 version; /* 0x09 = TOKVER_UV_SECRET */
u8 res1[3];
u16 secret_type; /* one of enum uv_secret_types from uv.h */
u16 secret_len; /* length in bytes of the secret */
u8 secret_id[UV_SECRET_ID_LEN]; /* the secret id for this secret */
} __packed;
/*
* Check key blob for known and supported UV key.
*/
static bool is_uv_key(const u8 *key, u32 keylen)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
if (keylen < sizeof(*t))
return false;
switch (t->type) {
case TOKTYPE_NON_CCA:
switch (t->version) {
case TOKVER_UV_SECRET:
switch (t->secret_type) {
case UV_SECRET_AES_128:
case UV_SECRET_AES_192:
case UV_SECRET_AES_256:
case UV_SECRET_AES_XTS_128:
case UV_SECRET_AES_XTS_256:
case UV_SECRET_HMAC_SHA_256:
case UV_SECRET_HMAC_SHA_512:
case UV_SECRET_ECDSA_P256:
case UV_SECRET_ECDSA_P384:
case UV_SECRET_ECDSA_P521:
case UV_SECRET_ECDSA_ED25519:
case UV_SECRET_ECDSA_ED448:
return true;
default:
return false;
}
default:
return false;
}
default:
return false;
}
}
static bool is_uv_keytype(enum pkey_key_type keytype)
{
switch (keytype) {
case PKEY_TYPE_UVSECRET:
return true;
default:
return false;
}
}
static int retrieve_secret(const u8 secret_id[UV_SECRET_ID_LEN],
u16 *secret_type, u8 *buf, u32 *buflen)
{
struct uv_secret_list_item_hdr secret_meta_data;
int rc;
rc = uv_get_secret_metadata(secret_id, &secret_meta_data);
if (rc)
return rc;
if (*buflen < secret_meta_data.length)
return -EINVAL;
rc = uv_retrieve_secret(secret_meta_data.index,
buf, secret_meta_data.length);
if (rc)
return rc;
*secret_type = secret_meta_data.type;
*buflen = secret_meta_data.length;
return 0;
}
static int uv_get_size_and_type(u16 secret_type, u32 *pkeysize, u32 *pkeytype)
{
int rc = 0;
switch (secret_type) {
case UV_SECRET_AES_128:
*pkeysize = 16 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_128;
break;
case UV_SECRET_AES_192:
*pkeysize = 24 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_192;
break;
case UV_SECRET_AES_256:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_256;
break;
case UV_SECRET_AES_XTS_128:
*pkeysize = 16 + 16 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_XTS_128;
break;
case UV_SECRET_AES_XTS_256:
*pkeysize = 32 + 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_AES_XTS_256;
break;
case UV_SECRET_HMAC_SHA_256:
*pkeysize = 64 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_HMAC_512;
break;
case UV_SECRET_HMAC_SHA_512:
*pkeysize = 128 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_HMAC_1024;
break;
case UV_SECRET_ECDSA_P256:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P256;
break;
case UV_SECRET_ECDSA_P384:
*pkeysize = 48 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P384;
break;
case UV_SECRET_ECDSA_P521:
*pkeysize = 80 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_P521;
break;
case UV_SECRET_ECDSA_ED25519:
*pkeysize = 32 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_ED25519;
break;
case UV_SECRET_ECDSA_ED448:
*pkeysize = 64 + AES_WK_VP_SIZE;
*pkeytype = PKEY_KEYTYPE_ECC_ED448;
break;
default:
rc = -EINVAL;
}
return rc;
}
static int uv_key2protkey(const struct pkey_apqn *_apqns __always_unused,
size_t _nr_apqns __always_unused,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *keyinfo)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
u32 pkeysize, pkeytype;
u16 secret_type;
int rc;
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
if (rc)
goto out;
if (*protkeylen < pkeysize) {
PKEY_DBF_ERR("%s prot key buffer size too small: %u < %u\n",
__func__, *protkeylen, pkeysize);
rc = -EINVAL;
goto out;
}
rc = retrieve_secret(t->secret_id, &secret_type, protkey, protkeylen);
if (rc) {
PKEY_DBF_ERR("%s retrieve_secret() failed with %d\n",
__func__, rc);
goto out;
}
if (secret_type != t->secret_type) {
PKEY_DBF_ERR("%s retrieved secret type %u != expected type %u\n",
__func__, secret_type, t->secret_type);
rc = -EINVAL;
goto out;
}
if (keyinfo)
*keyinfo = pkeytype;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static int uv_verifykey(const u8 *key, u32 keylen,
u16 *_card __always_unused,
u16 *_dom __always_unused,
u32 *keytype, u32 *keybitsize, u32 *flags)
{
struct uvsecrettoken *t = (struct uvsecrettoken *)key;
struct uv_secret_list_item_hdr secret_meta_data;
u32 pkeysize, pkeytype, bitsize;
int rc;
rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
if (rc)
goto out;
rc = uv_get_secret_metadata(t->secret_id, &secret_meta_data);
if (rc)
goto out;
if (secret_meta_data.type != t->secret_type) {
rc = -EINVAL;
goto out;
}
/* set keytype; keybitsize and flags are not supported */
if (keytype)
*keytype = PKEY_TYPE_UVSECRET;
if (keybitsize) {
bitsize = 8 * pkey_keytype_to_size(pkeytype);
*keybitsize = bitsize ?: PKEY_SIZE_UNKNOWN;
}
if (flags)
*flags = pkeytype;
out:
pr_debug("rc=%d\n", rc);
return rc;
}
static struct pkey_handler uv_handler = {
.module = THIS_MODULE,
.name = "PKEY UV handler",
.is_supported_key = is_uv_key,
.is_supported_keytype = is_uv_keytype,
.key_to_protkey = uv_key2protkey,
.verify_key = uv_verifykey,
};
/*
* Module init
*/
static int __init pkey_uv_init(void)
{
if (!is_prot_virt_guest())
return -ENODEV;
if (!test_bit_inv(BIT_UVC_CMD_RETR_SECRET, uv_info.inst_calls_list))
return -ENODEV;
return pkey_handler_register(&uv_handler);
}
/*
* Module exit
*/
static void __exit pkey_uv_exit(void)
{
pkey_handler_unregister(&uv_handler);
}
module_cpu_feature_match(S390_CPU_FEATURE_UV, pkey_uv_init);
module_exit(pkey_uv_exit);
|