Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: turris-mox-rwtm: Add support for ECDSA signatures with HW private key

Add support for digital message signing with the private key stored in
the rWTM secure coprocessor. Turris Mox devices have an ECDSA private
key generated and burned into rWTM eFuses when manufactured. This
private key is not readable from the rWTM, but rWTM firmware allows for
signing messages with it and retrieving the public key.

This is exposed to userspace via the keyctl API.

User can find the key by either looking at /proc/keys or listing the
keyring:

$ cat /proc/keys
0240b221 ... keyring .turris-signing-keys: 1
34ff9ac9 ... turris-si Turris MOX SN 0000000D30000005 rWTM ECDSA ke...

$ keyctl rlist %:.turris-signing-keys
889166537

To get the public key:

$ keyctl read 889166537
67 bytes of data in key:
0201a05c 1a79242b 13f2fc02 b48ffdbb 6ee8d5ba 812d6784 5f04f302 c0894d3e
b93474f9 46235777 5c926fb4 cce89b50 88cf5d10 c07fd9c5 fdcea257 3d8f1c33
1bf826

To sign a message:

$ dd if=/dev/urandom of=msg_to_sign bs=64 count=1
$ keyctl pkey_sign 889166537 0 msg_to_sign >signature

Signed-off-by: Marek Behún <kabel@kernel.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>

authored by

Marek Behún and committed by
Arnd Bergmann
ba8755ab 4110ad03

+174 -1
+17
drivers/firmware/Kconfig
··· 258 258 other manufacturing data and also utilize the Entropy Bit Generator 259 259 for hardware random number generation. 260 260 261 + if TURRIS_MOX_RWTM 262 + 263 + config TURRIS_MOX_RWTM_KEYCTL 264 + bool "Turris Mox rWTM ECDSA message signing" 265 + default y 266 + depends on KEYS 267 + depends on ASYMMETRIC_KEY_TYPE 268 + select CZNIC_PLATFORMS 269 + select TURRIS_SIGNING_KEY 270 + help 271 + Say Y here to add support for ECDSA message signing with board private 272 + key (each Turris Mox has an ECDSA private key generated in the secure 273 + coprocessor when manufactured). This functionality is exposed via the 274 + keyctl() syscall. 275 + 276 + endif # TURRIS_MOX_RWTM 277 + 261 278 source "drivers/firmware/arm_ffa/Kconfig" 262 279 source "drivers/firmware/broadcom/Kconfig" 263 280 source "drivers/firmware/cirrus/Kconfig"
+157 -1
drivers/firmware/turris-mox-rwtm.c
··· 2 2 /* 3 3 * Turris Mox rWTM firmware driver 4 4 * 5 - * Copyright (C) 2019, 2024 Marek Behún <kabel@kernel.org> 5 + * Copyright (C) 2019, 2024, 2025 Marek Behún <kabel@kernel.org> 6 6 */ 7 7 8 + #include <crypto/sha2.h> 8 9 #include <linux/align.h> 9 10 #include <linux/armada-37xx-rwtm-mailbox.h> 11 + #include <linux/cleanup.h> 10 12 #include <linux/completion.h> 11 13 #include <linux/container_of.h> 12 14 #include <linux/device.h> ··· 16 14 #include <linux/err.h> 17 15 #include <linux/hw_random.h> 18 16 #include <linux/if_ether.h> 17 + #include <linux/key.h> 19 18 #include <linux/kobject.h> 20 19 #include <linux/mailbox_client.h> 20 + #include <linux/math.h> 21 21 #include <linux/minmax.h> 22 22 #include <linux/module.h> 23 23 #include <linux/mutex.h> 24 24 #include <linux/platform_device.h> 25 25 #include <linux/sizes.h> 26 26 #include <linux/sysfs.h> 27 + #include <linux/turris-signing-key.h> 27 28 #include <linux/types.h> 28 29 29 30 #define DRIVER_NAME "turris-mox-rwtm" ··· 38 33 * This firmware is open source and it's sources can be found at 39 34 * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. 40 35 */ 36 + 37 + enum { 38 + MOX_ECC_NUM_BITS = 521, 39 + MOX_ECC_NUM_LEN = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 8), 40 + MOX_ECC_NUM_WORDS = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 32), 41 + MOX_ECC_SIG_LEN = 2 * MOX_ECC_NUM_LEN, 42 + MOX_ECC_PUBKEY_LEN = 1 + MOX_ECC_NUM_LEN, 43 + }; 41 44 42 45 #define MBOX_STS_SUCCESS (0 << 30) 43 46 #define MBOX_STS_FAIL (1 << 30) ··· 82 69 * @ram_size: RAM size of the device 83 70 * @mac_address1: first MAC address of the device 84 71 * @mac_address2: second MAC address of the device 72 + * @pubkey: board ECDSA public key 85 73 */ 86 74 struct mox_rwtm { 87 75 struct mbox_client mbox_client; ··· 101 87 u64 serial_number; 102 88 int board_version, ram_size; 103 89 u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN]; 90 + 91 + #ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL 92 + u8 pubkey[MOX_ECC_PUBKEY_LEN]; 93 + #endif 104 94 }; 105 95 106 96 static inline struct device *rwtm_dev(struct mox_rwtm *rwtm) ··· 278 260 return ret; 279 261 } 280 262 263 + #ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL 264 + 265 + static void mox_ecc_number_to_bin(void *dst, const u32 *src) 266 + { 267 + __be32 tmp[MOX_ECC_NUM_WORDS]; 268 + 269 + cpu_to_be32_array(tmp, src, MOX_ECC_NUM_WORDS); 270 + 271 + memcpy(dst, (void *)tmp + 2, MOX_ECC_NUM_LEN); 272 + } 273 + 274 + static void mox_ecc_public_key_to_bin(void *dst, u32 src_first, 275 + const u32 *src_rest) 276 + { 277 + __be32 tmp[MOX_ECC_NUM_WORDS - 1]; 278 + u8 *p = dst; 279 + 280 + /* take 3 bytes from the first word */ 281 + *p++ = src_first >> 16; 282 + *p++ = src_first >> 8; 283 + *p++ = src_first; 284 + 285 + /* take the rest of the words */ 286 + cpu_to_be32_array(tmp, src_rest, MOX_ECC_NUM_WORDS - 1); 287 + memcpy(p, tmp, sizeof(tmp)); 288 + } 289 + 290 + static int mox_rwtm_sign(const struct key *key, const void *data, void *signature) 291 + { 292 + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); 293 + struct armada_37xx_rwtm_tx_msg msg = {}; 294 + u32 offset_r, offset_s; 295 + int ret; 296 + 297 + guard(mutex)(&rwtm->busy); 298 + 299 + /* 300 + * For MBOX_CMD_SIGN command: 301 + * args[0] - must be 1 302 + * args[1] - address of message M to sign; message is a 521-bit number 303 + * args[2] - address where the R part of the signature will be stored 304 + * args[3] - address where the S part of the signature will be stored 305 + * 306 + * M, R and S are 521-bit numbers encoded as seventeen 32-bit words, 307 + * most significat word first. 308 + * Since the message in @data is a sha512 digest, the most significat 309 + * word is always zero. 310 + */ 311 + 312 + offset_r = MOX_ECC_NUM_WORDS * sizeof(u32); 313 + offset_s = 2 * MOX_ECC_NUM_WORDS * sizeof(u32); 314 + 315 + memset(rwtm->buf, 0, sizeof(u32)); 316 + memcpy(rwtm->buf + sizeof(u32), data, SHA512_DIGEST_SIZE); 317 + be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUM_WORDS); 318 + 319 + msg.args[0] = 1; 320 + msg.args[1] = rwtm->buf_phys; 321 + msg.args[2] = rwtm->buf_phys + offset_r; 322 + msg.args[3] = rwtm->buf_phys + offset_s; 323 + 324 + ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true); 325 + if (ret < 0) 326 + return ret; 327 + 328 + /* convert R and S parts of the signature */ 329 + mox_ecc_number_to_bin(signature, rwtm->buf + offset_r); 330 + mox_ecc_number_to_bin(signature + MOX_ECC_NUM_LEN, rwtm->buf + offset_s); 331 + 332 + return 0; 333 + } 334 + 335 + static const void *mox_rwtm_get_public_key(const struct key *key) 336 + { 337 + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); 338 + 339 + return rwtm->pubkey; 340 + } 341 + 342 + static const struct turris_signing_key_subtype mox_signing_key_subtype = { 343 + .key_size = MOX_ECC_NUM_BITS, 344 + .data_size = SHA512_DIGEST_SIZE, 345 + .sig_size = MOX_ECC_SIG_LEN, 346 + .public_key_size = MOX_ECC_PUBKEY_LEN, 347 + .hash_algo = "sha512", 348 + .get_public_key = mox_rwtm_get_public_key, 349 + .sign = mox_rwtm_sign, 350 + }; 351 + 352 + static int mox_register_signing_key(struct mox_rwtm *rwtm) 353 + { 354 + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; 355 + struct device *dev = rwtm_dev(rwtm); 356 + int ret; 357 + 358 + ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false); 359 + if (ret == -ENODATA) { 360 + dev_warn(dev, "Board has no public key burned!\n"); 361 + } else if (ret == -EOPNOTSUPP) { 362 + dev_notice(dev, 363 + "Firmware does not support the ECDSA_PUB_KEY command\n"); 364 + } else if (ret < 0) { 365 + return ret; 366 + } else { 367 + char sn[17] = "unknown"; 368 + char desc[46]; 369 + 370 + if (rwtm->has_board_info) 371 + sprintf(sn, "%016llX", rwtm->serial_number); 372 + 373 + sprintf(desc, "Turris MOX SN %s rWTM ECDSA key", sn); 374 + 375 + mox_ecc_public_key_to_bin(rwtm->pubkey, ret, reply->status); 376 + 377 + ret = devm_turris_signing_key_create(dev, 378 + &mox_signing_key_subtype, 379 + desc); 380 + if (ret) 381 + return dev_err_probe(dev, ret, 382 + "Cannot create signing key\n"); 383 + } 384 + 385 + return 0; 386 + } 387 + 388 + #else /* CONFIG_TURRIS_MOX_RWTM_KEYCTL */ 389 + 390 + static inline int mox_register_signing_key(struct mox_rwtm *rwtm) 391 + { 392 + return 0; 393 + } 394 + 395 + #endif /* !CONFIG_TURRIS_MOX_RWTM_KEYCTL */ 396 + 281 397 static void rwtm_devm_mbox_release(void *mbox) 282 398 { 283 399 mbox_free_channel(mbox); ··· 460 308 ret = mox_get_board_info(rwtm); 461 309 if (ret < 0) 462 310 dev_warn(dev, "Cannot read board information: %i\n", ret); 311 + 312 + ret = mox_register_signing_key(rwtm); 313 + if (ret < 0) 314 + return ret; 463 315 464 316 ret = check_get_random_support(rwtm); 465 317 if (ret < 0) {