Keyring Link to heading

keyring a safe way to work with sensitive data in memory. From link, It’s described as follows:

This service allows cryptographic keys, authentication tokens, cross-domain user mappings, and similar to be cached in the kernel for the use of filesystems and other kernel services.

In this context, keys represent units of cryptographic data, authentication tokens, keyrings, etc.. These are represented in the kernel by struct key.

keyring has user land interface using system calls but this document will focus on kernel APIs. The following snippet shows a small example to use keep x509 cert.

	struct key *keyring;
	char keyring_name[17];
	char root_serial[26];
	key_ref_t key_ref;
	size_t offset = 0;
	int rc, length;

	snprintf(keyring_name, sizeof(keyring_name), ".%d",
		 current->pid);

	keyring = keyring_alloc(keyring_name, KUIDT_INIT(0), KGIDT_INIT(0),
				current_cred(), KEY_POS_ALL,
				KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_SET_KEEP,
				NULL, NULL);

	...

	while (offset < total_length) {
		rc = x509_get_certificate_length(certs + offset,
						 total_length - offset);

		length = rc;
		key_ref = key_create_or_update(make_key_ref(keyring, true),
					       "asymmetric", NULL,
					       certs + offset, length,
					       (KEY_POS_ALL & ~KEY_POS_SETATTR),
					       KEY_ALLOC_NOT_IN_QUOTA);
        ...

		key_put(keyring->restrict_link->key);
		keyring->restrict_link->key = key_ref_to_ptr(key_ref);

		offset += length;
	}

	leaf_key = key_get(keyring->restrict_link->key);

Starting with keyring_alloc to create keyring

struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
                          const struct cred *cred,
                          key_perm_t perm,
                          struct key_restriction *restrict_link,
                          unsigned long flags,
                          struct key *dest);

key_create_or_update create key reference and link to keyring created above.

extern key_ref_t key_create_or_update(key_ref_t keyring,
				      const char *type,
				      const char *description,
				      const void *payload,
				      size_t plen,
				      key_perm_t perm,
				      unsigned long flags);

static inline key_ref_t make_key_ref(const struct key *key,
				     bool possession)
{
	return (key_ref_t) ((unsigned long) key | possession);
}
static inline struct key *key_ref_to_ptr(const key_ref_t key_ref)
{
	return (struct key *) ((unsigned long) key_ref & ~1UL);
}
struct key_restriction {
	key_restrict_link_func_t check;
	struct key *key;
	struct key_type *keytype;
};
struct key {
	refcount_t		usage;		
	
	...
	
	union {
		struct keyring_index_key index_key;
		struct {
			unsigned long	hash;
			unsigned long	len_desc;
			struct key_type	*type;		/* type of key */
			struct key_tag	*domain_tag;	/* Domain of operation */
			char		*description;
		};
	};

	union {
		union key_payload payload;
		struct {
			/* Keyring bits */
			struct list_head name_link;
			struct assoc_array keys;
		};
	};

	struct key_restriction *restrict_link;
};
typedef struct __key_reference_with_attributes *key_ref_t;

Hashing Link to heading

The linux kernel have hash utility that supports multiple alogs. There are 3 API to init, update and calculate.

  • crypto_shash_init
  • crypto_shash_update
  • crypto_shash_final

There is also crypto_shash_digest which is defines as

This function is a “short-hand” for the function calls of crypto_shash_init, crypto_shash_update and crypto_shash_final. The parameters have the same meaning as discussed for those separate three functions.

int 	(struct shash_desc * desc, const u8 * data, unsigned int len)

Updates the message digest state of the operational state handle.

int crypto_shash_final(struct shash_desc * desc, u8 * out)

Finalize the message digest operation and create the message digest based on all data added to the cipher handle. The message digest is placed into the output buffer. The caller must ensure that the output buffer is large enough by using crypto_shash_digestsize.

This is an example from SPDM digest calculation

	struct crypto_shash *shash;
	struct shash_desc *desc;
	size_t h;

	alg_name = "sha384";

	shash = crypto_alloc_shash(alg_name, 0, 0);

	desc = kzalloc(sizeof(*desc) +
				   crypto_shash_descsize(shash),
				   GFP_KERNEL);

	desc->tfm = shash;

	h = crypto_shash_digestsize(shash);

	rc = crypto_shash_init(desc);

	rc = crypto_shash_update(desc, (u8 *)req, req_sz);

	crypto_shash_final(desc, digest);
	if (version >= 0x12) {
		message = kmalloc(h + sizeof(spdm_prefix), GFP_KERNEL);
		memcpy(message, spdm_prefix, sizeof(spdm_prefix));
		memcpy(message + sizeof(spdm_prefix), digest, h);
		/*
		 * Not all 1.2 supported Asymmetric functions need this hashed
		 * but all the ones supported here do.
		 */
		rc = crypto_shash_digest(desc, message,
					 h + sizeof(spdm_prefix), digest);
		...

Public key verification Link to heading

verify_signature takes 2 arguments to verify

  • The signature
  • public_key_signature structure

public_key_signature contains the information needed which are hash algorithm and signing algorithm.

struct public_key_signature {
	struct asymmetric_key_id *auth_ids[3];
	u8 *s;			/* Signature */
	u8 *digest;
	u32 s_size;		/* Number of bytes in signature */
	u32 digest_size;	/* Number of bytes in digest */
	const char *pkey_algo;
	const char *hash_algo;
	const char *encoding;
	const void *data;
	unsigned int data_size;
};
extern int verify_signature(const struct key *,
			    const struct public_key_signature *);

This is an example from SPDM verification using pcks1 and sha384.

	const struct asymmetric_key_ids *ids;
	struct public_key_signature sig = {};
	unsigned char buffer2[128] = {};

	sig.s = sig_ptr;
	sig.s_size = s;
	sig.encoding = "pkcs1";

	sig.digest = digest;
	sig.digest_size = digest_size;
	ids = asymmetric_key_ids(leaf_key);
	sig.auth_ids[0] = ids->id[0];
	sig.auth_ids[1] = ids->id[1];

	sig.hash_algo = "sha384";

	rc = verify_signature(leaf_key, &sig);