Conversation

Sitting back and looking at my TPM Keyfile code and I'm getting super annoyed at the lack of optional parameters in Go.

None of the design patterns people opt for to solve this is.. just not great.

3
0
0

@Foxboron maybe you know all the solutions, from top of my head:
- use struct for many/complicated parameter(-combinations),
- use separate functions to embed defaults,
- use closures to provide vararg with function-type that perform (complicated) changes to internal struct-config.

Did you know/consider the closures too? Might apply, from what I can make/interpret of your problem.

1
0
0

@cobratbq
All of them are terrible UX for a public API, sadly.

1
0
0

@Foxboron the vararg-closures are predefined, so are strict and in dev-control. Arguably, the caller needs to understand the pattern -as is always the case- but you can maintain as much control as you like and provide exactly what you want.

1
0
0

@cobratbq

Sure, it is the implementation I'm probably landing on for this. But it's very far from great UX.

0
0
0

@reto
This is how I usually solve it too. For a public Api with many different combinations of valid config inputs it does the job well.
@Foxboron

1
0
0

@bmarinov @reto

I'm so not happy about this design pattern at all :/

1
0
0

@quitelost @reto

They are the same thing, yes

0
0
0

@Foxboron
I prefer not having optional arguments though. The amount of lazy APIs (internal apps or libs) I've experienced was just staggering that it made me dislike the feature in general. I'm sure it has its uses, but the cost for everyone who gets to clean up or deal with the mess is just too high.

1
0
0

@bmarinov
The issue is that the lack of optional arguments, or function overloading for that matter, is that it makes it more complicated to model define these things.

I could specify *everything* in the function signature, but then it would break the API om update and the user would need to fill out N number of empty/nil values to get started. And that feels very very unergonomic.

0
0
0

@Foxboron Kernel has IMHO nice approach for parsing ASN.1 written a long time ago by David Howells but still legit, safe and fast:

  1. scripts/asn1_compiler.c: compiles ASN.1 as bytecode with callbacks called ā€œactionsā€ written in C.
  2. lib/asn1_decoder.c: there’s a function that takes a blob containing bytecode program for a particular ASN.1 definition and buffer containing der-file. Then the function interprets the whole thing using two arrays: machine[] for opcodes and actions[] for callbacks.

Parsers become quite strict and safe as any instance of parser does not try to parse every possible ASN.1 but fails on anything else than one single particular ASN1 format.

I recently went through ASN.1 crates for Rust with the idea of doing experimental CONFIG_ASN1_RUST flag, but none of the crates I found (looked e.g. every single one from crates.io) was not even near the quality of this elegant approach. Well-designed C can be safer than improperly designed Rust, and this good example of it.

If I did an ASN.1 parser in user space with Go, I’d use the gist of this design even on that.

The project itself could have bunch for ASN.1 files in its repository and the build process would pre-compile the parsers for each one of them. Then based on the OID, the decoder could just pick the right parser from cache indexed by the OID.

Thought to share this because I love the design and have not seen this idea implemented in user space.

1
0
0

@Foxboron For encoding I actually prefer no library at all, e.g. this is first PoC actually first ECDSA signature generator existing in Linux kernel (existing ECDSA code is AFAIK just verification) although for now implemented only inside tpm2_key_ecdsa:

/* Encode the ASN.1 signature: */
#define TPM2_KEY_ECDSA_SIG_SIZE		(2 + 2 * (2 + SHA256_DIGEST_SIZE) + r_0 + s_0)
	pr_info("sig_size=%d\n", TPM2_KEY_ECDSA_SIG_SIZE);
	ptr[0] = 0x30; /* SEQUENCE */
	ptr[1] = TPM2_KEY_ECDSA_SIG_SIZE - 2;
#define TPM2_KEY_ECDSA_SIG_R_TAG	2
#define TPM2_KEY_ECDSA_SIG_R_SIZE	3
#define TPM2_KEY_ECDSA_SIG_R_BODY	4
	ptr[TPM2_KEY_ECDSA_SIG_R_TAG] = 0x02; /* INTEGER */
	ptr[TPM2_KEY_ECDSA_SIG_R_SIZE] = SHA256_DIGEST_SIZE + r_0;
	ptr[TPM2_KEY_ECDSA_SIG_R_BODY] = 0x00; /* maybe dummy write */
	memcpy(&ptr[TPM2_KEY_ECDSA_SIG_R_BODY + r_0], r, SHA256_DIGEST_SIZE);
#define TPM2_KEY_ECDSA_SIG_S_TAG	(4 + r_0 + SHA256_DIGEST_SIZE)
#define TPM2_KEY_ECDSA_SIG_S_SIZE	(5 + r_0 + SHA256_DIGEST_SIZE)
#define TPM2_KEY_ECDSA_SIG_S_BODY	(6 + r_0 + SHA256_DIGEST_SIZE)
	ptr[TPM2_KEY_ECDSA_SIG_S_TAG] = 0x02; /* INTEGER */
	ptr[TPM2_KEY_ECDSA_SIG_S_SIZE] = SHA256_DIGEST_SIZE + s_0;
	ptr[TPM2_KEY_ECDSA_SIG_S_BODY] = 0x00; /* maybe dummy write */
	memcpy(&ptr[TPM2_KEY_ECDSA_SIG_S_BODY + s_0], s, SHA256_DIGEST_SIZE);
	ret = TPM2_KEY_ECDSA_SIG_SIZE;

For encoding despite ugly it is just nice to be able to see the byte dump that gets generated at one site without having to cross-reference. I tried first asn1_encode_* functions but came into conclusion that they do more harm than good for my use because you only need to stare one location when you manually verify or debug it :-)

1
0
1

Jarkko Sakkinen

Edited 1 year ago

Sorry for spamming I’ve spent with ASN.1 for last 9-10 days almost without sleep writing asymmetric keys code and thinking mostly of this file format and tpm protocol shenanigans… Ongoing patch set it is here https://lore.kernel.org/linux-crypto/20240528210823.28798-1-jarkko@kernel.org/.

My biggest bummer in TCG ECC support is the lack of TPM_ECC_SECP_P256_K1 curve but I’ve already started campaign to add it to the TCG Algorithm Registry. I.e. crypto wallet inside TPM curve. I sort of pushed this because I really wanted to try if I could put Ethereum keys to TPM but I guess it was good that I did not because it kept the motivation high, and sleep low ;-)

1
0
0
This effort is spun of when I met some Ethereum Foundation ppl that I just happen to know outside of that spectrum, and they asked me to come to tell something about Linux to Ethprague (next weekend), and i needed a topic for that. I own 0 ETH and know only some rudimentary info how it works. As a follow-up for this patch set I'm planning to do p256k1 curve software implementation for crypto so that at least you can root those keys to a TPM.
0
0
0