// SPDX-License-Identifier: GPL-2.0 /* * Some of CZ.NIC's Turris devices support signing messages with a per-device unique asymmetric * cryptographic key that was burned into the device at manufacture. * * This helper module exposes this message signing ability via the keyctl() syscall. Upon load, it * creates the `.turris-signing-keys` keyring. A device-specific driver then has to create a signing * key by calling devm_turris_signing_key_create(). * * 2025 by Marek BehĂșn */ #include #include #include #include #include #include #include #include #include #include static int turris_signing_key_instantiate(struct key *key, struct key_preparsed_payload *payload) { return 0; } static void turris_signing_key_describe(const struct key *key, struct seq_file *m) { const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key); if (!subtype) return; seq_printf(m, "%s: %*phN", key->description, subtype->public_key_size, subtype->get_public_key(key)); } static long turris_signing_key_read(const struct key *key, char *buffer, size_t buflen) { const struct turris_signing_key_subtype *subtype = dereference_key_rcu(key); if (!subtype) return -EIO; if (buffer) { if (buflen > subtype->public_key_size) buflen = subtype->public_key_size; memcpy(buffer, subtype->get_public_key(key), subtype->public_key_size); } return subtype->public_key_size; } static bool turris_signing_key_asym_valid_params(const struct turris_signing_key_subtype *subtype, const struct kernel_pkey_params *params) { if (params->encoding && strcmp(params->encoding, "raw")) return false; if (params->hash_algo && strcmp(params->hash_algo, subtype->hash_algo)) return false; return true; } static int turris_signing_key_asym_query(const struct kernel_pkey_params *params, struct kernel_pkey_query *info) { const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key); if (!subtype) return -EIO; if (!turris_signing_key_asym_valid_params(subtype, params)) return -EINVAL; info->supported_ops = KEYCTL_SUPPORTS_SIGN; info->key_size = subtype->key_size; info->max_data_size = subtype->data_size; info->max_sig_size = subtype->sig_size; info->max_enc_size = 0; info->max_dec_size = 0; return 0; } static int turris_signing_key_asym_eds_op(struct kernel_pkey_params *params, const void *in, void *out) { const struct turris_signing_key_subtype *subtype = dereference_key_rcu(params->key); int err; if (!subtype) return -EIO; if (!turris_signing_key_asym_valid_params(subtype, params)) return -EINVAL; if (params->op != kernel_pkey_sign) return -EOPNOTSUPP; if (params->in_len != subtype->data_size || params->out_len != subtype->sig_size) return -EINVAL; err = subtype->sign(params->key, in, out); if (err) return err; return subtype->sig_size; } static struct key_type turris_signing_key_type = { .name = "turris-signing-key", .instantiate = turris_signing_key_instantiate, .describe = turris_signing_key_describe, .read = turris_signing_key_read, .asym_query = turris_signing_key_asym_query, .asym_eds_op = turris_signing_key_asym_eds_op, }; static struct key *turris_signing_keyring; static void turris_signing_key_release(void *key) { key_unlink(turris_signing_keyring, key); key_put(key); } int devm_turris_signing_key_create(struct device *dev, const struct turris_signing_key_subtype *subtype, const char *desc) { struct key *key; key_ref_t kref; kref = key_create(make_key_ref(turris_signing_keyring, true), turris_signing_key_type.name, desc, NULL, 0, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH, KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA); if (IS_ERR(kref)) return PTR_ERR(kref); key = key_ref_to_ptr(kref); key->payload.data[1] = dev; rcu_assign_keypointer(key, subtype); return devm_add_action_or_reset(dev, turris_signing_key_release, key); } EXPORT_SYMBOL_GPL(devm_turris_signing_key_create); static int turris_signing_key_init(void) { int err; err = register_key_type(&turris_signing_key_type); if (err) return err; turris_signing_keyring = keyring_alloc(".turris-signing-keys", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH, KEY_ALLOC_BUILT_IN | KEY_ALLOC_SET_KEEP | KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(turris_signing_keyring)) { pr_err("Cannot allocate Turris keyring\n"); unregister_key_type(&turris_signing_key_type); return PTR_ERR(turris_signing_keyring); } return 0; } module_init(turris_signing_key_init); static void turris_signing_key_exit(void) { key_put(turris_signing_keyring); unregister_key_type(&turris_signing_key_type); } module_exit(turris_signing_key_exit); MODULE_AUTHOR("Marek Behun "); MODULE_DESCRIPTION("CZ.NIC's Turris signing key helper"); MODULE_LICENSE("GPL");